Merge "Disable DBG logging for VMS classes." into qt-qpr1-dev
diff --git a/car-bugreportd/main.cpp b/car-bugreportd/main.cpp
index ebb2d5f..581bb03 100644
--- a/car-bugreportd/main.cpp
+++ b/car-bugreportd/main.cpp
@@ -61,9 +61,10 @@
constexpr const int kMaxDumpstateConnectAttempts = 20;
// Wait time between connect attempts
constexpr const int kWaitTimeBetweenConnectAttemptsInSec = 1;
-// Wait time for dumpstate. No timeout in dumpstate is longer than 60 seconds. Choose
-// a value that is twice longer.
-constexpr const int kDumpstateTimeoutInSec = 120;
+// Wait time for dumpstate. Set a timeout so that if nothing is read in 10 minutes, we'll stop
+// reading and quit. No timeout in dumpstate is longer than 60 seconds, so this gives lots of leeway
+// in case of unforeseen time outs.
+constexpr const int kDumpstateTimeoutInSec = 600;
// The prefix for screenshot filename in the generated zip file.
constexpr const char* kScreenshotPrefix = "/screenshot";
@@ -197,6 +198,7 @@
bool copyFile(const std::string& zip_path, int output_socket) {
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(zip_path.c_str(), O_RDONLY)));
if (fd == -1) {
+ ALOGE("Failed to open zip file %s.", zip_path.c_str());
return false;
}
while (1) {
@@ -206,6 +208,7 @@
break;
}
if (bytes_copied == -1) {
+ ALOGE("Failed to copy zip file %s to the output_socket.", zip_path.c_str());
return false;
}
}
@@ -239,13 +242,14 @@
std::string line;
std::string last_nonempty_line;
+ char buffer[65536];
while (true) {
- char buffer[65536];
ssize_t bytes_read = copyTo(s, progress_socket, buffer, sizeof(buffer));
if (bytes_read == 0) {
break;
}
if (bytes_read == -1) {
+ ALOGE("Failed to copy progress to the progress_socket.");
return false;
}
// Process the buffer line by line. this is needed for the filename.
@@ -468,12 +472,12 @@
bool ret_val = doBugreport(progress_socket, &bytes_written, &zip_path);
close(progress_socket);
- int output_socket = openSocket(kCarBrOutputSocket);
- if (output_socket != -1 && ret_val) {
- ret_val = copyFile(zip_path, output_socket);
- }
- if (output_socket != -1) {
- close(output_socket);
+ if (ret_val) {
+ int output_socket = openSocket(kCarBrOutputSocket);
+ if (output_socket != -1) {
+ ret_val = copyFile(zip_path, output_socket);
+ close(output_socket);
+ }
}
int extra_output_socket = openSocket(kCarBrExtraOutputSocket);
diff --git a/car-lib/src/android/car/Car.java b/car-lib/src/android/car/Car.java
index 1912803..67a00e3 100644
--- a/car-lib/src/android/car/Car.java
+++ b/car-lib/src/android/car/Car.java
@@ -16,7 +16,10 @@
package android.car;
+import static android.car.CarLibLog.TAG_CAR;
+
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
@@ -57,9 +60,12 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
import java.util.HashMap;
/**
@@ -607,22 +613,84 @@
public static final String CAR_EXTRA_CLUSTER_ACTIVITY_STATE =
"android.car.cluster.ClusterActivityState";
+
+ /**
+ * Callback to notify the Lifecycle of car service.
+ *
+ * <p>Access to car service should happen
+ * after {@link CarServiceLifecycleListener#onLifecycleChanged(Car, boolean)} call with
+ * {@code ready} set {@code true}.</p>
+ *
+ * <p>When {@link CarServiceLifecycleListener#onLifecycleChanged(Car, boolean)} is
+ * called with ready set to false, access to car service should stop until car service is ready
+ * again from {@link CarServiceLifecycleListener#onLifecycleChanged(Car, boolean)} call
+ * with {@code ready} set to {@code true}.</p>
+ * @hide
+ */
+ public interface CarServiceLifecycleListener {
+ /**
+ * Car service has gone through status change.
+ *
+ * <p>This is always called in the main thread context.</p>
+ *
+ * @param car {@code Car} object that was originally associated with this lister from
+ * {@link #createCar(Context, Handler, long, Car.CarServiceLifecycleListener)}
+ * call.
+ * @param ready When {@code true, car service is ready and all accesses are ok.
+ * Otherwise car service has crashed or killed and will be restarted.
+ */
+ void onLifecycleChanged(@NonNull Car car, boolean ready);
+ }
+
+ /**
+ * {@link #createCar(Context, Handler, long, CarServiceLifecycleListener)}'s
+ * waitTimeoutMs value to use to wait forever inside the call until car service is ready.
+ * @hide
+ */
+ public static final long CAR_WAIT_TIMEOUT_WAIT_FOREVER = -1;
+
+ /**
+ * {@link #createCar(Context, Handler, long, CarServiceLifecycleListener)}'s
+ * waitTimeoutMs value to use to skip any waiting inside the call.
+ * @hide
+ */
+ public static final long CAR_WAIT_TIMEOUT_DO_NOT_WAIT = 0;
+
private static final long CAR_SERVICE_BIND_RETRY_INTERVAL_MS = 500;
private static final long CAR_SERVICE_BIND_MAX_RETRY = 20;
private static final long CAR_SERVICE_BINDER_POLLING_INTERVAL_MS = 50;
private static final long CAR_SERVICE_BINDER_POLLING_MAX_RETRY = 100;
- private final Context mContext;
- @GuardedBy("this")
- private ICar mService;
- private final boolean mOwnsService;
private static final int STATE_DISCONNECTED = 0;
private static final int STATE_CONNECTING = 1;
private static final int STATE_CONNECTED = 2;
- @GuardedBy("this")
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "STATE_", value = {
+ STATE_DISCONNECTED,
+ STATE_CONNECTING,
+ STATE_CONNECTED,
+ })
+ @Target({ElementType.TYPE_USE})
+ public @interface StateTypeEnum {}
+
+ private static final boolean DBG = false;
+
+ private final Context mContext;
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private ICar mService;
+ @GuardedBy("mLock")
+ private boolean mServiceBound;
+
+ @GuardedBy("mLock")
+ @StateTypeEnum
private int mConnectionState;
- @GuardedBy("this")
+ @GuardedBy("mLock")
private int mConnectionRetryCount;
private final Runnable mConnectionRetryRunnable = new Runnable() {
@@ -642,33 +710,55 @@
private final ServiceConnection mServiceConnectionListener =
new ServiceConnection () {
+ @Override
public void onServiceConnected(ComponentName name, IBinder service) {
- synchronized (Car.this) {
- mService = ICar.Stub.asInterface(service);
+ synchronized (mLock) {
+ ICar newService = ICar.Stub.asInterface(service);
+ if (newService == null) {
+ Log.wtf(TAG_CAR, "null binder service", new RuntimeException());
+ return; // should not happen.
+ }
+ if (mService != null && mService.asBinder().equals(newService.asBinder())) {
+ // already connected.
+ return;
+ }
mConnectionState = STATE_CONNECTED;
+ mService = newService;
}
if (mServiceConnectionListenerClient != null) {
mServiceConnectionListenerClient.onServiceConnected(name, service);
}
+ if (mStatusChangeCallback != null) {
+ mStatusChangeCallback.onLifecycleChanged(Car.this, true);
+ }
}
+ @Override
public void onServiceDisconnected(ComponentName name) {
- synchronized (Car.this) {
+ synchronized (mLock) {
if (mConnectionState == STATE_DISCONNECTED) {
+ // can happen when client calls disconnect before onServiceDisconnected call.
return;
}
+ handleCarDisconnectLocked();
}
- // unbind explicitly and set connectionState to STATE_DISCONNECTED here.
- disconnect();
if (mServiceConnectionListenerClient != null) {
mServiceConnectionListenerClient.onServiceDisconnected(name);
}
+ if (mStatusChangeCallback != null) {
+ mStatusChangeCallback.onLifecycleChanged(Car.this, false);
+ }
}
};
+ @Nullable
private final ServiceConnection mServiceConnectionListenerClient;
- private final Object mCarManagerLock = new Object();
- @GuardedBy("mCarManagerLock")
+
+ /** Can be added after ServiceManager.getService call */
+ @Nullable
+ private final CarServiceLifecycleListener mStatusChangeCallback;
+
+ @GuardedBy("mLock")
private final HashMap<String, CarManagerBase> mServiceMap = new HashMap<>();
/** Handler for generic event dispatching. */
@@ -691,13 +781,14 @@
public static Car createCar(Context context, ServiceConnection serviceConnectionListener,
@Nullable Handler handler) {
if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
- Log.e(CarLibLog.TAG_CAR, "FEATURE_AUTOMOTIVE not declared while android.car is used");
+ Log.e(TAG_CAR, "FEATURE_AUTOMOTIVE not declared while android.car is used");
return null;
}
try {
- return new Car(context, serviceConnectionListener, handler);
+ return new Car(context, /* service= */ null , serviceConnectionListener,
+ /* statusChangeListener= */ null, handler);
} catch (IllegalArgumentException e) {
- // Expected when car service loader is not available.
+ // Expected when car service loader is not available.
}
return null;
}
@@ -746,7 +837,8 @@
service = ServiceManager.getService(CAR_SERVICE_BINDER_SERVICE_NAME);
if (car == null) {
// service can be still null. The constructor is safe for null service.
- car = new Car(context, ICar.Stub.asInterface(service), handler);
+ car = new Car(context, ICar.Stub.asInterface(service),
+ null /*serviceConnectionListener*/, null /*statusChangeListener*/, handler);
}
if (service != null) {
if (!started) { // specialization for most common case.
@@ -760,7 +852,7 @@
}
retryCount++;
if (retryCount > CAR_SERVICE_BINDER_POLLING_MAX_RETRY) {
- Log.e(CarLibLog.TAG_CAR, "cannot get car_service, waited for car service (ms):"
+ Log.e(TAG_CAR, "cannot get car_service, waited for car service (ms):"
+ CAR_SERVICE_BINDER_POLLING_INTERVAL_MS
* CAR_SERVICE_BINDER_POLLING_MAX_RETRY,
new RuntimeException());
@@ -778,7 +870,7 @@
synchronized (car) {
if (car.mService == null) {
car.mService = ICar.Stub.asInterface(service);
- Log.w(CarLibLog.TAG_CAR,
+ Log.w(TAG_CAR,
"waited for car_service (ms):"
+ CAR_SERVICE_BINDER_POLLING_INTERVAL_MS * retryCount,
new RuntimeException());
@@ -788,24 +880,141 @@
return car;
}
- private Car(Context context, ServiceConnection serviceConnectionListener,
- @Nullable Handler handler) {
- mContext = context;
- mEventHandler = determineEventHandler(handler);
- mMainThreadEventHandler = determineMainThreadEventHandler(mEventHandler);
-
- mService = null;
- mConnectionState = STATE_DISCONNECTED;
- mOwnsService = true;
- mServiceConnectionListenerClient = serviceConnectionListener;
- }
-
-
/**
- * Car constructor when ICar binder is already available.
+ * Creates new {@link Car} object with {@link CarServiceLifecycleListener}.
+ *
+ * <p> If car service is ready inside this call and if the caller is running in the main thread,
+ * {@link CarServiceLifecycleListener#onLifecycleChanged(Car, boolean)} will be called
+ * with ready set to be true. Otherwise,
+ * {@link CarServiceLifecycleListener#onLifecycleChanged(Car, boolean)} will be called
+ * from the main thread later. </p>
+ *
+ * <p>This call can block up to specified waitTimeoutMs to wait for car service to be ready.
+ * If car service is not ready within the given time, it will return a Car instance in
+ * disconnected state. Blocking main thread forever can lead into getting ANR (Application Not
+ * Responding) killing from system and should not be used if the app is supposed to survive
+ * across the crash / restart of car service. It can be still useful in case the app cannot do
+ * anything without car service being ready. In any waiting, if the thread is getting
+ * interrupted, it will return immediately.
+ * </p>
+ *
+ * <p>Note that returned {@link Car} object is not guaranteed to be connected when there is
+ * a limited timeout. Regardless of returned car being connected or not, it is recommended to
+ * implement all car related initialization inside
+ * {@link CarServiceLifecycleListener#onLifecycleChanged(Car, boolean)} and avoid the
+ * needs to check if returned {@link Car} is connected or not from returned {@link Car}.</p>
+ *
+ * @param handler dispatches all Car*Manager events to this Handler. Exception is
+ * {@link CarServiceLifecycleListener} which will be always dispatched to main
+ * thread. Passing null leads into dispatching all Car*Manager callbacks to main
+ * thread as well.
+ * @param waitTimeoutMs Setting this to {@link #CAR_WAIT_TIMEOUT_DO_NOT_WAIT} will guarantee
+ * that the API does not wait for the car service at all. Setting this to
+ * to {@link #CAR_WAIT_TIMEOUT_WAIT_FOREVER} will block the call forever
+ * until the car service is ready. Setting any positive value will be
+ * interpreted as timeout value.
+ *
* @hide
*/
- public Car(Context context, @Nullable ICar service, @Nullable Handler handler) {
+ @NonNull
+ public static Car createCar(@NonNull Context context,
+ @Nullable Handler handler, long waitTimeoutMs,
+ @NonNull CarServiceLifecycleListener statusChangeListener) {
+ Preconditions.checkNotNull(context);
+ Preconditions.checkNotNull(statusChangeListener);
+ Car car = null;
+ IBinder service = null;
+ boolean started = false;
+ int retryCount = 0;
+ long maxRetryCount = 0;
+ if (waitTimeoutMs > 0) {
+ maxRetryCount = waitTimeoutMs / CAR_SERVICE_BINDER_POLLING_INTERVAL_MS;
+ // at least wait once if it is positive value.
+ if (maxRetryCount == 0) {
+ maxRetryCount = 1;
+ }
+ }
+ boolean isMainThread = Looper.myLooper() == Looper.getMainLooper();
+ while (true) {
+ service = ServiceManager.getService(CAR_SERVICE_BINDER_SERVICE_NAME);
+ if (car == null) {
+ // service can be still null. The constructor is safe for null service.
+ car = new Car(context, ICar.Stub.asInterface(service), null, statusChangeListener,
+ handler);
+ }
+ if (service != null) {
+ if (!started) { // specialization for most common case : car service already ready
+ car.dispatchCarReadyToMainThread(isMainThread);
+ // Needs this for CarServiceLifecycleListener. Note that ServiceConnection
+ // will skip the callback as valid mService is set already.
+ car.startCarService();
+ return car;
+ }
+ // service available after starting.
+ break;
+ }
+ if (!started) {
+ car.startCarService();
+ started = true;
+ }
+ retryCount++;
+ if (waitTimeoutMs < 0 && retryCount >= CAR_SERVICE_BINDER_POLLING_MAX_RETRY
+ && retryCount % CAR_SERVICE_BINDER_POLLING_MAX_RETRY == 0) {
+ // Log warning if car service is not alive even for waiting forever case.
+ Log.w(TAG_CAR, "car_service not ready, waited for car service (ms):"
+ + retryCount * CAR_SERVICE_BINDER_POLLING_INTERVAL_MS,
+ new RuntimeException());
+ } else if (waitTimeoutMs >= 0 && retryCount > maxRetryCount) {
+ if (waitTimeoutMs > 0) {
+ Log.w(TAG_CAR, "car_service not ready, waited for car service (ms):"
+ + waitTimeoutMs,
+ new RuntimeException());
+ }
+ return car;
+ }
+
+ try {
+ Thread.sleep(CAR_SERVICE_BINDER_POLLING_INTERVAL_MS);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ Log.w(TAG_CAR, "interrupted", new RuntimeException());
+ return car;
+ }
+ }
+ // Can be accessed from mServiceConnectionListener in main thread.
+ synchronized (car.mLock) {
+ Log.w(TAG_CAR,
+ "waited for car_service (ms):"
+ + retryCount * CAR_SERVICE_BINDER_POLLING_INTERVAL_MS,
+ new RuntimeException());
+ // ServiceConnection has handled everything.
+ if (car.mService != null) {
+ return car;
+ }
+ // mService check in ServiceConnection prevents calling
+ // onLifecycleChanged. So onLifecycleChanged should be called explicitly
+ // but do it outside lock.
+ car.mService = ICar.Stub.asInterface(service);
+ car.mConnectionState = STATE_CONNECTED;
+ }
+ car.dispatchCarReadyToMainThread(isMainThread);
+ return car;
+ }
+
+ private void dispatchCarReadyToMainThread(boolean isMainThread) {
+ if (isMainThread) {
+ mStatusChangeCallback.onLifecycleChanged(this, true);
+ } else {
+ // should dispatch to main thread.
+ mMainThreadEventHandler.post(
+ () -> mStatusChangeCallback.onLifecycleChanged(this, true));
+ }
+ }
+
+ private Car(Context context, @Nullable ICar service,
+ @Nullable ServiceConnection serviceConnectionListener,
+ @Nullable CarServiceLifecycleListener statusChangeListener,
+ @Nullable Handler handler) {
mContext = context;
mEventHandler = determineEventHandler(handler);
mMainThreadEventHandler = determineMainThreadEventHandler(mEventHandler);
@@ -813,12 +1022,20 @@
mService = service;
if (service != null) {
mConnectionState = STATE_CONNECTED;
- mOwnsService = false;
} else {
mConnectionState = STATE_DISCONNECTED;
- mOwnsService = true;
}
- mServiceConnectionListenerClient = null;
+ mServiceConnectionListenerClient = serviceConnectionListener;
+ mStatusChangeCallback = statusChangeListener;
+ }
+
+ /**
+ * Car constructor when ICar binder is already available. The binder can be null.
+ * @hide
+ */
+ public Car(Context context, @Nullable ICar service, @Nullable Handler handler) {
+ this(context, service, null /*serviceConnectionListener*/, null /*statusChangeListener*/,
+ handler);
}
private static Handler determineMainThreadEventHandler(Handler eventHandler) {
@@ -844,7 +1061,7 @@
*/
@Deprecated
public void connect() throws IllegalStateException {
- synchronized (this) {
+ synchronized (mLock) {
if (mConnectionState != STATE_DISCONNECTED) {
throw new IllegalStateException("already connected or connecting");
}
@@ -853,25 +1070,30 @@
}
}
+ private void handleCarDisconnectLocked() {
+ if (mConnectionState == STATE_DISCONNECTED) {
+ // can happen when client calls disconnect with onServiceDisconnected already called.
+ return;
+ }
+ mEventHandler.removeCallbacks(mConnectionRetryRunnable);
+ mMainThreadEventHandler.removeCallbacks(mConnectionRetryFailedRunnable);
+ mConnectionRetryCount = 0;
+ tearDownCarManagersLocked();
+ mService = null;
+ mConnectionState = STATE_DISCONNECTED;
+ }
+
/**
* Disconnect from car service. This can be called while disconnected. Once disconnect is
* called, all Car*Managers from this instance becomes invalid, and
* {@link Car#getCarManager(String)} will return different instance if it is connected again.
*/
public void disconnect() {
- synchronized (this) {
- if (mConnectionState == STATE_DISCONNECTED) {
- return;
- }
- mEventHandler.removeCallbacks(mConnectionRetryRunnable);
- mMainThreadEventHandler.removeCallbacks(mConnectionRetryFailedRunnable);
- mConnectionRetryCount = 0;
- tearDownCarManagers();
- mService = null;
- mConnectionState = STATE_DISCONNECTED;
-
- if (mOwnsService) {
+ synchronized (mLock) {
+ handleCarDisconnectLocked();
+ if (mServiceBound) {
mContext.unbindService(mServiceConnectionListener);
+ mServiceBound = false;
}
}
}
@@ -882,7 +1104,7 @@
* @return
*/
public boolean isConnected() {
- synchronized (this) {
+ synchronized (mLock) {
return mService != null;
}
}
@@ -892,7 +1114,7 @@
* @return
*/
public boolean isConnecting() {
- synchronized (this) {
+ synchronized (mLock) {
return mConnectionState == STATE_CONNECTING;
}
}
@@ -915,21 +1137,20 @@
public Object getCarManager(String serviceName) {
CarManagerBase manager;
ICar service = getICarOrThrow();
- synchronized (mCarManagerLock) {
+ synchronized (mLock) {
manager = mServiceMap.get(serviceName);
if (manager == null) {
try {
IBinder binder = service.getCarService(serviceName);
if (binder == null) {
- Log.w(CarLibLog.TAG_CAR, "getCarManager could not get binder for service:" +
- serviceName);
+ Log.w(TAG_CAR, "getCarManager could not get binder for service:"
+ + serviceName);
return null;
}
manager = createCarManager(serviceName, binder);
if (manager == null) {
- Log.w(CarLibLog.TAG_CAR,
- "getCarManager could not create manager for service:" +
- serviceName);
+ Log.w(TAG_CAR, "getCarManager could not create manager for service:"
+ + serviceName);
return null;
}
mServiceMap.put(serviceName, manager);
@@ -1041,33 +1262,39 @@
intent.setAction(Car.CAR_SERVICE_INTERFACE_NAME);
boolean bound = mContext.bindServiceAsUser(intent, mServiceConnectionListener,
Context.BIND_AUTO_CREATE, UserHandle.CURRENT_OR_SELF);
- if (!bound) {
- mConnectionRetryCount++;
- if (mConnectionRetryCount > CAR_SERVICE_BIND_MAX_RETRY) {
- Log.w(CarLibLog.TAG_CAR, "cannot bind to car service after max retry");
- mMainThreadEventHandler.post(mConnectionRetryFailedRunnable);
+ synchronized (mLock) {
+ if (!bound) {
+ mConnectionRetryCount++;
+ if (mConnectionRetryCount > CAR_SERVICE_BIND_MAX_RETRY) {
+ Log.w(TAG_CAR, "cannot bind to car service after max retry");
+ mMainThreadEventHandler.post(mConnectionRetryFailedRunnable);
+ } else {
+ mEventHandler.postDelayed(mConnectionRetryRunnable,
+ CAR_SERVICE_BIND_RETRY_INTERVAL_MS);
+ }
} else {
- mEventHandler.postDelayed(mConnectionRetryRunnable,
- CAR_SERVICE_BIND_RETRY_INTERVAL_MS);
+ mEventHandler.removeCallbacks(mConnectionRetryRunnable);
+ mMainThreadEventHandler.removeCallbacks(mConnectionRetryFailedRunnable);
+ mConnectionRetryCount = 0;
+ mServiceBound = true;
}
- } else {
- mConnectionRetryCount = 0;
}
}
- private synchronized ICar getICarOrThrow() throws IllegalStateException {
- if (mService == null) {
- throw new IllegalStateException("not connected");
+ private ICar getICarOrThrow() throws IllegalStateException {
+ synchronized (mLock) {
+ if (mService == null) {
+ throw new IllegalStateException("not connected");
+ }
+ return mService;
}
- return mService;
}
- private void tearDownCarManagers() {
- synchronized (mCarManagerLock) {
- for (CarManagerBase manager: mServiceMap.values()) {
- manager.onCarDisconnected();
- }
- mServiceMap.clear();
+ private void tearDownCarManagersLocked() {
+ // All disconnected handling should be only doing its internal cleanup.
+ for (CarManagerBase manager: mServiceMap.values()) {
+ manager.onCarDisconnected();
}
+ mServiceMap.clear();
}
}
diff --git a/car-usb-handler/src/android/car/usb/handler/BootUsbService.java b/car-usb-handler/src/android/car/usb/handler/BootUsbService.java
index 698571a..909be07 100644
--- a/car-usb-handler/src/android/car/usb/handler/BootUsbService.java
+++ b/car-usb-handler/src/android/car/usb/handler/BootUsbService.java
@@ -107,7 +107,7 @@
Intent manageDevice = new Intent(context, UsbHostManagementActivity.class);
manageDevice.setAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
manageDevice.putExtra(UsbManager.EXTRA_DEVICE, device);
- manageDevice.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ manageDevice.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivityAsUser(manageDevice, UserHandle.CURRENT);
}
}
diff --git a/car-usb-handler/src/android/car/usb/handler/UsbHostManagementActivity.java b/car-usb-handler/src/android/car/usb/handler/UsbHostManagementActivity.java
index f710960..0a91089 100644
--- a/car-usb-handler/src/android/car/usb/handler/UsbHostManagementActivity.java
+++ b/car-usb-handler/src/android/car/usb/handler/UsbHostManagementActivity.java
@@ -80,7 +80,6 @@
if (connectedDevice != null) {
mController.processDevice(connectedDevice);
} else {
- unregisterResolveBroadcastReceiver();
finish();
}
}
@@ -90,8 +89,8 @@
// We could have been unregistered after receiving the intent but before processing it,
// so make sure we are still registered.
if (mReceiverRegistered) {
- processDevice();
unregisterResolveBroadcastReceiver();
+ processDevice();
}
}
}
diff --git a/car_product/sepolicy/test/kitchensink_app.te b/car_product/sepolicy/test/kitchensink_app.te
index 297b3e7..0ab9c43 100644
--- a/car_product/sepolicy/test/kitchensink_app.te
+++ b/car_product/sepolicy/test/kitchensink_app.te
@@ -11,6 +11,7 @@
activity_service
activity_task_service
autofill_service
+ carservice_service
connectivity_service
content_service
deviceidle_service
diff --git a/service/src/com/android/car/systeminterface/DisplayInterface.java b/service/src/com/android/car/systeminterface/DisplayInterface.java
index 4d8f180..d20a177 100644
--- a/service/src/com/android/car/systeminterface/DisplayInterface.java
+++ b/service/src/com/android/car/systeminterface/DisplayInterface.java
@@ -28,6 +28,7 @@
import android.database.ContentObserver;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
+import android.hardware.input.InputManager;
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
@@ -41,6 +42,7 @@
import android.view.Display;
import android.view.DisplayAddress;
import android.view.IWindowManager;
+import android.view.InputDevice;
import com.android.car.CarLog;
import com.android.car.CarPowerManagementService;
@@ -76,6 +78,7 @@
private final ContentResolver mContentResolver;
private final Context mContext;
private final DisplayManager mDisplayManager;
+ private final InputManager mInputManager;
private final int mMaximumBacklight;
private final int mMinimumBacklight;
private final PowerManager mPowerManager;
@@ -117,6 +120,7 @@
mContext = context;
mContentResolver = mContext.getContentResolver();
mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
+ mInputManager = (InputManager) mContext.getSystemService(Context.INPUT_SERVICE);
mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mMaximumBacklight = mPowerManager.getMaximumScreenBrightnessSetting();
mMinimumBacklight = mPowerManager.getMinimumScreenBrightnessSetting();
@@ -209,6 +213,19 @@
Log.i(CarLog.TAG_POWER, "off display");
mPowerManager.goToSleep(SystemClock.uptimeMillis());
}
+ // Turn touchscreen input devices on or off, the same as the display
+ for (int deviceId : mInputManager.getInputDeviceIds()) {
+ InputDevice inputDevice = mInputManager.getInputDevice(deviceId);
+ if (inputDevice != null
+ && (inputDevice.getSources() & InputDevice.SOURCE_TOUCHSCREEN)
+ == InputDevice.SOURCE_TOUCHSCREEN) {
+ if (on) {
+ mInputManager.enableInputDevice(deviceId);
+ } else {
+ mInputManager.disableInputDevice(deviceId);
+ }
+ }
+ }
}
@Override
diff --git a/tests/BugReportApp/src/com/google/android/car/bugreport/BugReportActivity.java b/tests/BugReportApp/src/com/google/android/car/bugreport/BugReportActivity.java
index ebcb804..456192e 100644
--- a/tests/BugReportApp/src/com/google/android/car/bugreport/BugReportActivity.java
+++ b/tests/BugReportApp/src/com/google/android/car/bugreport/BugReportActivity.java
@@ -384,6 +384,10 @@
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
+ mRecorder.setOnInfoListener((MediaRecorder recorder, int what, int extra) ->
+ Log.i(TAG, "OnMediaRecorderInfo: what=" + what + ", extra=" + extra));
+ mRecorder.setOnErrorListener((MediaRecorder recorder, int what, int extra) ->
+ Log.i(TAG, "OnMediaRecorderError: what=" + what + ", extra=" + extra));
mRecorder.setOutputFile(recordingFile);
try {
@@ -407,7 +411,13 @@
private void stopAudioRecording() {
if (mRecorder != null) {
Log.i(TAG, "Recording ended, stopping the MediaRecorder.");
- mRecorder.stop();
+ try {
+ mRecorder.stop();
+ } catch (IllegalStateException e) {
+ // Sometimes MediaRecorder doesn't start and stopping it throws an error.
+ // We just log these cases, no need to crash the app.
+ Log.w(TAG, "Couldn't stop media recorder", e);
+ }
mRecorder.release();
mRecorder = null;
}
diff --git a/tests/BugReportApp/src/com/google/android/car/bugreport/BugReportService.java b/tests/BugReportApp/src/com/google/android/car/bugreport/BugReportService.java
index 70ba65e..f6fc651 100644
--- a/tests/BugReportApp/src/com/google/android/car/bugreport/BugReportService.java
+++ b/tests/BugReportApp/src/com/google/android/car/bugreport/BugReportService.java
@@ -431,14 +431,22 @@
if (file.isDirectory()) {
continue;
}
+ if (file.length() == 0) {
+ // If there were issues with reading from dumpstate socket, the dumpstate zip
+ // file still might be available in
+ // /data/user_de/0/com.android.shell/files/bugreports/.
+ Log.w(TAG, "File " + file.getName() + " is empty, skipping.");
+ return;
+ }
String filename = file.getName();
- // only for the zipped output file, we add invidiual entries to zip file
+ // only for the zipped output file, we add individual entries to zip file.
if (filename.equals(OUTPUT_ZIP_FILE) || filename.equals(EXTRA_OUTPUT_ZIP_FILE)) {
extractZippedFileToOutputStream(file, zipStream);
} else {
- FileInputStream reader = new FileInputStream(file);
- addFileToOutputStream(filename, reader, zipStream);
+ try (FileInputStream reader = new FileInputStream(file)) {
+ addFileToOutputStream(filename, reader, zipStream);
+ }
}
}
} finally {
@@ -455,17 +463,21 @@
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
- InputStream stream = zipFile.getInputStream(entry);
- addFileToOutputStream(entry.getName(), stream, zipStream);
+ try (InputStream stream = zipFile.getInputStream(entry)) {
+ addFileToOutputStream(entry.getName(), stream, zipStream);
+ }
}
}
- private void addFileToOutputStream(String filename, InputStream reader,
- ZipOutputStream zipStream) throws IOException {
+ private void addFileToOutputStream(
+ String filename, InputStream reader, ZipOutputStream zipStream) {
ZipEntry entry = new ZipEntry(filename);
- zipStream.putNextEntry(entry);
- rawCopyStream(zipStream, reader);
- zipStream.closeEntry();
- reader.close();
+ try {
+ zipStream.putNextEntry(entry);
+ rawCopyStream(zipStream, reader);
+ zipStream.closeEntry();
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to add file " + filename + " to the zip.", e);
+ }
}
}
diff --git a/tests/CarDeveloperOptions/res/values-da/strings.xml b/tests/CarDeveloperOptions/res/values-da/strings.xml
index d13df6f..bcca00b 100644
--- a/tests/CarDeveloperOptions/res/values-da/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-da/strings.xml
@@ -2106,7 +2106,7 @@
<string name="accessibility_summary_state_disabled" msgid="9197369047683087620">"Fra"</string>
<string name="accessibility_summary_state_stopped" msgid="3170264683616172746">"Kører ikke. Tryk for at få flere oplysninger."</string>
<string name="accessibility_description_state_stopped" msgid="7666178628053039493">"Denne tjeneste fungerer ikke korrekt."</string>
- <string name="enable_quick_setting" msgid="1580451877998661255">"Vis i Hurtige indstillinger"</string>
+ <string name="enable_quick_setting" msgid="1580451877998661255">"Vis i Kvikmenu"</string>
<string name="daltonizer_type" msgid="6890356081036026791">"Korrektionstilstand"</string>
<plurals name="accessibilty_autoclick_preference_subtitle_extremely_short_delay" formatted="false" msgid="3810676455925024813">
<item quantity="one">Ekstremt lille forsinkelse (<xliff:g id="CLICK_DELAY_LABEL_1">%1$d</xliff:g> ms)</item>
@@ -4071,7 +4071,7 @@
<string name="dark_ui_mode_title" msgid="8774932716427742413">"Vælg tema"</string>
<string name="dark_ui_settings_light_summary" msgid="5219102347744462812">"Denne indstilling gælder også for apps"</string>
<string name="dark_ui_settings_dark_summary" msgid="7042737828943784289">"Understøttede apps skifter også til mørkt tema"</string>
- <string name="quick_settings_developer_tiles" msgid="7423485925757678719">"Felter for udviklere til Hurtige indstillinger"</string>
+ <string name="quick_settings_developer_tiles" msgid="7423485925757678719">"Felter for udviklere til Kvikmenu"</string>
<string name="winscope_trace_quick_settings_title" msgid="940971040388411374">"Sporing af Winscope"</string>
<string name="sensors_off_quick_settings_title" msgid="3655699045300438902">"Sensorer er slået fra"</string>
<string name="managed_profile_settings_title" msgid="4340409321523532402">"Indstillinger for arbejdsprofil"</string>
diff --git a/tests/CarDeveloperOptions/res/values-eu/strings.xml b/tests/CarDeveloperOptions/res/values-eu/strings.xml
index 6a242b6..5a16cfa 100644
--- a/tests/CarDeveloperOptions/res/values-eu/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-eu/strings.xml
@@ -762,7 +762,7 @@
<string name="bluetooth_device_context_unpair" msgid="250588431708253041">"Desparekatu"</string>
<string name="bluetooth_device_context_disconnect_unpair" msgid="4519151805677280077">"Deskonektatu eta desparekatu"</string>
<string name="bluetooth_device_context_connect_advanced" msgid="423463405499392444">"Aukerak…"</string>
- <string name="bluetooth_menu_advanced" msgid="7566858513372603652">"Ezarpen aurreratuak"</string>
+ <string name="bluetooth_menu_advanced" msgid="7566858513372603652">"Aurreratuak"</string>
<string name="bluetooth_advanced_titlebar" msgid="6459469494039004784">"Bluetooth ezarpen aurreratuak"</string>
<string name="bluetooth_empty_list_bluetooth_off" msgid="6255367297830430459">"Bluetooth aktibatuta badago, inguruko Bluetooth gailuekin komunika daiteke gailua."</string>
<string name="bluetooth_scanning_on_info_message" msgid="5460370815156050550">"Bluetooth konexioa aktibatuta dagoenean, inguruko Bluetooth gailuekin komunika daiteke gailua.\n\nGailuaren erabilera hobetzeko, aplikazioek eta zerbitzuek wifi-sareak bilatzen jarraituko dute, baita wifi-konexioa desaktibatuta dagoenean ere. Besteak beste, kokapenean oinarritutako eginbideak eta zerbitzuak hobetzeko erabil daiteke. Aukera hori aldatzeko, joan "<annotation id="link">"gailuak bilatzeko ezarpenetara"</annotation>"."</string>
@@ -881,7 +881,7 @@
<string name="wifi_menu_more_options" msgid="8318269834264035524">"Aukera gehiago"</string>
<string name="wifi_menu_p2p" msgid="4945665601551289791">"Wi-Fi Direct"</string>
<string name="wifi_menu_scan" msgid="9082691677803181629">"Bilatu"</string>
- <string name="wifi_menu_advanced" msgid="5984484498045511072">"Ezarpen aurreratuak"</string>
+ <string name="wifi_menu_advanced" msgid="5984484498045511072">"Aurreratuak"</string>
<string name="wifi_menu_configure" msgid="52192491120701266">"Konfiguratu"</string>
<string name="wifi_menu_connect" msgid="3984327567173931219">"Konektatu sarera"</string>
<string name="wifi_menu_remember" msgid="717257200269700641">"Gogoratu sarea"</string>
@@ -2526,7 +2526,7 @@
<string name="trusted_credentials_summary" msgid="7411781319056251582">"Bistaratu CA fidagarrien ziurtagiriak"</string>
<string name="user_credentials" msgid="8365731467650306757">"Erabiltzaile-kredentzialak"</string>
<string name="user_credentials_summary" msgid="7350223899317423252">"Ikusi eta aldatu gordetako kredentzialak"</string>
- <string name="advanced_security_title" msgid="286883005673855845">"Ezarpen aurreratuak"</string>
+ <string name="advanced_security_title" msgid="286883005673855845">"Aurreratuak"</string>
<string name="credential_storage_type" msgid="2585337320206095255">"Biltegiratze mota"</string>
<string name="credential_storage_type_hardware" msgid="5054143224259023600">"Hardwarean gordetakoa"</string>
<string name="credential_storage_type_software" msgid="1335905150062717150">"Softwarean soilik"</string>
@@ -3290,7 +3290,7 @@
<string name="configure_notification_settings" msgid="291914315140851270">"Jakinarazpenak"</string>
<string name="recent_notifications" msgid="8125865995065032049">"Bidalitako azkenak"</string>
<string name="recent_notifications_see_all_title" msgid="4089007770442871469">"Ikusi azken zazpi egunetako guztia"</string>
- <string name="advanced_section_header" msgid="984680389373090015">"Ezarpen aurreratuak"</string>
+ <string name="advanced_section_header" msgid="984680389373090015">"Aurreratuak"</string>
<string name="profile_section_header" msgid="5471479005472037417">"Laneko jakinarazpenak"</string>
<string name="asst_capability_prioritizer_title" msgid="3488284760645922160">"Jakinarazpen automatikoen hobespenen ezartzailea"</string>
<string name="asst_capability_prioritizer_summary" msgid="3525640645743790796">"Isilarazi eta jaitsi mailaz garrantzi gutxiagoko jakinarazpenak"</string>
@@ -3635,7 +3635,7 @@
<string name="filter_notif_low_channels" msgid="6859599463135775287">"Kategoriak: garrantzi txikikoak"</string>
<string name="filter_notif_blocked_channels" msgid="6110799550327612670">"Kategoriak: desaktibatutakoak"</string>
<string name="filter_notif_dnd_channels" msgid="3251570137256371092">"Kategoriak: \"Ez molestatu\" salbuespenak"</string>
- <string name="advanced_apps" msgid="6643869089344883537">"Ezarpen aurreratuak"</string>
+ <string name="advanced_apps" msgid="6643869089344883537">"Aurreratuak"</string>
<string name="configure_apps" msgid="4066683118857400943">"Konfiguratu aplikazioak"</string>
<string name="unknown_app" msgid="2312052973570376877">"Aplikazio ezezaguna"</string>
<string name="app_permissions" msgid="3215958256821756086">"Baimenen kudeatzailea"</string>
diff --git a/tests/CarDeveloperOptions/res/values-fr/strings.xml b/tests/CarDeveloperOptions/res/values-fr/strings.xml
index 2fd84c4..a088087 100644
--- a/tests/CarDeveloperOptions/res/values-fr/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-fr/strings.xml
@@ -3105,7 +3105,7 @@
<string name="keywords_face_settings" msgid="4117345666006836599">"visage"</string>
<string name="keywords_fingerprint_settings" msgid="902902368701134163">"empreinte digitale, ajouter une empreinte digitale"</string>
<string name="keywords_display_auto_brightness" msgid="1810596220466483996">"réduire la luminosité de l\'écran, écran tactile, batterie, réglage intelligent de la luminosité, luminosité dynamique"</string>
- <string name="keywords_display_adaptive_sleep" msgid="1695357782432822811">"assombrir l\'écran, veille, batterie, délai d\'inactivité, attention, affichage, écran, inactivité"</string>
+ <string name="keywords_display_adaptive_sleep" msgid="1695357782432822811">"assombrir l\'écran, veille, batterie, délai d\'inactivité, regard, affichage, écran, inactivité"</string>
<string name="keywords_auto_rotate" msgid="4320791369951647513">"faire pivoter, inverser, rotation, portrait, paysage, orientation, vertical, horizontal"</string>
<string name="keywords_system_update_settings" msgid="4419971277998986067">"mettre à jour, android"</string>
<string name="keywords_zen_mode_settings" msgid="4103819458182535493">"ne pas déranger, calendrier, notifications, bloquer, silence, vibreur, veille, travail, sélectionner, son, couper le son, jour, jour de semaine, week-end, soir de semaine, événement"</string>
diff --git a/tests/CarDeveloperOptions/res/values-in/arrays.xml b/tests/CarDeveloperOptions/res/values-in/arrays.xml
index be2c112..0d1ab0c 100644
--- a/tests/CarDeveloperOptions/res/values-in/arrays.xml
+++ b/tests/CarDeveloperOptions/res/values-in/arrays.xml
@@ -294,7 +294,7 @@
<item msgid="167418068739176448">"Aktivitas teratas"</item>
<item msgid="4760813290195199773">"Penting (latar depan)"</item>
<item msgid="2328684826817647595">"Penting (latar belakang)"</item>
- <item msgid="7746406490652867365">"Backup"</item>
+ <item msgid="7746406490652867365">"Pencadangan"</item>
<item msgid="5597404364389196754">"Beban berat"</item>
<item msgid="1290888779300174556">"Layanan (berjalan)"</item>
<item msgid="7241098542073939046">"Layanan (memulai ulang)"</item>
diff --git a/tests/CarDeveloperOptions/res/values-in/strings.xml b/tests/CarDeveloperOptions/res/values-in/strings.xml
index 36f93af..ebeffac 100644
--- a/tests/CarDeveloperOptions/res/values-in/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-in/strings.xml
@@ -555,7 +555,7 @@
<string name="setup_lock_settings_picker_message" product="device" msgid="2098404520816295371">"Cegah orang lain menggunakan perangkat ini tanpa izin Anda dengan mengaktifkan fitur perlindungan perangkat. Pilih kunci layar yang ingin Anda gunakan."</string>
<string name="setup_lock_settings_picker_message" product="default" msgid="2003984443953672040">"Cegah orang lain menggunakan ponsel ini tanpa izin Anda dengan mengaktifkan fitur perlindungan perangkat. Pilih kunci layar yang ingin Anda gunakan."</string>
<string name="lock_settings_picker_fingerprint_message" msgid="1344567476145156885">"Pilih metode kunci layar cadangan"</string>
- <string name="lock_settings_picker_face_message" msgid="6413145626861812959">"Pilih metode kunci layar backup"</string>
+ <string name="lock_settings_picker_face_message" msgid="6413145626861812959">"Pilih metode kunci layar cadangan"</string>
<string name="setup_lock_settings_options_button_label" msgid="4197315143877977385">"Opsi kunci layar"</string>
<string name="setup_lock_settings_options_dialog_title" msgid="5241946349173768827">"Opsi kunci layar"</string>
<string name="unlock_set_unlock_launch_picker_title" msgid="2731152716948003853">"Kunci layar"</string>
@@ -586,7 +586,7 @@
<string name="face_unlock_set_unlock_pin" msgid="3320824093518497476">"Autentikasi wajah + PIN"</string>
<string name="face_unlock_set_unlock_password" msgid="8962344604388383659">"Autentikasi wajah + Sandi"</string>
<string name="face_unlock_skip_face" msgid="7173197040501143880">"Lanjutkan tanpa autentikasi wajah"</string>
- <string name="face_unlock_title" msgid="1298031162909236127">"Anda dapat membuka kunci ponsel menggunakan wajah. Demi keamanan, opsi ini memerlukan kunci layar backup."</string>
+ <string name="face_unlock_title" msgid="1298031162909236127">"Anda dapat membuka kunci ponsel menggunakan wajah. Demi keamanan, opsi ini memerlukan kunci layar cadangan."</string>
<string name="unlock_set_unlock_disabled_summary" msgid="1713159782896140817">"Dinonaktifkan oleh admin, kebijakan enkripsi, atau penyimpanan kredensial"</string>
<string name="unlock_set_unlock_mode_off" msgid="2950701212659081973">"Tidak ada"</string>
<string name="unlock_set_unlock_mode_none" msgid="3441605629077912292">"Geser"</string>
@@ -1497,7 +1497,7 @@
<string name="storage_wizard_init_v2_external_action" msgid="4649591913020218098">"Penyimpanan portabel"</string>
<string name="storage_wizard_init_v2_later" msgid="2605006907172213466">"Siapkan nanti"</string>
<string name="storage_wizard_format_confirm_v2_title" msgid="1884699177320256159">"Format <xliff:g id="NAME">^1</xliff:g> ini?"</string>
- <string name="storage_wizard_format_confirm_v2_body" msgid="977657376082074305">"<xliff:g id="NAME_0">^1</xliff:g> perlu diformat untuk menyimpan aplikasi, file, dan media. \n\nMemformat akan menghapus konten yang ada di <xliff:g id="NAME_1">^2</xliff:g>. Agar konten tidak hilang, backup ke <xliff:g id="NAME_2">^3</xliff:g> atau perangkat lain."</string>
+ <string name="storage_wizard_format_confirm_v2_body" msgid="977657376082074305">"<xliff:g id="NAME_0">^1</xliff:g> perlu diformat untuk menyimpan aplikasi, file, dan media. \n\nMemformat akan menghapus konten yang ada di <xliff:g id="NAME_1">^2</xliff:g>. Agar konten tidak hilang, cadangkan ke <xliff:g id="NAME_2">^3</xliff:g> atau perangkat lain."</string>
<string name="storage_wizard_format_confirm_v2_action" msgid="5576917958786300415">"Format <xliff:g id="NAME">^1</xliff:g>"</string>
<string name="storage_wizard_migrate_v2_title" msgid="6728034411587320249">"Pindahkan konten ke <xliff:g id="NAME">^1</xliff:g>?"</string>
<string name="storage_wizard_migrate_v2_body" product="tablet" msgid="6943007011251294950">"Anda dapat memindahkan file, media, dan aplikasi tertentu ke <xliff:g id="NAME">^1</xliff:g> ini. \n\nPemindahan ini akan mengosongkan <xliff:g id="SIZE">^2</xliff:g> dari penyimpanan tablet dan memerlukan waktu sekitar <xliff:g id="DURATION">^3</xliff:g>."</string>
@@ -2539,19 +2539,19 @@
<string name="usage_access_title" msgid="7981321142726540574">"Apl dengan akses penggunaan"</string>
<string name="emergency_tone_title" msgid="130211364025984428">"Sinyal panggilan darurat"</string>
<string name="emergency_tone_summary" msgid="8035940153401622240">"Setel perilaku ketika panggilan darurat dilakukan"</string>
- <string name="privacy_settings_title" msgid="3573891462732375772">"Backup"</string>
+ <string name="privacy_settings_title" msgid="3573891462732375772">"Pencadangan"</string>
<string name="backup_summary_state_on" msgid="1725597360282574647">"Aktif"</string>
<string name="backup_summary_state_off" msgid="7138020503288730492">"Nonaktif"</string>
<string name="backup_section_title" msgid="8177209731777904656">"Pencadangan & pemulihan"</string>
<string name="personal_data_section_title" msgid="9161854418510071558">"Data pribadi"</string>
<string name="backup_data_title" msgid="4461508563849583624">"Cadangkan data saya"</string>
<string name="backup_data_summary" msgid="555459891017933746">"Mencadangkan data aplikasi, sandi Wi-Fi, dan setelan lainnya ke server Google"</string>
- <string name="backup_configure_account_title" msgid="1534734650559070294">"Akun backup"</string>
- <string name="backup_data_management_title" msgid="6299288795610243508">"Kelola akun backup"</string>
+ <string name="backup_configure_account_title" msgid="1534734650559070294">"Akun cadangan"</string>
+ <string name="backup_data_management_title" msgid="6299288795610243508">"Kelola akun cadangan"</string>
<string name="include_app_data_title" msgid="6117211611131913293">"Sertakan data aplikasi"</string>
<string name="auto_restore_title" msgid="8367486774010915221">"Pemulihan otomatis"</string>
- <string name="auto_restore_summary" msgid="1941047568966428377">"Pulihkan backup setelan dan data saat menginstal ulang aplikasi"</string>
- <string name="backup_inactive_title" msgid="5513496915638307750">"Layanan backup tidak aktif"</string>
+ <string name="auto_restore_summary" msgid="1941047568966428377">"Pulihkan cadangan setelan dan data saat menginstal ulang aplikasi"</string>
+ <string name="backup_inactive_title" msgid="5513496915638307750">"Layanan pencadangan tidak aktif"</string>
<string name="backup_configure_account_default_summary" msgid="5718298066335006412">"Saat ini, tidak ada akun yang menyimpan data cadangan"</string>
<string name="backup_erase_dialog_title" msgid="8178424339104463014"></string>
<string name="backup_erase_dialog_message" msgid="8767843355330070902">"Berhenti mencadangkan sandi Wi-Fi, bookmark, setelan lainnya, dan data aplikasi, serta menghapus semua salinan di server Google?"</string>
@@ -3039,7 +3039,7 @@
<string name="account_dashboard_title" msgid="4734300939532555885">"Akun"</string>
<string name="account_dashboard_default_summary" msgid="6822549669771936206">"Tidak ada akun yang ditambahkan"</string>
<string name="app_default_dashboard_title" msgid="6575301028225232193">"Aplikasi default"</string>
- <string name="system_dashboard_summary" msgid="6582464466735779394">"Bahasa, gestur, waktu, backup"</string>
+ <string name="system_dashboard_summary" msgid="6582464466735779394">"Bahasa, gestur, waktu, pencadangan"</string>
<string name="search_results_title" msgid="4160717656435503940">"Setelan"</string>
<string name="keywords_wifi" msgid="8477688080895466846">"wifi, wi-fi, sambungan jaringan, internet, nirkabel, data, wi fi"</string>
<string name="keywords_wifi_notify_open_networks" msgid="1031260564121854773">"Notifikasi Wi‑Fi, notifikasi wi‑fi"</string>
@@ -3861,7 +3861,7 @@
<string name="memory_summary" msgid="9121871336058042600">"Rata-rata <xliff:g id="USED_MEMORY">%1$s</xliff:g> dari <xliff:g id="TOTAL_MEMORY">%2$s</xliff:g> memori digunakan"</string>
<string name="users_summary" msgid="6693338169439092387">"Login sebagai <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
<string name="payment_summary" msgid="1381646849276543242">"<xliff:g id="APP_NAME">%1$s</xliff:g> default"</string>
- <string name="backup_disabled" msgid="6941165814784765643">"Backup dinonaktifkan"</string>
+ <string name="backup_disabled" msgid="6941165814784765643">"Pencadangan dinonaktifkan"</string>
<string name="android_version_summary" msgid="2192751442789395445">"Diupdate ke Android <xliff:g id="VERSION">%1$s</xliff:g>"</string>
<string name="android_version_pending_update_summary" msgid="3554543810520655076">"Ada update"</string>
<string name="disabled_by_policy_title" msgid="1238318274952958846">"Tindakan tidak diizinkan"</string>
diff --git a/tests/CarDeveloperOptions/res/values-pl/strings.xml b/tests/CarDeveloperOptions/res/values-pl/strings.xml
index 93804cd..e5d92b5 100644
--- a/tests/CarDeveloperOptions/res/values-pl/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-pl/strings.xml
@@ -2069,14 +2069,14 @@
<string name="usage_time_label" msgid="5615725415876461039">"Czas użycia"</string>
<string name="accessibility_settings" msgid="9140621093888234485">"Ułatwienia dostępu"</string>
<string name="accessibility_settings_title" msgid="1687226556576913576">"Ustawienia ułatwień dostępu"</string>
- <string name="accessibility_settings_summary" msgid="5742379519336396561">"Czytniki, wyświetlacz, sterowanie interakcjami"</string>
+ <string name="accessibility_settings_summary" msgid="5742379519336396561">"Czytniki, wyświetlacz, zarządzanie interakcjami"</string>
<string name="vision_settings_title" msgid="7315352351051423944">"Dla niedowidzących"</string>
<string name="vision_settings_description" msgid="3476589459009287332">"Możesz dostosować urządzenie do swoich potrzeb. Ułatwienia dostępu możesz zawsze zmienić w Ustawieniach."</string>
<string name="vision_settings_suggestion_title" msgid="7268661419110951128">"Zmień rozmiar czcionki"</string>
<string name="screen_reader_category_title" msgid="6300714148519645544">"Czytniki ekranu"</string>
<string name="audio_and_captions_category_title" msgid="6140472938769619212">"Dźwięk i tekst na ekranie"</string>
<string name="display_category_title" msgid="545168481672250195">"Wyświetlacz"</string>
- <string name="interaction_control_category_title" msgid="8775039211811947683">"Sterowanie interakcjami"</string>
+ <string name="interaction_control_category_title" msgid="8775039211811947683">"Zarządzanie interakcjami"</string>
<string name="user_installed_services_category_title" msgid="4288689493753221319">"Pobrane usługi"</string>
<string name="experimental_category_title" msgid="3797000069740110717">"Eksperymentalne"</string>
<string name="feature_flags_dashboard_title" msgid="3153034144122754381">"Flagi funkcji"</string>
diff --git a/tests/CarDeveloperOptions/res/values-ta-nokeys/strings.xml b/tests/CarDeveloperOptions/res/values-ta-nokeys/strings.xml
index d8f1761..4324c05 100644
--- a/tests/CarDeveloperOptions/res/values-ta-nokeys/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-ta-nokeys/strings.xml
@@ -16,5 +16,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="applications_settings_summary" msgid="1051076839604862240">"பயன்பாடுகளை நிர்வகித்தல்"</string>
+ <string name="applications_settings_summary" msgid="1051076839604862240">"ஆப்ஸை நிர்வகித்தல்"</string>
</resources>
diff --git a/tests/CarDeveloperOptions/res/values-ta/strings.xml b/tests/CarDeveloperOptions/res/values-ta/strings.xml
index 8841aca..24b769a 100644
--- a/tests/CarDeveloperOptions/res/values-ta/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-ta/strings.xml
@@ -1384,7 +1384,7 @@
<string name="sd_format_summary" product="nosdcard" msgid="1179857727779521920">"இசை மற்றும் படங்கள் போன்ற அக USB சேமிப்பிடத்தில் உள்ள எல்லா தரவையும் அழிக்கும்"</string>
<string name="sd_format_summary" product="default" msgid="4284028411908176234">"இசை மற்றும் படங்கள் போன்ற SD கார்டில் உள்ள எல்லா தரவையும் அழிக்கிறது"</string>
<string name="memory_clear_cache_title" msgid="4306793268129306684">"தற்காலிகத் தரவை அழிக்கவா?"</string>
- <string name="memory_clear_cache_message" msgid="6723120398411410031">"இது, எல்லா பயன்பாடுகளின் தற்காலிகச் சேமிப்பு தரவை அழிக்கும்."</string>
+ <string name="memory_clear_cache_message" msgid="6723120398411410031">"இது, எல்லா ஆப்ஸின் தற்காலிகச் சேமிப்பு தரவை அழிக்கும்."</string>
<string name="mtp_ptp_mode_summary" msgid="6074099855478444183">"MTP அல்லது PTP செயல்பாடு இயக்கத்தில் உள்ளது"</string>
<string name="dlg_confirm_unmount_title" product="nosdcard" msgid="3843209947310774105">"USB சேமிப்பிடத்தை அகற்றவா?"</string>
<string name="dlg_confirm_unmount_title" product="default" msgid="4400426555375434431">"SD கார்டை அகற்றவா?"</string>
@@ -1437,9 +1437,9 @@
<string name="storage_dialog_unmounted" msgid="515810851912430933">"<xliff:g id="NAME_0">^1</xliff:g> பாதுகாப்பாக வெளியேற்றப்பட்டது, ஆனால் இன்னும் கிடைக்கிறது. \n\n<xliff:g id="NAME_1">^1</xliff:g>ஐப் பயன்படுத்த, முதலில் அதைச் செருக வேண்டும்."</string>
<string name="storage_dialog_unmountable" msgid="7082856306456936054">"<xliff:g id="NAME_0">^1</xliff:g> சிதைந்துள்ளது. \n\n<xliff:g id="NAME_1">^1</xliff:g>ஐப் பயன்படுத்த, முதலில் அதை அமைக்க வேண்டும்."</string>
<string name="storage_dialog_unsupported" msgid="8274023677580782553">"சாதனம் <xliff:g id="NAME_0">^1</xliff:g>ஐ ஆதரிக்காது. \n\nசாதனத்தில் <xliff:g id="NAME_1">^1</xliff:g>ஐப் பயன்படுத்த, முதலில் அதை அமைக்க வேண்டும்."</string>
- <string name="storage_internal_format_details" msgid="2780806013122012384">"மீட்டமைவுக்குப் பிறகு, <xliff:g id="NAME_0">^1</xliff:g>ஐ மற்ற சாதனங்களில் பயன்படுத்தலாம். \n\n<xliff:g id="NAME_1">^1</xliff:g> இல் உள்ள எல்லா தரவும் அழிக்கப்படும். அதனால் முதலில் காப்புப் பிரதி எடுத்துக்கொள்ளவும். \n\n"<b>"படங்கள் & மற்ற மீடியாவைக் காப்புப் பிரதி எடுத்தல்"</b>" \nமீடியா கோப்புகளை சாதனத்தின் மாற்று சேமிப்பகத்திற்கு நகர்த்தவும் அல்லது USB கேபிளைப் பயன்படுத்தி கணினிக்கு மாற்றவும். \n\n"<b>"பயன்பாடுகளின் காப்புப் பிரதி"</b>" \n<xliff:g id="NAME_6">^1</xliff:g> இல் சேமிக்கப்பட்ட எல்லா பயன்பாடுகளும் நிறுவல்நீக்கப்பட்டு அவற்றின் தரவு அழிக்கப்படும். இந்தப் பயன்பாடுகளை வைத்திருக்க, சாதனத்தின் மாற்று சேமிப்பகத்திற்கு அவற்றை நகர்த்தவும்."</string>
+ <string name="storage_internal_format_details" msgid="2780806013122012384">"மீட்டமைவுக்குப் பிறகு, <xliff:g id="NAME_0">^1</xliff:g>ஐ மற்ற சாதனங்களில் பயன்படுத்தலாம். \n\n<xliff:g id="NAME_1">^1</xliff:g> இல் உள்ள எல்லா தரவும் அழிக்கப்படும். அதனால் முதலில் காப்புப் பிரதி எடுத்துக்கொள்ளவும். \n\n"<b>"படங்கள் & மற்ற மீடியாவைக் காப்புப் பிரதி எடுத்தல்"</b>" \nமீடியா கோப்புகளை சாதனத்தின் மாற்று சேமிப்பகத்திற்கு நகர்த்தவும் அல்லது USB கேபிளைப் பயன்படுத்தி கணினிக்கு மாற்றவும். \n\n"<b>"ஆப்ஸின் காப்புப் பிரதி"</b>" \n<xliff:g id="NAME_6">^1</xliff:g> இல் சேமிக்கப்பட்ட எல்லா பயன்பாடுகளும் நிறுவல்நீக்கப்பட்டு அவற்றின் தரவு அழிக்கப்படும். இந்த ஆப்ஸை வைத்திருக்க, சாதனத்தின் மாற்று சேமிப்பகத்திற்கு அவற்றை நகர்த்தவும்."</string>
<string name="storage_internal_unmount_details" msgid="4667435317528624039"><b>"<xliff:g id="NAME_0">^1</xliff:g>ஐ வெளியேற்றும்போது, அதில் சேமித்த பயன்பாடுகள் வேலை செய்யாததுடன், அதில் சேமித்திருந்த மீடியா கோப்புகள் மீண்டும் அதைச் செருகும் வரை கிடைக்காது."</b>" \n\nஇந்தச் சாதனத்தில் மட்டும் வேலை செய்யுமாறு <xliff:g id="NAME_1">^1</xliff:g> மீட்டமைக்கப்பட்டதால் பிற சாதனங்களில் அது வேலை செய்யாது."</string>
- <string name="storage_internal_forget_details" msgid="5655856574682184453">"<xliff:g id="NAME">^1</xliff:g> இல் உள்ள பயன்பாடுகள், படங்கள் அல்லது தரவைப் பயன்படுத்த, அதை மீண்டும் செருகவும். \n\nசாதனம் இல்லையெனில், இந்தச் சேமிப்பகத்தை அகற்றிவிடவும். \n\nஅவ்வாறு அகற்றினால், அதிலுள்ள தரவு இனி கிடைக்காது. \n\nபயன்பாடுகளை மீண்டும் நிறுவிக்கொள்ளலாம், எனினும் அவற்றின் தரவு மீண்டும் கிடைக்காது."</string>
+ <string name="storage_internal_forget_details" msgid="5655856574682184453">"<xliff:g id="NAME">^1</xliff:g> இல் உள்ள பயன்பாடுகள், படங்கள் அல்லது தரவைப் பயன்படுத்த, அதை மீண்டும் செருகவும். \n\nசாதனம் இல்லையெனில், இந்தச் சேமிப்பகத்தை அகற்றிவிடவும். \n\nஅவ்வாறு அகற்றினால், அதிலுள்ள தரவு இனி கிடைக்காது. \n\nஆப்ஸை மீண்டும் நிறுவிக்கொள்ளலாம், எனினும் அவற்றின் தரவு மீண்டும் கிடைக்காது."</string>
<string name="storage_internal_forget_confirm_title" msgid="331032276130605241">"<xliff:g id="NAME">^1</xliff:g>ஐ அகற்றவா?"</string>
<string name="storage_internal_forget_confirm" msgid="3052483375203727176">"<xliff:g id="NAME">^1</xliff:g> இல் சேமிக்கப்பட்ட அனைத்து பயன்பாடுகள், படங்கள் மற்றும் தரவு ஆகியவற்றை நிரந்தரமாக இழப்பீர்கள்."</string>
<string name="storage_detail_apps" msgid="8154648512504196820">"ஆப்ஸ்"</string>
@@ -1466,7 +1466,7 @@
<string name="storage_wizard_format_progress_title" msgid="6905902731208646436">"<xliff:g id="NAME">^1</xliff:g> வடிவமைக்கப்படுகிறது"</string>
<string name="storage_wizard_format_progress_body" msgid="5346709539457190419">"<xliff:g id="NAME">^1</xliff:g> ஃபார்மேட் செய்யப்படும்போது அகற்ற வேண்டாம்."</string>
<string name="storage_wizard_migrate_title" msgid="7440473364104826496">"புதிய சேமிப்பகத்திற்கு நகர்த்துக"</string>
- <string name="storage_wizard_migrate_body" msgid="4959356431201831339">"படங்கள், கோப்புகள் மற்றும் சில பயன்பாடுகளை புதிய <xliff:g id="NAME">^1</xliff:g>க்கு நகர்த்தலாம். \n\nநகர்த்துவதற்கு <xliff:g id="TIME">^2</xliff:g> ஆகும், மேலும் அகச் சேமிப்பகத்தில் <xliff:g id="SIZE">^3</xliff:g> இடத்தைக் காலிசெய்யும். இந்தச் செயல்பாட்டின் போது, சில பயன்பாடுகள் இயங்காது."</string>
+ <string name="storage_wizard_migrate_body" msgid="4959356431201831339">"படங்கள், கோப்புகள் மற்றும் சில ஆப்ஸை புதிய <xliff:g id="NAME">^1</xliff:g>க்கு நகர்த்தலாம். \n\nநகர்த்துவதற்கு <xliff:g id="TIME">^2</xliff:g> ஆகும், மேலும் அகச் சேமிப்பகத்தில் <xliff:g id="SIZE">^3</xliff:g> இடத்தைக் காலிசெய்யும். இந்தச் செயல்பாட்டின் போது, சில பயன்பாடுகள் இயங்காது."</string>
<string name="storage_wizard_migrate_now" msgid="9004605853000689024">"இப்போதே நகர்த்தவும்"</string>
<string name="storage_wizard_migrate_later" msgid="5303070653970922924">"பிறகு நகர்த்தவும்"</string>
<string name="storage_wizard_migrate_confirm_title" msgid="5768497751644935313">"தரவை நகர்த்தவும்"</string>
@@ -1777,8 +1777,8 @@
<string name="lock_settings_profile_unification_dialog_confirm" msgid="888942752619181804">"ஒரே பூட்டைப் பயன்படுத்து"</string>
<string name="lock_settings_profile_unification_dialog_uncompliant_confirm" msgid="8046452284593057185">"ஒரே பூட்டைப் பயன்படுத்து"</string>
<string name="lock_settings_profile_unified_summary" msgid="5347244550751740962">"சாதனத் திரைப் பூட்டைப் போன்றது"</string>
- <string name="manageapplications_settings_title" msgid="6876782217962262376">"பயன்பாடுகளை நிர்வகி"</string>
- <string name="manageapplications_settings_summary" msgid="5092964799412478962">"நிறுவப்பட்ட பயன்பாடுகளை நிர்வகி மற்றும் அகற்று"</string>
+ <string name="manageapplications_settings_title" msgid="6876782217962262376">"ஆப்ஸை நிர்வகி"</string>
+ <string name="manageapplications_settings_summary" msgid="5092964799412478962">"நிறுவப்பட்ட ஆப்ஸை நிர்வகி மற்றும் அகற்று"</string>
<string name="applications_settings" msgid="368331725658793179">"ஆப்ஸ் தகவல்"</string>
<string name="applications_settings_summary" msgid="8888258399577123906">"அமைப்புகளை நிர்வகிக்கும், விரைவு துவக்கத்திற்கான குறுக்குவழிகளை அமைக்கும்"</string>
<string name="applications_settings_header" msgid="3766501606045211098">"ஆப்ஸ் அமைப்பு"</string>
@@ -1786,8 +1786,8 @@
<string name="install_applications_title" msgid="8164828577588659496">"எல்லா ஆப்ஸ் ஆதாரங்களையும் அனுமதி"</string>
<string name="recent_app_category_title" msgid="7688788038277126727">"சமீபத்தில் திறந்தவை"</string>
<string name="see_all_apps_title" msgid="6435061912110347474">"<xliff:g id="COUNT">%1$d</xliff:g> பயன்பாடுகளையும் காட்டு"</string>
- <string name="install_all_warning" product="tablet" msgid="4580699862358542727">"அறியப்படாத பயன்பாடுகளால் உங்கள் டேப்லெட்டும் தனிப்பட்ட தரவும் அதிகம் பாதிப்பிற்கு உள்ளாகும். இந்த மூலத்திலிருந்து பயன்பாடுகளை நிறுவுவதால், அவற்றைப் பயன்படுத்தும் போது உங்கள் டேப்லெட்டுக்கு ஏதேனும் சேதம் ஏற்பட்டாலோ அல்லது தரவை இழந்தாலோ, அதற்கு நீங்கள்தான் பொறுப்பாவீர்கள் என்பதை ஏற்கிறீர்கள்."</string>
- <string name="install_all_warning" product="default" msgid="7445839116997296358">"அறியப்படாத பயன்பாடுககளால் உங்கள் மொபைலும் தனிப்பட்ட தரவும் அதிகம் பாதிப்பிற்கு உள்ளாகும். இந்த மூலத்திலிருந்து பயன்பாடுகளை நிறுவுவதால், அவற்றைப் பயன்படுத்தும் போது உங்கள் மொபைலுக்கு ஏதேனும் சேதம் ஏற்பட்டாலோ அல்லது தரவை இழந்தாலோ, அதற்கு நீங்கள்தான் பொறுப்பாவீர்கள் என்பதை ஏற்கிறீர்கள்."</string>
+ <string name="install_all_warning" product="tablet" msgid="4580699862358542727">"அறியப்படாத பயன்பாடுகளால் உங்கள் டேப்லெட்டும் தனிப்பட்ட தரவும் அதிகம் பாதிப்பிற்கு உள்ளாகும். இந்த மூலத்திலிருந்து ஆப்ஸை நிறுவுவதால், அவற்றைப் பயன்படுத்தும் போது உங்கள் டேப்லெட்டுக்கு ஏதேனும் சேதம் ஏற்பட்டாலோ அல்லது தரவை இழந்தாலோ, அதற்கு நீங்கள்தான் பொறுப்பாவீர்கள் என்பதை ஏற்கிறீர்கள்."</string>
+ <string name="install_all_warning" product="default" msgid="7445839116997296358">"அறியப்படாத பயன்பாடுககளால் உங்கள் மொபைலும் தனிப்பட்ட தரவும் அதிகம் பாதிப்பிற்கு உள்ளாகும். இந்த மூலத்திலிருந்து ஆப்ஸை நிறுவுவதால், அவற்றைப் பயன்படுத்தும் போது உங்கள் மொபைலுக்கு ஏதேனும் சேதம் ஏற்பட்டாலோ அல்லது தரவை இழந்தாலோ, அதற்கு நீங்கள்தான் பொறுப்பாவீர்கள் என்பதை ஏற்கிறீர்கள்."</string>
<string name="install_all_warning" product="device" msgid="9141585291103603515">"அறியப்படாத ஆப்ஸால் உங்கள் சாதனம் மற்றும் தனிப்பட்ட தரவு மிக எளிதாகப் பாதிப்புக்குள்ளாகும். இந்த மூலத்திலிருந்து ஆப்ஸை நிறுவி, பயன்படுத்தும்போது, உங்கள் சாதனத்திற்கு ஏதேனும் சேதம் ஏற்பட்டாலோ தரவை இழந்தாலோ, அதற்கு நீங்கள்தான் பொறுப்பாவீர்கள் என்பதை ஏற்கிறீர்கள்."</string>
<string name="advanced_settings" msgid="6282069364060968122">"மேம்பட்ட அமைப்பு"</string>
<string name="advanced_settings_summary" msgid="5912237062506771716">"மேலும் அமைப்பு விருப்பங்களை இயக்கு"</string>
@@ -3386,7 +3386,7 @@
<string name="manage_zen_access_title" msgid="3058206309728524196">"\'தொந்தரவு செய்யாதே\' அணுகல்"</string>
<string name="zen_access_detail_switch" msgid="8706332327904974500">"’தொந்தரவு செய்ய வேண்டாம்’ அம்சத்தை அனுமதி"</string>
<string name="zen_access_empty_text" msgid="7667538993781607731">"\'தொந்தரவு செய்யாதே\' அணுகலை நிறுவப்பட்ட பயன்பாடுகள் எதுவும் கோரவில்லை"</string>
- <string name="loading_notification_apps" msgid="1978345231934072091">"பயன்பாடுகளை ஏற்றுகிறது..."</string>
+ <string name="loading_notification_apps" msgid="1978345231934072091">"ஆப்ஸை ஏற்றுகிறது..."</string>
<string name="app_notifications_off_desc" msgid="3904090905748895146">"உங்கள் கோரிக்கையின் படி, சாதனத்தில் இந்த ஆப்ஸின் அறிவிப்புகள் தோன்றுவதை Android தடுக்கிறது"</string>
<string name="channel_notifications_off_desc" msgid="8005444443218306611">"உங்கள் கோரிக்கையின் படி, இந்தச் சாதனத்தில், இந்த வகை அறிவிப்புகள் தோன்றுவதை Android தடுக்கிறது"</string>
<string name="channel_group_notifications_off_desc" msgid="7154205544298672850">"உங்கள் கோரிக்கையின் படி, இந்தச் சாதனத்தில், இந்தக் குழு அறிவிப்புகள் தோன்றுவதை Android தடுக்கிறது"</string>
@@ -3636,7 +3636,7 @@
<string name="filter_notif_blocked_channels" msgid="6110799550327612670">"வகைகள்: முடக்கப்பட்டன"</string>
<string name="filter_notif_dnd_channels" msgid="3251570137256371092">"வகை: டிஎன்டியை மீறும்"</string>
<string name="advanced_apps" msgid="6643869089344883537">"மேம்பட்டவை"</string>
- <string name="configure_apps" msgid="4066683118857400943">"பயன்பாடுகளை உள்ளமை"</string>
+ <string name="configure_apps" msgid="4066683118857400943">"ஆப்ஸை உள்ளமை"</string>
<string name="unknown_app" msgid="2312052973570376877">"அறியப்படாத ஆப்ஸ்"</string>
<string name="app_permissions" msgid="3215958256821756086">"அனுமதி நிர்வாகம்"</string>
<string name="app_permissions_summary" msgid="8785798165776061594">"<xliff:g id="APPS">%1$s</xliff:g> பயன்படுத்தும் ஆப்ஸ்"</string>
@@ -3799,9 +3799,9 @@
<string name="permit_draw_overlay" msgid="9039092257052422344">"பிற ஆப்ஸின் மேலே காட்டுவதை அனுமதி"</string>
<string name="allow_overlay_description" msgid="6669524816705082807">"நீங்கள் பயன்படுத்தும் பிற ஆப்ஸின் மேலே உள்ளடக்கத்தைக் காட்ட, இந்த ஆப்ஸை அனுமதிக்கும். மேலும், அவற்றை நீங்கள் பயன்படுத்தும் போது இது குறுக்கிடக்கூடும் அல்லது அவை தோன்றும் விதத்தையோ, செயல்படும் விதத்தையோ மாற்றக்கூடும்."</string>
<string name="keywords_vr_listener" msgid="5312633527788917750">"vr விர்ச்சுவல் ரியாலிட்டி லிஷனர் ஸ்டீரியோ உதவிச் சேவை"</string>
- <string name="keywords_system_alert_window" msgid="3936658600272194599">"சாதனம் விழிப்பூட்டல் சாளரம் உரையாடல் காட்டு பிற பயன்பாடுகளின் மேல்"</string>
+ <string name="keywords_system_alert_window" msgid="3936658600272194599">"சாதனம் விழிப்பூட்டல் சாளரம் உரையாடல் காட்டு பிற ஆப்ஸின் மேல்"</string>
<string name="overlay_settings" msgid="3325154759946433666">"பிற ஆப்ஸின் மேலே காட்டு"</string>
- <string name="system_alert_window_summary" msgid="7703582115861844158">"பிற பயன்பாடுகளின் மேலே காட்டுவதற்கு <xliff:g id="COUNT_1">%2$d</xliff:g> இல் <xliff:g id="COUNT_0">%1$d</xliff:g> பயன்பாடுகள் அனுமதிக்கப்பட்டுள்ளன"</string>
+ <string name="system_alert_window_summary" msgid="7703582115861844158">"பிற ஆப்ஸின் மேலே காட்டுவதற்கு <xliff:g id="COUNT_1">%2$d</xliff:g> இல் <xliff:g id="COUNT_0">%1$d</xliff:g> பயன்பாடுகள் அனுமதிக்கப்பட்டுள்ளன"</string>
<string name="filter_overlay_apps" msgid="6336897660213304743">"அனுமதி பெற்ற பயன்பாடுகள்"</string>
<string name="app_permission_summary_allowed" msgid="6458476982015518778">"அனுமதிக்கப்பட்டது"</string>
<string name="app_permission_summary_not_allowed" msgid="1171642541675462584">"அனுமதிக்கப்படவில்லை"</string>
@@ -3810,7 +3810,7 @@
<string name="keywords_write_settings" msgid="3450405263390246293">"முறைமை அமைப்புகளை எழுது மாற்று"</string>
<string name="write_settings_summary" msgid="4650251358459404247">"<xliff:g id="COUNT_1">%2$d</xliff:g> இல் <xliff:g id="COUNT_0">%1$d</xliff:g> பயன்பாடுகள் முறைமை அமைப்புகளை மாற்ற அனுமதிக்கப்பட்டுள்ளன"</string>
<string name="financial_apps_sms_access_title" msgid="3422655018008259655">"நிதி ஆப்ஸிற்கான மெசேஜ் அணுகல்"</string>
- <string name="filter_install_sources_apps" msgid="4519839764020866701">"பிற பயன்பாடுகளை நிறுவலாம்"</string>
+ <string name="filter_install_sources_apps" msgid="4519839764020866701">"பிற ஆப்ஸை நிறுவலாம்"</string>
<string name="filter_write_settings_apps" msgid="6864144615530081121">"முறைமை அமைப்புகளை மாற்றலாம்"</string>
<string name="write_settings_title" msgid="5852614614193830632">"முறைமை அமைப்புகளை மாற்றலாம்"</string>
<string name="write_system_settings" msgid="20450765210832463">"சாதன அமைப்புகளை மாற்று"</string>
@@ -4166,14 +4166,14 @@
<string name="enterprise_privacy_exposure_changes_category" msgid="1877045221796512001">"உங்கள் நிறுவனத்தின் நிர்வாகி செய்த மாற்றங்கள்"</string>
<string name="enterprise_privacy_device_access_category" msgid="2967602674816237833">"இந்தச் சாதனத்திற்கான உங்கள் அணுகல்"</string>
<string name="enterprise_privacy_enterprise_data" msgid="6551504749971424942">"உங்கள் பணிக் கணக்குடன் தொடர்புடைய மின்னஞ்சல் மற்றும் கேலெண்டர் போன்ற தரவு"</string>
- <string name="enterprise_privacy_installed_packages" msgid="4376014821459811800">"உங்கள் சாதனத்தில் உள்ள பயன்பாடுகளின் பட்டியல்"</string>
+ <string name="enterprise_privacy_installed_packages" msgid="4376014821459811800">"உங்கள் சாதனத்தில் உள்ள ஆப்ஸின் பட்டியல்"</string>
<string name="enterprise_privacy_usage_stats" msgid="445762931318731975">"ஒவ்வொரு பயன்பாட்டிலும் செலவழித்த நேரமும் தரவும்"</string>
<string name="enterprise_privacy_network_logs" msgid="5427398751599441159">"மிகச் சமீபத்திய நெட்வொர்க் ட்ராஃபிக் பதிவு"</string>
<string name="enterprise_privacy_bug_reports" msgid="283443567328836380">"மிகச் சமீபத்திய பிழை அறிக்கை"</string>
<string name="enterprise_privacy_security_logs" msgid="8936969480449604726">"மிகச் சமீபத்திய பாதுகாப்புப் பதிவு"</string>
<string name="enterprise_privacy_none" msgid="5990646476868794882">"ஏதுமில்லை"</string>
<string name="enterprise_privacy_enterprise_installed_packages" msgid="6575025134782391212">"நிறுவிய ஆப்ஸ்"</string>
- <string name="enterprise_privacy_apps_count_estimation_info" msgid="5020730108878608500">"பயன்பாடுகளின் எண்ணிக்கை கணிப்பின் அடிப்படையிலானது. இதில் Play ஸ்டோரிலிருந்து நிறுவப்படாத பயன்பாடுகள் சேர்க்கப்படாமல் இருக்கலாம்."</string>
+ <string name="enterprise_privacy_apps_count_estimation_info" msgid="5020730108878608500">"ஆப்ஸின் எண்ணிக்கை கணிப்பின் அடிப்படையிலானது. இதில் Play ஸ்டோரிலிருந்து நிறுவப்படாத பயன்பாடுகள் சேர்க்கப்படாமல் இருக்கலாம்."</string>
<plurals name="enterprise_privacy_number_packages_lower_bound" formatted="false" msgid="5161417161943060602">
<item quantity="other">குறைந்தபட்சம் <xliff:g id="COUNT_1">%d</xliff:g> பயன்பாடுகள்</item>
<item quantity="one">குறைந்தபட்சம் <xliff:g id="COUNT_0">%d</xliff:g> பயன்பாடு</item>
diff --git a/tests/CarDeveloperOptions/res/values-zh-rTW/strings.xml b/tests/CarDeveloperOptions/res/values-zh-rTW/strings.xml
index 0b7e7a0..7e5a9ad 100644
--- a/tests/CarDeveloperOptions/res/values-zh-rTW/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-zh-rTW/strings.xml
@@ -3297,7 +3297,7 @@
<string name="asst_capabilities_actions_replies_title" msgid="3929395108744251338">"智慧操作和回覆"</string>
<string name="asst_capabilities_actions_replies_summary" msgid="5647029698181357902">"自動將內容通知操作和快速回覆新增至通知"</string>
<string name="hide_silent_icons_title" msgid="1070905516921542662">"隱藏無聲通知狀態圖示"</string>
- <string name="hide_silent_icons_summary" msgid="2624346914488256888">"在狀態列隱藏無聲通知圖示"</string>
+ <string name="hide_silent_icons_summary" msgid="2624346914488256888">"在狀態列隱藏靜音通知圖示"</string>
<string name="notification_badging_title" msgid="6311699476970264712">"允許使用通知圓點"</string>
<string name="notification_bubbles_title" msgid="9196562435741861317">"泡泡"</string>
<string name="notification_bubbles_summary" msgid="4624512775901949578">"使用浮動快速鍵,隨時隨地快速存取應用程式內容"</string>
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/carapi.xml b/tests/EmbeddedKitchenSinkApp/res/layout/carapi.xml
new file mode 100644
index 0000000..9cb733e
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/carapi.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <ScrollView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" >
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal" >
+ <Button
+ android:id="@+id/button_carapi_createandconnect"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/createandconnect" />
+ <Button
+ android:id="@+id/button_carapi_disconnect"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/disconnect" />
+ <TextView
+ android:id="@+id/carapi_createandconnect"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/empty"
+ android:layout_weight="1" />
+ </LinearLayout>
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal" >
+ <Button
+ android:id="@+id/button_carapi_createcar"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/createcar" />
+ <TextView
+ android:id="@+id/carapi_createcar"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/empty"
+ android:layout_weight="1" />
+ </LinearLayout>
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal" >
+ <Button
+ android:id="@+id/button_carapi_createcar_with_status_change"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/createcar_with_status_change" />
+ <TextView
+ android:id="@+id/carapi_createcar_with_status_change"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/empty"
+ android:layout_weight="1" />
+ </LinearLayout>
+
+ </LinearLayout>
+ </ScrollView>
+</LinearLayout>
diff --git a/tests/EmbeddedKitchenSinkApp/res/values/strings.xml b/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
index 4efec4d..27844f3 100644
--- a/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
@@ -321,4 +321,10 @@
<!-- Users -->
<string name="users_apply_button" translatable="false">Apply</string>
+
+ <!-- carapi -->
+ <string name="createandconnect" translatable="false">createCarAndConnect</string>
+ <string name="disconnect" translatable="false">disconnect</string>
+ <string name="createcar" translatable="false">createCar</string>
+ <string name="createcar_with_status_change" translatable="false">createCarWithStatusChange</string>
</resources>
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/CarApiTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/CarApiTestFragment.java
new file mode 100644
index 0000000..3e594f5
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/CarApiTestFragment.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.car.kitchensink;
+
+import android.car.Car;
+import android.content.ComponentName;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.fragment.app.Fragment;
+
+public class CarApiTestFragment extends Fragment {
+
+ private static final String TAG = CarApiTestFragment.class.getSimpleName();
+
+ private Car mCarForCreateAndConnect;
+ private Car mCarForCreateCar;
+ private Car mCarForStatusChange;
+
+ private TextView mTextForCreateAndConnect;
+ private TextView mTextForCreateCar;
+ private TextView mTextForCreateCarWithStatusChangeListener;
+
+ private int mConnectCountForStatusChange = 0;
+
+ private final ServiceConnection mServiceConnectionForCreateAndConnect =
+ new ServiceConnection() {
+
+ private int mConnectCount = 0;
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mConnectCount++;
+ mTextForCreateAndConnect.setText("bound service connected, isConnected:"
+ + mCarForCreateAndConnect.isConnected()
+ + " connect count:" + mConnectCount);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mTextForCreateAndConnect.setText("bound service disconnected, isConnected:"
+ + mCarForCreateAndConnect.isConnected());
+ }
+ };
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
+ View view = inflater.inflate(R.layout.carapi, container, false);
+ mTextForCreateAndConnect = view.findViewById(R.id.carapi_createandconnect);
+ mTextForCreateCar = view.findViewById(R.id.carapi_createcar);
+ mTextForCreateCarWithStatusChangeListener = view.findViewById(
+ R.id.carapi_createcar_with_status_change);
+ view.findViewById(R.id.button_carapi_createandconnect).setOnClickListener(
+ (View v) -> {
+ mCarForCreateAndConnect = Car.createCar(getContext(),
+ mServiceConnectionForCreateAndConnect);
+ mCarForCreateAndConnect.connect();
+ });
+ view.findViewById(R.id.button_carapi_createcar).setOnClickListener(
+ (View v) -> {
+ mCarForCreateCar = Car.createCar(getContext());
+ mTextForCreateCar.setText("isConnected:" + mCarForCreateCar.isConnected());
+ });
+ view.findViewById(R.id.button_carapi_createcar_with_status_change).setOnClickListener(
+ (View v) -> {
+ mCarForStatusChange = Car.createCar(getContext(), null,
+ Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER,
+ (Car car, boolean ready) -> {
+ if (ready) {
+ mConnectCountForStatusChange++;
+ mTextForCreateCarWithStatusChangeListener.setText(
+ "service ready, isConnected:"
+ + car.isConnected()
+ + " connect count:"
+ + mConnectCountForStatusChange);
+ } else {
+ mTextForCreateCarWithStatusChangeListener.setText(
+ "bound service crashed, isConnected:"
+ + car.isConnected());
+ }
+ });
+ });
+ return view;
+ }
+}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
index 1448ede..b4a4d1b 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
@@ -23,15 +23,12 @@
import android.car.hardware.hvac.CarHvacManager;
import android.car.hardware.power.CarPowerManager;
import android.car.hardware.property.CarPropertyManager;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
-import android.os.IBinder;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -165,8 +162,10 @@
new FragmentMenuEntry("alert window", AlertDialogTestFragment.class),
new FragmentMenuEntry("assistant", CarAssistantFragment.class),
new FragmentMenuEntry("audio", AudioTestFragment.class),
+
new FragmentMenuEntry("bluetooth headset", BluetoothHeadsetFragment.class),
new FragmentMenuEntry("bluetooth messaging test", MapMceTestFragment.class),
+ new FragmentMenuEntry("carapi", CarApiTestFragment.class),
new FragmentMenuEntry("carboard", KeyboardTestFragment.class),
new FragmentMenuEntry("connectivity", ConnectivityFragment.class),
new FragmentMenuEntry("cubes test", CubesTestFragment.class),
@@ -285,8 +284,12 @@
mCarApi.disconnect();
mCarApi = null;
}
- mCarApi = Car.createCar(this, mServiceConnection);
- mCarApi.connect();
+ mCarApi = Car.createCar(this, null, Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER,
+ (Car car, boolean ready) -> {
+ if (ready) {
+ initManagers(car);
+ }
+ });
}
@Override
@@ -340,32 +343,23 @@
.commit();
}
- private final ServiceConnection mServiceConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- Log.d(TAG, "Connected to Car Service");
- synchronized (mPropertyManagerReady) {
- mHvacManager = (CarHvacManager) mCarApi.getCarManager(
- android.car.Car.HVAC_SERVICE);
- mPowerManager = (CarPowerManager) mCarApi.getCarManager(
- android.car.Car.POWER_SERVICE);
- mPropertyManager = (CarPropertyManager) mCarApi.getCarManager(
- android.car.Car.PROPERTY_SERVICE);
- mSensorManager = (CarSensorManager) mCarApi.getCarManager(
- android.car.Car.SENSOR_SERVICE);
- mCarAppFocusManager =
- (CarAppFocusManager) mCarApi.getCarManager(Car.APP_FOCUS_SERVICE);
- mCarProjectionManager =
- (CarProjectionManager) mCarApi.getCarManager(Car.PROJECTION_SERVICE);
- mPropertyManagerReady.notifyAll();
- }
+ private void initManagers(Car car) {
+ synchronized (mPropertyManagerReady) {
+ mHvacManager = (CarHvacManager) car.getCarManager(
+ android.car.Car.HVAC_SERVICE);
+ mPowerManager = (CarPowerManager) car.getCarManager(
+ android.car.Car.POWER_SERVICE);
+ mPropertyManager = (CarPropertyManager) car.getCarManager(
+ android.car.Car.PROPERTY_SERVICE);
+ mSensorManager = (CarSensorManager) car.getCarManager(
+ android.car.Car.SENSOR_SERVICE);
+ mCarAppFocusManager =
+ (CarAppFocusManager) car.getCarManager(Car.APP_FOCUS_SERVICE);
+ mCarProjectionManager =
+ (CarProjectionManager) car.getCarManager(Car.PROJECTION_SERVICE);
+ mPropertyManagerReady.notifyAll();
}
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- Log.d(TAG, "Disconnect from Car Service");
- }
- };
+ }
public Car getCar() {
return mCarApi;
diff --git a/tests/MultiDisplayTest/Android.mk b/tests/MultiDisplayTest/Android.mk
index c5c7ce0..779c646 100644
--- a/tests/MultiDisplayTest/Android.mk
+++ b/tests/MultiDisplayTest/Android.mk
@@ -24,10 +24,12 @@
LOCAL_PACKAGE_NAME := MultiDisplayTest
-LOCAL_SDK_VERSION := current
-
LOCAL_DEX_PREOPT := false
+LOCAL_JAVA_LIBRARIES += android.car
+
+LOCAL_PRIVATE_PLATFORM_APIS := true
+
LOCAL_STATIC_ANDROID_LIBRARIES += \
androidx.lifecycle_lifecycle-livedata \
androidx.lifecycle_lifecycle-viewmodel \
diff --git a/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/MDTest.java b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/MDTest.java
index a665772..b93283e 100644
--- a/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/MDTest.java
+++ b/tests/MultiDisplayTest/src/com/google/android/car/multidisplaytest/MDTest.java
@@ -15,6 +15,7 @@
*/
package com.google.android.car.multidisplaytest;
+import android.car.Car;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
@@ -47,6 +48,7 @@
private Button mMenuButton;
private RecyclerView mMenu;
private View mMenuContent;
+ private Car mCar;
private interface ClickHandler {
void onClick();
@@ -129,6 +131,14 @@
Log.i(TAG, "Creating MDTest activity view");
mFragmentManager = MDTest.this.getSupportFragmentManager();
onNewIntent(getIntent());
+ mCar = Car.createCar(this, null, Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER,
+ (Car car, boolean ready) -> {
+ if (ready) {
+ Log.i(TAG, "car ready");
+ } else {
+ Log.i(TAG, "car not ready");
+ }
+ });
}
private void toggleMenuVisibility() {
diff --git a/tests/carservice_unit_test/src/android/car/CarTest.java b/tests/carservice_unit_test/src/android/car/CarTest.java
index 91a1ddb..9ac8d75 100644
--- a/tests/carservice_unit_test/src/android/car/CarTest.java
+++ b/tests/carservice_unit_test/src/android/car/CarTest.java
@@ -32,10 +32,14 @@
import android.content.ComponentName;
import android.content.Context;
import android.os.IBinder;
+import android.os.Looper;
import android.os.ServiceManager;
+import android.util.Pair;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import com.android.car.CarServiceUtils;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -44,7 +48,8 @@
import org.mockito.MockitoSession;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.quality.Strictness;
-import org.mockito.stubbing.Answer;
+
+import java.util.concurrent.CopyOnWriteArrayList;
/**
* Unit test for Car API.
@@ -58,6 +63,8 @@
@Mock
private Context mContext;
+ private int mGetServiceCallCount;
+
// It is tricky to mock this. So create dummy version instead.
private ICar.Stub mService = new ICar.Stub() {
@Override
@@ -83,6 +90,19 @@
}
};
+ private class LifecycleListener implements Car.CarServiceLifecycleListener {
+ // Use thread safe one to prevent adding another lock for testing
+ private CopyOnWriteArrayList<Pair<Car, Boolean>> mEvents = new CopyOnWriteArrayList<>();
+
+ @Override
+ public void onLifecycleChanged(Car car, boolean ready) {
+ assertThat(Looper.getMainLooper()).isEqualTo(Looper.myLooper());
+ mEvents.add(new Pair<>(car, ready));
+ }
+ }
+
+ private final LifecycleListener mLifecycleListener = new LifecycleListener();
+
@Before
public void setUp() {
mMockingSession = mockitoSession()
@@ -90,6 +110,7 @@
.mockStatic(ServiceManager.class)
.strictness(Strictness.LENIENT)
.startMocking();
+ mGetServiceCallCount = 0;
}
@After
@@ -102,6 +123,32 @@
() -> ServiceManager.getService(Car.CAR_SERVICE_BINDER_SERVICE_NAME));
}
+ private void expectBindService() {
+ when(mContext.bindServiceAsUser(anyObject(), anyObject(), anyInt(),
+ anyObject())).thenReturn(true);
+ }
+
+ private void returnServiceAfterNSereviceManagerCalls(int returnNonNullAfterThisCall) {
+ doAnswer((InvocationOnMock invocation) -> {
+ mGetServiceCallCount++;
+ if (mGetServiceCallCount > returnNonNullAfterThisCall) {
+ return mService;
+ } else {
+ return null;
+ }
+ }).when(() -> ServiceManager.getService(Car.CAR_SERVICE_BINDER_SERVICE_NAME));
+ }
+
+ private void assertServiceBoundOnce() {
+ verify(mContext, times(1)).bindServiceAsUser(anyObject(), anyObject(), anyInt(),
+ anyObject());
+ }
+
+ private void assertOneListenerCallAndClear(Car expectedCar, boolean ready) {
+ assertThat(mLifecycleListener.mEvents).containsExactly(new Pair<>(expectedCar, ready));
+ mLifecycleListener.mEvents.clear();
+ }
+
@Test
public void testCreateCarSuccessWithCarServiceRunning() {
expectService(mService);
@@ -118,32 +165,110 @@
@Test
public void testCreateCarOkWhenCarServiceIsStarted() {
+ returnServiceAfterNSereviceManagerCalls(10);
// Car service is not running yet and binsService call should start it.
- when(mContext.bindServiceAsUser(anyObject(), anyObject(), anyInt(),
- anyObject())).thenReturn(true);
- final int returnNonNullAfterThisCall = 10;
- doAnswer(new Answer() {
-
- private int mCallCount = 0;
-
- @Override
- public Object answer(InvocationOnMock invocation) {
- mCallCount++;
- if (mCallCount > returnNonNullAfterThisCall) {
- return mService;
- } else {
- return null;
- }
- }
- }).when(() -> ServiceManager.getService(Car.CAR_SERVICE_BINDER_SERVICE_NAME));
+ expectBindService();
Car car = Car.createCar(mContext);
assertThat(car).isNotNull();
- verify(mContext, times(1)).bindServiceAsUser(anyObject(), anyObject(),
- anyInt(), anyObject());
+ assertServiceBoundOnce();
// Just call these to guarantee that nothing crashes when service is connected /
// disconnected.
- car.getServiceConnectionListener().onServiceConnected(new ComponentName("", ""), mService);
- car.getServiceConnectionListener().onServiceDisconnected(new ComponentName("", ""));
+ runOnMainSyncSafe(() -> {
+ car.getServiceConnectionListener().onServiceConnected(new ComponentName("", ""),
+ mService);
+ car.getServiceConnectionListener().onServiceDisconnected(new ComponentName("", ""));
+ });
+ }
+
+ @Test
+ public void testCreateCarWithStatusChangeNoServiceConnectionWithCarServiceStarted() {
+ returnServiceAfterNSereviceManagerCalls(10);
+ expectBindService();
+ Car car = Car.createCar(mContext, null,
+ Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER, mLifecycleListener);
+ assertThat(car).isNotNull();
+ assertServiceBoundOnce();
+ waitForMainToBeComplete();
+ assertOneListenerCallAndClear(car, true);
+
+ // Just call these to guarantee that nothing crashes with these call.
+ runOnMainSyncSafe(() -> {
+ car.getServiceConnectionListener().onServiceConnected(new ComponentName("", ""),
+ mService);
+ car.getServiceConnectionListener().onServiceDisconnected(new ComponentName("", ""));
+ });
+ }
+
+ @Test
+ public void testCreateCarWithStatusChangeNoServiceHandleCarServiceRestart() {
+ expectService(mService);
+ expectBindService();
+ Car car = Car.createCar(mContext, null,
+ Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER, mLifecycleListener);
+ assertThat(car).isNotNull();
+ assertServiceBoundOnce();
+
+ // fake connection
+ runOnMainSyncSafe(() ->
+ car.getServiceConnectionListener().onServiceConnected(new ComponentName("", ""),
+ mService));
+ waitForMainToBeComplete();
+ assertOneListenerCallAndClear(car, true);
+
+ // fake crash
+ runOnMainSyncSafe(() ->
+ car.getServiceConnectionListener().onServiceDisconnected(
+ new ComponentName("", "")));
+ waitForMainToBeComplete();
+ assertOneListenerCallAndClear(car, false);
+
+
+ // fake restart
+ runOnMainSyncSafe(() ->
+ car.getServiceConnectionListener().onServiceConnected(new ComponentName("", ""),
+ mService));
+ waitForMainToBeComplete();
+ assertOneListenerCallAndClear(car, true);
+ }
+
+ @Test
+ public void testCreateCarWithStatusChangeDirectCallInsideMainForServiceAlreadyReady() {
+ expectService(mService);
+ expectBindService();
+ runOnMainSyncSafe(() -> {
+ Car car = Car.createCar(mContext, null,
+ Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER, mLifecycleListener);
+ assertThat(car).isNotNull();
+ verify(mContext, times(1)).bindServiceAsUser(anyObject(), anyObject(), anyInt(),
+ anyObject());
+ // mLifecycleListener should have been called as this is main thread.
+ assertOneListenerCallAndClear(car, true);
+ });
+ }
+
+ @Test
+ public void testCreateCarWithStatusChangeDirectCallInsideMainForServiceReadyLater() {
+ returnServiceAfterNSereviceManagerCalls(10);
+ expectBindService();
+ runOnMainSyncSafe(() -> {
+ Car car = Car.createCar(mContext, null,
+ Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER, mLifecycleListener);
+ assertThat(car).isNotNull();
+ assertServiceBoundOnce();
+ assertOneListenerCallAndClear(car, true);
+ });
+ }
+
+ private void runOnMainSyncSafe(Runnable runnable) {
+ if (Looper.getMainLooper() == Looper.myLooper()) {
+ runnable.run();
+ } else {
+ CarServiceUtils.runOnMainSync(runnable);
+ }
+ }
+ private void waitForMainToBeComplete() {
+ // dispatch dummy runnable and confirm that it is done.
+ runOnMainSyncSafe(() -> { });
}
}