App 2.19.6: remove the CarPlay full-screen switch and the export button

CarPlay's 1920x640 is fixed in Allgo's system app. On start the car
build removes the CarPlay entry from policy_control if the switch was
left on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LhzAVaUyqcw5PkGjz4mWW6
This commit is contained in:
2026-10-23 22:02:15 +02:00
co-authored by Claude Opus 5.5
parent 4c224dcb3b
commit e6f8258203
13 changed files with 37 additions and 382 deletions
@@ -1,234 +0,0 @@
package com.evsuite.abrp;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.os.Environment;
import android.util.Log;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
/**
* Read-only diagnostic for the CarPlay resolution question: copies what decides CarPlay's size
* to a folder on the USB stick, for reading on a computer. It reads the car and writes only to
* the stick.
*
* What it writes to {@code <stick>/Android/data/<package>/files/nilsmg4-carplay-<time>/}:
* <ul>
* <li>the CarPlay app's APK files and its own native libraries;</li>
* <li>libraries and configuration files under /system, /vendor, /odm and /product whose path
* names CarPlay (see {@link #isCarPlayPath});</li>
* <li>the display-related system properties, never identifiers ({@link #keepProperty});</li>
* <li>the window manager, SurfaceFlinger and display state, captured while CarPlay is in front.</li>
* </ul>
* Only the system user (the car build) may run dumpsys; elsewhere those files say why not.
*/
final class CarPlayExport {
private static final String TAG = "EVABRP.CarPlayExport";
static final String CARPLAY_PACKAGE = "com.allgo.carplay.service";
private static final long MAX_FILE_BYTES = 64L << 20;
private static final int MAX_FOUND_FILES = 200;
private static final String[] SEARCH_ROOTS = {
"/system/etc", "/system/lib", "/system/lib64", "/system/priv-app", "/system/app",
"/vendor/etc", "/vendor/lib", "/vendor/lib64", "/vendor/app", "/vendor/priv-app",
"/odm/etc", "/product/etc", "/product/app", "/product/priv-app",
};
private static final Pattern CARPLAY_PATH = Pattern.compile("(?i)carplay|allgo|iap2|airplay");
private static final Pattern PROPERTY_KEEP = Pattern.compile(
"(?i)carplay|allgo|iap|airplay|display|lcd|screen|resolution|density|surface|hwui|"
+ "\\bsf\\.|\\.sf\\.|width|height|ro\\.build\\.(version|display|fingerprint|id)|"
+ "vehicle_type|projection");
private static final Pattern PROPERTY_DROP = Pattern.compile(
"(?i)serial|vin\\b|\\.vin|imei|iccid|imsi|mac|bt\\.addr|bdaddr|wifi|ssid|password|token|"
+ "key|account|phone|msisdn|sim");
private CarPlayExport() {}
/** A path whose name says it belongs to CarPlay. */
static boolean isCarPlayPath(String path) {
return path != null && CARPLAY_PATH.matcher(path).find();
}
/** A {@code [key]: [value]} getprop line worth exporting: display-related, never an identifier. */
static boolean keepProperty(String getpropLine) {
if (getpropLine == null) return false;
int end = getpropLine.indexOf("]:");
String key = end > 0 ? getpropLine.substring(0, end) : getpropLine;
return PROPERTY_KEEP.matcher(key).find() && !PROPERTY_DROP.matcher(key).find();
}
/** Runs the export; returns one line for the screen. Blocking: call it off the main thread. */
static String run(Context context) {
File stick = stickFolder(context);
if (stick == null) {
Log.w(TAG, "no USB stick");
return "No USB stick found";
}
String stamp = new SimpleDateFormat("yyyyMMdd-HHmmss", Locale.US).format(new Date());
File out = new File(stick, "nilsmg4-carplay-" + stamp);
if (!out.mkdirs()) {
Log.w(TAG, "cannot create " + out);
return "Could not create " + out;
}
List<String> notes = new ArrayList<>();
int copied = 0;
// The live state first, while CarPlay is still in front.
command(out, "dumpsys-window.txt", notes, "dumpsys", "window", "windows");
command(out, "dumpsys-surfaceflinger.txt", notes, "dumpsys", "SurfaceFlinger");
command(out, "dumpsys-display.txt", notes, "dumpsys", "display");
command(out, "dumpsys-activity.txt", notes, "dumpsys", "activity", "activities");
command(out, "wm.txt", notes, "sh", "-c", "wm size; wm density");
properties(out, notes);
try {
ApplicationInfo app = context.getPackageManager().getApplicationInfo(CARPLAY_PACKAGE, 0);
notes.add("uid " + app.uid + ", flags 0x" + Integer.toHexString(app.flags)
+ ", source " + app.sourceDir + ", native " + app.nativeLibraryDir);
File apkDir = new File(out, "apk");
copied += copy(new File(app.sourceDir), apkDir, notes);
if (app.splitSourceDirs != null) {
for (String split : app.splitSourceDirs) copied += copy(new File(split), apkDir, notes);
}
File[] libs = app.nativeLibraryDir == null ? null : new File(app.nativeLibraryDir).listFiles();
if (libs != null) {
for (File lib : libs) copied += copy(lib, new File(out, "native"), notes);
}
} catch (Exception e) {
notes.add("CarPlay app not found: " + e);
}
List<File> found = new ArrayList<>();
for (String root : SEARCH_ROOTS) find(new File(root), 0, found);
for (File f : found) {
copied += copy(f, new File(out, "found" + f.getParent()), notes);
}
notes.add(found.size() + " CarPlay-named files under the system partitions");
write(new File(out, "notes.txt"), String.join("\n", notes) + "\n");
String result = "Exported " + copied + " files to " + out;
Log.i(TAG, result);
return result;
}
/**
* This app's own folder on the removable volume, or on any volume (the emulator) when there is
* none: <stick>/Android/data/<package>/files. Android 9 lets an app write there and nowhere
* else on a removable volume — the stick's top folder is read-only to it.
*/
private static File stickFolder(Context context) {
File fallback = null;
for (File dir : context.getExternalFilesDirs(null)) {
if (dir == null || UsbVolume.root(dir) == null) continue;
boolean removable;
try {
removable = Environment.isExternalStorageRemovable(dir);
} catch (IllegalArgumentException e) {
removable = false;
}
if (removable) return dir;
if (fallback == null) fallback = dir;
}
return fallback;
}
private static void find(File dir, int depth, List<File> found) {
if (depth > 5 || found.size() >= MAX_FOUND_FILES) return;
File[] entries = dir.listFiles();
if (entries == null) return;
for (File f : entries) {
if (found.size() >= MAX_FOUND_FILES) return;
if (f.isDirectory()) {
if (isCarPlayPath(f.getName())) {
File[] inner = f.listFiles();
if (inner != null) for (File g : inner) if (g.isFile()) found.add(g);
}
find(f, depth + 1, found);
} else if (isCarPlayPath(f.getName())) {
found.add(f);
}
}
}
private static void properties(File out, List<String> notes) {
String all = exec(notes, "getprop");
if (all == null) return;
StringBuilder kept = new StringBuilder();
for (String line : all.split("\n")) if (keepProperty(line)) kept.append(line).append('\n');
write(new File(out, "properties.txt"), kept.toString());
}
private static void command(File out, String name, List<String> notes, String... argv) {
String text = exec(notes, argv);
if (text != null) write(new File(out, name), text);
}
private static String exec(List<String> notes, String... argv) {
Process p = null;
try {
p = new ProcessBuilder(argv).redirectErrorStream(true).start();
p.getOutputStream().close();
byte[] bytes = readAll(p.getInputStream(), 8 << 20);
if (!p.waitFor(20, TimeUnit.SECONDS)) notes.add(String.join(" ", argv) + ": timed out");
return new String(bytes, StandardCharsets.UTF_8);
} catch (Exception e) {
notes.add(String.join(" ", argv) + ": " + e);
return null;
} finally {
if (p != null) p.destroy();
}
}
private static int copy(File source, File dir, List<String> notes) {
if (!source.isFile()) return 0;
if (source.length() > MAX_FILE_BYTES) {
notes.add("skipped (too large): " + source + " " + source.length());
return 0;
}
if (!dir.isDirectory() && !dir.mkdirs()) {
notes.add("no folder for " + source);
return 0;
}
try (InputStream in = new FileInputStream(source);
OutputStream os = new FileOutputStream(new File(dir, source.getName()))) {
byte[] buffer = new byte[64 * 1024];
int n;
while ((n = in.read(buffer)) != -1) os.write(buffer, 0, n);
return 1;
} catch (IOException e) {
notes.add("not copied: " + source + ": " + e.getMessage());
return 0;
}
}
private static byte[] readAll(InputStream in, int max) throws IOException {
java.io.ByteArrayOutputStream buffer = new java.io.ByteArrayOutputStream();
byte[] chunk = new byte[16 * 1024];
int n;
while ((n = in.read(chunk)) != -1 && buffer.size() < max) buffer.write(chunk, 0, n);
return buffer.toByteArray();
}
private static void write(File file, String text) {
try (OutputStream os = new FileOutputStream(file)) {
os.write(text.getBytes(StandardCharsets.UTF_8));
} catch (IOException e) {
Log.w(TAG, "not written: " + file, e);
}
}
}
@@ -242,46 +242,9 @@ public class MainActivity extends AppCompatActivity {
if (r.statusBarPx != null) {
sb.append('\n').append(getString(R.string.foreground_status_bar, r.statusBarPx));
}
if (lastCarPlayExport != null) {
sb.append('\n').append(getString(R.string.carplay_export_last, lastCarPlayExport));
}
return sb.toString();
}
/** The last CarPlay export's outcome, for the Foreground apps card; null before the first. */
private static volatile String lastCarPlayExport;
/**
* Waits 20 seconds, so the driver can bring CarPlay to the front, then exports what decides
* its size to the USB stick (CarPlayExport). Runs in the app's process, which the upload
* service keeps alive while this activity is in the background.
*/
private void startCarPlayExport() {
android.content.Context app = getApplicationContext();
android.os.Handler main = new android.os.Handler(android.os.Looper.getMainLooper());
Toast.makeText(app, R.string.carplay_export_started, Toast.LENGTH_LONG).show();
new Thread(() -> {
try {
Thread.sleep(20_000);
} catch (InterruptedException e) {
return;
}
main.post(() -> Toast.makeText(app, R.string.carplay_export_running, Toast.LENGTH_SHORT).show());
String result;
try {
result = CarPlayExport.run(app);
} catch (Throwable t) {
result = "Export failed: " + t;
}
lastCarPlayExport = result;
final String shown = result;
main.post(() -> {
Toast.makeText(app, shown, Toast.LENGTH_LONG).show();
refreshForeground();
});
}, "carplay-export").start();
}
/** Refreshes state + call log while the screen is visible. */
private final android.os.Handler uiHandler = new android.os.Handler(android.os.Looper.getMainLooper());
private final Runnable uiRefresh = new Runnable() {
@@ -335,7 +298,6 @@ public class MainActivity extends AppCompatActivity {
logPane.findViewById(R.id.flush_now_button).setOnClickListener(v -> manualFlush());
foregroundText = logPane.findViewById(R.id.foreground_text);
logPane.findViewById(R.id.foreground_refresh_button).setOnClickListener(v -> refreshForeground());
logPane.findViewById(R.id.carplay_export_button).setOnClickListener(v -> startCarPlayExport());
setUpPager();
// Back on the page that was open when the activity was recreated (accent colour change).
@@ -1105,54 +1067,27 @@ public class MainActivity extends AppCompatActivity {
// ---------- Upload page ----------
/** The car's CarPlay app, as the Logs tab's foreground list showed it on SWI68. */
private static final String CARPLAY_PACKAGE = "com.allgo.carplay.service";
/**
* Experiment: forces CarPlay into immersive mode through Android 9's policy_control setting.
* The switch shows what the setting holds, not a preference, and switching off removes only
* our entry. Writing it needs WRITE_SECURE_SETTINGS, which only the car build (system user)
* has; elsewhere the switch is disabled.
* App 2.19.2–2.19.5 had a "Full screen for CarPlay" experiment that added CarPlay to Android 9's
* policy_control setting; CarPlay's 1920×640 picture turned out to be fixed in its own app, so
* the switch is gone. This takes our entry out again where the switch was left on, and leaves
* anything else in the setting alone. Only the car build (system user) may write it.
*/
private void bindCarPlayFullScreen() {
com.google.android.material.switchmaterial.SwitchMaterial sw =
settingsPane.findViewById(R.id.carplay_full_screen_switch);
boolean mayWrite = checkSelfPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
== android.content.pm.PackageManager.PERMISSION_GRANTED;
String current = android.provider.Settings.Global.getString(getContentResolver(), PolicyControl.KEY);
sw.setOnCheckedChangeListener(null);
sw.setChecked(PolicyControl.hasFull(current, CARPLAY_PACKAGE));
sw.setEnabled(mayWrite);
if (!mayWrite) {
((TextView) settingsPane.findViewById(R.id.carplay_full_screen_hint))
.setText(R.string.hint_carplay_full_screen_unavailable);
return;
}
sw.setOnCheckedChangeListener((btn, checked) -> diagReader.execute(() -> {
boolean ok;
private void clearCarPlayFullScreen() {
if (checkSelfPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
!= android.content.pm.PackageManager.PERMISSION_GRANTED) return;
diagReader.execute(() -> {
try {
android.content.ContentResolver cr = getContentResolver();
String before = android.provider.Settings.Global.getString(cr, PolicyControl.KEY);
String after = checked ? PolicyControl.withFull(before, CARPLAY_PACKAGE)
: PolicyControl.withoutFull(before, CARPLAY_PACKAGE);
android.provider.Settings.Global.putString(cr, PolicyControl.KEY, after);
String now = android.provider.Settings.Global.getString(cr, PolicyControl.KEY);
ok = PolicyControl.hasFull(now, CARPLAY_PACKAGE) == checked;
android.util.Log.i("EVABRP.Settings", "policy_control " + (checked ? "on" : "off") + ": " + now);
if (!PolicyControl.hasFull(before, PolicyControl.CARPLAY_PACKAGE)) return;
android.provider.Settings.Global.putString(cr, PolicyControl.KEY,
PolicyControl.withoutFull(before, PolicyControl.CARPLAY_PACKAGE));
android.util.Log.i("EVABRP.Settings", "CarPlay entry removed from policy_control");
} catch (RuntimeException e) {
android.util.Log.w("EVABRP.Settings", "policy_control not written", e);
ok = false;
android.util.Log.w("EVABRP.Settings", "policy_control not cleared", e);
}
final boolean written = ok;
runOnUiThread(() -> {
if (isDestroyed()) return;
if (!written) {
Toast.makeText(this, R.string.carplay_full_screen_failed, Toast.LENGTH_LONG).show();
bindCarPlayFullScreen();
}
refreshForeground();
});
}));
});
}
private void bindServicePane() {
@@ -1168,7 +1103,7 @@ public class MainActivity extends AppCompatActivity {
prefs.edit().putBoolean(UploadSettings.KEY_FULL_SCREEN, checked).apply();
applyFullScreen();
});
bindCarPlayFullScreen();
clearCarPlayFullScreen();
liveTelemetrySwitch = servicePane.findViewById(R.id.abrp_upload_switch);
intervalChoices = servicePane.findViewById(R.id.interval_choices);
boostSwitch = servicePane.findViewById(R.id.boost_switch);
@@ -7,14 +7,16 @@ import java.util.Map;
/**
* The value of Android 9's {@code policy_control} global setting, which forces other apps into
* immersive mode: {@code immersive.full=pkg1,pkg2:immersive.status=pkg3}. This adds or removes
* one package in {@code immersive.full} and leaves every other entry as it was, so switching
* our experiment off restores what was there before. Pure Java, so it is unit-tested.
* immersive mode: {@code immersive.full=pkg1,pkg2:immersive.status=pkg3}. Adds or removes one
* package in {@code immersive.full} and leaves every other entry as it was. Kept to clear the
* entry the removed CarPlay experiment wrote. Pure Java, so it is unit-tested.
*/
final class PolicyControl {
static final String KEY = "policy_control";
static final String FULL = "immersive.full";
/** The car's CarPlay app, which the removed full-screen experiment added. */
static final String CARPLAY_PACKAGE = "com.allgo.carplay.service";
private PolicyControl() {}
@@ -10,7 +10,6 @@
<LinearLayout android:layout_width="0dp" android:layout_weight="1" android:layout_height="wrap_content" android:orientation="vertical" android:paddingStart="24dp" android:paddingEnd="24dp" android:paddingTop="16dp" android:paddingBottom="16dp" android:background="@drawable/bg_glass">
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center_vertical">
<TextView android:layout_width="0dp" android:layout_weight="1" android:layout_height="wrap_content" android:text="@string/foreground_title" android:textAppearance="@style/Text.EV.Eyebrow" />
<com.google.android.material.button.MaterialButton android:id="@+id/carplay_export_button" style="@style/Widget.EV.Button.Outlined" android:layout_width="wrap_content" android:layout_height="48dp" android:layout_marginEnd="8dp" android:minWidth="0dp" android:paddingStart="16dp" android:paddingEnd="16dp" android:text="@string/btn_carplay_export" />
<com.google.android.material.button.MaterialButton android:id="@+id/foreground_refresh_button" style="@style/Widget.EV.Button.Outlined" android:layout_width="48dp" android:layout_height="48dp" android:minWidth="0dp" android:padding="0dp" android:contentDescription="@string/btn_refresh" app:icon="@drawable/ic_refresh" app:iconGravity="textStart" app:iconPadding="0dp" app:iconTint="?attr/evAccent" />
</LinearLayout>
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="4dp" android:text="@string/foreground_hint" android:textAppearance="@style/Text.EV.Secondary" />
@@ -10,8 +10,6 @@
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/hint_full_screen" android:textAppearance="@style/Text.EV.Secondary" />
<com.google.android.material.switchmaterial.SwitchMaterial android:id="@+id/voice_key_switch" android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="72dp" android:text="@string/label_voice_key" android:textAppearance="@style/Text.EV.Body" android:paddingTop="8dp" android:paddingBottom="8dp" app:useMaterialThemeColors="true" app:thumbTint="@color/switch_thumb" app:trackTint="@color/switch_track" android:textColor="@color/ev_text_primary" />
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/hint_voice_key" android:textAppearance="@style/Text.EV.Secondary" />
<com.google.android.material.switchmaterial.SwitchMaterial android:id="@+id/carplay_full_screen_switch" android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="72dp" android:text="@string/label_carplay_full_screen" android:textAppearance="@style/Text.EV.Body" android:paddingTop="8dp" android:paddingBottom="8dp" app:useMaterialThemeColors="true" app:thumbTint="@color/switch_thumb" app:trackTint="@color/switch_track" android:textColor="@color/ev_text_primary" />
<TextView android:id="@+id/carplay_full_screen_hint" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/hint_carplay_full_screen" android:textAppearance="@style/Text.EV.Secondary" />
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:baselineAligned="false">
<LinearLayout android:layout_width="0dp" android:layout_weight="2" android:layout_height="wrap_content" android:layout_marginEnd="12dp" android:orientation="vertical">
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/label_tire_unit" android:textAppearance="@style/Text.EV.Body" android:layout_marginTop="8dp" />
-1
View File
@@ -9,7 +9,6 @@
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="24dp" android:background="@drawable/bg_glass" android:layout_marginTop="16dp">
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center_vertical">
<TextView android:layout_width="0dp" android:layout_weight="1" android:layout_height="wrap_content" android:text="@string/foreground_title" android:textAppearance="@style/Text.EV.Eyebrow" />
<com.google.android.material.button.MaterialButton android:id="@+id/carplay_export_button" style="@style/Widget.EV.Button.Outlined" android:layout_width="wrap_content" android:layout_height="48dp" android:layout_marginEnd="8dp" android:minWidth="0dp" android:paddingStart="16dp" android:paddingEnd="16dp" android:text="@string/btn_carplay_export" />
<com.google.android.material.button.MaterialButton android:id="@+id/foreground_refresh_button" style="@style/Widget.EV.Button.Outlined" android:layout_width="48dp" android:layout_height="48dp" android:minWidth="0dp" android:padding="0dp" android:contentDescription="@string/btn_refresh" app:icon="@drawable/ic_refresh" app:iconGravity="textStart" app:iconPadding="0dp" app:iconTint="?attr/evAccent" />
</LinearLayout>
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="4dp" android:text="@string/foreground_hint" android:textAppearance="@style/Text.EV.Secondary" />
@@ -7,8 +7,6 @@
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/hint_full_screen" android:textAppearance="@style/Text.EV.Secondary" />
<com.google.android.material.switchmaterial.SwitchMaterial android:id="@+id/voice_key_switch" android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="72dp" android:text="@string/label_voice_key" android:textAppearance="@style/Text.EV.Body" android:paddingTop="8dp" android:paddingBottom="8dp" app:useMaterialThemeColors="true" app:thumbTint="@color/switch_thumb" app:trackTint="@color/switch_track" android:textColor="@color/ev_text_primary" />
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/hint_voice_key" android:textAppearance="@style/Text.EV.Secondary" />
<com.google.android.material.switchmaterial.SwitchMaterial android:id="@+id/carplay_full_screen_switch" android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="72dp" android:text="@string/label_carplay_full_screen" android:textAppearance="@style/Text.EV.Body" android:paddingTop="8dp" android:paddingBottom="8dp" app:useMaterialThemeColors="true" app:thumbTint="@color/switch_thumb" app:trackTint="@color/switch_track" android:textColor="@color/ev_text_primary" />
<TextView android:id="@+id/carplay_full_screen_hint" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/hint_carplay_full_screen" android:textAppearance="@style/Text.EV.Secondary" />
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/label_tire_unit" android:textAppearance="@style/Text.EV.Body" android:layout_marginTop="20dp" />
<com.google.android.material.textfield.TextInputLayout style="@style/Widget.EV.Dropdown" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="12dp">
<com.google.android.material.textfield.MaterialAutoCompleteTextView android:id="@+id/tire_unit_choices" android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="72dp" android:inputType="none" android:textSize="20sp" app:simpleItemLayout="@layout/dropdown_item" />
+1 -9
View File
@@ -114,10 +114,6 @@
<string name="label_full_screen">Full screen</string>
<string name="hint_full_screen">Hides the car\'s top and bottom bars while this app is open. Swipe in from the top or bottom edge to show them briefly, e.g. for the climate controls.</string>
<string name="label_carplay_full_screen">Full screen for CarPlay (experiment)</string>
<string name="hint_carplay_full_screen">Hides the car\'s bars over CarPlay too, from its next start. A black strip? Reconnect the phone. Off restores the setting.</string>
<string name="hint_carplay_full_screen_unavailable">Needs the car build: only a system app may change this.</string>
<string name="carplay_full_screen_failed">Could not change the CarPlay full-screen setting</string>
<string name="label_voice_key">Double-press the voice key to open this app</string>
<string name="hint_voice_key">Press the voice key on the steering wheel twice quickly. The voice assistant may still react to the presses.</string>
<string name="label_abrp_upload_enabled">Send live data</string>
@@ -174,11 +170,7 @@
<string name="update_log_title">Updates, newest first:</string>
<string name="btn_flush_now">Upload now</string>
<string name="foreground_title">Foreground apps</string>
<string name="btn_carplay_export">Export CarPlay data</string>
<string name="carplay_export_started">Open CarPlay now: the export to the USB stick starts in 20 seconds</string>
<string name="carplay_export_running">Exporting CarPlay data to the USB stick…</string>
<string name="carplay_export_last">Last export: %1$s</string>
<string name="foreground_hint">For the CarPlay full-screen test: open CarPlay, come back here, read its name off the list. Reads only.</string>
<string name="foreground_hint">Which app was in front and when, from Android\'s own record. Reads only.</string>
<string name="foreground_recent">Last 30 minutes, newest first:</string>
<string name="foreground_none">No app came to the front in the last 30 minutes.</string>
<string name="foreground_unavailable">Not readable in this build: only the car build (system app) may see other apps.</string>
@@ -1,43 +0,0 @@
package com.evsuite.abrp;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
/** What the CarPlay export takes along — pure JVM. */
public class CarPlayExportTest {
@Test
public void displayPropertiesAreKept() {
assertTrue(CarPlayExport.keepProperty("[ro.sf.lcd_density]: [160]"));
assertTrue(CarPlayExport.keepProperty("[persist.allgo.carplay.height]: [640]"));
assertTrue(CarPlayExport.keepProperty("[ro.build.mtk2712.vehicle_type]: [AS28]"));
assertTrue(CarPlayExport.keepProperty("[ro.build.display.id]: [SWI68-29958-1300R69]"));
}
@Test
public void identifiersNeverLeave() {
assertFalse(CarPlayExport.keepProperty("[ro.serialno]: [ABC123]"));
assertFalse(CarPlayExport.keepProperty("[persist.vehicle.vin]: [LSJ...]"));
assertFalse(CarPlayExport.keepProperty("[persist.carplay.wifi.ssid]: [MG4-1234]"));
assertFalse(CarPlayExport.keepProperty("[persist.carplay.bt.addr]: [00:11:22:33:44:55]"));
assertFalse(CarPlayExport.keepProperty("[ro.boot.serialno]: [x]"));
assertFalse(CarPlayExport.keepProperty(null));
}
@Test
public void unrelatedPropertiesStay() {
assertFalse(CarPlayExport.keepProperty("[dalvik.vm.heapsize]: [512m]"));
assertFalse(CarPlayExport.keepProperty("[persist.sys.timezone]: [Europe/Berlin]"));
}
@Test
public void carPlayPathsAreRecognised() {
assertTrue(CarPlayExport.isCarPlayPath("/vendor/etc/allgo/config.xml"));
assertTrue(CarPlayExport.isCarPlayPath("libCarPlayCore.so"));
assertTrue(CarPlayExport.isCarPlayPath("iap2_config.ini"));
assertFalse(CarPlayExport.isCarPlayPath("/system/etc/hosts"));
assertFalse(CarPlayExport.isCarPlayPath(null));
}
}