Merge "Add metrics domain for Bluetooth"
diff --git a/car-lib/src/android/car/cluster/renderer/IInstrumentCluster.aidl b/car-lib/src/android/car/cluster/renderer/IInstrumentCluster.aidl
index 7deecc7..4f41796 100644
--- a/car-lib/src/android/car/cluster/renderer/IInstrumentCluster.aidl
+++ b/car-lib/src/android/car/cluster/renderer/IInstrumentCluster.aidl
@@ -28,6 +28,8 @@
/**
* Returns {@link IInstrumentClusterNavigation} that will be passed to the navigation
* application.
+ *
+ * TODO(b/141992448) : remove blocking call
*/
IInstrumentClusterNavigation getNavigationService();
diff --git a/car-lib/src/android/car/cluster/renderer/IInstrumentClusterHelper.aidl b/car-lib/src/android/car/cluster/renderer/IInstrumentClusterHelper.aidl
new file mode 100644
index 0000000..680e241
--- /dev/null
+++ b/car-lib/src/android/car/cluster/renderer/IInstrumentClusterHelper.aidl
@@ -0,0 +1,49 @@
+/*
+ * 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 android.car.cluster.renderer;
+
+import android.content.Intent;
+import android.os.Bundle;
+
+/**
+ * Helper binder API for InstrumentClusterRenderingService. This contains binder calls to car
+ * service.
+ *
+ * @hide
+ */
+interface IInstrumentClusterHelper {
+ /**
+ * Start an activity to specified display / user. The activity is considered as
+ * in fixed mode for the display and will be re-launched if the activity crashes, the package
+ * is updated or goes to background for whatever reason.
+ * Only one activity can exist in fixed mode for the target display and calling this multiple
+ * times with different {@code Intent} will lead into making all previous activities into
+ * non-fixed normal state (= will not be re-launched.)
+ *
+ * Do not change binder transaction number.
+ */
+ boolean startFixedActivityModeForDisplayAndUser(in Intent intent,
+ in Bundle activityOptionsBundle, int userId) = 0;
+ /**
+ * The activity lauched on the display is no longer in fixed mode. Re-launching or finishing
+ * should not trigger re-launfhing any more. Note that Activity for non-current user will
+ * be auto-stopped and there is no need to call this for user swiching. Note that this does not
+ * stop the activity but it will not be re-launched any more.
+ *
+ * Do not change binder transaction number.
+ */
+ void stopFixedActivityMode(int displayId) = 1;
+}
diff --git a/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java b/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java
index 6996119..ffe7910 100644
--- a/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java
+++ b/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java
@@ -22,6 +22,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.UserIdInt;
import android.app.ActivityOptions;
import android.app.Service;
import android.car.Car;
@@ -82,20 +83,34 @@
*/
@SystemApi
public abstract class InstrumentClusterRenderingService extends Service {
+ /**
+ * Key to pass IInstrumentClusterHelper binder in onBind call {@link Intent} through extra
+ * {@link Bundle). Both extra bundle and binder itself use this key.
+ *
+ * @hide
+ */
+ public static final String EXTRA_BUNDLE_KEY_FOR_INSTRUMENT_CLUSTER_HELPER =
+ "android.car.cluster.renderer.IInstrumentClusterHelper";
+
private static final String TAG = CarLibLog.TAG_CLUSTER;
private static final String BITMAP_QUERY_WIDTH = "w";
private static final String BITMAP_QUERY_HEIGHT = "h";
+ private final Handler mUiHandler = new Handler(Looper.getMainLooper());
+
private final Object mLock = new Object();
+ // Main thread only
private RendererBinder mRendererBinder;
- private Handler mUiHandler = new Handler(Looper.getMainLooper());
private ActivityOptions mActivityOptions;
private ClusterActivityState mActivityState;
private ComponentName mNavigationComponent;
@GuardedBy("mLock")
private ContextOwner mNavContextOwner;
+ @GuardedBy("mLock")
+ private IInstrumentClusterHelper mInstrumentClusterHelper;
+
private static final int IMAGE_CACHE_SIZE_BYTES = 4 * 1024 * 1024; /* 4 mb */
private final LruCache<String, Bitmap> mCache = new LruCache<String, Bitmap>(
IMAGE_CACHE_SIZE_BYTES) {
@@ -157,6 +172,18 @@
Log.d(TAG, "onBind, intent: " + intent);
}
+ Bundle bundle = intent.getBundleExtra(EXTRA_BUNDLE_KEY_FOR_INSTRUMENT_CLUSTER_HELPER);
+ IBinder binder = null;
+ if (bundle != null) {
+ binder = bundle.getBinder(EXTRA_BUNDLE_KEY_FOR_INSTRUMENT_CLUSTER_HELPER);
+ }
+ if (binder == null) {
+ Log.wtf(TAG, "IInstrumentClusterHelper not passed through binder");
+ } else {
+ synchronized (mLock) {
+ mInstrumentClusterHelper = IInstrumentClusterHelper.Stub.asInterface(binder);
+ }
+ }
if (mRendererBinder == null) {
mRendererBinder = new RendererBinder(getNavigationRenderer());
}
@@ -196,6 +223,76 @@
public void onNavigationComponentReleased() {
}
+ @Nullable
+ private IInstrumentClusterHelper getClusterHelper() {
+ synchronized (mLock) {
+ if (mInstrumentClusterHelper == null) {
+ Log.w("mInstrumentClusterHelper still null, should wait until onBind",
+ new RuntimeException());
+ }
+ return mInstrumentClusterHelper;
+ }
+ }
+
+ /**
+ * Start Activity in fixed mode.
+ *
+ * <p>Activity launched in this way will stay visible across crash, package updatge
+ * or other Activity launch. So this should be carefully used for case like apps running
+ * in instrument cluster.</p>
+ *
+ * <p> Only one Activity can stay in this mode for a display and launching other Activity
+ * with this call means old one get out of the mode. Alternatively
+ * {@link #stopFixedActivityMode(int)} can be called to get the top activitgy out of this
+ * mode.</p>
+ *
+ * @param intent Should include specific {@code ComponentName}.
+ * @param options Should include target display.
+ * @param userId Target user id
+ * @return {@code true} if succeeded. {@code false} may mean the target component is not ready
+ * or available. Note that failure can happen during early boot-up stage even if the
+ * target Activity is in normal state and client should retry when it fails. Once it is
+ * successfully launched, car service will guarantee that it is running across crash or
+ * other events.
+ *
+ * @hide
+ */
+ protected boolean startFixedActivityModeFoDisplayAndUser(@NonNull Intent intent,
+ @NonNull ActivityOptions options, @UserIdInt int userId) {
+ IInstrumentClusterHelper helper = getClusterHelper();
+ if (helper == null) {
+ return false;
+ }
+ try {
+ return helper.startFixedActivityModeForDisplayAndUser(intent, options.toBundle(),
+ userId);
+ } catch (RemoteException e) {
+ Log.w("Remote exception from car service", e);
+ // Probably car service will restart and rebind. So do nothing.
+ }
+ return false;
+ }
+
+
+ /**
+ * Stop fixed mode for top Activity in the display. Crashing or launching other Activity
+ * will not re-launch the top Activity any more.
+ *
+ * @hide
+ */
+ protected void stopFixedActivityMode(int displayId) {
+ IInstrumentClusterHelper helper = getClusterHelper();
+ if (helper == null) {
+ return;
+ }
+ try {
+ helper.stopFixedActivityMode(displayId);
+ } catch (RemoteException e) {
+ Log.w("Remote exception from car service, displayId:" + displayId, e);
+ // Probably car service will restart and rebind. So do nothing.
+ }
+ }
+
/**
* Updates the cluster navigation activity by checking which activity to show (an activity of
* the {@link #mNavContextOwner}). If not yet launched, it will do so.
@@ -370,16 +467,19 @@
@CallSuper
@Override
protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
- writer.println("**" + getClass().getSimpleName() + "**");
- writer.println("renderer binder: " + mRendererBinder);
- if (mRendererBinder != null) {
- writer.println("navigation renderer: " + mRendererBinder.mNavigationRenderer);
+ synchronized (mLock) {
+ writer.println("**" + getClass().getSimpleName() + "**");
+ writer.println("renderer binder: " + mRendererBinder);
+ if (mRendererBinder != null) {
+ writer.println("navigation renderer: " + mRendererBinder.mNavigationRenderer);
+ }
+ writer.println("navigation focus owner: " + getNavigationContextOwner());
+ writer.println("activity options: " + mActivityOptions);
+ writer.println("activity state: " + mActivityState);
+ writer.println("current nav component: " + mNavigationComponent);
+ writer.println("current nav packages: " + getNavigationContextOwner().mPackageNames);
+ writer.println("mInstrumentClusterHelper" + mInstrumentClusterHelper);
}
- writer.println("navigation focus owner: " + getNavigationContextOwner());
- writer.println("activity options: " + mActivityOptions);
- writer.println("activity state: " + mActivityState);
- writer.println("current nav component: " + mNavigationComponent);
- writer.println("current nav packages: " + getNavigationContextOwner().mPackageNames);
}
private class RendererBinder extends IInstrumentCluster.Stub {
diff --git a/car_product/sepolicy/public/property_contexts b/car_product/sepolicy/public/property_contexts
index b1987c6..9646ac9 100644
--- a/car_product/sepolicy/public/property_contexts
+++ b/car_product/sepolicy/public/property_contexts
@@ -1,3 +1,3 @@
-android.car.number_guest_user_templates u:object_r:car_bootuser_prop:s0
-android.car.number_user_templates u:object_r:car_bootuser_prop:s0
+android.car.number_pre_created_guests u:object_r:car_bootuser_prop:s0
+android.car.number_pre_created_users u:object_r:car_bootuser_prop:s0
android.car.systemuser.bootuseroverrideid u:object_r:car_bootuser_prop:s0
diff --git a/service/res/values-nb/strings.xml b/service/res/values-nb/strings.xml
index 6e20ffa..528d9c0 100644
--- a/service/res/values-nb/strings.xml
+++ b/service/res/values-nb/strings.xml
@@ -123,7 +123,7 @@
<string name="car_permission_desc_car_powertrain" msgid="1116007372551797796">"Tilgang til informasjon om bilens drivlinje."</string>
<string name="car_permission_label_car_power" msgid="8111448088314368268">"lese bilens batterinivå"</string>
<string name="car_permission_desc_car_power" msgid="9202079903668652864">"Tilgang til bilens batterinivå."</string>
- <string name="car_permission_label_enroll_trust" msgid="3512907900486690218">"Registrer en pålitelig enhet"</string>
- <string name="car_permission_desc_enroll_trust" msgid="4148649994602185130">"Tillat registrering av pålitelige enheter"</string>
+ <string name="car_permission_label_enroll_trust" msgid="3512907900486690218">"Registrer en godkjent enhet"</string>
+ <string name="car_permission_desc_enroll_trust" msgid="4148649994602185130">"Tillat registrering av godkjente enheter"</string>
<string name="trust_device_default_name" msgid="4213625926070261253">"Enheten min"</string>
</resources>
diff --git a/service/src/com/android/car/ICarImpl.java b/service/src/com/android/car/ICarImpl.java
index e168af3..c2eab7c 100644
--- a/service/src/com/android/car/ICarImpl.java
+++ b/service/src/com/android/car/ICarImpl.java
@@ -38,6 +38,7 @@
import android.util.Slog;
import android.util.TimingsTraceLog;
+import com.android.car.am.FixedActivityService;
import com.android.car.audio.CarAudioService;
import com.android.car.cluster.InstrumentClusterService;
import com.android.car.garagemode.GarageModeService;
@@ -82,6 +83,7 @@
private final CarPropertyService mCarPropertyService;
private final CarNightService mCarNightService;
private final AppFocusService mAppFocusService;
+ private final FixedActivityService mFixedActivityService;
private final GarageModeService mGarageModeService;
private final InstrumentClusterService mInstrumentClusterService;
private final CarLocationService mCarLocationService;
@@ -131,7 +133,7 @@
final Resources res = mContext.getResources();
final int maxRunningUsers = res.getInteger(
com.android.internal.R.integer.config_multiuserMaxRunningUsers);
- mCarUserService = new CarUserService(serviceContext, userManager,
+ mCarUserService = new CarUserService(serviceContext, mUserManagerHelper, userManager,
ActivityManager.getService(), maxRunningUsers);
mCarOccupantZoneService = new CarOccupantZoneService(serviceContext);
mSystemActivityMonitoringService = new SystemActivityMonitoringService(serviceContext);
@@ -155,6 +157,7 @@
mAppFocusService = new AppFocusService(serviceContext, mSystemActivityMonitoringService);
mCarAudioService = new CarAudioService(serviceContext);
mCarNightService = new CarNightService(serviceContext, mCarPropertyService);
+ mFixedActivityService = new FixedActivityService(serviceContext);
mInstrumentClusterService = new InstrumentClusterService(serviceContext,
mAppFocusService, mCarInputService);
mSystemStateControllerService = new SystemStateControllerService(
@@ -184,6 +187,7 @@
CarLocalServices.addService(SystemInterface.class, mSystemInterface);
CarLocalServices.addService(CarDrivingStateService.class, mCarDrivingStateService);
CarLocalServices.addService(PerUserCarServiceHelper.class, mPerUserCarServiceHelper);
+ CarLocalServices.addService(FixedActivityService.class, mFixedActivityService);
// Be careful with order. Service depending on other service should be inited later.
List<CarServiceBase> allServices = new ArrayList<>();
@@ -201,6 +205,7 @@
allServices.add(mAppFocusService);
allServices.add(mCarAudioService);
allServices.add(mCarNightService);
+ allServices.add(mFixedActivityService);
allServices.add(mInstrumentClusterService);
allServices.add(mSystemStateControllerService);
allServices.add(mPerUserCarServiceHelper);
diff --git a/service/src/com/android/car/am/FixedActivityService.java b/service/src/com/android/car/am/FixedActivityService.java
new file mode 100644
index 0000000..b1ca176
--- /dev/null
+++ b/service/src/com/android/car/am/FixedActivityService.java
@@ -0,0 +1,427 @@
+/*
+ * 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.android.car.am;
+
+import static com.android.car.CarLog.TAG_AM;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.app.ActivityManager.StackInfo;
+import android.app.ActivityOptions;
+import android.app.IActivityManager;
+import android.app.IProcessObserver;
+import android.app.TaskStackListener;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.Display;
+
+import com.android.car.CarLocalServices;
+import com.android.car.CarServiceBase;
+import com.android.car.user.CarUserService;
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * Monitors top activity for a display and guarantee activity in fixed mode is re-launched if it has
+ * crashed or gone to background for whatever reason.
+ *
+ * <p>This component also monitors the upddate of the target package and re-launch it once
+ * update is complete.</p>
+ */
+public final class FixedActivityService implements CarServiceBase {
+
+ private static final boolean DBG = false;
+
+ private static class RunningActivityInfo {
+ @NonNull
+ public final Intent intent;
+
+ @NonNull
+ public final ActivityOptions activityOptions;
+
+ @UserIdInt
+ public final int userId;
+
+ // Only used in a method for local book-keeping. So do not need a lock.
+ // This does not represent the current visibility.
+ public boolean isVisible;
+
+ RunningActivityInfo(@NonNull Intent intent, @NonNull ActivityOptions activityOptions,
+ @UserIdInt int userId) {
+ this.intent = intent;
+ this.activityOptions = activityOptions;
+ this.userId = userId;
+ }
+
+ @Override
+ public String toString() {
+ return "RunningActivityInfo{intent:" + intent + ",activityOptions:" + activityOptions
+ + ",userId:" + userId + "}";
+ }
+ }
+
+ private final Context mContext;
+
+ private final IActivityManager mAm;
+
+ private final UserManager mUm;
+
+ private final CarUserService.UserCallback mUserCallback = new CarUserService.UserCallback() {
+ @Override
+ public void onUserLockChanged(@UserIdInt int userId, boolean unlocked) {
+ // Nothing to do
+ }
+
+ @Override
+ public void onSwitchUser(@UserIdInt int userId) {
+ synchronized (mLock) {
+ mRunningActivities.clear();
+ }
+ }
+ };
+
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (Intent.ACTION_PACKAGE_CHANGED.equals(action)
+ || Intent.ACTION_PACKAGE_REPLACED.equals(
+ action)) {
+ launchIfNecessary();
+ }
+ }
+ };
+
+ // It says listener but is actually callback.
+ private final TaskStackListener mTaskStackListener = new TaskStackListener() {
+ @Override
+ public void onTaskStackChanged() {
+ launchIfNecessary();
+ }
+ };
+
+ private final IProcessObserver mProcessObserver = new IProcessObserver.Stub() {
+ @Override
+ public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
+ launchIfNecessary();
+ }
+
+ @Override
+ public void onForegroundServicesChanged(int pid, int uid, int fgServiceTypes) {
+ // ignore
+ }
+
+ @Override
+ public void onProcessDied(int pid, int uid) {
+ launchIfNecessary();
+ }
+ };
+
+ private final Object mLock = new Object();
+
+ // key: displayId
+ @GuardedBy("mLock")
+ private final SparseArray<RunningActivityInfo> mRunningActivities =
+ new SparseArray<>(/* capacity= */ 1); // default to one cluster only case
+
+ @GuardedBy("mLock")
+ private boolean mEventMonitoringActive;
+
+ public FixedActivityService(Context context) {
+ mContext = context;
+ mAm = ActivityManager.getService();
+ mUm = context.getSystemService(UserManager.class);
+ }
+
+
+ @Override
+ public void init() {
+ // nothing to do
+ }
+
+ @Override
+ public void release() {
+ stopMonitoringEvents();
+ }
+
+ @Override
+ public void dump(PrintWriter writer) {
+ writer.println("*FixedActivityService*");
+ synchronized (mLock) {
+ writer.println("mRunningActivities:" + mRunningActivities
+ + " ,mEventMonitoringActive:" + mEventMonitoringActive);
+ }
+ }
+
+ private void startMonitoringEvents() {
+ synchronized (mLock) {
+ if (mEventMonitoringActive) {
+ return;
+ }
+ mEventMonitoringActive = true;
+ }
+ CarUserService userService = CarLocalServices.getService(CarUserService.class);
+ userService.addUserCallback(mUserCallback);
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+ mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter,
+ /* broadcastPermission= */ null, /* scheduler= */ null);
+ try {
+ mAm.registerTaskStackListener(mTaskStackListener);
+ mAm.registerProcessObserver(mProcessObserver);
+ } catch (RemoteException e) {
+ Log.e(TAG_AM, "remote exception from AM", e);
+ }
+ }
+
+ private void stopMonitoringEvents() {
+ synchronized (mLock) {
+ if (!mEventMonitoringActive) {
+ return;
+ }
+ mEventMonitoringActive = false;
+ }
+ CarUserService userService = CarLocalServices.getService(CarUserService.class);
+ userService.removeUserCallback(mUserCallback);
+ try {
+ mAm.unregisterTaskStackListener(mTaskStackListener);
+ mAm.unregisterProcessObserver(mProcessObserver);
+ } catch (RemoteException e) {
+ Log.e(TAG_AM, "remote exception from AM", e);
+ }
+ }
+
+ @Nullable
+ private List<StackInfo> getStackInfos() {
+ try {
+ return mAm.getAllStackInfos();
+ } catch (RemoteException e) {
+ Log.e(TAG_AM, "remote exception from AM", e);
+ }
+ return null;
+ }
+
+ /**
+ * Launches all stored fixed mode activities if necessary.
+ * @param displayId Display id to check if it is visible. If check is not necessary, should pass
+ * {@link Display#INVALID_DISPLAY}.
+ * @return true if fixed Activity for given {@code displayId} is visible / successfully
+ * launched. It will return false for {@link Display#INVALID_DISPLAY} {@code displayId}.
+ */
+ private boolean launchIfNecessary(int displayId) {
+ List<StackInfo> infos = getStackInfos();
+ if (infos == null) {
+ Log.e(TAG_AM, "cannot get StackInfo from AM");
+ return false;
+ }
+ synchronized (mLock) {
+ if (mRunningActivities.size() == 0) {
+ // it must have been stopped.
+ if (DBG) {
+ Log.i(TAG_AM, "empty activity list", new RuntimeException());
+ }
+ return false;
+ }
+ for (int i = 0; i < mRunningActivities.size(); i++) {
+ mRunningActivities.valueAt(i).isVisible = false;
+ }
+ for (StackInfo stackInfo : infos) {
+ RunningActivityInfo activityInfo = mRunningActivities.get(stackInfo.displayId);
+ if (activityInfo == null) {
+ continue;
+ }
+ int topUserId = stackInfo.taskUserIds[stackInfo.taskUserIds.length - 1];
+ if (activityInfo.intent.getComponent().equals(stackInfo.topActivity)
+ && activityInfo.userId == topUserId && stackInfo.visible) {
+ // top one is matching.
+ activityInfo.isVisible = true;
+ continue;
+ }
+ if (DBG) {
+ Log.i(TAG_AM, "Unmatched top activity:" + stackInfo.topActivity
+ + " user:" + topUserId + " display:" + stackInfo.displayId);
+ }
+ }
+ for (int i = 0; i < mRunningActivities.size(); i++) {
+ RunningActivityInfo activityInfo = mRunningActivities.valueAt(i);
+ if (activityInfo.isVisible) {
+ continue;
+ }
+ if (!isComponentAvailable(activityInfo.intent.getComponent(),
+ activityInfo.userId) || !isUserAllowedToLaunchActivity(
+ activityInfo.userId)) {
+ continue;
+ }
+ Log.i(TAG_AM, "Launching Activity for fixed mode. Intent:" + activityInfo.intent
+ + ",userId:" + UserHandle.of(activityInfo.userId) + ",displayId:"
+ + mRunningActivities.keyAt(i));
+ try {
+ mContext.startActivityAsUser(activityInfo.intent,
+ activityInfo.activityOptions.toBundle(),
+ UserHandle.of(activityInfo.userId));
+ activityInfo.isVisible = true;
+ } catch (Exception e) { // Catch all for any app related issues.
+ Log.w(TAG_AM, "Cannot start activity:" + activityInfo.intent, e);
+ }
+ }
+ RunningActivityInfo activityInfo = mRunningActivities.get(displayId);
+ if (activityInfo == null) {
+ return false;
+ }
+ return activityInfo.isVisible;
+ }
+ }
+
+ private void launchIfNecessary() {
+ launchIfNecessary(Display.INVALID_DISPLAY);
+ }
+
+ private void logComponentNotFound(ComponentName component, @UserIdInt int userId,
+ Exception e) {
+ Log.e(TAG_AM, "Specified Component not found:" + component
+ + " for userid:" + userId, e);
+ }
+
+ private boolean isComponentAvailable(ComponentName component, @UserIdInt int userId) {
+ PackageInfo packageInfo;
+ try {
+ packageInfo = mContext.getPackageManager().getPackageInfoAsUser(
+ component.getPackageName(), PackageManager.GET_ACTIVITIES, userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ logComponentNotFound(component, userId, e);
+ return false;
+ }
+ if (packageInfo == null || packageInfo.activities == null) {
+ // may not be necessary but additional safety check
+ logComponentNotFound(component, userId, new RuntimeException());
+ return false;
+ }
+ String fullName = component.getClassName();
+ String shortName = component.getShortClassName();
+ for (ActivityInfo info : packageInfo.activities) {
+ if (info.name.equals(fullName) || info.name.equals(shortName)) {
+ return true;
+ }
+ }
+ logComponentNotFound(component, userId, new RuntimeException());
+ return false;
+ }
+
+ private boolean isUserAllowedToLaunchActivity(@UserIdInt int userId) {
+ int currentUser = ActivityManager.getCurrentUser();
+ if (userId == currentUser) {
+ return true;
+ }
+ int[] profileIds = mUm.getEnabledProfileIds(currentUser);
+ for (int id : profileIds) {
+ if (id == userId) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isDisplayAllowedForFixedMode(int displayId) {
+ if (displayId == Display.DEFAULT_DISPLAY || displayId == Display.INVALID_DISPLAY) {
+ Log.w(TAG_AM, "Target display cannot be used for fixed mode, displayId:" + displayId,
+ new RuntimeException());
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Checks {@link InstrumentClusterRenderingService#startFixedActivityModeFoDisplayAndUser(
+ * Intent, ActivityOptions, int)}
+ */
+ public boolean startFixedActivityModeForDisplayAndUser(@NonNull Intent intent,
+ @NonNull ActivityOptions options, int displayId, @UserIdInt int userId) {
+ if (!isDisplayAllowedForFixedMode(displayId)) {
+ return false;
+ }
+ if (!isUserAllowedToLaunchActivity(userId)) {
+ Log.e(TAG_AM, "startFixedActivityModeForDisplayAndUser, requested user:" + userId
+ + " cannot launch activity, Intent:" + intent);
+ return false;
+ }
+ ComponentName component = intent.getComponent();
+ if (component == null) {
+ Log.e(TAG_AM,
+ "startFixedActivityModeForDisplayAndUser: No component specified for "
+ + "requested Intent"
+ + intent);
+ return false;
+ }
+ if (!isComponentAvailable(component, userId)) {
+ return false;
+ }
+ boolean startMonitoringEvents = false;
+ synchronized (mLock) {
+ if (mRunningActivities.size() == 0) {
+ startMonitoringEvents = true;
+ }
+ RunningActivityInfo activityInfo = mRunningActivities.get(displayId);
+ if (activityInfo == null) {
+ activityInfo = new RunningActivityInfo(intent, options, userId);
+ mRunningActivities.put(displayId, activityInfo);
+ }
+ }
+ boolean launched = launchIfNecessary(displayId);
+ if (!launched) {
+ synchronized (mLock) {
+ mRunningActivities.remove(displayId);
+ }
+ }
+ // If first trial fails, let client know and do not retry as it can be wrong setting.
+ if (startMonitoringEvents && launched) {
+ startMonitoringEvents();
+ }
+ return launched;
+ }
+
+ /** Check {@link InstrumentClusterRenderingService#stopFixedActivityMode(int)} */
+ public void stopFixedActivityMode(int displayId) {
+ if (!isDisplayAllowedForFixedMode(displayId)) {
+ return;
+ }
+ boolean stopMonitoringEvents = false;
+ synchronized (mLock) {
+ mRunningActivities.remove(displayId);
+ if (mRunningActivities.size() == 0) {
+ stopMonitoringEvents = true;
+ }
+ }
+ if (stopMonitoringEvents) {
+ stopMonitoringEvents();
+ }
+ }
+}
diff --git a/service/src/com/android/car/cluster/InstrumentClusterService.java b/service/src/com/android/car/cluster/InstrumentClusterService.java
index 7963e0f..6e186d5 100644
--- a/service/src/com/android/car/cluster/InstrumentClusterService.java
+++ b/service/src/com/android/car/cluster/InstrumentClusterService.java
@@ -15,19 +15,23 @@
*/
package com.android.car.cluster;
+import static android.car.cluster.renderer.InstrumentClusterRenderingService.EXTRA_BUNDLE_KEY_FOR_INSTRUMENT_CLUSTER_HELPER;
import static android.car.settings.CarSettings.Global.DISABLE_INSTRUMENTATION_SERVICE;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.app.ActivityOptions;
import android.car.CarAppFocusManager;
import android.car.cluster.IInstrumentClusterManagerCallback;
import android.car.cluster.IInstrumentClusterManagerService;
import android.car.cluster.renderer.IInstrumentCluster;
+import android.car.cluster.renderer.IInstrumentClusterHelper;
import android.car.cluster.renderer.IInstrumentClusterNavigation;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
@@ -46,6 +50,7 @@
import com.android.car.CarLog;
import com.android.car.CarServiceBase;
import com.android.car.R;
+import com.android.car.am.FixedActivityService;
import com.android.car.user.CarUserService;
import com.android.internal.annotations.GuardedBy;
@@ -72,16 +77,18 @@
*/
@Deprecated
private final ClusterManagerService mClusterManagerService = new ClusterManagerService();
- private final Object mSync = new Object();
- @GuardedBy("mSync")
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
private ContextOwner mNavContextOwner = NO_OWNER;
- @GuardedBy("mSync")
+ @GuardedBy("mLock")
private IInstrumentCluster mRendererService;
// If renderer service crashed / stopped and this class fails to rebind with it immediately,
// we should wait some time before next attempt. This may happen during APK update for example.
+ @GuardedBy("mLock")
private DeferredRebinder mDeferredRebinder;
// Whether {@link android.car.cluster.renderer.InstrumentClusterRendererService} is bound
// (although not necessarily connected)
+ @GuardedBy("mLock")
private boolean mRendererBound = false;
/**
@@ -95,7 +102,7 @@
}
IInstrumentCluster service = IInstrumentCluster.Stub.asInterface(binder);
ContextOwner navContextOwner;
- synchronized (mSync) {
+ synchronized (mLock) {
mRendererService = service;
navContextOwner = mNavContextOwner;
}
@@ -110,19 +117,39 @@
Log.d(TAG, "onServiceDisconnected, name: " + name);
}
mContext.unbindService(this);
- mRendererBound = false;
-
- synchronized (mSync) {
+ DeferredRebinder rebinder;
+ synchronized (mLock) {
+ mRendererBound = false;
mRendererService = null;
+ if (mDeferredRebinder == null) {
+ mDeferredRebinder = new DeferredRebinder();
+ }
+ rebinder = mDeferredRebinder;
}
-
- if (mDeferredRebinder == null) {
- mDeferredRebinder = new DeferredRebinder();
- }
- mDeferredRebinder.rebind();
+ rebinder.rebind();
}
};
+ private final IInstrumentClusterHelper mInstrumentClusterHelper =
+ new IInstrumentClusterHelper.Stub() {
+ @Override
+ public boolean startFixedActivityModeForDisplayAndUser(Intent intent,
+ Bundle activityOptionsBundle, int userId) {
+ ActivityOptions options = new ActivityOptions(activityOptionsBundle);
+ FixedActivityService service = CarLocalServices.getService(
+ FixedActivityService.class);
+ return service.startFixedActivityModeForDisplayAndUser(intent, options,
+ options.getLaunchDisplayId(), userId);
+ }
+
+ @Override
+ public void stopFixedActivityMode(int displayId) {
+ FixedActivityService service = CarLocalServices.getService(
+ FixedActivityService.class);
+ service.stopFixedActivityMode(displayId);
+ }
+ };
+
public InstrumentClusterService(Context context, AppFocusService appFocusService,
CarInputService carInputService) {
mContext = context;
@@ -184,7 +211,7 @@
IInstrumentCluster service;
ContextOwner requester = new ContextOwner(uid, pid);
ContextOwner newOwner = acquire ? requester : NO_OWNER;
- synchronized (mSync) {
+ synchronized (mLock) {
if ((acquire && Objects.equals(mNavContextOwner, requester))
|| (!acquire && !Objects.equals(mNavContextOwner, requester))) {
// Nothing to do here. Either the same owner is acquiring twice, or someone is
@@ -231,6 +258,11 @@
Intent intent = new Intent();
intent.setComponent(ComponentName.unflattenFromString(rendererService));
+ // Litle bit inefficiency here as Intent.getIBinderExtra() is a hidden API.
+ Bundle bundle = new Bundle();
+ bundle.putBinder(EXTRA_BUNDLE_KEY_FOR_INSTRUMENT_CLUSTER_HELPER,
+ mInstrumentClusterHelper.asBinder());
+ intent.putExtra(EXTRA_BUNDLE_KEY_FOR_INSTRUMENT_CLUSTER_HELPER, bundle);
return mContext.bindServiceAsUser(intent, mRendererServiceConnection,
Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM);
}
@@ -272,7 +304,7 @@
private IInstrumentCluster getInstrumentClusterRendererService() {
IInstrumentCluster service;
- synchronized (mSync) {
+ synchronized (mLock) {
service = mRendererService;
}
return service;
diff --git a/service/src/com/android/car/trust/BleMessageStream.java b/service/src/com/android/car/trust/BleMessageStream.java
new file mode 100644
index 0000000..19da2c2
--- /dev/null
+++ b/service/src/com/android/car/trust/BleMessageStream.java
@@ -0,0 +1,60 @@
+/*
+ * 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.android.car.trust;
+
+import android.annotation.NonNull;
+
+import com.android.car.BLEStreamProtos.BLEOperationProto.OperationType;
+
+/**
+ * Handles the streaming of BLE messages to a specific {@link android.bluetooth.BluetoothDevice}.
+ *
+ * <p>This stream will handle if messages to a particular peripheral need to be split into
+ * multiple messages or if the messages can be sent all at once. Internally, it will have its own
+ * protocol for how the split messages are structured.
+ */
+interface BleMessageStream {
+ /** Registers the given callback to be notified of various events within the stream. */
+ void registerCallback(@NonNull BleMessageStreamCallback callback);
+
+ /** Unregisters the given callback from being notified of stream events. */
+ void unregisterCallback(@NonNull BleMessageStreamCallback callback);
+
+ /** Sets the maximum size of a message that can be sent. */
+ void setMaxWriteSize(int maxWriteSize);
+
+ /** Returns the maximum size of a message that can be sent. */
+ int getMaxWriteSize();
+
+ /**
+ * Writes the given message to the write characteristic set on this stream to the
+ * {@code BleutoothDevice} associated with this stream.
+ *
+ * <p>The given message will adhere to the max write size set on this stream. If the message is
+ * larger than this size, then this stream should take the appropriate actions necessary to
+ * chunk the message to the device so that no parts of the message is dropped.
+ *
+ * <p>If there was an error, then this stream will notify the [callback] of this stream via a
+ * call to its {@code onWriteMessageError} method.
+ *
+ * @param message The message to send.
+ * @param operationType The {@link OperationType} of this message.
+ * @param isPayloadEncrypted {@code true} if the message to send has been encrypted.
+ */
+ void writeMessage(@NonNull byte[] message, @NonNull OperationType operationType,
+ boolean isPayloadEncrypted);
+}
diff --git a/service/src/com/android/car/trust/BleMessageStream.kt b/service/src/com/android/car/trust/BleMessageStream.kt
deleted file mode 100644
index 0759fb0..0000000
--- a/service/src/com/android/car/trust/BleMessageStream.kt
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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.android.car.trust
-
-import android.bluetooth.BluetoothDevice
-import android.bluetooth.BluetoothGattCharacteristic
-import com.android.car.BLEStreamProtos.BLEOperationProto.OperationType
-
-/**
- * Handles the streaming of BLE messages to a specific [BluetoothDevice].
- *
- * This stream will handle if messages to a particular peripheral need to be split into
- * multiple messages or if the messages can be sent all at once. Internally, it will have its own
- * protocol for how the split messages are structured.
- */
-internal interface BleMessageStream {
- /** The [BluetoothDevice] to write and receive messages from. */
- val device: BluetoothDevice
-
- /** The characteristic on the device to write messages to. */
- val writeCharacteristic: BluetoothGattCharacteristic
-
- /** The characteristic that messages will be written to by the remote device. */
- val readCharacteristic: BluetoothGattCharacteristic
-
- /** An optional callback that will be notified of the progress of writes and reads. */
- var callback: BleMessageStreamCallback?
-
- /** The maximum size of a message that can be sent. */
- var maxWriteSize: Int
-
- /**
- * Writes the given message to the [writeCharacteristic] of the [device] of this stream.
- *
- * The given message will adhere to the [maxWriteSize]. If the message is larger than this size,
- * then this stream should take the appropriate actions necessary to chunk the message to
- * the device so that no parts of the message is dropped.
- *
- * If there was an error, then this stream will notify the [callback] of this stream via a call
- * to its `onWriteMessageError` mthod.
- *
- * @param message The message to send.
- * @param operationType The [OperationType] of this message.
- * @param isPayloadEncrypted `true` if the message to send has been encrypted.
- */
- fun writeMessage(message: ByteArray, operationType: OperationType, isPayloadEncrypted: Boolean)
-}
diff --git a/service/src/com/android/car/trust/BleMessageStreamCallback.kt b/service/src/com/android/car/trust/BleMessageStreamCallback.java
similarity index 64%
rename from service/src/com/android/car/trust/BleMessageStreamCallback.kt
rename to service/src/com/android/car/trust/BleMessageStreamCallback.java
index a3068e3..d497c85 100644
--- a/service/src/com/android/car/trust/BleMessageStreamCallback.kt
+++ b/service/src/com/android/car/trust/BleMessageStreamCallback.java
@@ -14,31 +14,35 @@
* limitations under the License.
*/
-package com.android.car.trust
+package com.android.car.trust;
-import java.util.UUID
+import android.annotation.NonNull;
+
+import java.util.UUID;
/**
- * The callback that will be notified of various actions that occur in a [BleMessageStream].
+ * The callback that will be notified of various actions that occur in a {@link BleMessageStream}.
*/
-internal interface BleMessageStreamCallback {
+interface BleMessageStreamCallback {
/**
* Called if an error was encountered during a processing of a client message.
*
- * @param uuid The UUID of the characteristic that the client message was retrieved from.
+ * @param uuid The {@link UUID} of the characteristic that the client message was retrieved
+ * from.
*/
- fun onMessageReceivedError(uuid: UUID)
+ void onMessageReceivedError(@NonNull UUID uuid);
/**
* Called when a complete message is received from the client.
*
* @param message The complete message.
- * @param uuid The UUID of the characteristic that the client message was retrieved from.
+ * @param uuid The {@link UUID} of the characteristic that the client message was retrieved
+ * from.
*/
- fun onMessageReceived(message: ByteArray, uuid: UUID)
+ void onMessageReceived(@NonNull byte[] message, UUID uuid);
/**
* Called if there was an error during a write of a message to the stream.
*/
- fun onWriteMessageError()
+ void onWriteMessageError();
}
diff --git a/service/src/com/android/car/trust/BleMessageStreamV1.java b/service/src/com/android/car/trust/BleMessageStreamV1.java
new file mode 100644
index 0000000..d3f68cf
--- /dev/null
+++ b/service/src/com/android/car/trust/BleMessageStreamV1.java
@@ -0,0 +1,304 @@
+/*
+ * 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.android.car.trust;
+
+import android.annotation.NonNull;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.os.Handler;
+import android.util.Log;
+
+import com.android.car.BLEStreamProtos.BLEMessageProto.BLEMessage;
+import com.android.car.BLEStreamProtos.BLEOperationProto.OperationType;
+import com.android.car.protobuf.InvalidProtocolBufferException;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.IOException;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Version 1 of the message stream.
+ */
+class BleMessageStreamV1 implements BleMessageStream {
+ private static final String TAG = "BleMessageStreamV1";
+
+ @VisibleForTesting
+ static final int BLE_MESSAGE_RETRY_LIMIT = 5;
+
+ /**
+ * The delay in milliseconds before a failed message is retried for sending.
+ *
+ * <p>This delay is only present for a message has been chunked. Each part of this chunked
+ * message requires an ACK from the remote device before the next chunk is sent. If this ACK is
+ * not received, then a second message is sent after this delay.
+ *
+ * <p>The value of this delay is 2 seconds to allow for 1 second to notify the remote device of
+ * a new message and 1 second to wait for an ACK.
+ */
+ private static final long BLE_MESSAGE_RETRY_DELAY_MS = TimeUnit.SECONDS.toMillis(2);
+
+ private final Handler mHandler;
+ private final BlePeripheralManager mBlePeripheralManager;
+ private final BluetoothDevice mDevice;
+ private final BluetoothGattCharacteristic mWriteCharacteristic;
+ private final BluetoothGattCharacteristic mReadCharacteristic;
+
+ // Explicitly using an ArrayDequeue here for performance when used as a queue.
+ private final Deque<BLEMessage> mMessageQueue = new ArrayDeque<>();
+ private final BLEMessagePayloadStream mPayloadStream = new BLEMessagePayloadStream();
+
+ /** The number of times that a message to send has been retried. */
+ private int mRetryCount = 0;
+
+ /**
+ * The maximum write size for a single message.
+ *
+ * <p>By default, this value is 20 because the smaller possible write size over BLE is 23 bytes.
+ * However, 3 bytes need to be subtracted due to them being used by the header of the BLE
+ * packet. Thus, the final value is 20.
+ */
+ private int mMaxWriteSize = 20;
+
+ private final List<BleMessageStreamCallback> mCallbacks = new ArrayList<>();
+
+ BleMessageStreamV1(@NonNull Handler handler, @NonNull BlePeripheralManager blePeripheralManager,
+ @NonNull BluetoothDevice device,
+ @NonNull BluetoothGattCharacteristic writeCharacteristic,
+ @NonNull BluetoothGattCharacteristic readCharacteristic) {
+ mHandler = handler;
+ mBlePeripheralManager = blePeripheralManager;
+ mDevice = device;
+ mWriteCharacteristic = writeCharacteristic;
+ mReadCharacteristic = readCharacteristic;
+
+ mBlePeripheralManager.addOnCharacteristicWriteListener(this::onCharacteristicWrite);
+ }
+
+ /** Registers the given callback to be notified of various events within the stream. */
+ @Override
+ public void registerCallback(@NonNull BleMessageStreamCallback callback) {
+ mCallbacks.add(callback);
+ }
+
+ /** Unregisters the given callback from being notified of stream events. */
+ @Override
+ public void unregisterCallback(@NonNull BleMessageStreamCallback callback) {
+ mCallbacks.remove(callback);
+ }
+
+ /** Sets the maximum size of a message that can be sent. */
+ @Override
+ public void setMaxWriteSize(int maxWriteSize) {
+ mMaxWriteSize = maxWriteSize;
+ }
+
+ /** Returns the maximum size of a message that can be sent. */
+ @Override
+ public int getMaxWriteSize() {
+ return mMaxWriteSize;
+ }
+
+ /**
+ * Writes the given message to the write characteristic of this stream.
+ *
+ * <p>This method will handle the chunking of messages based on maximum write size assigned to
+ * this stream.. If there is an error during the send, any callbacks on this stream will be
+ * notified of the error.
+ *
+ * @param message The message to send.
+ * @param operationType The {@link OperationType} of this message.
+ * @param isPayloadEncrypted {@code true} if the message to send has been encrypted.
+ */
+ @Override
+ public void writeMessage(@NonNull byte[] message, @NonNull OperationType operationType,
+ boolean isPayloadEncrypted) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Writing message to device with name: " + mDevice.getName());
+ }
+
+ List<BLEMessage> bleMessages = BLEMessageV1Factory.makeBLEMessages(message, operationType,
+ mMaxWriteSize, isPayloadEncrypted);
+
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Number of messages to send to device: " + bleMessages.size());
+ }
+
+ // Each write will override previous messages.
+ if (!mMessageQueue.isEmpty()) {
+ mMessageQueue.clear();
+ Log.w(TAG, "Request to write a new message when there are still messages in the "
+ + "queue.");
+ }
+
+ mMessageQueue.addAll(bleMessages);
+
+ writeNextMessageInQueue();
+ }
+
+ /**
+ * Processes a message from the client and notifies any callbacks of the success of this
+ * call.
+ */
+ @VisibleForTesting
+ void onCharacteristicWrite(@NonNull BluetoothDevice device,
+ @NonNull BluetoothGattCharacteristic characteristic, @NonNull byte[] value) {
+ if (!mDevice.equals(device)) {
+ Log.w(TAG, "Received a message from a device (" + device.getAddress() + ") that is not "
+ + "the expected device (" + mDevice.getAddress() + ") registered to this "
+ + "stream. Ignoring.");
+ return;
+ }
+
+ if (!characteristic.getUuid().equals(mReadCharacteristic.getUuid())) {
+ Log.w(TAG, "Received a write to a characteristic (" + characteristic.getUuid()
+ + ") that is not the expected UUID (" + mReadCharacteristic.getUuid()
+ + "). Ignoring.");
+ return;
+ }
+
+ BLEMessage bleMessage;
+ try {
+ bleMessage = BLEMessage.parseFrom(value);
+ } catch (InvalidProtocolBufferException e) {
+ Log.e(TAG, "Can not parse BLE message from client.", e);
+
+ for (BleMessageStreamCallback callback : mCallbacks) {
+ callback.onMessageReceivedError(characteristic.getUuid());
+ }
+ return;
+ }
+
+ if (bleMessage.getOperation() == OperationType.ACK) {
+ handleClientAckMessage();
+ return;
+ }
+
+ try {
+ mPayloadStream.write(bleMessage);
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to parse the BLE message's payload from client.", e);
+
+ for (BleMessageStreamCallback callback : mCallbacks) {
+ callback.onMessageReceivedError(characteristic.getUuid());
+ }
+ return;
+ }
+
+ // If it's not complete, make sure the client knows that this message was received.
+ if (!mPayloadStream.isComplete()) {
+ sendAcknowledgmentMessage();
+ return;
+ }
+
+ for (BleMessageStreamCallback callback : mCallbacks) {
+ callback.onMessageReceived(mPayloadStream.toByteArray(), characteristic.getUuid());
+ }
+
+ mPayloadStream.reset();
+ }
+
+ /**
+ * Writes the next message in the message queue to the write characteristic.
+ *
+ * <p>If the message queue is empty, then this method will do nothing.
+ */
+ private void writeNextMessageInQueue() {
+ // This should not happen in practice since this method is private and should only be called
+ // for a non-empty queue.
+ if (mMessageQueue.isEmpty()) {
+ Log.e(TAG, "Call to write next message in queue, but the message queue is empty.");
+ return;
+ }
+
+ if (mMessageQueue.size() == 1) {
+ writeValueAndNotify(mMessageQueue.remove().toByteArray());
+ return;
+ }
+
+ mHandler.post(mSendMessageWithTimeoutRunnable);
+ }
+
+ private void handleClientAckMessage() {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Received ACK from client. Attempting to write next message in queue.");
+ }
+
+ mHandler.removeCallbacks(mSendMessageWithTimeoutRunnable);
+ mRetryCount = 0;
+
+ if (mMessageQueue.isEmpty()) {
+ Log.e(TAG, "Received ACK, but the message queue is empty. Ignoring.");
+ return;
+ }
+
+ // Previous message has been sent successfully so we can start the next message.
+ mMessageQueue.remove();
+ writeNextMessageInQueue();
+ }
+
+ private void sendAcknowledgmentMessage() {
+ writeValueAndNotify(BLEMessageV1Factory.makeAcknowledgementMessage().toByteArray());
+ }
+
+ /**
+ * Convenience method to write the given message to the {@link #mWriteCharacteristic} of this
+ * class. After writing, this method will also send notifications to any listening devices that
+ * the write was made.
+ */
+ private void writeValueAndNotify(@NonNull byte[] message) {
+ mWriteCharacteristic.setValue(message);
+
+ mBlePeripheralManager.notifyCharacteristicChanged(mDevice, mWriteCharacteristic,
+ /* confirm= */ false);
+ }
+
+ /**
+ * A runnable that will write the message at the head of the {@link #mMessageQueue} and set up
+ * a timeout for receiving an ACK for that write.
+ *
+ * <p>If the timeout is reached before an ACK is received, the message write is retried.
+ */
+ private final Runnable mSendMessageWithTimeoutRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Sending BLE message; retry count: " + mRetryCount);
+ }
+
+ if (mRetryCount < BLE_MESSAGE_RETRY_LIMIT) {
+ writeValueAndNotify(mMessageQueue.peek().toByteArray());
+ mRetryCount++;
+ mHandler.postDelayed(this, BLE_MESSAGE_RETRY_DELAY_MS);
+ return;
+ }
+
+ mHandler.removeCallbacks(this);
+ mRetryCount = 0;
+ mMessageQueue.clear();
+
+ Log.e(TAG, "Error during BLE message sending - exceeded retry limit.");
+
+ for (BleMessageStreamCallback callback : mCallbacks) {
+ callback.onWriteMessageError();
+ }
+ }
+ };
+}
diff --git a/service/src/com/android/car/trust/BleMessageStreamV1.kt b/service/src/com/android/car/trust/BleMessageStreamV1.kt
deleted file mode 100644
index 984791a..0000000
--- a/service/src/com/android/car/trust/BleMessageStreamV1.kt
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * 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.android.car.trust
-
-import android.bluetooth.BluetoothDevice
-import android.bluetooth.BluetoothGattCharacteristic
-import android.os.Handler
-import android.util.Log
-import com.android.car.BLEStreamProtos.BLEMessageProto.BLEMessage
-import com.android.car.BLEStreamProtos.BLEOperationProto.OperationType
-import com.android.car.protobuf.InvalidProtocolBufferException
-import com.android.internal.annotations.VisibleForTesting
-import java.io.IOException
-import java.util.ArrayDeque
-import java.util.concurrent.TimeUnit
-
-private const val TAG = "BleMessageStreamV1"
-
-@VisibleForTesting
-const val BLE_MESSAGE_RETRY_LIMIT = 5
-
-private val BLE_MESSAGE_RETRY_DELAY_MS = TimeUnit.SECONDS.toMillis(2)
-
-/**
- * Version 1 of the message stream.
- */
-internal class BleMessageStreamV1(
- private val handler: Handler,
- private val blePeripheralManager: BlePeripheralManager,
- override val device: BluetoothDevice,
- override val writeCharacteristic: BluetoothGattCharacteristic,
- override val readCharacteristic: BluetoothGattCharacteristic
-) : BleMessageStream {
- // Explicitly using an ArrayDequeue here for performance when used as a queue.
- private val messageQueue = ArrayDeque<BLEMessage>()
- private val payloadStream = BLEMessagePayloadStream()
-
- private var retryCount = 0
-
- override var maxWriteSize = 20
- override var callback: BleMessageStreamCallback? = null
-
- init {
- blePeripheralManager.addOnCharacteristicWriteListener(::onCharacteristicWrite)
- }
-
- /**
- * Writes the given message to the `writeCharacteristic` of this stream.
- *
- * This method will handle the chunking of messages based on [maxWriteSize]. If there is an
- * error during the send, the [callback] of this method will be notified via a call to
- * `onWriteMessageError`.
- *
- * @param message The message to send.
- * @param operationType The [OperationType] of this message.
- * @param isPayloadEncrypted `true` if the message to send has been encrypted.
- */
- override fun writeMessage(
- message: ByteArray,
- operationType: OperationType,
- isPayloadEncrypted: Boolean
- ) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Writing message to device with name: ${device.name}")
- }
-
- val bleMessages = BLEMessageV1Factory.makeBLEMessages(
- message,
- operationType,
- maxWriteSize,
- isPayloadEncrypted)
-
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Number of messages to send to device: ${bleMessages.size}")
- }
-
- // Each write will override previous messages.
- messageQueue.takeUnless { it.isEmpty() }?.apply {
- clear()
- Log.w(TAG, "Request to write a new message when there are still messages in the queue.")
- }
-
- messageQueue.addAll(bleMessages)
-
- writeNextMessageInQueue()
- }
-
- /**
- * Processes a message from the client and notifies the [callback] of the success of this
- * call.
- */
- @VisibleForTesting
- fun onCharacteristicWrite(
- device: BluetoothDevice,
- characteristic: BluetoothGattCharacteristic,
- value: ByteArray
- ) {
- if (this.device != device) {
- Log.w(
- TAG,
- "Received a message from a device (${device.address}) that is not the " +
- "expected device (${device.address}) registered to this stream. Ignoring.")
- return
- }
-
- if (characteristic.uuid != readCharacteristic.uuid) {
- Log.w(
- TAG,
- "Received a write to a characteristic (${characteristic.uuid}) that is not the " +
- "expected UUID (${readCharacteristic.uuid}). Ignoring.")
- return
- }
-
- val bleMessage = try {
- BLEMessage.parseFrom(value)
- } catch (e: InvalidProtocolBufferException) {
- Log.e(TAG, "Can not parse BLE message from client.", e)
- callback?.onMessageReceivedError(characteristic.uuid)
- return
- }
-
- if (bleMessage.operation == OperationType.ACK) {
- handleClientAckMessage()
- return
- }
-
- try {
- payloadStream.write(bleMessage)
- } catch (e: IOException) {
- Log.e(TAG, "Unable to parse the BLE message's payload from client.", e)
- callback?.onMessageReceivedError(characteristic.uuid)
- return
- }
-
- // If it's not complete, make sure the client knows that this message was received.
- if (!payloadStream.isComplete()) {
- sendAcknowledgmentMessage()
- return
- }
-
- callback?.onMessageReceived(payloadStream.toByteArray(), characteristic.uuid)
- payloadStream.reset()
- }
-
- /**
- * Writes the next message in [messageQueue] to the given characteristic.
- *
- * If the message queue is empty, then this method will do nothing.
- */
- private fun writeNextMessageInQueue() {
- when (messageQueue.size) {
- 0 -> Log.e(TAG, "Call to write next message in queue, but the message queue is empty.")
- 1 -> writeValueAndNotify(messageQueue.remove().toByteArray())
- else -> handler.post(sendMessageWithTimeout)
- }
- }
-
- private fun handleClientAckMessage() {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Received ACK from client. Attempting to write next message in queue.")
- }
-
- handler.removeCallbacks(sendMessageWithTimeout)
- retryCount = 0
-
- if (messageQueue.isEmpty()) {
- Log.e(TAG, "Received ACK, but the message queue is empty. Ignoring.")
- return
- }
-
- // Previous message has been sent successfully so we can start the next message.
- messageQueue.remove()
- writeNextMessageInQueue()
- }
-
- private fun sendAcknowledgmentMessage() {
- writeValueAndNotify(BLEMessageV1Factory.makeAcknowledgementMessage().toByteArray())
- }
-
- /**
- * Convenience method to write the given message to the `writeCharacteristic` of this class.
- * After writing, this method will also send notifications to any listening devices that the
- * write was made.
- */
- private fun writeValueAndNotify(message: ByteArray) {
- writeCharacteristic.setValue(message)
-
- blePeripheralManager.notifyCharacteristicChanged(
- device,
- writeCharacteristic,
- /* confirm= */ false)
- }
-
- /**
- * A runnable that will write the message at the head of the [messageQueue] and set up a timeout
- * for receiving an ACK for that write.
- *
- * If the timeout is reached before an ACK is received, the message write is retried.
- */
- private val sendMessageWithTimeout by lazy {
- // Note: using explicit "object" declaration to access "this".
- object : Runnable {
- override fun run() {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Sending BLE message; retry count: $retryCount")
- }
-
- if (retryCount < BLE_MESSAGE_RETRY_LIMIT) {
- writeValueAndNotify(messageQueue.peek().toByteArray())
- retryCount++
- handler.postDelayed(this, BLE_MESSAGE_RETRY_DELAY_MS)
- return
- }
-
- handler.removeCallbacks(this)
- retryCount = 0
- messageQueue.clear()
-
- Log.e(TAG, "Error during BLE message sending - exceeded retry limit.")
-
- callback?.onWriteMessageError()
- }
- }
- }
-}
diff --git a/service/src/com/android/car/trust/CarTrustAgentBleManager.java b/service/src/com/android/car/trust/CarTrustAgentBleManager.java
index a9061d1..b1fedd0 100644
--- a/service/src/com/android/car/trust/CarTrustAgentBleManager.java
+++ b/service/src/com/android/car/trust/CarTrustAgentBleManager.java
@@ -174,7 +174,7 @@
}
if (mMessageStream != null) {
- mMessageStream.setCallback(null);
+ mMessageStream.unregisterCallback(this);
mMessageStream = null;
}
@@ -192,7 +192,7 @@
bleEventCallback.onRemoteDeviceDisconnected(device));
if (mMessageStream != null) {
- mMessageStream.setCallback(null);
+ mMessageStream.unregisterCallback(this);
mMessageStream = null;
}
@@ -279,7 +279,7 @@
deviceVersion, device, mBlePeripheralManager, writeCharacteristic,
readCharacteristic);
mMessageStream.setMaxWriteSize(mMaxWriteSize);
- mMessageStream.setCallback(this);
+ mMessageStream.registerCallback(this);
if (mMessageStream == null) {
Log.e(TAG, "No supported version found during version exchange. "
diff --git a/service/src/com/android/car/trust/SecureBleChannel.kt b/service/src/com/android/car/trust/SecureBleChannel.kt
index e4939f0..c04e780 100644
--- a/service/src/com/android/car/trust/SecureBleChannel.kt
+++ b/service/src/com/android/car/trust/SecureBleChannel.kt
@@ -91,7 +91,7 @@
// associated before. The first connect happens in association. So it is always a reconnect
// in this channel.
runner.setIsReconnect(true)
- stream.callback = streamCallback
+ stream.registerCallback(streamCallback)
}
@Throws(HandshakeException::class)
diff --git a/service/src/com/android/car/user/CarUserService.java b/service/src/com/android/car/user/CarUserService.java
index 8436fe7..04fd9c8 100644
--- a/service/src/com/android/car/user/CarUserService.java
+++ b/service/src/com/android/car/user/CarUserService.java
@@ -25,12 +25,11 @@
import android.app.IActivityManager;
import android.car.ICarUserService;
import android.car.settings.CarSettings;
+import android.car.userlib.CarUserManagerHelper;
import android.content.Context;
import android.content.pm.UserInfo;
-import android.graphics.Bitmap;
import android.location.LocationManager;
import android.os.Binder;
-import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
@@ -41,7 +40,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
-import com.android.internal.util.UserIcons;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -61,40 +59,11 @@
*/
public final class CarUserService extends ICarUserService.Stub implements CarServiceBase {
private final Context mContext;
+ private final CarUserManagerHelper mCarUserManagerHelper;
private final IActivityManager mAm;
private final UserManager mUserManager;
private final int mMaxRunningUsers;
- /**
- * Default restrictions for Non-Admin users.
- */
- private static final String[] DEFAULT_NON_ADMIN_RESTRICTIONS = new String[] {
- UserManager.DISALLOW_FACTORY_RESET
- };
-
- /**
- * Default restrictions for Guest users.
- */
- private static final String[] DEFAULT_GUEST_RESTRICTIONS = new String[] {
- UserManager.DISALLOW_FACTORY_RESET,
- UserManager.DISALLOW_REMOVE_USER,
- UserManager.DISALLOW_MODIFY_ACCOUNTS,
- UserManager.DISALLOW_INSTALL_APPS,
- UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
- UserManager.DISALLOW_UNINSTALL_APPS
- };
-
- /**
- * List of restrictions relaxed for Non-Admin users.
- *
- * <p>Each non-admin has sms and outgoing call restrictions applied by the UserManager on
- * creation. We want to enable these permissions by default in the car.
- */
- private static final String[] RELAXED_RESTRICTIONS_FOR_NON_ADMIN = new String[] {
- UserManager.DISALLOW_SMS,
- UserManager.DISALLOW_OUTGOING_CALLS
- };
-
private final Object mLock = new Object();
@GuardedBy("mLock")
private boolean mUser0Unlocked;
@@ -122,12 +91,14 @@
void onSwitchUser(@UserIdInt int userId);
}
- public CarUserService(@NonNull Context context, @NonNull UserManager userManager,
- @NonNull IActivityManager am, int maxRunningUsers) {
+ public CarUserService(
+ @NonNull Context context, @NonNull CarUserManagerHelper carUserManagerHelper,
+ @NonNull UserManager userManager, @NonNull IActivityManager am, int maxRunningUsers) {
if (Log.isLoggable(TAG_USER, Log.DEBUG)) {
Log.d(TAG_USER, "constructed");
}
mContext = context;
+ mCarUserManagerHelper = carUserManagerHelper;
mAm = am;
mMaxRunningUsers = maxRunningUsers;
mUserManager = userManager;
@@ -170,7 +141,7 @@
if (admin) {
return createNewAdminUser(name);
}
- return createNewNonAdminUser(name);
+ return mCarUserManagerHelper.createNewNonAdminUser(name);
}
/**
@@ -198,8 +169,8 @@
return null;
}
// Passenger user should be a non-admin user.
- setDefaultNonAdminRestrictions(user, /* enable= */ true);
- assignDefaultIcon(user);
+ mCarUserManagerHelper.setDefaultNonAdminRestrictions(user, /* enable= */ true);
+ mCarUserManagerHelper.assignDefaultIcon(user);
return user;
}
@@ -219,7 +190,7 @@
Log.w(TAG_USER, "current process is not allowed to switch user");
return false;
}
- if (driverId == getCurrentUserId()) {
+ if (driverId == mCarUserManagerHelper.getCurrentForegroundUserId()) {
// The current user is already the given user.
return true;
}
@@ -247,7 +218,6 @@
/**
* @see CarUserManager.getPassengers
- * @return
*/
@Override
@NonNull
@@ -284,11 +254,6 @@
return userId == UserHandle.USER_SYSTEM;
}
- /** Returns whether the user running the current process has a restriction. */
- private boolean isCurrentProcessUserHasRestriction(String restriction) {
- return mUserManager.hasUserRestriction(restriction);
- }
-
private void updateDefaultUserRestriction() {
// We want to set restrictions on system and guest users only once. These are persisted
// onto disk, so it's sufficient to do it once + we minimize the number of disk writes.
@@ -300,24 +265,11 @@
if (UserManager.isHeadlessSystemUserMode()) {
setSystemUserRestrictions();
}
- initDefaultGuestRestrictions();
+ mCarUserManagerHelper.initDefaultGuestRestrictions();
Settings.Global.putInt(mContext.getContentResolver(),
CarSettings.Global.DEFAULT_USER_RESTRICTIONS_SET, 1);
}
- /**
- * Sets default guest restrictions that will be applied every time a Guest user is created.
- *
- * <p> Restrictions are written to disk and persistent across boots.
- */
- private void initDefaultGuestRestrictions() {
- Bundle defaultGuestRestrictions = new Bundle();
- for (String restriction : DEFAULT_GUEST_RESTRICTIONS) {
- defaultGuestRestrictions.putBoolean(restriction, true);
- }
- mUserManager.setDefaultGuestRestrictions(defaultGuestRestrictions);
- }
-
private boolean isPersistentUser(@UserIdInt int userId) {
return !mUserManager.getUserInfo(userId).isEphemeral();
}
@@ -360,7 +312,7 @@
Integer user = userId;
if (isPersistentUser(userId)) {
// current foreground user should stay in top priority.
- if (userId == getCurrentUserId()) {
+ if (userId == mCarUserManagerHelper.getCurrentForegroundUserId()) {
mBackgroundUsersToRestart.remove(user);
mBackgroundUsersToRestart.add(0, user);
}
@@ -399,7 +351,7 @@
}
ArrayList<Integer> startedUsers = new ArrayList<>();
for (Integer user : users) {
- if (user == getCurrentUserId()) {
+ if (user == mCarUserManagerHelper.getCurrentForegroundUserId()) {
continue;
}
try {
@@ -444,7 +396,7 @@
if (userId == UserHandle.USER_SYSTEM) {
return false;
}
- if (userId == getCurrentUserId()) {
+ if (userId == mCarUserManagerHelper.getCurrentForegroundUserId()) {
Log.i(TAG_USER, "stopBackgroundUser, already a FG user:" + userId);
return false;
}
@@ -475,7 +427,7 @@
*/
public void onSwitchUser(@UserIdInt int userId) {
if (!isSystemUser(userId) && isPersistentUser(userId)) {
- setLastActiveUser(userId);
+ mCarUserManagerHelper.setLastActiveUser(userId);
}
for (UserCallback callback : mUserCallbacks) {
callback.onSwitchUser(userId);
@@ -529,7 +481,7 @@
* Creates a new user on the system, the created user would be granted admin role.
*
* @param name Name to be given to the newly created user.
- * @return Newly created admin user, {@code null} if it fails to create a user.
+ * @return newly created admin user, {@code null} if it fails to create a user.
*/
@Nullable
private UserInfo createNewAdminUser(String name) {
@@ -545,79 +497,11 @@
Log.w(TAG_USER, "can't create admin user.");
return null;
}
- assignDefaultIcon(user);
+ mCarUserManagerHelper.assignDefaultIcon(user);
return user;
}
- /**
- * Creates a new non-admin user on the system.
- *
- * @param name Name to be given to the newly created user.
- * @return Newly created non-admin user, {@code null} if failed to create a user.
- */
- @Nullable
- private UserInfo createNewNonAdminUser(String name) {
- UserInfo user = mUserManager.createUser(name, /* flags= */ 0);
- if (user == null) {
- // Couldn't create user, most likely because there are too many.
- Log.w(TAG_USER, "can't create non-admin user.");
- return null;
- }
- setDefaultNonAdminRestrictions(user, /* enable= */ true);
-
- // Remove restrictions which are allowed for non-admin car users.
- for (String restriction : RELAXED_RESTRICTIONS_FOR_NON_ADMIN) {
- mUserManager.setUserRestriction(restriction, /* enable= */ false, user.getUserHandle());
- }
-
- assignDefaultIcon(user);
- return user;
- }
-
- private Bitmap getUserDefaultIcon(UserInfo userInfo) {
- return UserIcons.convertToBitmap(
- UserIcons.getDefaultUserIcon(mContext.getResources(), userInfo.id, false));
- }
-
- private Bitmap getGuestDefaultIcon() {
- return UserIcons.convertToBitmap(UserIcons.getDefaultUserIcon(mContext.getResources(),
- UserHandle.USER_NULL, false));
- }
-
- /** Assigns a default icon to a user according to the user's id. */
- private void assignDefaultIcon(UserInfo userInfo) {
- Bitmap bitmap = userInfo.isGuest()
- ? getGuestDefaultIcon() : getUserDefaultIcon(userInfo);
- mUserManager.setUserIcon(userInfo.id, bitmap);
- }
-
- private void setDefaultNonAdminRestrictions(UserInfo userInfo, boolean enable) {
- for (String restriction : DEFAULT_NON_ADMIN_RESTRICTIONS) {
- mUserManager.setUserRestriction(restriction, enable, userInfo.getUserHandle());
- }
- }
-
- /** Gets the current user on the device. */
- @VisibleForTesting
- @UserIdInt
- int getCurrentUserId() {
- UserInfo user = getCurrentUser();
- return user != null ? user.id : UserHandle.USER_NULL;
- }
-
- @Nullable
- private UserInfo getCurrentUser() {
- UserInfo user = null;
- try {
- user = mAm.getCurrentUser();
- } catch (RemoteException e) {
- // ignore
- Log.w(TAG_USER, "error while getting current user", e);
- }
- return user;
- }
-
private interface UserFilter {
boolean isEligibleUser(UserInfo user);
}
@@ -636,17 +520,6 @@
}
/**
- * Sets last active user.
- *
- * @param userId Last active user id.
- */
- @VisibleForTesting
- void setLastActiveUser(@UserIdInt int userId) {
- Settings.Global.putInt(
- mContext.getContentResolver(), Settings.Global.LAST_ACTIVE_USER_ID, userId);
- }
-
- /**
* Enforces that apps which have the
* {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS}
* can make certain calls to the CarUserManager.
diff --git a/service/src/com/android/car/vms/VmsClientManager.java b/service/src/com/android/car/vms/VmsClientManager.java
index 710793c..a00e179 100644
--- a/service/src/com/android/car/vms/VmsClientManager.java
+++ b/service/src/com/android/car/vms/VmsClientManager.java
@@ -146,16 +146,16 @@
CarUserService userService, CarUserManagerHelper userManagerHelper,
VmsHalService halService) {
this(context, brokerService, userService, userManagerHelper, halService,
- Binder::getCallingUid);
+ new Handler(Looper.getMainLooper()), Binder::getCallingUid);
}
@VisibleForTesting
VmsClientManager(Context context, VmsBrokerService brokerService,
CarUserService userService, CarUserManagerHelper userManagerHelper,
- VmsHalService halService, IntSupplier getCallingUid) {
+ VmsHalService halService, Handler handler, IntSupplier getCallingUid) {
mContext = context;
mPackageManager = context.getPackageManager();
- mHandler = new Handler(Looper.getMainLooper());
+ mHandler = handler;
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
mUserService = userService;
mUserManagerHelper = userManagerHelper;
diff --git a/tests/CarDeveloperOptions/res/values-be/strings.xml b/tests/CarDeveloperOptions/res/values-be/strings.xml
index 565b045..23e5580 100644
--- a/tests/CarDeveloperOptions/res/values-be/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-be/strings.xml
@@ -483,9 +483,9 @@
<string name="security_settings_fingerprint_enroll_enrolling_skip" msgid="1473280156532146933">"Пазней"</string>
<string name="setup_fingerprint_enroll_enrolling_skip_title" msgid="2816424026528101690">"Прапусціць усталёўку адбітка пальца?"</string>
<string name="setup_fingerprint_enroll_enrolling_skip_message" msgid="8139299964344809780">"Вы вырашылі выкарыстоўваць свой адбітак пальца як адзін са спосабоў разблакіроўкі тэлефона. Калі вы прапусціце гэты крок зараз, вам трэба будзе ўсталяваць гэты спосаб пазней. Усталёўка займае ўсяго каля хвіліны."</string>
- <string name="fingerprint_lock_screen_setup_skip_dialog_text" product="tablet" msgid="1384438077720821127">"Абараніце планшэт з дапамогай опцыі блакіроўкі экрана, каб ніхто не змог ім карыстацца ў выпадку прапажы ці крадзяжу. Опцыя блакіроўкі экрана таксама спатрэбіцца вам, каб наладзіць лічбавы адбітак. Націсніце \"Скасаваць\", а потым задайце PIN-код ці выберыце іншую опцыю блакіроўкі экрана."</string>
- <string name="fingerprint_lock_screen_setup_skip_dialog_text" product="device" msgid="7207112623501771824">"Абараніце прыладу з дапамогай опцыі блакіроўкі экрана, каб ніхто не змог ёю карыстацца ў выпадку прапажы ці крадзяжу. Опцыя блакіроўкі экрана таксама спатрэбіцца вам, каб наладзіць лічбавы адбітак. Націсніце \"Скасаваць\", а потым задайце PIN-код ці выберыце іншую опцыю блакіроўкі экрана."</string>
- <string name="fingerprint_lock_screen_setup_skip_dialog_text" product="default" msgid="7623975730623531606">"Абараніце тэлефон з дапамогай опцыі блакіроўкі экрана, каб ніхто не змог ім карыстацца ў выпадку прапажы ці крадзяжу. Опцыя блакіроўкі экрана таксама спатрэбіцца вам, каб наладзіць лічбавы адбітак. Націсніце \"Скасаваць\", а потым задайце PIN-код ці выберыце іншую опцыю блакіроўкі экрана."</string>
+ <string name="fingerprint_lock_screen_setup_skip_dialog_text" product="tablet" msgid="1384438077720821127">"Абараніце планшэт з дапамогай опцыі блакіроўкі экрана, каб ніхто не змог ім карыстацца ў выпадку прапажы ці крадзяжу. Опцыя блакіроўкі экрана таксама спатрэбіцца вам, каб наладзіць аўтэнтыфікацыю па адбітку пальца. Націсніце \"Скасаваць\", а потым задайце PIN-код ці выберыце іншую опцыю блакіроўкі экрана."</string>
+ <string name="fingerprint_lock_screen_setup_skip_dialog_text" product="device" msgid="7207112623501771824">"Абараніце прыладу з дапамогай опцыі блакіроўкі экрана, каб ніхто не змог ёю карыстацца ў выпадку прапажы ці крадзяжу. Опцыя блакіроўкі экрана таксама спатрэбіцца вам, каб наладзіць аўтэнтыфікацыю па адбітку пальца. Націсніце \"Скасаваць\", а потым задайце PIN-код ці выберыце іншую опцыю блакіроўкі экрана."</string>
+ <string name="fingerprint_lock_screen_setup_skip_dialog_text" product="default" msgid="7623975730623531606">"Абараніце тэлефон з дапамогай опцыі блакіроўкі экрана, каб ніхто не змог ім карыстацца ў выпадку прапажы ці крадзяжу. Опцыя блакіроўкі экрана таксама спатрэбіцца вам, каб наладзіць аўтэнтыфікацыю па адбітку пальца. Націсніце \"Скасаваць\", а потым задайце PIN-код ці выберыце іншую опцыю блакіроўкі экрана."</string>
<string name="face_lock_screen_setup_skip_dialog_text" product="tablet" msgid="2998863111689476550">"Абараніце планшэт з дапамогай опцыі блакіроўкі экрана, каб ніхто не змог ім карыстацца ў выпадку прапажы ці крадзяжу. Опцыя блакіроўкі экрана таксама спатрэбіцца вам, каб наладзіць распазнаванне твару. Націсніце \"Скасаваць\", а потым задайце PIN-код ці выберыце іншую опцыю блакіроўкі экрана."</string>
<string name="face_lock_screen_setup_skip_dialog_text" product="device" msgid="6780557259734235952">"Абараніце прыладу з дапамогай опцыі блакіроўкі экрана, каб ніхто не змог ёю карыстацца ў выпадку прапажы ці крадзяжу. Опцыя блакіроўкі экрана таксама спатрэбіцца вам, каб наладзіць распазнаванне твару. Націсніце \"Скасаваць\", а потым задайце PIN-код ці выберыце іншую опцыю блакіроўкі экрана."</string>
<string name="face_lock_screen_setup_skip_dialog_text" product="default" msgid="8541640018478926775">"Абараніце тэлефон з дапамогай опцыі блакіроўкі экрана, каб ніхто не змог ім карыстацца ў выпадку прапажы ці крадзяжу. Опцыя блакіроўкі экрана таксама спатрэбіцца вам, каб наладзіць распазнаванне твару. Націсніце \"Скасаваць\", а потым задайце PIN-код ці выберыце іншую опцыю блакіроўкі экрана."</string>
@@ -1615,8 +1615,8 @@
<string name="reset_esim_error_msg" msgid="4716366079119742235">"Памылка сцірання спампаваных SIM.\n\nПеразапусціце прыладу і паўтарыце спробу."</string>
<string name="master_clear_title" msgid="1560712943955904673">"Сцерці ўсе даныя (cкід да заводскіх налад)"</string>
<string name="master_clear_short_title" msgid="919098101581335101">"Сцерці ўсе даныя (скід да заводскіх налад)"</string>
- <string name="master_clear_desc" product="tablet" msgid="3432373610755760899">"Гэта дазволіць сцерці ўсе даныя з "<b>"унутранага сховішча"</b>" вашага планшэта, у тым ліку:\n\n"<li>"ваш Уліковы запiс Google;"</li>\n<li>"сістэмныя даныя і налады, а таксама даныя і налады праграм;"</li>\n<li>"спампаваныя праграмы"</li></string>
- <string name="master_clear_desc" product="default" msgid="8765543541962866697">"Гэта дазволіць сцерці ўсе даныя з "<b>"унутранага сховішча"</b>" вашага тэлефона, у тым ліку:\n\n"<li>"ваш Уліковы запіс Google;"</li>\n<li>"сістэмныя даныя і налады, а таксама даныя і налады праграм;"</li>\n<li>"спампаваныя праграмы"</li></string>
+ <string name="master_clear_desc" product="tablet" msgid="3432373610755760899">"Гэта дазволіць сцерці ўсе даныя з "<b>"унутранага сховішча"</b>" вашага планшэта, у тым ліку:\n\n"<li>"Ваш Уліковы запiс Google"</li>\n<li>"Сістэмныя даныя і налады, а таксама даныя і налады праграм"</li>\n<li>"Спампаваныя праграмы"</li></string>
+ <string name="master_clear_desc" product="default" msgid="8765543541962866697">"Гэта дазволіць сцерці ўсе даныя з "<b>"унутранага сховішча"</b>" вашага тэлефона, у тым ліку:\n\n"<li>"Ваш Уліковы запіс Google"</li>\n<li>"Сістэмныя даныя і налады, а таксама даныя і налады праграм"</li>\n<li>"Спампаваныя праграмы"</li></string>
<string name="master_clear_accounts" product="default" msgid="3432884235445405376">\n\n"У дадзены момант вы зайшлі ў наступныя ўліковыя запісы:\n"</string>
<string name="master_clear_other_users_present" product="default" msgid="5993259656117566767">\n\n"На гэтай прыладзе ёсць іншыя карыстальнікі.\n"</string>
<string name="master_clear_desc_also_erases_external" msgid="3947303501615091903"><li>"Музыка"</li>\n<li>"Фатаграфіі"</li>\n<li>"Іншыя дадзеныя карыстальніка"</li></string>
diff --git a/tests/CarDeveloperOptions/res/values-es/strings.xml b/tests/CarDeveloperOptions/res/values-es/strings.xml
index 050ede3..8ce3fe7 100644
--- a/tests/CarDeveloperOptions/res/values-es/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-es/strings.xml
@@ -3413,8 +3413,8 @@
<string name="app_notification_block_summary" msgid="4502146897785692336">"No mostrar nunca estas notificaciones"</string>
<string name="notification_content_block_title" msgid="2805138591864484587">"Mostrar notificaciones"</string>
<string name="notification_content_block_summary" msgid="2743896875255591743">"No mostrar nunca notificaciones en el panel de notificaciones ni en dispositivos periféricos"</string>
- <string name="notification_badge_title" msgid="8989086619255666442">"Permitir punto de notificación"</string>
- <string name="notification_channel_badge_title" msgid="8228215248332054612">"Mostrar punto de notificación"</string>
+ <string name="notification_badge_title" msgid="8989086619255666442">"Permitir burbuja de notificación"</string>
+ <string name="notification_channel_badge_title" msgid="8228215248332054612">"Mostrar burbuja de notificación"</string>
<string name="app_notification_override_dnd_title" msgid="1757042206738172601">"Omitir No molestar"</string>
<string name="app_notification_override_dnd_summary" msgid="3152957611171210980">"Permitir notificaciones cuando el modo No molestar esté activado"</string>
<string name="app_notification_visibility_override_title" msgid="2349335170165637672">"En la pantalla de bloqueo"</string>
diff --git a/tests/CarDeveloperOptions/res/values-eu/strings.xml b/tests/CarDeveloperOptions/res/values-eu/strings.xml
index 5a16cfa..0364c80 100644
--- a/tests/CarDeveloperOptions/res/values-eu/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-eu/strings.xml
@@ -363,7 +363,7 @@
<string name="trust_lost_locks_screen_summary" msgid="2058567484625606803">"Gaitzen bada, gailua blokeatu egingo da azken fidagarritasun-agenteak fidagarritasuna galtzen duenean"</string>
<string name="owner_info_settings_summary" msgid="4208702251226725583">"Bat ere ez"</string>
<string name="owner_info_settings_status" msgid="8049975536717685126">"<xliff:g id="COUNT_0">%1$d</xliff:g>/<xliff:g id="COUNT_1">%2$d</xliff:g>"</string>
- <string name="owner_info_settings_edit_text_hint" msgid="2250029417257939706">"Adibidez, Jonen Android gailua."</string>
+ <string name="owner_info_settings_edit_text_hint" msgid="2250029417257939706">"Adibidez, Jonen Android-eko gailua."</string>
<string name="user_info_settings_title" msgid="1125111518759995748">"Erabiltzaile-info."</string>
<string name="show_profile_info_on_lockscreen_label" msgid="5204282771893683026">"Erakutsi profileko informazioa pantaila blokeatuan."</string>
<string name="profile_info_settings_title" msgid="4855892878512562551">"Profileko informazioa"</string>
@@ -1168,7 +1168,7 @@
<string name="color_mode_option_natural" msgid="1292837781836645320">"Naturalak"</string>
<string name="color_mode_option_boosted" msgid="453557938434778933">"Nabarmenduak"</string>
<string name="color_mode_option_saturated" msgid="7758384943407859851">"Saturatuak"</string>
- <string name="color_mode_option_automatic" msgid="6572718611315165117">"Doigarria"</string>
+ <string name="color_mode_option_automatic" msgid="6572718611315165117">"Doigarriak"</string>
<string name="color_mode_summary_natural" msgid="1247153893843263340">"Erabili kolore errealistak soilik"</string>
<string name="color_mode_summary_automatic" msgid="6066740785261330514">"Doitu kolore bizi eta errealisten artean"</string>
<string name="accelerometer_summary_on" product="tablet" msgid="5750977897791656412">"Aldatu orientazioa automatikoki tableta biratzean"</string>
@@ -1202,7 +1202,7 @@
<string name="adaptive_sleep_summary_on" msgid="6670369739228487082">"Aktibatuta / Pantaila ez da itzaliko hari begira zauden bitartean"</string>
<string name="adaptive_sleep_summary_off" msgid="2891586225954973431">"Desaktibatuta"</string>
<string name="adaptive_sleep_description" msgid="812673735459170009">"Pantaila itzaltzea eragozten du hari begira zauden bitartean."</string>
- <string name="adaptive_sleep_privacy" msgid="5706802215479902623">"Pantaila kontzienteak aurreko kamera erabiltzen du inor pantailari begira dagoen jakiteko. Gailuko eginbidea da, eta irudiak ez dira inoiz gordetzen, ez eta Google-ra bidaltzen ere."</string>
+ <string name="adaptive_sleep_privacy" msgid="5706802215479902623">"Pantaila kontzientea eginbideak aurreko kamera erabiltzen du inor pantailari begira dagoen jakiteko. Gailuko eginbidea da, eta irudiak ez dira inoiz gordetzen, ez eta Google-ra bidaltzen ere."</string>
<string name="night_display_title" msgid="1305002424893349814">"Gaueko argia"</string>
<string name="night_display_text" msgid="5330502493684652527">"Gaueko argiak tindu horikaraz janzten du pantaila. Horrela, ez zaizu horren nekagarria egingo argi gutxirekin pantailari begira egotea eta errazago hartuko duzu lo, gainera."</string>
<string name="night_display_auto_mode_title" msgid="8493573087102481588">"Ordutegia"</string>
@@ -2628,9 +2628,9 @@
<string name="remove_account_failed" msgid="491458185327106966">"Administratzaileak ez du eman aldaketa egiteko baimena"</string>
<string name="cant_sync_dialog_title" msgid="5483419398223189881">"Ezin da eskuz sinkronizatu"</string>
<string name="cant_sync_dialog_message" msgid="3467126947262857534">"Elementua ezin da unean desgaitu. Ezarpena aldatzeko, aktibatu atzeko planoko datuak eta sinkronizazio automatikoa aldi baterako."</string>
- <string name="enter_password" msgid="2963496904625715235">"Android gailua abiarazteko, idatzi pasahitza"</string>
- <string name="enter_pin" msgid="7140938268709546890">"Android gailua abiarazteko, idatzi PIN kodea"</string>
- <string name="enter_pattern" msgid="1653841963422825336">"Android gailua abiarazteko, marraztu eredua"</string>
+ <string name="enter_password" msgid="2963496904625715235">"Android-eko gailua abiarazteko, idatzi pasahitza"</string>
+ <string name="enter_pin" msgid="7140938268709546890">"Android-eko gailua abiarazteko, idatzi PIN kodea"</string>
+ <string name="enter_pattern" msgid="1653841963422825336">"Android-eko gailua abiarazteko, marraztu eredua"</string>
<string name="cryptkeeper_wrong_pattern" msgid="4580105105385125467">"Eredu okerra"</string>
<string name="cryptkeeper_wrong_password" msgid="1709534330303983166">"Pasahitz okerra"</string>
<string name="cryptkeeper_wrong_pin" msgid="857757190077859245">"PIN okerra"</string>
@@ -3365,14 +3365,14 @@
<string name="no_notification_listeners" msgid="1366386609506834717">"Ez dago jakinarazpenetarako sarbidea eskatu duen aplikaziorik."</string>
<string name="notification_assistant_security_warning_title" msgid="4190584438086738496">"<xliff:g id="SERVICE">%1$s</xliff:g> zerbitzuari jakinarazpenetarako sarbidea eman nahi diozu?"</string>
<string name="notification_assistant_security_warning_summary" msgid="6924513399671031930">"<xliff:g id="NOTIFICATION_ASSISTANT_NAME">%1$s</xliff:g> aplikazioak jakinarazpen guztiak irakurri ahalko ditu eta, haien barnean, informazio pertsonala egon daiteke (adibidez, kontaktuen izenak eta jasotzen dituzun testu-mezuak). Gainera, jakinarazpen horiek aldatu edo baztertu egin ahal izango ditu, baita haietako ekintza-botoiak sakatu ere. \n\nHorrez gain, Ez molestatu modua aktibatzeko eta desaktibatzeko aukera izango du aplikazioak, baita harekin erlazionatutako ezarpenak aldatzekoa ere."</string>
- <string name="notification_listener_security_warning_title" msgid="4902253246428777797">"<xliff:g id="SERVICE">%1$s</xliff:g> zerbitzuari jakinarazpenak atzitzeko baimena eman nahi diozu?"</string>
+ <string name="notification_listener_security_warning_title" msgid="4902253246428777797">"Jakinarazpenak atzitzeko baimena eman nahi diozu <xliff:g id="SERVICE">%1$s</xliff:g> zerbitzuari?"</string>
<string name="notification_listener_security_warning_summary" msgid="4454702907350100288">"<xliff:g id="NOTIFICATION_LISTENER_NAME">%1$s</xliff:g> aplikazioak jakinarazpen guztiak irakurri ahal izango ditu, informazio pertsonala (adibidez, kontaktuen izenak eta jasotako testu-mezuak) dutenak barne. Horrez gain, jakinarazpenak baztertu eta haietako ekintza-botoiak abiarazi ahal izango ditu. \n\nGainera, Ez molestatu modua aktibatzeko eta desaktibatzeko aukera izango du aplikazioak, baita horrekin erlazionatutako ezarpenak aldatzekoa ere."</string>
<string name="notification_listener_disable_warning_summary" msgid="162165151519082978">"<xliff:g id="NOTIFICATION_LISTENER_NAME">%1$s</xliff:g> aplikazioari jakinarazpenak atzitzeko aukera desaktibatzen badiozu, baliteke Ez molestatu modurako sarbidea era desaktibatzea."</string>
<string name="notification_listener_disable_warning_confirm" msgid="7863495391671154188">"Desaktibatu"</string>
<string name="notification_listener_disable_warning_cancel" msgid="6264631825225298458">"Utzi"</string>
<string name="vr_listeners_title" msgid="511483902408792832">"Errealitate birtualeko laguntza-zerbitzuak"</string>
<string name="no_vr_listeners" msgid="7675484190394450979">"Ez dago errealitate birtualeko laguntza-zerbitzu gisa abiaraztea eskatu duen aplikaziorik instalatuta."</string>
- <string name="vr_listener_security_warning_title" msgid="7019322246707645361">"<xliff:g id="SERVICE">%1$s</xliff:g> zerbitzuari errealitate birtualeko zerbitzuak atzitzeko baimena eman nahi diozu?"</string>
+ <string name="vr_listener_security_warning_title" msgid="7019322246707645361">"Errealitate birtualeko zerbitzuak atzitzeko baimena eman nahi diozu <xliff:g id="SERVICE">%1$s</xliff:g> zerbitzuari?"</string>
<string name="vr_listener_security_warning_summary" msgid="5093225583584522067">"Aplikazioak errealitate birtualean erabiltzen dituzunean abiarazi ahal izango da <xliff:g id="VR_LISTENER_NAME">%1$s</xliff:g>."</string>
<string name="display_vr_pref_title" msgid="1088464812293416981">"Errealitate birtualeko moduan"</string>
<string name="display_vr_pref_low_persistence" msgid="3132583929174794245">"Murriztu lausotzea (gomendatua)"</string>
diff --git a/tests/CarDeveloperOptions/res/values-fi/strings.xml b/tests/CarDeveloperOptions/res/values-fi/strings.xml
index 484167b..2b717f7 100644
--- a/tests/CarDeveloperOptions/res/values-fi/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-fi/strings.xml
@@ -1706,7 +1706,7 @@
<string name="lockpassword_choose_your_pin_message" msgid="8942598950627277885">"Määritä PIN-koodi tietoturvan parantamiseksi."</string>
<string name="lockpassword_choose_your_pin_header_for_fingerprint" msgid="1102927520952116303">"Sormenjälki: aseta PIN-koodi"</string>
<string name="lockpassword_choose_your_pattern_message" msgid="1503075455752279687">"Aseta kuvio tietoturvan parantamiseksi."</string>
- <string name="lockpassword_confirm_your_password_header" msgid="9055242184126838887">"Anna salasana uudelleen"</string>
+ <string name="lockpassword_confirm_your_password_header" msgid="9055242184126838887">"Lisää salasana uudelleen"</string>
<string name="lockpassword_confirm_your_pattern_header" msgid="7137526922696316545">"Vahvista kuvio"</string>
<string name="lockpassword_confirm_your_pin_header" msgid="4335593948303036343">"Anna PIN-koodi uudelleen"</string>
<string name="lockpassword_confirm_passwords_dont_match" msgid="1783767008133345784">"Salasanat eivät täsmää"</string>
@@ -4442,7 +4442,7 @@
<string name="network_connection_request_dialog_title" msgid="3150489262902506588">"Käytettävä laite: <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
<string name="network_connection_timeout_dialog_message" msgid="7036704569274087733">"Laitteita ei löytynyt. Varmista, että laitteet ovat päällä ja voivat muodostaa yhteyden."</string>
<string name="network_connection_timeout_dialog_ok" msgid="5156496438627748361">"Yritä uudelleen"</string>
- <string name="network_connection_errorstate_dialog_message" msgid="1599445930536043943">"Jokin meni vikaan. Sovellus peruutti laitteenvalintapyynnön."</string>
+ <string name="network_connection_errorstate_dialog_message" msgid="1599445930536043943">"Jotain meni pieleen.. Sovellus peruutti laitteenvalintapyynnön."</string>
<string name="network_connection_connect_successful" msgid="888912275986965748">"Yhteyden muodostus onnistui"</string>
<string name="network_connection_request_dialog_showall" msgid="396928496030183071">"Näytä kaikki"</string>
<plurals name="show_bluetooth_devices" formatted="false" msgid="7451733907387872891">
diff --git a/tests/CarDeveloperOptions/res/values-gu/strings.xml b/tests/CarDeveloperOptions/res/values-gu/strings.xml
index fa4b21a..ec50237 100644
--- a/tests/CarDeveloperOptions/res/values-gu/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-gu/strings.xml
@@ -1321,7 +1321,7 @@
<string name="storage_settings" msgid="7009733301485139652">"સ્ટોરેજ"</string>
<string name="storage_settings_for_app" msgid="3028887232073069965">"સ્ટોરેજ અને કૅશ મેમરી"</string>
<string name="storage_usb_settings" msgid="4470138799276333403">"સ્ટોરેજ"</string>
- <string name="storage_settings_title" msgid="7348362600789024415">"સ્ટોરેજ સેટિંગ્સ"</string>
+ <string name="storage_settings_title" msgid="7348362600789024415">"સ્ટોરેજ સેટિંગ"</string>
<string name="storage_settings_summary" product="nosdcard" msgid="600267641559698394">"USB સંગ્રહને અનમાઉન્ટ કરો, ઉપલબ્ધ સંગ્રહ જુઓ"</string>
<string name="storage_settings_summary" product="default" msgid="6233147555590816245">"SD કાર્ડ અનમાઉન્ટ કરો, ઉપલબ્ધ સંગ્રહ જુઓ"</string>
<string name="imei_multi_sim" msgid="6462221220123699987">"IMEI (સિમ સ્લૉટ %1$d)"</string>
@@ -2902,7 +2902,7 @@
<string name="user_enable_calling_confirm_message" msgid="2490126715153125970">"કૉલ ઇતિહાસ આ વપરાશકર્તા સાથે શેર કરવામાં આવશે."</string>
<string name="user_enable_calling_and_sms_confirm_title" msgid="4153856398523366976">"ફોન કૉલ્સ અને SMS ચાલુ કરીએ?"</string>
<string name="user_enable_calling_and_sms_confirm_message" msgid="3278802798876095734">"કૉલ અને SMS ઇતિહાસ આ વપરાશકર્તા સાથે શેર કરવામાં આવશે."</string>
- <string name="emergency_info_title" msgid="1522609271881425375">"કટોકટી માહિતી"</string>
+ <string name="emergency_info_title" msgid="1522609271881425375">"ઇમર્જન્સીની માહિતી"</string>
<string name="emergency_info_summary" msgid="7280464759837387342">"<xliff:g id="USER_NAME">%1$s</xliff:g> માટે માહિતી અને સંપર્કો"</string>
<string name="application_restrictions" msgid="6871981013736536763">"ઍપ્લિકેશનો અને સામગ્રીને મંજૂરી આપો"</string>
<string name="apps_with_restrictions_header" msgid="8656739605673756176">"પ્રતિબંધો ધરાવતી ઍપ્લિકેશનો"</string>
diff --git a/tests/CarDeveloperOptions/res/values-hy/strings.xml b/tests/CarDeveloperOptions/res/values-hy/strings.xml
index 845af38..1f4b6aa 100644
--- a/tests/CarDeveloperOptions/res/values-hy/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-hy/strings.xml
@@ -4068,7 +4068,7 @@
<string name="bluetooth_connected_multiple_devices_summary" msgid="596205630653123250">"Կապակցված է բազմակի շարժական սարքերի"</string>
<string name="demo_mode" msgid="3831081808592541104">"Համակարգի միջերեսի ցուցադրական ռեժիմ"</string>
<string name="dark_ui_mode" msgid="703844190192599217">"Թեմա"</string>
- <string name="dark_ui_mode_title" msgid="8774932716427742413">"Ընտրել թեման"</string>
+ <string name="dark_ui_mode_title" msgid="8774932716427742413">"Ընտրել թեմա"</string>
<string name="dark_ui_settings_light_summary" msgid="5219102347744462812">"Այս կարգավորումը կիրառվում է նաև հավելվածների համար"</string>
<string name="dark_ui_settings_dark_summary" msgid="7042737828943784289">"Աջակցվող հավելվածները ևս կանցնեն մուգ թեմային"</string>
<string name="quick_settings_developer_tiles" msgid="7423485925757678719">"Մշակողի արագ կարգավորման սալիկներ"</string>
diff --git a/tests/CarDeveloperOptions/res/values-ky/strings.xml b/tests/CarDeveloperOptions/res/values-ky/strings.xml
index 556af73..96fd48d 100644
--- a/tests/CarDeveloperOptions/res/values-ky/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-ky/strings.xml
@@ -2639,7 +2639,7 @@
<string name="misc_files" msgid="1012397035001764693">"Башка файлдар"</string>
<string name="misc_files_selected_count" msgid="1434146080729502726">"<xliff:g id="TOTAL">%2$d</xliff:g> ичинен <xliff:g id="NUMBER">%1$d</xliff:g> тандалды"</string>
<string name="misc_files_selected_count_bytes" msgid="3752262902203465861">"<xliff:g id="TOTAL">%2$s</xliff:g> ичинен <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="select_all" msgid="452240217913675728">"Бардыгын тандоо"</string>
+ <string name="select_all" msgid="452240217913675728">"Баарын тандоо"</string>
<string name="data_usage_summary_title" msgid="7288431048564861043">"Дайындардын өткөрүлүшү"</string>
<string name="data_usage_app_summary_title" msgid="8277327968906074983">"Мобилдик Интернет жана Wi‑Fi"</string>
<string name="data_usage_accounting" msgid="4681642832010140640">"Оператор эсептеген дайындар түзмөгүңүздө эсептелген дайындардан айырмаланышы мүмкүн."</string>
diff --git a/tests/CarDeveloperOptions/res/values-nb/strings.xml b/tests/CarDeveloperOptions/res/values-nb/strings.xml
index 050a199..9858ab3 100644
--- a/tests/CarDeveloperOptions/res/values-nb/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-nb/strings.xml
@@ -2902,7 +2902,7 @@
<string name="user_enable_calling_confirm_message" msgid="2490126715153125970">"Anropsloggen deles med denne brukeren."</string>
<string name="user_enable_calling_and_sms_confirm_title" msgid="4153856398523366976">"Vil du slå på telefonsamtaler og SMS?"</string>
<string name="user_enable_calling_and_sms_confirm_message" msgid="3278802798876095734">"Anrops- og tekstmeldingsloggen deles med denne brukeren."</string>
- <string name="emergency_info_title" msgid="1522609271881425375">"Informasjon for nødstilfeller"</string>
+ <string name="emergency_info_title" msgid="1522609271881425375">"Nødinformasjon"</string>
<string name="emergency_info_summary" msgid="7280464759837387342">"Informasjon og kontakter for <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
<string name="application_restrictions" msgid="6871981013736536763">"Tillat apper og innhold"</string>
<string name="apps_with_restrictions_header" msgid="8656739605673756176">"Apper med begrensninger"</string>
@@ -3731,7 +3731,7 @@
<string name="usb_use_MIDI_desc" msgid="1770966187150010947">"Bruk denne enheten som MIDI"</string>
<string name="usb_use" msgid="8940500223316278632">"Bruk USB for"</string>
<string name="usb_default_label" msgid="7471316635263936101">"Standard USB-konfigurasjon"</string>
- <string name="usb_default_info" msgid="953775292571786528">"Disse innstillingene gjelder når en annen enhet er koblet til og telefonen din er låst opp. Du bør bare koble til pålitelige enheter."</string>
+ <string name="usb_default_info" msgid="953775292571786528">"Disse innstillingene gjelder når en annen enhet er koblet til og telefonen din er låst opp. Du bør bare koble til godkjente enheter."</string>
<string name="usb_pref" msgid="6194821550693495068">"USB"</string>
<string name="usb_preference" msgid="7092987095048592826">"USB-innstillinger"</string>
<string name="usb_control_title" msgid="2379698856760894768">"USB kontrolleres av"</string>
diff --git a/tests/CarDeveloperOptions/res/values-ta/strings.xml b/tests/CarDeveloperOptions/res/values-ta/strings.xml
index 410d2fa..8214042 100644
--- a/tests/CarDeveloperOptions/res/values-ta/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-ta/strings.xml
@@ -208,7 +208,7 @@
<string name="proxy_error_empty_port" msgid="8034561724923076215">"நீங்கள் போர்ட் புலத்தை நிரப்ப வேண்டும்."</string>
<string name="proxy_error_empty_host_set_port" msgid="8471455809508588255">"ஹோஸ்ட் புலம் வெறுமையாக இருந்தால் போர்ட்டின் புலம் வெறுமையாக இருக்க வேண்டும்."</string>
<string name="proxy_error_invalid_port" msgid="4046559920586100637">"நீங்கள் உள்ளிட்ட போர்ட் தவறானது."</string>
- <string name="proxy_warning_limited_support" msgid="9026539134219095768">"HTTP ப்ராக்ஸியை உலாவி பயன்படுத்தும் ஆனால் பிற பயன்பாடுகள் பயன்படுத்தாமல் போகலாம்."</string>
+ <string name="proxy_warning_limited_support" msgid="9026539134219095768">"HTTP ப்ராக்ஸியை உலாவி பயன்படுத்தும் ஆனால் பிற ஆப்ஸ் பயன்படுத்தாமல் போகலாம்."</string>
<string name="proxy_url_title" msgid="882042361706435904">"PAC URL: "</string>
<string name="radio_info_dl_kbps" msgid="2903778264453410272">"DL இணைய வேகம் (kbps):"</string>
<string name="radio_info_ul_kbps" msgid="3802245899811732716">"UL இணைய வேகம் (kbps):"</string>
@@ -510,8 +510,8 @@
<string name="crypt_keeper_encrypt_title" product="tablet" msgid="2292129135369853167">"டேப்லெட்டை என்க்ரிப்ட் செய்"</string>
<string name="crypt_keeper_encrypt_title" product="default" msgid="3110852053238357832">"மொபைலை என்க்ரிப்ட் செய்"</string>
<string name="crypt_keeper_encrypted_summary" msgid="2438498691741626642">"என்க்ரிப்ட் செய்யப்பட்டது"</string>
- <string name="crypt_keeper_desc" product="tablet" msgid="9142792050252407734">"உங்கள் கணக்குகள், அமைப்புகள், பதிவிறக்கிய பயன்பாடுகள் மற்றும் அவற்றின் தரவு, மீடியா மற்றும் பிற கோப்புகள் என அனைத்தையும் என்க்ரிப்ட் செய்யலாம். உங்கள் டேப்லெட்டை என்க்ரிப்ட் செய்த பிறகு, திரைப்பூட்டை (அதாவது வடிவம் அல்லது பின் அல்லது கடவுச்சொல்) அமைத்திருந்தால், ஒவ்வொரு முறையும் டேப்லெட்டை இயக்கும்போது குறிநீக்குவதற்கு திரையைத் திறக்க வேண்டும். உங்களின் எல்லா தரவையும் அழித்து, ஆரம்ப நிலைக்கு மீட்டமைப்பதே குறிநீக்குவதற்கான மற்றொரு வழியாகும்.\n\nஎன்க்ரிப்ட் செய்வதற்கு ஒரு மணிநேரம் அல்லது அதற்கு மேல் ஆகலாம். சார்ஜ் செய்த பேட்டரியுடன் தொடங்கி, செயல் முடியும் வரை சார்ஜ் ஆகும் நிலையிலேயே வைக்கவும். செயலில் குறுக்கிட்டால், உங்கள் தரவில் சிலவற்றை அல்லது மொத்தத்தையும் இழப்பீர்கள்."</string>
- <string name="crypt_keeper_desc" product="default" msgid="1996334685607444282">"உங்கள் கணக்குகள், அமைப்புகள், பதிவிறக்கிய பயன்பாடுகள் மற்றும் அவற்றின் தரவு, மீடியா மற்றும் பிற கோப்புகள் என அனைத்தையும் என்க்ரிப்ட் செய்யலாம். உங்கள் மொபைலை என்க்ரிப்ட் செய்த பிறகு, திரைப்பூட்டை (அதாவது வடிவம் அல்லது பின் அல்லது கடவுச்சொல்) அமைத்திருந்தால், ஒவ்வொரு முறையும் மொபைலை இயக்கும்போது குறிநீக்குவதற்கு திரையைத் திறக்க வேண்டும். உங்களின் எல்லா தரவையும் அழித்து, ஆரம்ப நிலைக்கு மீட்டமைப்பதே குறிநீக்குவதற்கான மற்றொரு வழியாகும்.\n\nஎன்க்ரிப்ட் செய்வதற்கு ஒரு மணிநேரம் அல்லது அதற்கு மேல் ஆகலாம். சார்ஜ் செய்த பேட்டரியுடன் தொடங்கி, செயல் முடியும் வரை சார்ஜ் ஆகும் நிலையிலேயே வைக்கவும். செயலில் குறுக்கிட்டால், உங்கள் தரவில் சிலவற்றை அல்லது மொத்தத்தையும் இழப்பீர்கள்."</string>
+ <string name="crypt_keeper_desc" product="tablet" msgid="9142792050252407734">"உங்கள் கணக்குகள், அமைப்புகள், பதிவிறக்கிய ஆப்ஸ் மற்றும் அவற்றின் தரவு, மீடியா மற்றும் பிற கோப்புகள் என அனைத்தையும் என்க்ரிப்ட் செய்யலாம். உங்கள் டேப்லெட்டை என்க்ரிப்ட் செய்த பிறகு, திரைப்பூட்டை (அதாவது வடிவம் அல்லது பின் அல்லது கடவுச்சொல்) அமைத்திருந்தால், ஒவ்வொரு முறையும் டேப்லெட்டை இயக்கும்போது குறிநீக்குவதற்கு திரையைத் திறக்க வேண்டும். உங்களின் எல்லா தரவையும் அழித்து, ஆரம்ப நிலைக்கு மீட்டமைப்பதே குறிநீக்குவதற்கான மற்றொரு வழியாகும்.\n\nஎன்க்ரிப்ட் செய்வதற்கு ஒரு மணிநேரம் அல்லது அதற்கு மேல் ஆகலாம். சார்ஜ் செய்த பேட்டரியுடன் தொடங்கி, செயல் முடியும் வரை சார்ஜ் ஆகும் நிலையிலேயே வைக்கவும். செயலில் குறுக்கிட்டால், உங்கள் தரவில் சிலவற்றை அல்லது மொத்தத்தையும் இழப்பீர்கள்."</string>
+ <string name="crypt_keeper_desc" product="default" msgid="1996334685607444282">"உங்கள் கணக்குகள், அமைப்புகள், பதிவிறக்கிய ஆப்ஸ் மற்றும் அவற்றின் தரவு, மீடியா மற்றும் பிற கோப்புகள் என அனைத்தையும் என்க்ரிப்ட் செய்யலாம். உங்கள் மொபைலை என்க்ரிப்ட் செய்த பிறகு, திரைப்பூட்டை (அதாவது வடிவம் அல்லது பின் அல்லது கடவுச்சொல்) அமைத்திருந்தால், ஒவ்வொரு முறையும் மொபைலை இயக்கும்போது குறிநீக்குவதற்கு திரையைத் திறக்க வேண்டும். உங்களின் எல்லா தரவையும் அழித்து, ஆரம்ப நிலைக்கு மீட்டமைப்பதே குறிநீக்குவதற்கான மற்றொரு வழியாகும்.\n\nஎன்க்ரிப்ட் செய்வதற்கு ஒரு மணிநேரம் அல்லது அதற்கு மேல் ஆகலாம். சார்ஜ் செய்த பேட்டரியுடன் தொடங்கி, செயல் முடியும் வரை சார்ஜ் ஆகும் நிலையிலேயே வைக்கவும். செயலில் குறுக்கிட்டால், உங்கள் தரவில் சிலவற்றை அல்லது மொத்தத்தையும் இழப்பீர்கள்."</string>
<string name="crypt_keeper_button_text" product="tablet" msgid="7918671468758813824">"டேப்லெட்டை என்க்ரிப்ட் செய்"</string>
<string name="crypt_keeper_button_text" product="default" msgid="8737394386627318489">"மொபைலை என்க்ரிப்ட் செய்"</string>
<string name="crypt_keeper_low_charge_text" msgid="1422879728632636311">"உங்கள் பேட்டரியை சார்ஜ் செய்து மீண்டும் முயற்சிக்கவும்."</string>
@@ -1067,7 +1067,7 @@
<string name="wifi_hotspot_password_title" msgid="4289338152595154889">"ஹாட்ஸ்பாட் கடவுச்சொல்"</string>
<string name="wifi_hotspot_ap_band_title" msgid="3485744480410441949">"AP அலைவரிசை"</string>
<string name="wifi_hotspot_footer_info_regular" msgid="3876006922622827363">"மற்ற சாதனங்களுக்கு வைஃபை நெட்வொர்க்கை உருவாக்க, ஹாட்ஸ்பாட்டைப் பயன்படுத்தவும். ஹாட்ஸ்பாட்டானது, மொபைல் டேட்டா இணைப்பைப் பயன்படுத்தி இண்டர்நெட்டை வழங்குகிறது. கூடுதல் மொபைல் டேட்டா பேமெண்ட்கள் விதிக்கப்படலாம்."</string>
- <string name="wifi_hotspot_footer_info_local_only" msgid="3339582350894639261">"அருகிலுள்ள சாதனங்களுடன் உள்ளடக்கத்தைப் பகிர, பயன்பாடுகள் ஹாட்ஸ்பாட்டையும் உருவாக்கலாம்."</string>
+ <string name="wifi_hotspot_footer_info_local_only" msgid="3339582350894639261">"அருகிலுள்ள சாதனங்களுடன் உள்ளடக்கத்தைப் பகிர, ஆப்ஸ் ஹாட்ஸ்பாட்டையும் உருவாக்கலாம்."</string>
<string name="wifi_hotspot_auto_off_title" msgid="7416022590415189590">"ஹாட்ஸ்பாட்டைத் தானாக ஆஃப் செய்"</string>
<string name="wifi_hotspot_auto_off_summary" msgid="3866769400624802105">"சாதனங்கள் எதுவும் இணைக்கப்படவில்லை எனில், வைஃபை ஹாட்ஸ்பாட் ஆஃப் செய்யப்படும்"</string>
<string name="wifi_tether_starting" msgid="7676952148471297900">"ஹாட்ஸ்பாட்டை இயக்குகிறது…"</string>
@@ -1388,8 +1388,8 @@
<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>
- <string name="dlg_confirm_unmount_text" product="nosdcard" msgid="1423648405874813948">"USB சேமிப்பிடத்தை அகற்றிவிட்டால், நீங்கள் பயன்படுத்துகின்ற சில பயன்பாடுகள் நின்றுவிடும், மேலும் நீங்கள் USB சேமிப்பிடத்தை மீண்டும் செருகும்வரை அவை கிடைக்காமல் இருக்கலாம்."</string>
- <string name="dlg_confirm_unmount_text" product="default" msgid="4099391737780732622">"SD கார்டை அகற்றினால், நீங்கள் பயன்படுத்தும் சில பயன்பாடுகள் நின்றுவிடும், மேலும் SD கார்டை மீண்டும் செருகும் வரை அவை பயன்படுத்த கிடைக்காமல் இருக்கலாம்."</string>
+ <string name="dlg_confirm_unmount_text" product="nosdcard" msgid="1423648405874813948">"USB சேமிப்பிடத்தை அகற்றிவிட்டால், நீங்கள் பயன்படுத்துகின்ற சில ஆப்ஸ் நின்றுவிடும், மேலும் நீங்கள் USB சேமிப்பிடத்தை மீண்டும் செருகும்வரை அவை கிடைக்காமல் இருக்கலாம்."</string>
+ <string name="dlg_confirm_unmount_text" product="default" msgid="4099391737780732622">"SD கார்டை அகற்றினால், நீங்கள் பயன்படுத்தும் சில ஆப்ஸ் நின்றுவிடும், மேலும் SD கார்டை மீண்டும் செருகும் வரை அவை பயன்படுத்த கிடைக்காமல் இருக்கலாம்."</string>
<string name="dlg_error_unmount_title" product="nosdcard" msgid="3132640848329117857"></string>
<string name="dlg_error_unmount_title" product="default" msgid="3132640848329117857"></string>
<string name="dlg_error_unmount_text" product="nosdcard" msgid="4710773826053117136">"USB கார்டை அகற்ற முடியவில்லை. பிறகு முயற்சிக்கவும்."</string>
@@ -1399,7 +1399,7 @@
<string name="sd_ejecting_title" msgid="595074246815112145">"அகற்றுகிறது"</string>
<string name="sd_ejecting_summary" msgid="5708943172014003213">"அகற்றுதல் செயலில் உள்ளது"</string>
<string name="storage_low_title" msgid="6957178208426099592">"சேமிப்பிடம் குறைகிறது"</string>
- <string name="storage_low_summary" msgid="4475275204869514141">"ஒத்திசைத்தல் போன்ற அமைப்பின் சில செயல்பாடுகள் வேலைசெய்யாமல் போகலாம். பயன்பாடுகள் அல்லது மீடியா உள்ளடக்கம் போன்ற உருப்படிகளை நீக்குதல் அல்லது அகற்றுவதன் மூலம் சேமிப்பிடத்தை காலியாக்க முயற்சிக்கவும்."</string>
+ <string name="storage_low_summary" msgid="4475275204869514141">"ஒத்திசைத்தல் போன்ற அமைப்பின் சில செயல்பாடுகள் வேலைசெய்யாமல் போகலாம். ஆப்ஸ் அல்லது மீடியா உள்ளடக்கம் போன்ற உருப்படிகளை நீக்குதல் அல்லது அகற்றுவதன் மூலம் சேமிப்பிடத்தை காலியாக்க முயற்சிக்கவும்."</string>
<string name="storage_menu_rename" msgid="3731682449294417745">"மறுபெயரிடு"</string>
<string name="storage_menu_mount" msgid="6395893560780365473">"பொருத்து"</string>
<string name="storage_menu_unmount" msgid="5041360076873514189">"வெளியேற்று"</string>
@@ -1419,7 +1419,7 @@
<string name="usb_ptp_title" msgid="6629335976394685361">"கேமரா (PTP)"</string>
<string name="usb_ptp_summary" msgid="460425275251168189">"கேமரா மென்பொருளைப் பயன்படுத்தி நீங்கள் படங்களை அனுப்பவும், மேலும் MTP ஆதரிக்காத கணினிகளில் எந்தக் கோப்புகளையும் பரிமாற்றவும் உதவுகிறது."</string>
<string name="usb_midi_title" msgid="8626512517313340943">"MIDI"</string>
- <string name="usb_midi_summary" msgid="3607444815743771712">"MIDI இயக்கப்பட்ட பயன்பாடுகள், USB மூலம் MIDI மென்பொருளைப் பயன்படுத்தி கணினியில் செயல்பட அனுமதிக்கும்."</string>
+ <string name="usb_midi_summary" msgid="3607444815743771712">"MIDI இயக்கப்பட்ட ஆப்ஸ், USB மூலம் MIDI மென்பொருளைப் பயன்படுத்தி கணினியில் செயல்பட அனுமதிக்கும்."</string>
<string name="storage_other_users" msgid="1055693465220962928">"பிற பயனர்கள்"</string>
<string name="storage_internal_title" msgid="7969898703086593200">"சாதனச் சேமிப்பகம்"</string>
<string name="storage_external_title" msgid="3308178326521953306">"கையடக்கச் சேமிப்பகம்"</string>
@@ -1438,10 +1438,10 @@
<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_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_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_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_internal_forget_confirm" msgid="3052483375203727176">"<xliff:g id="NAME">^1</xliff:g> இல் சேமிக்கப்பட்ட அனைத்து ஆப்ஸ், படங்கள் மற்றும் தரவு ஆகியவற்றை நிரந்தரமாக இழப்பீர்கள்."</string>
<string name="storage_detail_apps" msgid="8154648512504196820">"ஆப்ஸ்"</string>
<string name="storage_detail_images" msgid="6996202225684468964">"படங்கள்"</string>
<string name="storage_detail_videos" msgid="6030983354721080849">"வீடியோக்கள்"</string>
@@ -1452,7 +1452,7 @@
<string name="storage_detail_explore" msgid="8206900269596580264">"<xliff:g id="NAME">^1</xliff:g> இல் உலாவு"</string>
<string name="storage_detail_dialog_other" msgid="5073511663616043370">"ஆப்ஸ் சேமித்துள்ள பகிர்ந்த ஃபைல்கள், இண்டர்நெட் அல்லது புளூடூத் மூலம் பதிவிறக்கிய ஃபைல்கள், Android ஃபைல்கள் போன்றவை மற்ற ஃபைல்களில் அடங்கும். \n\n<xliff:g id="NAME">^1</xliff:g> இல் தெரியக்கூடிய உள்ளடக்கத்தைப் பார்க்க, ’உலாவு’ என்பதைத் தட்டவும்."</string>
<string name="storage_detail_dialog_system" msgid="1472572861360014226">"Android <xliff:g id="VERSION">%s</xliff:g> பதிப்பை இயக்குவதற்குப் பயன்படுத்தப்படும் ஃபைல்களும் இயங்குதளத்தில் அடங்கும்"</string>
- <string name="storage_detail_dialog_user" msgid="1663117417635010371">"<xliff:g id="USER_0">^1</xliff:g> சேமிப்பகத்தின் <xliff:g id="SIZE">^2</xliff:g> அளவைப் பயன்படுத்தி, படங்கள், இசை, பயன்பாடுகள் அல்லது பிற தரவைச் சேமித்திருக்கலாம். \n\nவிவரங்களைப் பார்க்க, <xliff:g id="USER_1">^1</xliff:g>க்கு மாறவும்."</string>
+ <string name="storage_detail_dialog_user" msgid="1663117417635010371">"<xliff:g id="USER_0">^1</xliff:g> சேமிப்பகத்தின் <xliff:g id="SIZE">^2</xliff:g> அளவைப் பயன்படுத்தி, படங்கள், இசை, ஆப்ஸ் அல்லது பிற தரவைச் சேமித்திருக்கலாம். \n\nவிவரங்களைப் பார்க்க, <xliff:g id="USER_1">^1</xliff:g>க்கு மாறவும்."</string>
<string name="storage_wizard_init_title" msgid="3407283236421089014">"<xliff:g id="NAME">^1</xliff:g>ஐ அமைக்கவும்"</string>
<string name="storage_wizard_init_external_title" msgid="6853250619674645478">"கையடக்க சேமிப்பகமாகப் பயன்படுத்தவும்"</string>
<string name="storage_wizard_init_external_summary" msgid="6993815290050489327">"சாதனங்களுக்கிடையே படங்களையும் பிற மீடியாவையும் நகர்த்தலாம்."</string>
@@ -1466,14 +1466,14 @@
<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>
<string name="storage_wizard_migrate_confirm_body" msgid="7297723787416643076"><b>"நகர்த்துவதற்கு <xliff:g id="TIME">^1</xliff:g> ஆகலாம். இதனால் <xliff:g id="NAME">^3</xliff:g> இல் <xliff:g id="SIZE">^2</xliff:g> அளவு சேமிப்பகம் கிடைக்கும்."</b></string>
<string name="storage_wizard_migrate_confirm_next" msgid="6539804689462991087">"நகர்த்து"</string>
<string name="storage_wizard_migrate_progress_title" msgid="7542196688665109833">"தரவு நகர்த்தப்படுகிறது…"</string>
- <string name="storage_wizard_migrate_details" msgid="4269509141637554985">"நகர்த்தும்போது: \n• <xliff:g id="NAME">^1</xliff:g>ஐ அகற்ற வேண்டாம். \n• சில பயன்பாடுகள் சரியாக வேலை செய்யாது. \n• சாதனம் சார்ஜ் செய்யப்பட்டிருக்க வேண்டும்."</string>
+ <string name="storage_wizard_migrate_details" msgid="4269509141637554985">"நகர்த்தும்போது: \n• <xliff:g id="NAME">^1</xliff:g>ஐ அகற்ற வேண்டாம். \n• சில ஆப்ஸ் சரியாக வேலை செய்யாது. \n• சாதனம் சார்ஜ் செய்யப்பட்டிருக்க வேண்டும்."</string>
<string name="storage_wizard_ready_title" msgid="4905921139763520341">"<xliff:g id="NAME">^1</xliff:g> பயன்படுத்துவதற்குத் தயார்"</string>
<string name="storage_wizard_ready_external_body" msgid="8785407468656286236">"<xliff:g id="NAME">^1</xliff:g> படங்களையும் பிற மீடியாவையும் பயன்படுத்த, தயாராக உள்ளது."</string>
<string name="storage_wizard_ready_internal_body" msgid="2258287496678469217">"புதிய <xliff:g id="NAME">^1</xliff:g> வேலை செய்கிறது. \n\nசாதனத்திற்கு படங்கள், கோப்புகள், ஆப்ஸ் டேட்டாவை நகர்த்த, அமைப்புகள் > சேமிப்பகம் என்பதற்குச் செல்லவும்."</string>
@@ -1483,7 +1483,7 @@
<string name="storage_wizard_move_progress_title" msgid="5250929161803336592">"<xliff:g id="APP">^1</xliff:g> நகர்த்தப்படுகிறது…"</string>
<string name="storage_wizard_move_progress_body" msgid="1713792142250410169">"நகர்த்தும்போது <xliff:g id="NAME">^1</xliff:g>ஐ அகற்ற வேண்டாம். \n\nநகர்த்தி முடிக்கும்வரை, சாதனத்தில் <xliff:g id="APP">^2</xliff:g> ஆப்ஸ் கிடைக்காது."</string>
<string name="storage_wizard_move_progress_cancel" msgid="9047521329704060401">"நகர்த்துவதை ரத்துசெய்"</string>
- <string name="storage_wizard_slow_body" msgid="2307974936036261069">"இந்த <xliff:g id="NAME_0">^1</xliff:g> வேகம் குறைவானது போல் தெரிகிறது. \n\nநீங்கள் தொடரலாம், இந்த இடத்திற்கு நகர்த்தப்பட்ட பயன்பாடுகள் தடங்கல்களுடன் இயங்கலாம் மற்றும் தரவைப் பரிமாற்றுவதற்கு அதிக நேரம் எடுக்கலாம். \n\nசிறந்த செயல்திறனுக்கு, வேகமான <xliff:g id="NAME_1">^1</xliff:g>ஐப் பயன்படுத்தவும்."</string>
+ <string name="storage_wizard_slow_body" msgid="2307974936036261069">"இந்த <xliff:g id="NAME_0">^1</xliff:g> வேகம் குறைவானது போல் தெரிகிறது. \n\nநீங்கள் தொடரலாம், இந்த இடத்திற்கு நகர்த்தப்பட்ட ஆப்ஸ் தடங்கல்களுடன் இயங்கலாம் மற்றும் தரவைப் பரிமாற்றுவதற்கு அதிக நேரம் எடுக்கலாம். \n\nசிறந்த செயல்திறனுக்கு, வேகமான <xliff:g id="NAME_1">^1</xliff:g>ஐப் பயன்படுத்தவும்."</string>
<string name="storage_wizard_init_v2_title" msgid="7408910177547901960">"<xliff:g id="NAME">^1</xliff:g>ஐ எப்படிப் பயன்படுத்துவீர்கள்?"</string>
<string name="storage_wizard_init_v2_internal_title" product="tablet" msgid="7948795312504302810">"கூடுதல் டேப்லெட் சேமிப்பகம் உபயோகி"</string>
<string name="storage_wizard_init_v2_internal_summary" product="tablet" msgid="6237770506398410172">"இந்த டேப்லெட்டில் உள்ள ஆப்ஸ், ஃபைல்கள் & மீடியாவிற்கு மட்டும்"</string>
@@ -1660,18 +1660,18 @@
<string name="location_scanning_bluetooth_always_scanning_description" msgid="3796673798637848690">"புளூடூத் ஆஃப் செய்யப்பட்டிருந்தாலும்கூட, எந்தநேரத்திலும் அருகிலுள்ள சாதனங்களைத் தேட, ஆப்ஸையும் சேவைகளையும் அனுமதிக்கும். உதாரணத்திற்கு, இருப்பிடம் சார்ந்த அம்சங்கள் மற்றும் சேவைகளை மேம்படுத்துவதற்கும் இதைப் பயன்படுத்தலாம்."</string>
<string name="managed_profile_location_services" msgid="224925483299159541">"பணிக் கணக்கிற்கான இருப்பிடச் சேவைகள்"</string>
<string name="location_network_based" msgid="1535812159327454835">"வைஃபை & மொபைல் நெட்வொர்க்கின் இருப்பிடம்"</string>
- <string name="location_neighborhood_level" msgid="8459352741296587916">"உங்கள் இருப்பிடத்தை விரைவாகக் கணிக்கும் வகையில் பயன்பாடுகள், Google இன் இருப்பிடச் சேவையைப் பயன்படுத்தலாம். அநாமதேய இருப்பிடத் தரவு சேகரிக்கப்பட்டு Google க்கு அனுப்பப்படும்."</string>
+ <string name="location_neighborhood_level" msgid="8459352741296587916">"உங்கள் இருப்பிடத்தை விரைவாகக் கணிக்கும் வகையில் ஆப்ஸ், Google இன் இருப்பிடச் சேவையைப் பயன்படுத்தலாம். அநாமதேய இருப்பிடத் தரவு சேகரிக்கப்பட்டு Google க்கு அனுப்பப்படும்."</string>
<string name="location_neighborhood_level_wifi" msgid="6120133551482003840">"வைஃபை மூலம் இருப்பிடம் கண்டறியப்பட்டது"</string>
<string name="location_gps" msgid="688049341158297763">"GPS சாட்டிலைட்டுகள்"</string>
- <string name="location_street_level" product="tablet" msgid="4459804798444296650">"உங்கள் இருப்பிடத்தைக் குறிப்பதற்காக, பயன்பாடுகள் உங்கள் டேப்லெட்டில் GPS ஐப் பயன்படுத்தும்"</string>
- <string name="location_street_level" product="default" msgid="7407688345675450051">"உங்கள் இருப்பிடத்தைக் குறிப்பதற்காக, பயன்பாடுகள் உங்கள் தொலைபேசியில் GPS ஐப் பயன்படுத்தும்"</string>
+ <string name="location_street_level" product="tablet" msgid="4459804798444296650">"உங்கள் இருப்பிடத்தைக் குறிப்பதற்காக, ஆப்ஸ் உங்கள் டேப்லெட்டில் GPS ஐப் பயன்படுத்தும்"</string>
+ <string name="location_street_level" product="default" msgid="7407688345675450051">"உங்கள் இருப்பிடத்தைக் குறிப்பதற்காக, ஆப்ஸ் உங்கள் தொலைபேசியில் GPS ஐப் பயன்படுத்தும்"</string>
<string name="assisted_gps" msgid="5411780261117055175">"துணை GPS ஐப் பயன்படுத்து"</string>
<string name="assisted_gps_enabled" msgid="2561022181775725369">"GPS க்கு உதவ, சேவையகத்தைப் பயன்படுத்து (நெட்வொர்க் பயன்பாட்டைக் குறைக்க, தேர்வுநீக்கு)"</string>
<string name="assisted_gps_disabled" msgid="6448758788217415937">"GPS க்கு உதவ, சேவையகத்தைப் பயன்படுத்து (GPS செயல்திறனை மேம்படுத்த தேர்வுநீக்கு)"</string>
<string name="use_location_title" msgid="7724788634359496634">"இருப்பிடம் & Google தேடல்"</string>
<string name="use_location_summary" msgid="7396716606067400283">"தேடல் முடிவுகள் மற்றும் பிற சேவைகளை மேம்படுத்துவதற்காக Google உங்கள் இருப்பிடத்தைப் பயன்படுத்தும்"</string>
<string name="location_access_title" msgid="8587974819606800029">"எனது இருப்பிடத்திற்கான அணுகல்"</string>
- <string name="location_access_summary" msgid="6919495149026354355">"உங்கள் அனுமதியைக் கேட்ட பயன்பாடுகள் உங்களின் இருப்பிடத் தகவலைப் பயன்படுத்தும்"</string>
+ <string name="location_access_summary" msgid="6919495149026354355">"உங்கள் அனுமதியைக் கேட்ட ஆப்ஸ் உங்களின் இருப்பிடத் தகவலைப் பயன்படுத்தும்"</string>
<string name="location_sources_heading" msgid="8526658357120282741">"இருப்பிட ஆதாரங்கள்"</string>
<string name="about_settings" product="tablet" msgid="4869626690708456341">"டேப்லெட் அறிமுகம்"</string>
<string name="about_settings" product="default" msgid="6019547763377294261">"மொபைல் விவரம்"</string>
@@ -1843,7 +1843,7 @@
<string name="filter" msgid="2426943916212457962">"வடிகட்டு"</string>
<string name="filter_dlg_title" msgid="115313222190512670">"வடிப்பான் விருப்பங்களைத் தேர்வுசெய்யவும்"</string>
<string name="filter_apps_all" msgid="3938077534861382701">"எல்லாப் பயன்பாடுகளும்"</string>
- <string name="filter_apps_disabled" msgid="5394488790555678117">"முடக்கிய பயன்பாடுகள்"</string>
+ <string name="filter_apps_disabled" msgid="5394488790555678117">"முடக்கிய ஆப்ஸ்"</string>
<string name="filter_apps_third_party" msgid="3985794876813232322">"பதிவிறக்கப்பட்டது"</string>
<string name="filter_apps_running" msgid="6852975378502426359">"இயங்குகிறது"</string>
<string name="filter_apps_onsdcard" product="nosdcard" msgid="3501701148760911442">"USB சேமிப்பகம்"</string>
@@ -1889,13 +1889,13 @@
<string name="instant_app_details_summary" msgid="6384264315914966114">"<xliff:g id="APP_STORE">%1$s</xliff:g> பற்றிய கூடுதல் தகவல்"</string>
<string name="app_ops_running" msgid="6378418969742957805">"இயங்குகிறது"</string>
<string name="app_ops_never_used" msgid="8305262378162525813">"(ஒருபோதும் பயன்படுத்தவில்லை)"</string>
- <string name="no_default_apps" msgid="4519038578011412532">"இயல்பு பயன்பாடுகள் இல்லை."</string>
+ <string name="no_default_apps" msgid="4519038578011412532">"இயல்பு ஆப்ஸ் இல்லை."</string>
<string name="storageuse_settings_title" msgid="3390798597982116048">"சேமிப்பிடத்தின் பயன்பாடு"</string>
- <string name="storageuse_settings_summary" msgid="3013328092465903687">"பயன்பாடுகள் பயன்படுத்திய சேமிப்பிடத்தைக் காட்டு"</string>
+ <string name="storageuse_settings_summary" msgid="3013328092465903687">"ஆப்ஸ் பயன்படுத்திய சேமிப்பிடத்தைக் காட்டு"</string>
<string name="service_restarting" msgid="1190995225643385568">"மீண்டும் தொடங்குகிறது"</string>
<string name="cached" msgid="4019482949725020855">"தற்காலிகச் சேமிப்பின் பின்புலச் செயல்முறை"</string>
<string name="no_running_services" msgid="618823924559385173">"எதுவும் இயக்கத்தில் இல்லை."</string>
- <string name="service_started_by_app" msgid="6906027340122215035">"பயன்பாட்டால் தொடங்கப்பட்டது."</string>
+ <string name="service_started_by_app" msgid="6906027340122215035">"ஆப்ஸால் தொடங்கப்பட்டது."</string>
<!-- no translation found for service_client_name (7083258170099389413) -->
<skip />
<string name="service_background_processes" msgid="2678557055039755445">"<xliff:g id="MEMORY">%1$s</xliff:g> மீதமுள்ளது"</string>
@@ -1923,7 +1923,7 @@
<string name="runningservicedetails_processes_title" msgid="5496507383850423763">"செயல்கள்"</string>
<string name="service_stop" msgid="8812777462903125191">"நிறுத்து"</string>
<string name="service_manage" msgid="7045214643721276662">"அமைப்பு"</string>
- <string name="service_stop_description" msgid="4184180745938573707">"இந்த ஆப்ஸ் ஏற்கனவே இதன் பயன்பாட்டால் தொடங்கப்பட்டது. இதை நிறுத்துவதால் ஆப்ஸ் தோல்வியடையலாம்."</string>
+ <string name="service_stop_description" msgid="4184180745938573707">"இந்த ஆப்ஸ் ஏற்கனவே இதன் ஆப்ஸால் தொடங்கப்பட்டது. இதை நிறுத்துவதால் ஆப்ஸ் தோல்வியடையலாம்."</string>
<string name="heavy_weight_stop_description" msgid="7444148811046611463">"பயன்பாட்டைப் பாதுகாப்பாக நிறுத்த முடியாது. இதை நிறுத்தினால், நீங்கள் நடப்பு செயல்கள் சிலவற்றை இழக்க நேரிடலாம்."</string>
<string name="background_process_stop_description" msgid="8971810208873038109">"இது பழைய ஆப்ஸ் செயல்முறையாகும், மீண்டும் தேவைப்பட்டால் வழங்குவதற்காக இன்னமும் தொடர்ந்து செயல்படுத்தப்படுகிறது. பொதுவாக அதை நிறுத்துவதற்கு எந்தக் காரணமும் இல்லை."</string>
<string name="service_manage_description" msgid="8058123524402833082">"<xliff:g id="CLIENT_NAME">%1$s</xliff:g>: தற்போது பயன்பாட்டில் உள்ளது. அதைக் கட்டுப்படுத்த, அமைப்புகளைத் தட்டவும்."</string>
@@ -2405,7 +2405,7 @@
<string name="battery_desc_bluetooth" msgid="3468061900485447679">"புளூடூத்தால் பயன்படுத்தப்பட்ட பேட்டரி அளவு"</string>
<string name="battery_sugg_bluetooth_basic" msgid="6353294067057749310">"புளூடூத் ஐப் பயன்படுத்தாதபோது அதை முடக்கவும்"</string>
<string name="battery_sugg_bluetooth_headset" msgid="2421931037149315202">"வேறு புளூடூத் சாதனத்துடன் இணைக்க முயற்சிக்கவும்"</string>
- <string name="battery_desc_apps" msgid="6826726880149226823">"பயன்பாட்டால் பயன்படுத்தப்பட்ட பேட்டரியின் அளவு"</string>
+ <string name="battery_desc_apps" msgid="6826726880149226823">"ஆப்ஸால் பயன்படுத்தப்பட்ட பேட்டரியின் அளவு"</string>
<string name="battery_sugg_apps_info" msgid="9175761965559743977">"ஆப்ஸை நிறுத்தவும் அல்லது நிறுவல் நீக்கவும்"</string>
<string name="battery_sugg_apps_gps" msgid="489694612870772770">"பேட்டரி சேமிப்பு முறையைத் தேர்ந்தெடுக்கவும்"</string>
<string name="battery_sugg_apps_settings" msgid="20465932930350295">"பேட்டரி அளவின் பயன்பாட்டைக் குறைப்பதற்கான அமைப்புகளை ஆப்ஸ் வழங்கலாம்"</string>
@@ -2563,7 +2563,7 @@
<string name="uninstall_device_admin" msgid="9017499299961719830">"ஆப்ஸை நிறுவல் நீக்கு"</string>
<string name="remove_and_uninstall_device_admin" msgid="5607703579731496253">"செயலற்றதாக்கி, நிறுவல் நீக்கு"</string>
<string name="select_device_admin_msg" msgid="4173769638399075387">"சாதனநிர்வாகி ஆப்ஸ்"</string>
- <string name="no_device_admins" msgid="4129231900385977460">"சாதன நிர்வாகிப் பயன்பாடுகள் எதுவுமில்லை"</string>
+ <string name="no_device_admins" msgid="4129231900385977460">"சாதன நிர்வாகிப் ஆப்ஸ் எதுவுமில்லை"</string>
<string name="personal_device_admin_title" msgid="759440849188565661">"தனிப்பட்டவை"</string>
<string name="managed_device_admin_title" msgid="8021522755492551726">"பணியிடம்"</string>
<string name="sms_access_restriction_enabled" msgid="3006320256764718303">"மெசேஜ், அழைப்புப் பதிவு அணுகலைக் கட்டுப்படுத்துதல்"</string>
@@ -2575,7 +2575,7 @@
<string name="device_admin_warning" msgid="4421817419326480449">"இந்த நிர்வாகி ஆப்ஸைச் செயல்படுத்தினால், பின்வரும் செயல்பாடுகளைச் செய்ய <xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸ் அனுமதிக்கப்படும்:"</string>
<string name="device_admin_status" msgid="5424944611789040723">"இந்த நிர்வாகி ஆப்ஸ் செயலில் உள்ளது, அத்துடன் பின்வரும் செயல்பாடுகளைச் செய்ய <xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸ் அனுமதிக்கும்:"</string>
<string name="profile_owner_add_title" msgid="735805919754362791">"சுயவிவர நிர்வாகியை இயக்கவா?"</string>
- <string name="adding_profile_owner_warning" msgid="1284541194547382194">"தொடர்வதன் மூலம், நிர்வாகி (உங்கள் தனிப்பட்ட தரவுடன் சேர்த்து, தொடர்புடைய தரவையும் சேமிக்கக்கூடும்) உங்கள் பயனரை நிர்வகிக்கும்.\n\nஉங்கள் நிர்வாகியால் அமைப்புகள், அணுகல், பயன்பாடுகள் மற்றும் நெட்வொர்க் செயல்பாடு, உங்கள் சாதனத்தின் இருப்பிடத் தகவல் உட்பட இந்தப் பயனருடன் தொடர்புடைய தரவு ஆகியவற்றை நிர்வகிக்க முடியும்."</string>
+ <string name="adding_profile_owner_warning" msgid="1284541194547382194">"தொடர்வதன் மூலம், நிர்வாகி (உங்கள் தனிப்பட்ட தரவுடன் சேர்த்து, தொடர்புடைய தரவையும் சேமிக்கக்கூடும்) உங்கள் பயனரை நிர்வகிக்கும்.\n\nஉங்கள் நிர்வாகியால் அமைப்புகள், அணுகல், ஆப்ஸ் மற்றும் நெட்வொர்க் செயல்பாடு, உங்கள் சாதனத்தின் இருப்பிடத் தகவல் உட்பட இந்தப் பயனருடன் தொடர்புடைய தரவு ஆகியவற்றை நிர்வகிக்க முடியும்."</string>
<string name="admin_disabled_other_options" msgid="8097063307730025707">"உங்கள் நிர்வாகி பிற விருப்பங்களை முடக்கியுள்ளார்"</string>
<string name="admin_more_details" msgid="1719819589822345585">"மேலும் விவரங்கள்"</string>
<string name="notification_log_title" msgid="4200467765474474753">"அறிவிப்பு பதிவு"</string>
@@ -2598,7 +2598,7 @@
<string name="background_data" msgid="8275750862371471171">"பின்புல டேட்டா உபயோகம்"</string>
<string name="background_data_summary" msgid="799640633948841990">"பயன்பாடுகளால் எந்நேரத்திலும் தரவை ஒத்திசைக்கவும், அனுப்பவும் பெறவும் முடியும்"</string>
<string name="background_data_dialog_title" msgid="8306650658158895976">"பின்புல டேட்டா உபயோகத்தை முடக்கவா?"</string>
- <string name="background_data_dialog_message" msgid="8126774244911656527">"பின்புல டேட்டா உபயோகத்தை முடக்குவது பேட்டரியின் ஆயுளை நீட்டிக்கிறது மற்றும் தரவு பயன்பாட்டைக் குறைக்கிறது. சில பயன்பாடுகள் தொடர்ந்து பின்புல டேட்டா உபயோகம் இணைப்பைப் பயன்படுத்தலாம்."</string>
+ <string name="background_data_dialog_message" msgid="8126774244911656527">"பின்புல டேட்டா உபயோகத்தை முடக்குவது பேட்டரியின் ஆயுளை நீட்டிக்கிறது மற்றும் தரவு பயன்பாட்டைக் குறைக்கிறது. சில ஆப்ஸ் தொடர்ந்து பின்புல டேட்டா உபயோகம் இணைப்பைப் பயன்படுத்தலாம்."</string>
<string name="sync_automatically" msgid="5746117156896468099">"ஆப்ஸின் டேட்டாவைத் தானாக ஒத்திசை"</string>
<string name="sync_enabled" msgid="535172627223336983">"ஒத்திசைவு முடக்கத்தில்"</string>
<string name="sync_disabled" msgid="713721807204805062">"ஒத்திசைவு முடக்கத்தில்"</string>
@@ -2715,8 +2715,8 @@
<string name="data_usage_restrict_background_multiuser" product="default" msgid="6846901756455789858">"பின்னணி மொபைல் டேட்டாவைக் கட்டுப்படுத்தினால், வைஃபையுடன் இணைக்கும் வரை சில பயன்பாடுகளும் சேவைகளும் வேலை செய்யாது.\n\nஇதனால் இந்த மொபைலைப் பயன்படுத்தும் எல்லாப் பயனர்களும் பாதிக்கப்படுவார்கள்."</string>
<string name="data_usage_sweep_warning" msgid="4646401408698778092"><font size="18">"<xliff:g id="NUMBER">^1</xliff:g>"</font>" "<font size="9">"<xliff:g id="UNIT">^2</xliff:g>"</font>\n<font size="12">"எச்சரிக்கை"</font></string>
<string name="data_usage_sweep_limit" msgid="6101105504557548269"><font size="18">"<xliff:g id="NUMBER">^1</xliff:g>"</font>" "<font size="9">"<xliff:g id="UNIT">^2</xliff:g>"</font>\n<font size="12">"வரம்பு"</font></string>
- <string name="data_usage_uninstalled_apps" msgid="4152786786140875769">"அகற்றப்பட்ட பயன்பாடுகள்"</string>
- <string name="data_usage_uninstalled_apps_users" msgid="61092462416505112">"அகற்றப்பட்ட பயன்பாடுகள் மற்றும் பயனர்கள்"</string>
+ <string name="data_usage_uninstalled_apps" msgid="4152786786140875769">"அகற்றப்பட்ட ஆப்ஸ்"</string>
+ <string name="data_usage_uninstalled_apps_users" msgid="61092462416505112">"அகற்றப்பட்ட ஆப்ஸ் மற்றும் பயனர்கள்"</string>
<string name="data_usage_received_sent" msgid="5532467049487334656">"<xliff:g id="RECEIVED">%1$s</xliff:g> பெறப்பட்டது, <xliff:g id="SENT">%2$s</xliff:g> அனுப்பப்பட்டது"</string>
<string name="data_usage_total_during_range" msgid="7307562900020512747">"<xliff:g id="RANGE">%2$s</xliff:g>: <xliff:g id="TOTAL">%1$s</xliff:g> பயன்படுத்தப்பட்டது."</string>
<string name="data_usage_total_during_range_mobile" product="tablet" msgid="366118962920532455">"<xliff:g id="RANGE">%2$s</xliff:g>: உங்கள் டேப்லெட் அளவீட்டின் படி <xliff:g id="TOTAL">%1$s</xliff:g> பயன்படுத்தப்பட்டுள்ளது. உங்கள் மொபைல் நிறுவனத்தின் டேட்டா உபயோகத்தின் கணக்கு மாறுபடலாம்."</string>
@@ -2828,14 +2828,14 @@
<item quantity="one">சான்றிதழை நம்பு அல்லது அகற்று</item>
</plurals>
<plurals name="ssl_ca_cert_info_message_device_owner" formatted="false" msgid="9046046586061880100">
- <item quantity="other"><xliff:g id="MANAGING_DOMAIN_1">%s</xliff:g> உங்கள் சாதனத்தில் சான்றிதழ் அங்கீகாரங்களை நிறுவியுள்ளது. இது மின்னஞ்சல்கள், பயன்பாடுகள், பாதுகாப்பான இணையதளங்கள் உட்பட சாதன நெட்வொர்க் செயல்பாட்டைக் கண்காணிப்பதற்கு அவற்றை அனுமதிக்கக்கூடும்.\n\nசான்றிதழ்களைப் பற்றிய கூடுதல் தகவலுக்கு, உங்கள் நிர்வாகியைத் தொடர்புகொள்ளவும்.</item>
- <item quantity="one"><xliff:g id="MANAGING_DOMAIN_0">%s</xliff:g> உங்கள் சாதனத்தில் சான்றிதழ் அங்கீகாரத்தை நிறுவியுள்ளது. இது மின்னஞ்சல்கள், பயன்பாடுகள், பாதுகாப்பான இணையதளங்கள் உட்பட சாதன நெட்வொர்க் செயல்பாட்டைக் கண்காணிப்பதற்கு அதை அனுமதிக்கக்கூடும்.\n\nசான்றிதழைப் பற்றிய கூடுதல் தகவலுக்கு, உங்கள் நிர்வாகியைத் தொடர்புகொள்ளவும்.</item>
+ <item quantity="other"><xliff:g id="MANAGING_DOMAIN_1">%s</xliff:g> உங்கள் சாதனத்தில் சான்றிதழ் அங்கீகாரங்களை நிறுவியுள்ளது. இது மின்னஞ்சல்கள், ஆப்ஸ், பாதுகாப்பான இணையதளங்கள் உட்பட சாதன நெட்வொர்க் செயல்பாட்டைக் கண்காணிப்பதற்கு அவற்றை அனுமதிக்கக்கூடும்.\n\nசான்றிதழ்களைப் பற்றிய கூடுதல் தகவலுக்கு, உங்கள் நிர்வாகியைத் தொடர்புகொள்ளவும்.</item>
+ <item quantity="one"><xliff:g id="MANAGING_DOMAIN_0">%s</xliff:g> உங்கள் சாதனத்தில் சான்றிதழ் அங்கீகாரத்தை நிறுவியுள்ளது. இது மின்னஞ்சல்கள், ஆப்ஸ், பாதுகாப்பான இணையதளங்கள் உட்பட சாதன நெட்வொர்க் செயல்பாட்டைக் கண்காணிப்பதற்கு அதை அனுமதிக்கக்கூடும்.\n\nசான்றிதழைப் பற்றிய கூடுதல் தகவலுக்கு, உங்கள் நிர்வாகியைத் தொடர்புகொள்ளவும்.</item>
</plurals>
<plurals name="ssl_ca_cert_info_message" formatted="false" msgid="8271858091418779584">
- <item quantity="other"><xliff:g id="MANAGING_DOMAIN_1">%s</xliff:g> உங்கள் பணிச் சுயவிவரத்திற்குச் சான்றிதழ் அங்கீகாரங்களை நிறுவியுள்ளது. இது மின்னஞ்சல்கள், பயன்பாடுகள், பாதுகாப்பான இணையதளங்கள் உட்பட பணி நெட்வொர்க் செயல்பாட்டைக் கண்காணிப்பதற்கு அவற்றை அனுமதிக்கக்கூடும்.\n\nசான்றிதழ்களைப் பற்றிய கூடுதல் தகவலுக்கு, உங்கள் நிர்வாகியைத் தொடர்புகொள்ளவும்.</item>
- <item quantity="one"><xliff:g id="MANAGING_DOMAIN_0">%s</xliff:g> உங்கள் பணிச் சுயவிவரத்திற்குச் சான்றிதழ் அங்கீகாரத்தை நிறுவியுள்ளது. இது மின்னஞ்சல்கள், பயன்பாடுகள், பாதுகாப்பான இணையதளங்கள் உட்பட பணி நெட்வொர்க் செயல்பாட்டைக் கண்காணிப்பதற்கு அதை அனுமதிக்கக்கூடும்.\n\nசான்றிதழைப் பற்றிய கூடுதல் தகவலுக்கு, உங்கள் நிர்வாகியைத் தொடர்புகொள்ளவும்.</item>
+ <item quantity="other"><xliff:g id="MANAGING_DOMAIN_1">%s</xliff:g> உங்கள் பணிச் சுயவிவரத்திற்குச் சான்றிதழ் அங்கீகாரங்களை நிறுவியுள்ளது. இது மின்னஞ்சல்கள், ஆப்ஸ், பாதுகாப்பான இணையதளங்கள் உட்பட பணி நெட்வொர்க் செயல்பாட்டைக் கண்காணிப்பதற்கு அவற்றை அனுமதிக்கக்கூடும்.\n\nசான்றிதழ்களைப் பற்றிய கூடுதல் தகவலுக்கு, உங்கள் நிர்வாகியைத் தொடர்புகொள்ளவும்.</item>
+ <item quantity="one"><xliff:g id="MANAGING_DOMAIN_0">%s</xliff:g> உங்கள் பணிச் சுயவிவரத்திற்குச் சான்றிதழ் அங்கீகாரத்தை நிறுவியுள்ளது. இது மின்னஞ்சல்கள், ஆப்ஸ், பாதுகாப்பான இணையதளங்கள் உட்பட பணி நெட்வொர்க் செயல்பாட்டைக் கண்காணிப்பதற்கு அதை அனுமதிக்கக்கூடும்.\n\nசான்றிதழைப் பற்றிய கூடுதல் தகவலுக்கு, உங்கள் நிர்வாகியைத் தொடர்புகொள்ளவும்.</item>
</plurals>
- <string name="ssl_ca_cert_warning_message" msgid="8692156828262606685">"மின்னஞ்சல்கள், பயன்பாடுகள் மற்றும் பாதுகாப்பான இணையதளங்கள் உள்ளிட்ட உங்களின் நெட்வொர்க் செயல்பாட்டை மூன்றாம் தரப்பினர் கண்காணிக்க முடியும்.\n\nஉங்கள் சாதனத்தில் நிறுவப்பட்ட நம்பிக்கையான சான்று இதைச் சாத்தியமாக்கும்."</string>
+ <string name="ssl_ca_cert_warning_message" msgid="8692156828262606685">"மின்னஞ்சல்கள், ஆப்ஸ் மற்றும் பாதுகாப்பான இணையதளங்கள் உள்ளிட்ட உங்களின் நெட்வொர்க் செயல்பாட்டை மூன்றாம் தரப்பினர் கண்காணிக்க முடியும்.\n\nஉங்கள் சாதனத்தில் நிறுவப்பட்ட நம்பிக்கையான சான்று இதைச் சாத்தியமாக்கும்."</string>
<plurals name="ssl_ca_cert_settings_button" formatted="false" msgid="3227175122066058245">
<item quantity="other">சான்றிதழ்களைச் சரிபார்</item>
<item quantity="one">சான்றிதழைச் சரிபார்</item>
@@ -2848,7 +2848,7 @@
<string name="user_add_user_or_profile_menu" msgid="4220679989900149336">"பயனர் அல்லது சுயவிவரத்தைச் சேர்"</string>
<string name="user_add_user_menu" msgid="9006572936456324794">"பயனரைச் சேர்"</string>
<string name="user_summary_restricted_profile" msgid="5214838615043574917">"கட்டுப்படுத்தப்பட்ட சுயவிவரம்"</string>
- <string name="user_need_lock_message" msgid="3421243467724322311">"நீங்கள் வரையறுக்கப்பட்டச் சுயவிவரத்தை உருவாக்குவதற்கு முன்பு, உங்கள் பயன்பாடுகள் மற்றும் தனிப்பட்ட தரவைப் பாதுகாக்கும் வகையில் நீங்கள் திரைப் பூட்டை அமைக்க வேண்டும்."</string>
+ <string name="user_need_lock_message" msgid="3421243467724322311">"நீங்கள் வரையறுக்கப்பட்டச் சுயவிவரத்தை உருவாக்குவதற்கு முன்பு, உங்கள் ஆப்ஸ் மற்றும் தனிப்பட்ட தரவைப் பாதுகாக்கும் வகையில் நீங்கள் திரைப் பூட்டை அமைக்க வேண்டும்."</string>
<string name="user_set_lock_button" msgid="4660971133148866612">"பூட்டை அமை"</string>
<string name="user_summary_not_set_up" msgid="6436691939044332679">"அமைக்கவில்லை"</string>
<string name="user_summary_restricted_not_set_up" msgid="896552290436689508">"அமைக்கவில்லை - கட்டுப்படுத்தப்பட்ட சுயவிவரம்"</string>
@@ -2858,8 +2858,8 @@
<string name="user_nickname" msgid="1088216221559125529">"செல்லப்பெயர்"</string>
<string name="user_add_user_type_title" msgid="8672326434351387845">"சேர்"</string>
<string name="user_add_max_count" msgid="4524573950126500416">"<xliff:g id="USER_COUNT">%1$d</xliff:g> பயனர்கள் வரை சேர்க்கலாம்"</string>
- <string name="user_add_user_item_summary" msgid="6114355152711455716">"பயனர்கள் தங்களுக்குச் சொந்தமான பயன்பாடுகள் மற்றும் உள்ளடக்கத்தை வைத்திருக்க வேண்டும்"</string>
- <string name="user_add_profile_item_summary" msgid="6386283837789573755">"உங்கள் கணக்கிலிருந்து பயன்பாடுகள் மற்றும் உள்ளடக்கத்திற்கான அணுகலை நீங்கள் வரையறுக்கலாம்"</string>
+ <string name="user_add_user_item_summary" msgid="6114355152711455716">"பயனர்கள் தங்களுக்குச் சொந்தமான ஆப்ஸ் மற்றும் உள்ளடக்கத்தை வைத்திருக்க வேண்டும்"</string>
+ <string name="user_add_profile_item_summary" msgid="6386283837789573755">"உங்கள் கணக்கிலிருந்து ஆப்ஸ் மற்றும் உள்ளடக்கத்திற்கான அணுகலை நீங்கள் வரையறுக்கலாம்"</string>
<string name="user_add_user_item_title" msgid="6835385073795492410">"பயனர்"</string>
<string name="user_add_profile_item_title" msgid="4932743891449790664">"கட்டுப்படுத்தப்பட்ட சுயவிவரம்"</string>
<string name="user_add_user_title" msgid="2320897397066676472">"புதியவரைச் சேர்க்கவா?"</string>
@@ -2905,7 +2905,7 @@
<string name="emergency_info_title" msgid="1522609271881425375">"அவசரத் தகவல்"</string>
<string name="emergency_info_summary" msgid="7280464759837387342">"<xliff:g id="USER_NAME">%1$s</xliff:g> தகவலும் தொடர்புகளும்"</string>
<string name="application_restrictions" msgid="6871981013736536763">"பயன்பாடுகளையும் உள்ளடக்கத்தையும் அனுமதி"</string>
- <string name="apps_with_restrictions_header" msgid="8656739605673756176">"வரையறைகளுடனான பயன்பாடுகள்"</string>
+ <string name="apps_with_restrictions_header" msgid="8656739605673756176">"வரையறைகளுடனான ஆப்ஸ்"</string>
<string name="apps_with_restrictions_settings_button" msgid="5065896213467171744">"பயன்பாட்டிற்கான அமைப்புகளை விரிவுபடுத்து"</string>
<string name="nfc_payment_settings_title" msgid="5070077706735415291">"தட்டி, கட்டணம் செலுத்துதல்"</string>
<string name="nfc_payment_how_it_works" msgid="7607901964687787177">"இது எவ்வாறு இயங்குகிறது"</string>
@@ -2960,7 +2960,7 @@
<string name="app_restrictions_custom_label" msgid="8791627858467265176">"ஆப்ஸின் வரையறைகளை அமை"</string>
<string name="user_restrictions_controlled_by" msgid="3442508299902131033">"<xliff:g id="APP">%1$s</xliff:g> ஆல் கட்டுப்படுத்தப்படுகிறது"</string>
<string name="app_sees_restricted_accounts" msgid="2210750497683265281">"இந்த ஆப்ஸ் உங்கள் கணக்குகளை அணுகலாம்"</string>
- <string name="app_sees_restricted_accounts_and_controlled_by" msgid="5028333644657350816">"இந்தப் பயன்பாட்டால் உங்கள் கணக்குகளை அணுக முடியும். கட்டுப்படுத்தும் ஆப்ஸ்: <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="app_sees_restricted_accounts_and_controlled_by" msgid="5028333644657350816">"இந்த ஆப்ஸால் உங்கள் கணக்குகளை அணுக முடியும். கட்டுப்படுத்தும் ஆப்ஸ்: <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="restriction_wifi_config_title" msgid="1689176998451296068">"வைஃபை மற்றும் மொபைல்"</string>
<string name="restriction_wifi_config_summary" msgid="2450206736438594690">"வைஃபை மற்றும் மொபைல் அமைப்புகளின் மாற்றத்தை அனுமதிக்கவும்"</string>
<string name="restriction_bluetooth_config_title" msgid="34551118506640221">"புளூடூத்"</string>
@@ -2970,7 +2970,7 @@
<string name="restriction_nfc_enable_summary" product="tablet" msgid="3292205836938064931">"டேப்லெட்டானது வேறொரு சாதனத்தைத் தொடும்போது தரவுப் பரிமாற்றத்தை அனுமதி"</string>
<string name="restriction_nfc_enable_summary" product="default" msgid="226439584043333608">"வேறொரு சாதனத்தைத் தொடும்போது டேட்டா பரிமாற்றத்தை அனுமதி"</string>
<string name="restriction_location_enable_title" msgid="358506740636434856">"இருப்பிடம்"</string>
- <string name="restriction_location_enable_summary" msgid="4159500201124004463">"பயன்பாடுகள் உங்கள் இருப்பிடத் தகவலைப் பயன்படுத்தலாம்"</string>
+ <string name="restriction_location_enable_summary" msgid="4159500201124004463">"ஆப்ஸ் உங்கள் இருப்பிடத் தகவலைப் பயன்படுத்தலாம்"</string>
<string name="wizard_back" msgid="223654213898117594">"பின் செல்"</string>
<string name="wizard_next" msgid="5239664512608113542">"அடுத்து"</string>
<string name="wizard_finish" msgid="3742102879981212094">"முடி"</string>
@@ -3078,9 +3078,9 @@
<string name="keywords_users" msgid="5880705776023155640">"வரம்பிடல், வரம்பு, வரம்பிட்டது"</string>
<string name="keywords_keyboard_and_ime" msgid="3327265741354129990">"உரை திருத்தம், சரிசெய், ஒலி, அதிர்வு, தானியங்கு, மொழி, சைகை, பரிந்துரை, பரிந்துரைப்பு, தீம், வன்மொழி, சொல், வகை, ஈமோஜி, சர்வதேசம்"</string>
<string name="keywords_reset_apps" msgid="2645701455052020435">"மீட்டமை, விருப்பத்தேர்வுகள், இயல்பு"</string>
- <string name="keywords_all_apps" msgid="846444448435698930">"ஆப்ஸ், பதிவிறக்கு, பயன்பாடுகள், முறைமை"</string>
- <string name="keywords_app_permissions" msgid="8539841019997048500">"பயன்பாடுகள், அனுமதிகள், பாதுகாப்பு"</string>
- <string name="keywords_default_apps" msgid="7435952699323965532">"பயன்பாடுகள், இயல்பு"</string>
+ <string name="keywords_all_apps" msgid="846444448435698930">"ஆப்ஸ், பதிவிறக்கு, ஆப்ஸ், முறைமை"</string>
+ <string name="keywords_app_permissions" msgid="8539841019997048500">"ஆப்ஸ், அனுமதிகள், பாதுகாப்பு"</string>
+ <string name="keywords_default_apps" msgid="7435952699323965532">"ஆப்ஸ், இயல்பு"</string>
<string name="keywords_ignore_optimizations" msgid="9127632532176249438">"மேம்படுத்தல்களைத் தவிர்த்தல், பேட்டரியைக் குறைவாகப் பயன்படுத்துதல், ஆப்ஸ் காத்திருப்பு நிலை"</string>
<string name="keywords_color_mode" msgid="8893345199519181751">"அதிர்வு, RGB, sRGB, வண்ணம், இயற்கை, நிலையானது"</string>
<string name="keywords_color_temperature" msgid="2255253972992035046">"வண்ணம், வண்ண வெப்பநிலை, D65, D73, வெள்ளை, மஞ்சள், நீலம், அடர், வெளிர்"</string>
@@ -3362,7 +3362,7 @@
</plurals>
<string name="notification_assistant_title" msgid="8216604031352764011">"அறிவிப்பு அஸிஸ்டண்ட்"</string>
<string name="no_notification_assistant" msgid="9140123568386413264">"அறிவிப்பு அசிஸ்டண்ட் இல்லை"</string>
- <string name="no_notification_listeners" msgid="1366386609506834717">"அறிவிப்பு அணுகலைக் கோரும் பயன்பாடுகள் எதுவும் நிறுவப்படவில்லை."</string>
+ <string name="no_notification_listeners" msgid="1366386609506834717">"அறிவிப்பு அணுகலைக் கோரும் ஆப்ஸ் எதுவும் நிறுவப்படவில்லை."</string>
<string name="notification_assistant_security_warning_title" msgid="4190584438086738496">"<xliff:g id="SERVICE">%1$s</xliff:g> சேவைக்கான அறிவிப்பு அணுகலை அனுமதிக்கவா?"</string>
<string name="notification_assistant_security_warning_summary" msgid="6924513399671031930">"தொடர்புகளின் பெயர்கள் மற்றும் உங்களுக்கான மெசேஜ்களில் இருக்கும் உரைகள் போன்ற தனிப்பட்ட தகவல்கள் அடங்கிய அனைத்து அறிவிப்புகளையும் <xliff:g id="NOTIFICATION_ASSISTANT_NAME">%1$s</xliff:g> சேவையால் படிக்க இயலும். அறிவிப்புகளை மாற்றியமைக்கவோ நிராகரிக்கவோ அவற்றிலுள்ள செயல் பட்டன்களைத் தூண்டவோ இதனால் இயலும். \n\n’தொந்தரவு செய்ய வேண்டாம்’ அம்சத்தை ஆன் அல்லது ஆஃப் செய்வதற்கு ஆப்ஸை அனுமதிப்பதோடு அது தொடர்பான அமைப்புகளை மாற்றவும் இதனால் இயலும்."</string>
<string name="notification_listener_security_warning_title" msgid="4902253246428777797">"<xliff:g id="SERVICE">%1$s</xliff:g>க்கான அறிவிப்பு அணுகலை அனுமதிக்கவா?"</string>
@@ -3371,7 +3371,7 @@
<string name="notification_listener_disable_warning_confirm" msgid="7863495391671154188">"முடக்கு"</string>
<string name="notification_listener_disable_warning_cancel" msgid="6264631825225298458">"ரத்துசெய்"</string>
<string name="vr_listeners_title" msgid="511483902408792832">"VR உதவிச் சேவைகள்"</string>
- <string name="no_vr_listeners" msgid="7675484190394450979">"VR உதவிச் சேவைகளாக இயங்குவதற்காகக் கோரிய பயன்பாடுகள் எதுவும் நிறுவப்படவில்லை."</string>
+ <string name="no_vr_listeners" msgid="7675484190394450979">"VR உதவிச் சேவைகளாக இயங்குவதற்காகக் கோரிய ஆப்ஸ் எதுவும் நிறுவப்படவில்லை."</string>
<string name="vr_listener_security_warning_title" msgid="7019322246707645361">"<xliff:g id="SERVICE">%1$s</xliff:g>ஐ அணுக VR சேவையை அனுமதிக்கவா?"</string>
<string name="vr_listener_security_warning_summary" msgid="5093225583584522067">"விர்ச்சுவல் ரியாலிட்டி பயன்முறையில் பயன்பாடுகளைப் பயன்படுத்தும்போது, <xliff:g id="VR_LISTENER_NAME">%1$s</xliff:g> இயங்க முடியும்."</string>
<string name="display_vr_pref_title" msgid="1088464812293416981">"VRரில் இருக்கும் போது"</string>
@@ -3385,7 +3385,7 @@
<string name="picture_in_picture_app_detail_summary" msgid="918632751775525347">"ஆப்ஸ் திறந்திருக்கும் போது அல்லது அதிலிருந்து நீங்கள் வெளியேறும் போது (எடுத்துக்காட்டாக, வீடியோவைத் தொடர்ந்து பார்க்க), பிக்ச்சர்-இன்-பிக்ச்சர் சாளரத்தை உருவாக்க, இந்த ஆப்ஸை அனுமதிக்கும். இந்தச் சாளரம் நீங்கள் பயன்படுத்தும் பிற ஆப்ஸ்களின் மேல் காட்டப்படும்."</string>
<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="zen_access_empty_text" msgid="7667538993781607731">"\'தொந்தரவு செய்யாதே\' அணுகலை நிறுவப்பட்ட ஆப்ஸ் எதுவும் கோரவில்லை"</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>
@@ -3629,7 +3629,7 @@
<string name="filter_instant_apps" msgid="8087483282854072366">"இன்ஸ்டண்ட் ஆப்ஸ்"</string>
<string name="filter_personal_apps" msgid="3473268022652904457">"தனிப்பட்டவை"</string>
<string name="filter_work_apps" msgid="4202483998339465542">"பணியிடம்"</string>
- <string name="filter_notif_all_apps" msgid="1862666327228804896">"பயன்பாடுகள்: எல்லாம்"</string>
+ <string name="filter_notif_all_apps" msgid="1862666327228804896">"ஆப்ஸ்: எல்லாம்"</string>
<string name="filter_notif_blocked_apps" msgid="5694956954776028202">"ஆஃப் செய்யப்பட்டவை"</string>
<string name="filter_notif_urgent_channels" msgid="5000735867167027148">"வகைகள்: அதிக முக்கியத்துவம்"</string>
<string name="filter_notif_low_channels" msgid="6859599463135775287">"வகைகள்: குறைந்த முக்கியத்துவம்"</string>
@@ -3659,7 +3659,7 @@
<string name="assist_and_voice_input_title" msgid="324148194703846130">"அசிஸ்ட் & குரல் உள்ளீடு"</string>
<string name="default_assist_title" msgid="2060846994203235317">"அசிஸ்ட் ஆப்ஸ்"</string>
<string name="assistant_security_warning_title" msgid="8014460924169723059">"<xliff:g id="ASSISTANT_APP_NAME">%s</xliff:g>ஐ அசிஸ்டண்ட் பயன்பாடாக அமைக்கவா?"</string>
- <string name="assistant_security_warning" msgid="1304057692847069938">"உங்கள் திரையில் தெரியும் தகவல் அல்லது பயன்பாடுகளுக்குள் அணுகத்தக்க தகவல் உள்பட உங்கள் சாதனத்தில் உபயோகத்தில் இருக்கும் பயன்பாடுகள் பற்றிய தகவலை அசிஸ்டண்ட் படிக்க முடியும்."</string>
+ <string name="assistant_security_warning" msgid="1304057692847069938">"உங்கள் திரையில் தெரியும் தகவல் அல்லது பயன்பாடுகளுக்குள் அணுகத்தக்க தகவல் உள்பட உங்கள் சாதனத்தில் உபயோகத்தில் இருக்கும் ஆப்ஸ் பற்றிய தகவலை அசிஸ்டண்ட் படிக்க முடியும்."</string>
<string name="assistant_security_warning_agree" msgid="5105692801460137289">"ஏற்கிறேன்"</string>
<string name="assistant_security_warning_disagree" msgid="4217490999193100459">"ஏற்கவில்லை"</string>
<string name="choose_voice_input_title" msgid="5369311838580756359">"குரல் உள்ளீட்டைத் தேர்வுசெய்க"</string>
@@ -3692,11 +3692,11 @@
<string name="show_all_apps" msgid="5442552004569634846">"முழு பயன்பாட்டைக் காட்டு"</string>
<string name="hide_extra_apps" msgid="6798261081113299441">"ஆப்ஸின் உபயோகத்தை காட்டு"</string>
<plurals name="power_high_usage_summary" formatted="false" msgid="4658343710126205199">
- <item quantity="other">வழக்கத்திற்கு மாறாக <xliff:g id="NUMBER">%2$d</xliff:g> பயன்பாடுகள் செயல்படுகின்றன</item>
+ <item quantity="other">வழக்கத்திற்கு மாறாக <xliff:g id="NUMBER">%2$d</xliff:g> ஆப்ஸ் செயல்படுகின்றன</item>
<item quantity="one">வழக்கத்திற்கு மாறாக <xliff:g id="APP">%1$s</xliff:g> செயல்படுகிறது</item>
</plurals>
<plurals name="power_high_usage_title" formatted="false" msgid="63134064262760835">
- <item quantity="other">பேட்டரியை அதிகமாகப் பயன்படுத்தும் பயன்பாடுகள்</item>
+ <item quantity="other">பேட்டரியை அதிகமாகப் பயன்படுத்தும் ஆப்ஸ்</item>
<item quantity="one">பேட்டரியை அதிகமாகப் பயன்படுத்தும் <xliff:g id="APP">%1$s</xliff:g> பயன்பாடு</item>
</plurals>
<string name="high_power_filter_on" msgid="5294209328473386403">"பவர் சேமிக்காதவை"</string>
@@ -3705,7 +3705,7 @@
<string name="high_power_system" msgid="739584574711292753">"பேட்டரியைச் சேமிக்காது"</string>
<string name="high_power_desc" msgid="333756885680362741">"பேட்டரி மேம்படுத்தலைப் பயன்படுத்தவில்லை எனில், உங்கள் பேட்டரி மிக விரைவில் தீர்ந்துவிடக்கூடும்."</string>
<string name="high_power_prompt_title" msgid="2805745781720454052">"எப்போதும் பின்னணியில் இயங்க, ஆப்ஸை அனுமதிக்கவா?"</string>
- <string name="high_power_prompt_body" msgid="8067395096053552289">"எப்போதும் பின்னணியில் இயங்க <xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸை அனுமதிப்பதால், பேட்டரியின் ஆயுள் குறையக்கூடும். \n\nஇதை அமைப்புகள் > பயன்பாடுகள் & அறிவிப்புகள் என்பதற்குச் சென்று, மாற்றலாம்."</string>
+ <string name="high_power_prompt_body" msgid="8067395096053552289">"எப்போதும் பின்னணியில் இயங்க <xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸை அனுமதிப்பதால், பேட்டரியின் ஆயுள் குறையக்கூடும். \n\nஇதை அமைப்புகள் > ஆப்ஸ் & அறிவிப்புகள் என்பதற்குச் சென்று, மாற்றலாம்."</string>
<string name="battery_summary" msgid="4345690800899981339">"முழு சார்ஜ் ஆனதிலிருந்து <xliff:g id="PERCENTAGE">%1$s</xliff:g> பயன்படுத்தப்பட்டுள்ளது"</string>
<string name="battery_power_management" msgid="2853925857548647969">"பேட்டரி திறன் மேலாண்மை"</string>
<string name="no_battery_summary" msgid="4105932628367471314">"கடைசியாக முழு சார்ஜ் செய்த நேரத்திலிருந்து, பேட்டரி பயன்படுத்தப்படவில்லை"</string>
@@ -3771,7 +3771,7 @@
<string name="total_memory" msgid="7352192982476976453">"மொத்த நினைவகம்"</string>
<string name="average_used" msgid="3022736210190754669">"பயன்படுத்தியது (%)"</string>
<string name="free_memory" msgid="4758919141048544927">"இருப்பது"</string>
- <string name="memory_usage_apps" msgid="2839241373381152354">"பயன்பாடுகள் உபயோகிக்கும் நினைவகம்"</string>
+ <string name="memory_usage_apps" msgid="2839241373381152354">"ஆப்ஸ் உபயோகிக்கும் நினைவகம்"</string>
<plurals name="memory_usage_apps_summary" formatted="false" msgid="1235024908546944704">
<item quantity="other">கடந்த <xliff:g id="DURATION_1">%2$s</xliff:g> இல் <xliff:g id="COUNT">%1$d</xliff:g> ஆப்ஸ்கள் நினைவகத்தைப் பயன்படுத்தியுள்ளன</item>
<item quantity="one">கடந்த <xliff:g id="DURATION_0">%2$s</xliff:g> இல் 1 ஆப்ஸ் நினைவகத்தைப் பயன்படுத்தியுள்ளது</item>
@@ -3801,14 +3801,14 @@
<string name="keywords_vr_listener" msgid="5312633527788917750">"vr விர்ச்சுவல் ரியாலிட்டி லிஷனர் ஸ்டீரியோ உதவிச் சேவை"</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="filter_overlay_apps" msgid="6336897660213304743">"அனுமதி பெற்ற பயன்பாடுகள்"</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>
- <string name="keywords_install_other_apps" msgid="5383559540695847668">"நிறுவு பயன்பாடுகள் அறியப்படாத மூலங்கள்"</string>
+ <string name="keywords_install_other_apps" msgid="5383559540695847668">"நிறுவு ஆப்ஸ் அறியப்படாத மூலங்கள்"</string>
<string name="write_settings" msgid="9009040811145552108">"சாதன அமைப்புகளை மாற்று"</string>
<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="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_write_settings_apps" msgid="6864144615530081121">"முறைமை அமைப்புகளை மாற்றலாம்"</string>
@@ -3849,7 +3849,7 @@
<item quantity="one">1 பயன்பாட்டிற்கு முடக்கப்பட்டுள்ளது</item>
</plurals>
<string name="notification_summary_none" msgid="5003043219430054784">"அனைத்திற்கும் இயக்கப்பட்டுள்ளது"</string>
- <string name="apps_summary" msgid="8355759446490212195">"<xliff:g id="COUNT">%1$d</xliff:g> பயன்பாடுகள் நிறுவப்பட்டுள்ளன"</string>
+ <string name="apps_summary" msgid="8355759446490212195">"<xliff:g id="COUNT">%1$d</xliff:g> ஆப்ஸ் நிறுவப்பட்டுள்ளன"</string>
<string name="apps_summary_example" msgid="3011143598675185269">"24 ஆப்ஸ் நிறுவப்பட்டன"</string>
<string name="storage_summary" msgid="4835916510511133784">"பயன்படுத்தியது: <xliff:g id="PERCENTAGE">%1$s</xliff:g>, காலியிடம்: <xliff:g id="FREE_SPACE">%2$s</xliff:g>"</string>
<string name="storage_summary_with_sdcard" msgid="8742907204848352697">"சாதனச் சேமிப்பகம்: <xliff:g id="PERCENTAGE">%1$s</xliff:g> பயன்படுத்தப்பட்டது - <xliff:g id="FREE_SPACE">%2$s</xliff:g> பயன்படுத்துவதற்கு உள்ளது"</string>
@@ -3955,7 +3955,7 @@
<string name="data_limit" msgid="5793521160051596228">"டேட்டா வரம்பு"</string>
<string name="data_usage_template" msgid="6848274347956096882">"<xliff:g id="ID_1">%1$s</xliff:g> பயன்படுத்தியது: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="configure" msgid="8232696842838580549">"உள்ளமை"</string>
- <string name="data_usage_other_apps" msgid="7002491980141402084">"தரவு உபயோகத்தில் உள்ளடங்கும் பிற பயன்பாடுகள்"</string>
+ <string name="data_usage_other_apps" msgid="7002491980141402084">"தரவு உபயோகத்தில் உள்ளடங்கும் பிற ஆப்ஸ்"</string>
<plurals name="data_saver_unrestricted_summary" formatted="false" msgid="6046013861315713697">
<item quantity="other">டேட்டா சேமிப்பான் இயக்கப்பட்டிருக்கும் போது, பயனரின் எல்லா தகவலையும் பயன்படுத்த <xliff:g id="COUNT">%1$d</xliff:g> ஆப்ஸ் அனுமதிக்கப்பட்டுள்ளன</item>
<item quantity="one">டேட்டா சேமிப்பான் இயக்கப்பட்டிருக்கும் போது, பயனரின் எல்லா தகவலையும் பயன்படுத்த 1 ஆப்ஸ் அனுமதிக்கப்பட்டுள்ளது</item>
@@ -4060,7 +4060,7 @@
<string name="page_tab_title_summary" msgid="4824744863994538006">"எல்லாம்"</string>
<string name="page_tab_title_support" msgid="5569262185010367870">"உதவிக்குறிப்பு & உதவி"</string>
<string name="developer_smallest_width" msgid="2603134476228805075">"மிகக் குறைந்த அகலம்"</string>
- <string name="premium_sms_none" msgid="940723020871007898">"பிரீமிய SMS அணுகலைக் கோரும் பயன்பாடுகள் எதுவும் நிறுவப்படவில்லை"</string>
+ <string name="premium_sms_none" msgid="940723020871007898">"பிரீமிய SMS அணுகலைக் கோரும் ஆப்ஸ் எதுவும் நிறுவப்படவில்லை"</string>
<string name="premium_sms_warning" msgid="7604011651486294515">"பிரீமிய SMSக்குக் கட்டணம் விதிக்கப்படலாம், அது மொபைல் நிறுவன பில்களில் சேர்க்கப்படும். பயன்பாட்டிற்கான அனுமதியை இயக்கினால், அந்தப் பயன்பாட்டைப் பயன்படுத்தி பிரீமிய SMSஐ அனுப்ப முடியும்."</string>
<string name="premium_sms_access" msgid="4550027460595822851">"பிரீமிய SMS அணுகல்"</string>
<string name="bluetooth_disabled" msgid="6588102116819268238">"ஆஃப்"</string>
@@ -4173,9 +4173,9 @@
<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="other">குறைந்தபட்சம் <xliff:g id="COUNT_1">%d</xliff:g> ஆப்ஸ்</item>
<item quantity="one">குறைந்தபட்சம் <xliff:g id="COUNT_0">%d</xliff:g> பயன்பாடு</item>
</plurals>
<string name="enterprise_privacy_location_access" msgid="110406267468274216">"இருப்பிடத்திற்கான அனுமதிகள்"</string>
@@ -4212,7 +4212,7 @@
<string name="do_disclosure_learn_more_separator" msgid="702345537118848010">" "</string>
<string name="learn_more" msgid="6844160787130206258">"மேலும் அறிக"</string>
<plurals name="default_camera_app_title" formatted="false" msgid="8762954032197483848">
- <item quantity="other">கேமரா பயன்பாடுகள்</item>
+ <item quantity="other">கேமரா ஆப்ஸ்</item>
<item quantity="one">கேமரா பயன்பாடு</item>
</plurals>
<string name="default_calendar_app_title" msgid="6484001237347739255">"கேலெண்டர் ஆப்ஸ்"</string>
@@ -4223,7 +4223,7 @@
</plurals>
<string name="default_map_app_title" msgid="6919751358166607185">"வரைபட ஆப்ஸ்"</string>
<plurals name="default_phone_app_title" formatted="false" msgid="7593838689002912108">
- <item quantity="other">ஃபோன் பயன்பாடுகள்</item>
+ <item quantity="other">ஃபோன் ஆப்ஸ்</item>
<item quantity="one">ஃபோன் பயன்பாடு</item>
</plurals>
<string name="app_names_concatenation_template_2" msgid="8267577900046506189">"<xliff:g id="FIRST_APP_NAME">%1$s</xliff:g>, <xliff:g id="SECOND_APP_NAME">%2$s</xliff:g>"</string>
diff --git a/tests/CarDeveloperOptions/res/values-uz/strings.xml b/tests/CarDeveloperOptions/res/values-uz/strings.xml
index 38235b9..eea063b 100644
--- a/tests/CarDeveloperOptions/res/values-uz/strings.xml
+++ b/tests/CarDeveloperOptions/res/values-uz/strings.xml
@@ -62,7 +62,7 @@
<string name="radioInfo_roaming_not" msgid="7733269160603599835">"Rouming o‘chirilgan"</string>
<string name="radioInfo_phone_idle" msgid="1893851191227617344">"Faol emas"</string>
<string name="radioInfo_phone_ringing" msgid="5587975376222853265">"Jiringlamoqda"</string>
- <string name="radioInfo_phone_offhook" msgid="3186071430568806208">"Qo‘ng‘iroq"</string>
+ <string name="radioInfo_phone_offhook" msgid="3186071430568806208">"Chaqiruv"</string>
<string name="radioInfo_data_disconnected" msgid="5311119240521915279">"Uzildi"</string>
<string name="radioInfo_data_connecting" msgid="47095003276717745">"Ulanilmoqda"</string>
<string name="radioInfo_data_connected" msgid="3755289851642913750">"Ulandi"</string>
diff --git a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/dashboard/DashboardFeatureProviderImpl.java b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/dashboard/DashboardFeatureProviderImpl.java
index db6efbf..4f05073 100644
--- a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/dashboard/DashboardFeatureProviderImpl.java
+++ b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/dashboard/DashboardFeatureProviderImpl.java
@@ -30,6 +30,7 @@
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
+import android.net.Uri;
import android.os.Bundle;
import android.os.UserHandle;
import android.provider.Settings;
@@ -180,7 +181,7 @@
ThreadUtils.postOnBackgroundThread(() -> {
final Map<String, IContentProvider> providerMap = new ArrayMap<>();
- final String uri = tile.getMetaData().getString(META_DATA_PREFERENCE_SUMMARY_URI);
+ final Uri uri = TileUtils.getCompleteUri(tile, META_DATA_PREFERENCE_SUMMARY_URI);
final String summaryFromUri = TileUtils.getTextFromUri(
mContext, uri, providerMap, META_DATA_PREFERENCE_SUMMARY);
ThreadUtils.postOnMainThread(() -> preference.setSummary(summaryFromUri));
@@ -214,7 +215,7 @@
packageName = intent.getComponent().getPackageName();
}
final Map<String, IContentProvider> providerMap = new ArrayMap<>();
- final String uri = tile.getMetaData().getString(META_DATA_PREFERENCE_ICON_URI);
+ final Uri uri = TileUtils.getCompleteUri(tile, META_DATA_PREFERENCE_ICON_URI);
final Pair<String, Integer> iconInfo = TileUtils.getIconFromUri(
mContext, packageName, uri, providerMap);
if (iconInfo == null) {
diff --git a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/deviceinfo/simstatus/SimStatusDialogController.java b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/deviceinfo/simstatus/SimStatusDialogController.java
index e0c47b5..946d986 100644
--- a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/deviceinfo/simstatus/SimStatusDialogController.java
+++ b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/deviceinfo/simstatus/SimStatusDialogController.java
@@ -19,6 +19,7 @@
import static android.content.Context.CARRIER_CONFIG_SERVICE;
import static android.content.Context.EUICC_SERVICE;
import static android.content.Context.TELEPHONY_SERVICE;
+import static android.content.Context.TELEPHONY_SUBSCRIPTION_SERVICE;
import android.Manifest;
import android.content.BroadcastReceiver;
@@ -30,10 +31,10 @@
import android.os.PersistableBundle;
import android.os.UserHandle;
import android.telephony.CarrierConfigManager;
-import android.telephony.CellBroadcastMessage;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
+import android.telephony.SmsCbMessage;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
@@ -102,6 +103,7 @@
private final SubscriptionInfo mSubscriptionInfo;
private final TelephonyManager mTelephonyManager;
private final CarrierConfigManager mCarrierConfigManager;
+ private final SubscriptionManager mSubscriptionManager;
private final EuiccManager mEuiccManager;
private final Resources mRes;
private final Context mContext;
@@ -117,11 +119,16 @@
if (extras == null) {
return;
}
- final CellBroadcastMessage cbMessage = (CellBroadcastMessage) extras.get("message");
- if (cbMessage != null
- && mSubscriptionInfo.getSubscriptionId() == cbMessage.getSubId()) {
- final String latestAreaInfo = cbMessage.getMessageBody();
- mDialog.setText(OPERATOR_INFO_VALUE_ID, latestAreaInfo);
+ final SmsCbMessage cbMessage = (SmsCbMessage) extras.get("message");
+ if (cbMessage != null) {
+ int[] subIds = mSubscriptionManager.getSubscriptionIds(
+ cbMessage.getSlotIndex());
+ int subId = (subIds == null || subIds.length == 0)
+ ? SubscriptionManager.INVALID_SUBSCRIPTION_ID : subIds[0];
+ if (mSubscriptionInfo.getSubscriptionId() == subId) {
+ final String latestAreaInfo = cbMessage.getMessageBody();
+ mDialog.setText(OPERATOR_INFO_VALUE_ID, latestAreaInfo);
+ }
}
}
}
@@ -139,6 +146,8 @@
mCarrierConfigManager = (CarrierConfigManager) mContext.getSystemService(
CARRIER_CONFIG_SERVICE);
mEuiccManager = (EuiccManager) mContext.getSystemService(EUICC_SERVICE);
+ mSubscriptionManager = (SubscriptionManager) mContext.getSystemService(
+ TELEPHONY_SUBSCRIPTION_SERVICE);
mRes = mContext.getResources();
diff --git a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/notification/BubbleNotificationPreferenceController.java b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/notification/BubbleNotificationPreferenceController.java
index 5a26bf2..7e797c8 100644
--- a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/notification/BubbleNotificationPreferenceController.java
+++ b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/notification/BubbleNotificationPreferenceController.java
@@ -16,8 +16,7 @@
package com.android.car.developeroptions.notification;
-import static android.provider.Settings.Secure.NOTIFICATION_BUBBLES;
-import static android.provider.Settings.Secure.NOTIFICATION_BUBBLES;
+import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
import android.content.ContentResolver;
import android.content.Context;
@@ -83,20 +82,20 @@
@Override
public boolean isChecked() {
- return Settings.Secure.getInt(mContext.getContentResolver(),
+ return Settings.Global.getInt(mContext.getContentResolver(),
NOTIFICATION_BUBBLES, ON) == ON;
}
@Override
public boolean setChecked(boolean isChecked) {
- return Settings.Secure.putInt(mContext.getContentResolver(),
+ return Settings.Global.putInt(mContext.getContentResolver(),
NOTIFICATION_BUBBLES, isChecked ? ON : OFF);
}
class SettingObserver extends ContentObserver {
private final Uri NOTIFICATION_BUBBLES_URI =
- Settings.Secure.getUriFor(NOTIFICATION_BUBBLES);
+ Settings.Global.getUriFor(NOTIFICATION_BUBBLES);
private final Preference mPreference;
diff --git a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/notification/BubblePreferenceController.java b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/notification/BubblePreferenceController.java
index 82495c0..9a4cd85 100644
--- a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/notification/BubblePreferenceController.java
+++ b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/notification/BubblePreferenceController.java
@@ -16,7 +16,7 @@
package com.android.car.developeroptions.notification;
-import static android.provider.Settings.Secure.NOTIFICATION_BUBBLES;
+import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
import android.content.Context;
import android.provider.Settings;
@@ -62,7 +62,7 @@
return false;
}
if (mChannel != null) {
- if (Settings.Secure.getInt(mContext.getContentResolver(),
+ if (Settings.Global.getInt(mContext.getContentResolver(),
NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON) == SYSTEM_WIDE_OFF) {
return false;
}
@@ -84,7 +84,7 @@
pref.setEnabled(isChannelConfigurable() && !pref.isDisabledByAdmin());
} else {
pref.setChecked(mAppRow.allowBubbles
- && Settings.Secure.getInt(mContext.getContentResolver(),
+ && Settings.Global.getInt(mContext.getContentResolver(),
NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON) == SYSTEM_WIDE_ON);
pref.setSummary(mContext.getString(
R.string.bubbles_app_toggle_summary, mAppRow.label));
@@ -103,7 +103,7 @@
RestrictedSwitchPreference pref = (RestrictedSwitchPreference) preference;
// if the global setting is off, toggling app level permission requires extra
// confirmation
- if (Settings.Secure.getInt(mContext.getContentResolver(),
+ if (Settings.Global.getInt(mContext.getContentResolver(),
NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON) == SYSTEM_WIDE_OFF
&& !pref.isChecked()) {
new BubbleWarningDialogFragment()
@@ -125,7 +125,7 @@
backend.setAllowBubbles(pkg, uid, false);
// changing the global settings will cause the observer on the host page to reload
// correct preference state
- Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Global.putInt(mContext.getContentResolver(),
NOTIFICATION_BUBBLES, SYSTEM_WIDE_OFF);
}
@@ -135,7 +135,7 @@
backend.setAllowBubbles(pkg, uid, true);
// changing the global settings will cause the observer on the host page to reload
// correct preference state
- Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Global.putInt(mContext.getContentResolver(),
NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
}
}
diff --git a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/notification/BubbleSummaryNotificationPreferenceController.java b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/notification/BubbleSummaryNotificationPreferenceController.java
index 78ab78a..1543c14 100644
--- a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/notification/BubbleSummaryNotificationPreferenceController.java
+++ b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/notification/BubbleSummaryNotificationPreferenceController.java
@@ -16,7 +16,7 @@
package com.android.car.developeroptions.notification;
-import static android.provider.Settings.Secure.NOTIFICATION_BUBBLES;
+import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
import android.content.Context;
import android.provider.Settings;
@@ -47,7 +47,7 @@
}
private boolean areBubblesEnabled() {
- return Settings.Secure.getInt(mContext.getContentResolver(),
+ return Settings.Global.getInt(mContext.getContentResolver(),
NOTIFICATION_BUBBLES, ON) == ON;
}
}
diff --git a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/notification/BubbleSummaryPreferenceController.java b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/notification/BubbleSummaryPreferenceController.java
index ac24781..a02b9ab 100644
--- a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/notification/BubbleSummaryPreferenceController.java
+++ b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/notification/BubbleSummaryPreferenceController.java
@@ -16,7 +16,7 @@
package com.android.car.developeroptions.notification;
-import static android.provider.Settings.Secure.NOTIFICATION_BUBBLES;
+import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
import android.app.settings.SettingsEnums;
import android.content.Context;
@@ -53,7 +53,7 @@
return false;
}
if (mChannel != null) {
- if (Settings.Secure.getInt(mContext.getContentResolver(),
+ if (Settings.Global.getInt(mContext.getContentResolver(),
NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON) == SYSTEM_WIDE_OFF) {
return false;
}
@@ -92,7 +92,7 @@
canBubble |= mChannel.canBubble();
} else {
canBubble |= mAppRow.allowBubbles
- && (Settings.Secure.getInt(mContext.getContentResolver(),
+ && (Settings.Global.getInt(mContext.getContentResolver(),
NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON) == SYSTEM_WIDE_ON);
}
}
diff --git a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/notification/GlobalBubblePermissionObserverMixin.java b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/notification/GlobalBubblePermissionObserverMixin.java
index 08d5a51..96ced17 100644
--- a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/notification/GlobalBubblePermissionObserverMixin.java
+++ b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/notification/GlobalBubblePermissionObserverMixin.java
@@ -47,8 +47,8 @@
public void onStart() {
mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(
- Settings.Secure.NOTIFICATION_BUBBLES),
+ Settings.Global.getUriFor(
+ Settings.Global.NOTIFICATION_BUBBLES),
false /* notifyForDescendants */,
this /* observer */);
}
@@ -56,4 +56,4 @@
public void onStop() {
mContext.getContentResolver().unregisterContentObserver(this /* observer */);
}
-}
\ No newline at end of file
+}
diff --git a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/password/BiometricFragment.java b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/password/BiometricFragment.java
index a6c43bf..9720013 100644
--- a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/password/BiometricFragment.java
+++ b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/password/BiometricFragment.java
@@ -16,23 +16,17 @@
package com.android.car.developeroptions.password;
-import android.app.Activity;
import android.app.settings.SettingsEnums;
import android.content.DialogInterface;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.BiometricPrompt.AuthenticationCallback;
import android.hardware.biometrics.BiometricPrompt.AuthenticationResult;
-import android.hardware.biometrics.IBiometricConfirmDeviceCredentialCallback;
import android.os.Bundle;
import android.os.CancellationSignal;
-import android.os.Handler;
-import android.os.Looper;
-import android.util.Log;
import androidx.annotation.NonNull;
-import com.android.car.developeroptions.R;
import com.android.car.developeroptions.core.InstrumentedFragment;
import java.util.concurrent.Executor;
@@ -85,20 +79,6 @@
}
};
- // TODO(b/123378871): Remove when moved.
- private final IBiometricConfirmDeviceCredentialCallback mCancelCallback
- = new IBiometricConfirmDeviceCredentialCallback.Stub() {
- @Override
- public void cancel() {
- final Activity activity = getActivity();
- if (activity != null) {
- activity.finish();
- } else {
- Log.e(TAG, "Activity null!");
- }
- }
- };
-
/**
* @param bundle Bundle passed from {@link BiometricPrompt.Builder#buildIntent()}
* @return
@@ -140,20 +120,17 @@
mBiometricPrompt = new BiometricPrompt.Builder(getContext())
.setTitle(mBundle.getString(BiometricPrompt.KEY_TITLE))
.setUseDefaultTitle() // use default title if title is null/empty
- .setFromConfirmDeviceCredential()
+ .setDeviceCredentialAllowed(true)
.setSubtitle(mBundle.getString(BiometricPrompt.KEY_SUBTITLE))
.setDescription(mBundle.getString(BiometricPrompt.KEY_DESCRIPTION))
.setConfirmationRequired(
mBundle.getBoolean(BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true))
- .setNegativeButton(getResources().getString(
- R.string.confirm_device_credential_use_alternate_method),
- mClientExecutor, mNegativeButtonListener)
.build();
mCancellationSignal = new CancellationSignal();
// TODO: CC doesn't use crypto for now
mBiometricPrompt.authenticateUser(mCancellationSignal, mClientExecutor,
- mAuthenticationCallback, mUserId, mCancelCallback);
+ mAuthenticationCallback, mUserId);
}
@Override
diff --git a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/password/ConfirmDeviceCredentialActivity.java b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/password/ConfirmDeviceCredentialActivity.java
index 12afab1..6eef486 100644
--- a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/password/ConfirmDeviceCredentialActivity.java
+++ b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/password/ConfirmDeviceCredentialActivity.java
@@ -39,9 +39,9 @@
import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentActivity;
-import com.android.internal.widget.LockPatternUtils;
import com.android.car.developeroptions.R;
import com.android.car.developeroptions.Utils;
+import com.android.internal.widget.LockPatternUtils;
import java.util.concurrent.Executor;
@@ -108,10 +108,6 @@
if (!mGoingToBackground) {
if (errorCode == BiometricPrompt.BIOMETRIC_ERROR_USER_CANCELED
|| errorCode == BiometricPrompt.BIOMETRIC_ERROR_CANCELED) {
- if (mIsFallback) {
- mBiometricManager.onConfirmDeviceCredentialError(
- errorCode, getStringForError(errorCode));
- }
finish();
} else {
// All other errors go to some version of CC
@@ -128,10 +124,6 @@
ConfirmDeviceCredentialUtils.checkForPendingIntent(
ConfirmDeviceCredentialActivity.this);
- if (mIsFallback) {
- mBiometricManager.onConfirmDeviceCredentialSuccess();
- }
-
setResult(Activity.RESULT_OK);
finish();
}
@@ -183,17 +175,11 @@
mChooseLockSettingsHelper = new ChooseLockSettingsHelper(this);
final LockPatternUtils lockPatternUtils = new LockPatternUtils(this);
- Bundle bpBundle =
- intent.getBundleExtra(KeyguardManager.EXTRA_BIOMETRIC_PROMPT_BUNDLE);
- if (bpBundle != null) {
- mIsFallback = true;
- mTitle = bpBundle.getString(BiometricPrompt.KEY_TITLE);
- mDetails = bpBundle.getString(BiometricPrompt.KEY_SUBTITLE);
- } else {
- bpBundle = new Bundle();
- bpBundle.putString(BiometricPrompt.KEY_TITLE, mTitle);
- bpBundle.putString(BiometricPrompt.KEY_DESCRIPTION, mDetails);
- }
+ final Bundle bpBundle = new Bundle();
+ mTitle = bpBundle.getString(BiometricPrompt.KEY_TITLE);
+ mDetails = bpBundle.getString(BiometricPrompt.KEY_SUBTITLE);
+ bpBundle.putString(BiometricPrompt.KEY_TITLE, mTitle);
+ bpBundle.putString(BiometricPrompt.KEY_DESCRIPTION, mDetails);
boolean launchedBiometric = false;
boolean launchedCDC = false;
@@ -254,12 +240,6 @@
mBiometricFragment.cancel();
}
- if (mIsFallback && !mCCLaunched) {
- mBiometricManager.onConfirmDeviceCredentialError(
- BiometricConstants.BIOMETRIC_ERROR_CANCELED,
- getString(com.android.internal.R.string.biometric_error_user_canceled));
- }
-
finish();
} else {
mGoingToBackground = false;
diff --git a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/password/ConfirmDeviceCredentialBaseActivity.java b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/password/ConfirmDeviceCredentialBaseActivity.java
index 29fefa8..e5faa5f 100644
--- a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/password/ConfirmDeviceCredentialBaseActivity.java
+++ b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/password/ConfirmDeviceCredentialBaseActivity.java
@@ -17,9 +17,6 @@
package com.android.car.developeroptions.password;
import android.app.KeyguardManager;
-import android.hardware.biometrics.BiometricConstants;
-import android.hardware.biometrics.BiometricManager;
-import android.hardware.biometrics.IBiometricConfirmDeviceCredentialCallback;
import android.os.Bundle;
import android.os.UserManager;
import android.util.Log;
@@ -50,16 +47,6 @@
private boolean mFirstTimeVisible = true;
private boolean mIsKeyguardLocked = false;
private ConfirmCredentialTheme mConfirmCredentialTheme;
- private BiometricManager mBiometricManager;
-
- // TODO(b/123378871): Remove when moved.
- private final IBiometricConfirmDeviceCredentialCallback mCancelCallback
- = new IBiometricConfirmDeviceCredentialCallback.Stub() {
- @Override
- public void cancel() {
- finish();
- }
- };
private boolean isInternalActivity() {
return (this instanceof ConfirmLockPassword.InternalActivity)
@@ -90,9 +77,6 @@
}
super.onCreate(savedState);
- mBiometricManager = getSystemService(BiometricManager.class);
- mBiometricManager.registerCancellationCallback(mCancelCallback);
-
if (mConfirmCredentialTheme == ConfirmCredentialTheme.NORMAL) {
// Prevent the content parent from consuming the window insets because GlifLayout uses
// it to show the status bar background.
@@ -168,12 +152,6 @@
@Override
public void onStop() {
super.onStop();
- // TODO(b/123378871): Remove when moved.
- if (!isChangingConfigurations()) {
- mBiometricManager.onConfirmDeviceCredentialError(
- BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED,
- getString(com.android.internal.R.string.biometric_error_user_canceled));
- }
}
@Override
diff --git a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/password/ConfirmLockPassword.java b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/password/ConfirmLockPassword.java
index f5e3dce..f6792d3 100644
--- a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/password/ConfirmLockPassword.java
+++ b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/password/ConfirmLockPassword.java
@@ -42,11 +42,11 @@
import androidx.fragment.app.Fragment;
+import com.android.car.developeroptions.R;
+import com.android.car.developeroptions.widget.ImeAwareEditText;
import com.android.internal.widget.LockPatternChecker;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.TextViewInputDisabler;
-import com.android.car.developeroptions.R;
-import com.android.car.developeroptions.widget.ImeAwareEditText;
import com.android.settingslib.animation.AppearAnimationUtils;
import com.android.settingslib.animation.DisappearAnimationUtils;
@@ -438,7 +438,6 @@
ConfirmDeviceCredentialUtils.reportSuccessfulAttempt(mLockPatternUtils,
mUserManager, mEffectiveUserId);
}
- mBiometricManager.onConfirmDeviceCredentialSuccess();
startDisappearAnimation(intent);
ConfirmDeviceCredentialUtils.checkForPendingIntent(getActivity());
} else {
diff --git a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/password/ConfirmLockPattern.java b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/password/ConfirmLockPattern.java
index fedd126..ab26f10 100644
--- a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/password/ConfirmLockPattern.java
+++ b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/password/ConfirmLockPattern.java
@@ -32,12 +32,12 @@
import android.view.animation.Interpolator;
import android.widget.TextView;
+import com.android.car.developeroptions.R;
import com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient;
import com.android.internal.widget.LockPatternChecker;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternView;
import com.android.internal.widget.LockPatternView.Cell;
-import com.android.car.developeroptions.R;
import com.android.settingslib.animation.AppearAnimationCreator;
import com.android.settingslib.animation.AppearAnimationUtils;
import com.android.settingslib.animation.DisappearAnimationUtils;
@@ -490,7 +490,6 @@
ConfirmDeviceCredentialUtils.reportSuccessfulAttempt(mLockPatternUtils,
mUserManager, mEffectiveUserId);
}
- mBiometricManager.onConfirmDeviceCredentialSuccess();
startDisappearAnimation(intent);
ConfirmDeviceCredentialUtils.checkForPendingIntent(getActivity());
} else {
diff --git a/tests/SecondaryHomeApp/Android.bp b/tests/SecondaryHomeApp/Android.bp
new file mode 100644
index 0000000..9a2ee73
--- /dev/null
+++ b/tests/SecondaryHomeApp/Android.bp
@@ -0,0 +1,47 @@
+//
+// 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.
+//
+android_app {
+ name: "SecondaryHomeApp",
+
+ srcs: [
+ "src/**/*.java",
+ "src/**/I*.aidl",
+ ],
+
+ static_libs: [
+ "android.car.userlib",
+ "androidx.appcompat_appcompat",
+ "androidx.recyclerview_recyclerview",
+ "androidx.legacy_legacy-support-v4",
+ "androidx.lifecycle_lifecycle-extensions",
+ "com.google.android.material_material",
+ ],
+
+ libs: [
+ "android.car",
+ ],
+
+ manifest: "AndroidManifest.xml",
+
+ platform_apis: true,
+ product_specific: true,
+ certificate: "platform",
+ privileged: true,
+
+ resource_dirs: [
+ "res",
+ ],
+}
diff --git a/tests/SecondaryHomeApp/AndroidManifest.xml b/tests/SecondaryHomeApp/AndroidManifest.xml
new file mode 100644
index 0000000..1928efb
--- /dev/null
+++ b/tests/SecondaryHomeApp/AndroidManifest.xml
@@ -0,0 +1,50 @@
+<?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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="com.android.car.secondaryhome">
+ <!-- System permission to host maps activity -->
+ <uses-permission android:name="android.permission.ACTIVITY_EMBEDDING"/>
+ <!-- System permission to send events to hosted maps activity -->
+ <uses-permission android:name="android.permission.INJECT_EVENTS"/>
+ <!-- System permission to use internal system windows -->
+ <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"/>
+ <!-- System permissions to bring hosted activity to front on current display -->
+ <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS"/>
+ <uses-permission android:name="android.permission.REORDER_TASKS"/>
+
+ <application
+ android:label="@string/app_name"
+ tools:replace="android:label">
+
+ <activity
+ android:name=".launcher.LauncherActivity"
+ android:label="@string/app_launcher"
+ android:theme="@style/LauncherTheme"
+ android:configChanges="orientation|screenSize|smallestScreenSize|
+ screenLayout|colorMode|density"
+ android:documentLaunchMode="always"
+ android:multiprocess="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
+
diff --git a/tests/SecondaryHomeApp/res/drawable/ic_arrow_back.xml b/tests/SecondaryHomeApp/res/drawable/ic_arrow_back.xml
new file mode 100644
index 0000000..658a068
--- /dev/null
+++ b/tests/SecondaryHomeApp/res/drawable/ic_arrow_back.xml
@@ -0,0 +1,26 @@
+<?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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:width="48dp"
+ android:height="48dp">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M15.41 16.09l-4.58-4.59 4.58-4.59L14 5.5l-6 6 6 6z"/>
+</vector>
diff --git a/tests/SecondaryHomeApp/res/drawable/ic_home.xml b/tests/SecondaryHomeApp/res/drawable/ic_home.xml
new file mode 100644
index 0000000..18c26ad
--- /dev/null
+++ b/tests/SecondaryHomeApp/res/drawable/ic_home.xml
@@ -0,0 +1,26 @@
+<?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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:viewportWidth="48"
+ android:viewportHeight="48"
+ android:width="48dp"
+ android:height="48dp">
+ <path
+ android:pathData="M7.33333333 14.6666667L14.6666667 14.6666667L14.6666667 7.33333333L7.33333333 7.33333333L7.33333333 14.6666667ZM18.3333333 36.6666667L25.6666667 36.6666667L25.6666667 29.3333333L18.3333333 29.3333333L18.3333333 36.6666667ZM7.33333333 36.6666667L14.6666667 36.6666667L14.6666667 29.3333333L7.33333333 29.3333333L7.33333333 36.6666667ZM7.33333333 25.6666667L14.6666667 25.6666667L14.6666667 18.3333333L7.33333333 18.3333333L7.33333333 25.6666667ZM18.3333333 25.6666667L25.6666667 25.6666667L25.6666667 18.3333333L18.3333333 18.3333333L18.3333333 25.6666667ZM29.3333333 7.33333333L29.3333333 14.6666667L36.6666667 14.6666667L36.6666667 7.33333333L29.3333333 7.33333333ZM18.3333333 14.6666667L25.6666667 14.6666667L25.6666667 7.33333333L18.3333333 7.33333333L18.3333333 14.6666667ZM29.3333333 25.6666667L36.6666667 25.6666667L36.6666667 18.3333333L29.3333333 18.3333333L29.3333333 25.6666667ZM29.3333333 36.6666667L36.6666667 36.6666667L36.6666667 29.3333333L29.3333333 29.3333333L29.3333333 36.6666667Z"
+ android:fillColor="@color/nav_icon_fill_color" />
+</vector>
diff --git a/tests/SecondaryHomeApp/res/drawable/nav_button_background.xml b/tests/SecondaryHomeApp/res/drawable/nav_button_background.xml
new file mode 100644
index 0000000..0c1bd93
--- /dev/null
+++ b/tests/SecondaryHomeApp/res/drawable/nav_button_background.xml
@@ -0,0 +1,26 @@
+<?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
+ -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/nav_bar_ripple_background_color">
+ <item android:id="@android:id/mask">
+ <shape android:shape="rectangle">
+ <solid android:color="?android:colorAccent"/>
+ <corners android:radius="6dp"/>
+ </shape>
+ </item>
+</ripple>
diff --git a/tests/SecondaryHomeApp/res/layout/activity_main.xml b/tests/SecondaryHomeApp/res/layout/activity_main.xml
new file mode 100644
index 0000000..1156f56
--- /dev/null
+++ b/tests/SecondaryHomeApp/res/layout/activity_main.xml
@@ -0,0 +1,41 @@
+<?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
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/RootView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/background_color"
+ android:fitsSystemWindows="true"
+ android:orientation="vertical" >
+
+ <FrameLayout
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1">
+ </FrameLayout>
+
+ <FrameLayout
+ android:id="@+id/navigation_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom">
+ <include layout="@layout/car_navigation_bar"/>
+ </FrameLayout>
+</LinearLayout>
diff --git a/tests/SecondaryHomeApp/res/layout/app_fragment.xml b/tests/SecondaryHomeApp/res/layout/app_fragment.xml
new file mode 100644
index 0000000..3f778f5
--- /dev/null
+++ b/tests/SecondaryHomeApp/res/layout/app_fragment.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/app_fragment"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ActivityView
+ android:id="@+id/app_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+</FrameLayout>
diff --git a/tests/SecondaryHomeApp/res/layout/app_grid_item.xml b/tests/SecondaryHomeApp/res/layout/app_grid_item.xml
new file mode 100644
index 0000000..df2b723
--- /dev/null
+++ b/tests/SecondaryHomeApp/res/layout/app_grid_item.xml
@@ -0,0 +1,35 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center">
+
+ <ImageView
+ android:id="@+id/app_icon"
+ android:layout_width="@dimen/app_icon_width"
+ android:layout_height="@dimen/app_icon_height" />
+
+ <TextView
+ android:id="@+id/app_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:maxLines="1" />
+</LinearLayout>
diff --git a/tests/SecondaryHomeApp/res/layout/car_navigation_bar.xml b/tests/SecondaryHomeApp/res/layout/car_navigation_bar.xml
new file mode 100644
index 0000000..6141df1
--- /dev/null
+++ b/tests/SecondaryHomeApp/res/layout/car_navigation_bar.xml
@@ -0,0 +1,49 @@
+<?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
+ -->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:id="@+id/nav_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:gravity="center">
+
+ <ImageButton
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:id="@+id/nav_back"
+ android:src="@drawable/ic_arrow_back"
+ android:gravity="center"
+ />
+
+ <ImageButton
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:id="@+id/nav_home"
+ android:src="@drawable/ic_home"
+ android:gravity="center"
+ />
+ </LinearLayout>
+</FrameLayout>
diff --git a/tests/SecondaryHomeApp/res/layout/home_fragment.xml b/tests/SecondaryHomeApp/res/layout/home_fragment.xml
new file mode 100644
index 0000000..6fe0c79
--- /dev/null
+++ b/tests/SecondaryHomeApp/res/layout/home_fragment.xml
@@ -0,0 +1,35 @@
+<?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.
+ -->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/home_fragment"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/background_color"
+ android:layout_margin="@dimen/app_grid_margin_top">
+
+ <GridView
+ android:id="@+id/app_grid"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:columnWidth="@dimen/app_list_col_width"
+ android:verticalSpacing="@dimen/app_list_horizontal_spacing"
+ android:horizontalSpacing="@dimen/app_list_vertical_spacing"
+ android:numColumns="auto_fit"
+ android:name="app_grid" />
+</FrameLayout>
diff --git a/tests/SecondaryHomeApp/res/values/colors.xml b/tests/SecondaryHomeApp/res/values/colors.xml
new file mode 100644
index 0000000..a81e554
--- /dev/null
+++ b/tests/SecondaryHomeApp/res/values/colors.xml
@@ -0,0 +1,22 @@
+<?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.
+ -->
+
+<resources>
+ <color name="background_color">#263238</color>
+ <color name="nav_icon_fill_color">#8Fffffff</color>
+ <color name="nav_bar_ripple_background_color">#40ffffff</color>
+</resources>
diff --git a/tests/SecondaryHomeApp/res/values/dimens.xml b/tests/SecondaryHomeApp/res/values/dimens.xml
new file mode 100644
index 0000000..3d19fc7
--- /dev/null
+++ b/tests/SecondaryHomeApp/res/values/dimens.xml
@@ -0,0 +1,27 @@
+<?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.
+ -->
+
+<resources>
+ <dimen name="app_list_col_width">72dp</dimen>
+ <dimen name="app_list_horizontal_spacing">24dp</dimen>
+ <dimen name="app_list_vertical_spacing">24dp</dimen>
+ <dimen name="app_icon_width">64dp</dimen>
+ <dimen name="app_icon_height">64dp</dimen>
+ <dimen name="app_grid_margin_top">24dp</dimen>
+ <dimen name="app_grid_margin_left">8dp</dimen>
+ <dimen name="app_grid_margin_right">8dp</dimen>
+</resources>
diff --git a/tests/SecondaryHomeApp/res/values/strings.xml b/tests/SecondaryHomeApp/res/values/strings.xml
new file mode 100644
index 0000000..afc1c938
--- /dev/null
+++ b/tests/SecondaryHomeApp/res/values/strings.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<resources>
+ <string name="app_name">SecondaryHomeApp</string>
+ <string name="app_launcher">SecHome Launcher</string>
+ <string name="launch_fail_msg">Couldn\'t launch</string>
+ <string-array name="hidden_apps" translatable="false">
+ <item>com.android.car.secondaryhome</item>
+ </string-array>
+</resources>
diff --git a/tests/SecondaryHomeApp/res/values/styles.xml b/tests/SecondaryHomeApp/res/values/styles.xml
new file mode 100644
index 0000000..f38bfa5
--- /dev/null
+++ b/tests/SecondaryHomeApp/res/values/styles.xml
@@ -0,0 +1,34 @@
+<?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.
+ -->
+
+<resources>
+ <style name="LauncherTheme" parent="Theme.MaterialComponents.NoActionBar" >
+ <item name="android:windowShowWallpaper">true</item>
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowTranslucentStatus">true</item>
+ <item name="android:windowTranslucentNavigation">true</item>
+ <item name="android:colorBackgroundCacheHint">@null</item>
+ <item name="android:windowBackground">@android:color/transparent</item>
+ </style>
+
+ <style name="NavigationBarButton">
+ <item name="android:layout_height">96dp</item>
+ <item name="android:layout_width">96dp</item>
+ <item name="android:background">@drawable/nav_button_background</item>
+ </style>
+</resources>
diff --git a/tests/SecondaryHomeApp/src/com/android/car/secondaryhome/launcher/AppEntry.java b/tests/SecondaryHomeApp/src/com/android/car/secondaryhome/launcher/AppEntry.java
new file mode 100644
index 0000000..f77b6c9
--- /dev/null
+++ b/tests/SecondaryHomeApp/src/com/android/car/secondaryhome/launcher/AppEntry.java
@@ -0,0 +1,68 @@
+/**
+ * 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.android.car.secondaryhome.launcher;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
+
+import java.util.Comparator;
+
+/** An entry that represents a single activity that can be launched. */
+public final class AppEntry {
+ @NonNull
+ private final Intent mLaunchIntent;
+ @NonNull
+ private final String mLabel;
+ @Nullable
+ private final Drawable mIcon;
+
+ AppEntry(@NonNull ResolveInfo info, @NonNull PackageManager packageManager) {
+ mLabel = info.loadLabel(packageManager).toString();
+ mIcon = info.loadIcon(packageManager);
+ mLaunchIntent = new Intent().setComponent(new ComponentName(
+ info.activityInfo.packageName, info.activityInfo.name));
+ }
+
+ @NonNull String getLabel() {
+ return mLabel;
+ }
+
+ @Nullable Drawable getIcon() {
+ return mIcon;
+ }
+
+ @NonNull Intent getLaunchIntent() {
+ return mLaunchIntent;
+ }
+
+ @NonNull ComponentName getComponentName() {
+ return mLaunchIntent.getComponent();
+ }
+
+ @Override
+ public String toString() {
+ return mLabel;
+ }
+
+ public static final Comparator<AppEntry> AppNameComparator =
+ Comparator.comparing(AppEntry::getLabel, String::compareToIgnoreCase);
+}
diff --git a/tests/SecondaryHomeApp/src/com/android/car/secondaryhome/launcher/AppFragment.java b/tests/SecondaryHomeApp/src/com/android/car/secondaryhome/launcher/AppFragment.java
new file mode 100644
index 0000000..a05beee
--- /dev/null
+++ b/tests/SecondaryHomeApp/src/com/android/car/secondaryhome/launcher/AppFragment.java
@@ -0,0 +1,262 @@
+/**
+ * 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.android.car.secondaryhome.launcher;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.ActivityManager.StackInfo;
+import android.app.ActivityView;
+import android.app.IActivityManager;
+import android.app.TaskStackBuilder;
+import android.app.TaskStackListener;
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
+import android.hardware.input.InputManager;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.Display;
+import android.view.InputDevice;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.fragment.app.Fragment;
+
+import com.android.car.secondaryhome.R;
+import com.android.internal.annotations.GuardedBy;
+
+/**
+ * {@link Fragment} that contains an ActivityView to run embedded application
+ */
+public final class AppFragment extends Fragment {
+ public static final int INVALID_TASK_STACK_ID = -1;
+
+ private static final String TAG = "SecHome.AppFragment";
+
+ private final IActivityManager mAm;
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private int mVirtualDisplayId = Display.INVALID_DISPLAY;
+ @GuardedBy("mLock")
+ private int mTaskStackId = INVALID_TASK_STACK_ID;
+ private boolean mActivityViewReady;
+ private Intent mLaunchIntent;
+ private TaskListener mTaskListener;
+ private TaskStackBuilder mTaskStackBuilder;
+
+ private Activity mActivity;
+ private ActivityView mActivityView;
+ private int mHomeDisplayId = Display.INVALID_DISPLAY;
+
+ private final ActivityView.StateCallback mActivityViewCallback =
+ new ActivityView.StateCallback() {
+ @Override
+ public void onActivityViewReady(ActivityView view) {
+ mActivityViewReady = true;
+ view.startActivity(mLaunchIntent);
+ synchronized (mLock) {
+ try {
+ mTaskStackId = mAm.getFocusedStackInfo().stackId;
+ } catch (RemoteException e) {
+ Log.e(TAG, "cannot get new taskstackid in ActivityView.StateCallback");
+ }
+ mVirtualDisplayId = view.getVirtualDisplayId();
+ }
+ }
+
+ @Override
+ public void onActivityViewDestroyed(ActivityView view) {
+ mActivityViewReady = false;
+ }
+ };
+
+ public AppFragment() {
+ mAm = ActivityManager.getService();
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mActivity = getActivity();
+ mHomeDisplayId = mActivity.getWindowManager().getDefaultDisplay().getDisplayId();
+
+ mTaskListener = new TaskListener();
+ mTaskStackBuilder = TaskStackBuilder.create(mActivity);
+
+ try {
+ mAm.registerTaskStackListener(mTaskListener);
+ } catch (RemoteException e) {
+ mTaskListener = null;
+ }
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.app_fragment, container, false);
+ mActivityView = view.findViewById(R.id.app_view);
+ return view;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (mActivityView != null && mActivityViewReady) {
+ try {
+ mActivityView.release();
+ mActivityView = null;
+ mActivityViewReady = false;
+ } catch (Exception e) {
+ Log.e(TAG, "Fail to release ActivityView");
+ }
+ }
+
+ if (mTaskListener != null) {
+ mLaunchIntent = null;
+ mTaskListener = null;
+ synchronized (mLock) {
+ mTaskStackId = INVALID_TASK_STACK_ID;
+ mVirtualDisplayId = Display.INVALID_DISPLAY;
+ }
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ // If task stack is not empty, launch the top intent
+ if (mTaskStackBuilder.getIntentCount() > 0) {
+ launchTopAppInActivityView();
+ }
+ }
+
+ private void launchTopAppInActivityView() {
+ try {
+ if (mTaskStackBuilder.getIntentCount() == 0) {
+ return;
+ }
+ mLaunchIntent = mTaskStackBuilder
+ .editIntentAt(mTaskStackBuilder.getIntentCount() - 1);
+
+ if (mActivityView != null) {
+ // Set callback to launch the app when ActivityView is ready
+ mActivityView.setCallback(mActivityViewCallback);
+ } else if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "ActivityView is null ");
+ }
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "App activity not found ..", e);
+ }
+ }
+
+ public void addLaunchIntentToStack(Intent launchIntent) {
+ mTaskStackBuilder.addNextIntent(launchIntent);
+ launchTopAppInActivityView();
+ }
+
+ public void onBackButtonPressed() {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "onBackButtonPressed..");
+ }
+ if (mActivityView != null) {
+ performBackPress();
+ }
+ }
+
+ public int getTaskStackId() {
+ synchronized (mLock) {
+ return mTaskStackId;
+ }
+ }
+
+ private void performBackPress() {
+ InputManager im = mActivity.getSystemService(InputManager.class);
+ int displayId;
+ synchronized (mLock) {
+ displayId = mVirtualDisplayId;
+ }
+
+ im.injectInputEvent(createKeyEvent(KeyEvent.ACTION_DOWN,
+ KeyEvent.KEYCODE_BACK, displayId),
+ InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
+ im.injectInputEvent(createKeyEvent(KeyEvent.ACTION_UP,
+ KeyEvent.KEYCODE_BACK, displayId),
+ InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
+ }
+
+ private static KeyEvent createKeyEvent(int action, int code, int displayId) {
+ long when = SystemClock.uptimeMillis();
+ KeyEvent ev = new KeyEvent(when, when, action, code, /* repeat= */ 0,
+ /* metaState= */ 0, KeyCharacterMap.VIRTUAL_KEYBOARD, /* scancode= */ 0,
+ KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
+ InputDevice.SOURCE_KEYBOARD);
+ ev.setDisplayId(displayId);
+ return ev;
+ }
+
+ private boolean isTaskStackEmpty() {
+ synchronized (mLock) {
+ try {
+ return mAm.getAllStackInfos().stream().anyMatch(info
+ -> (info.stackId == mTaskStackId && info.topActivity == null));
+ } catch (RemoteException e) {
+ Log.e(TAG, "cannot getFocusedStackInfos", e);
+ return true;
+ }
+ }
+ }
+
+ private final class TaskListener extends TaskStackListener {
+ @Override
+ public void onTaskStackChanged() {
+ StackInfo focusedStackInfo;
+ try {
+ focusedStackInfo = mAm.getFocusedStackInfo();
+ } catch (RemoteException e) {
+ Log.e(TAG, "cannot getFocusedStackInfo", e);
+ return;
+ }
+
+ // App could be exited in two ways, and HomeFragment should be shown
+ if (isTaskStackEmpty()) {
+ ((LauncherActivity) mActivity).navigateHome();
+ synchronized (mLock) {
+ mTaskStackId = INVALID_TASK_STACK_ID;
+ }
+ }
+
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "OnTaskStackChanged: virtual display: "
+ + mVirtualDisplayId + " homeDisplay: " + mHomeDisplayId
+ + "\nFocused stack: " + focusedStackInfo);
+ try {
+ for (StackInfo info: mAm.getAllStackInfos()) {
+ Log.d(TAG, " stackId: " + info.stackId + " displayId: "
+ + info.displayId + " component: " + info.topActivity);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "cannot getFocusedStackInfos", e);
+ }
+ }
+ }
+ }
+}
diff --git a/tests/SecondaryHomeApp/src/com/android/car/secondaryhome/launcher/AppListAdapter.java b/tests/SecondaryHomeApp/src/com/android/car/secondaryhome/launcher/AppListAdapter.java
new file mode 100644
index 0000000..4f02654
--- /dev/null
+++ b/tests/SecondaryHomeApp/src/com/android/car/secondaryhome/launcher/AppListAdapter.java
@@ -0,0 +1,75 @@
+/**
+ * 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.android.car.secondaryhome.launcher;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.car.secondaryhome.R;
+
+import java.util.List;
+import java.util.Set;
+
+/** Adapter for available apps list. */
+public final class AppListAdapter extends ArrayAdapter<AppEntry> {
+ private final LayoutInflater mInflater;
+
+ AppListAdapter(@NonNull Context context) {
+ super(context, android.R.layout.simple_list_item_2);
+ mInflater = LayoutInflater.from(context);
+ }
+
+ /**
+ * Sets data for AppListAdaptor and exclude the app from blackList
+ * @param data List of {@link AppEntry}
+ * @param blackList A (possibly empty but not null) list of apps (package names) to hide
+ */
+ void setData(@NonNull List<AppEntry> data, @NonNull Set<String> blackList) {
+ clear();
+
+ data.stream().filter(app -> !blackList.contains(app.getComponentName()))
+ .forEach(app -> add(app));
+
+ sort(AppEntry.AppNameComparator);
+ }
+
+ @Override
+ public View getView(@NonNull int position,
+ @Nullable View convertView,
+ @NonNull ViewGroup parent) {
+ View view;
+ if (convertView == null) {
+ view = mInflater.inflate(R.layout.app_grid_item, parent, false);
+ } else {
+ view = convertView;
+ }
+
+ AppEntry item = getItem(position);
+ if (item != null) {
+ ((ImageView) view.findViewById(R.id.app_icon)).setImageDrawable(item.getIcon());
+ ((TextView) view.findViewById(R.id.app_name)).setText(item.getLabel());
+ }
+ return view;
+ }
+}
diff --git a/tests/SecondaryHomeApp/src/com/android/car/secondaryhome/launcher/AppListLiveData.java b/tests/SecondaryHomeApp/src/com/android/car/secondaryhome/launcher/AppListLiveData.java
new file mode 100644
index 0000000..e1540ae
--- /dev/null
+++ b/tests/SecondaryHomeApp/src/com/android/car/secondaryhome/launcher/AppListLiveData.java
@@ -0,0 +1,71 @@
+/**
+ * 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.android.car.secondaryhome.launcher;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.AsyncTask;
+
+import androidx.lifecycle.LiveData;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public final class AppListLiveData extends LiveData<List<AppEntry>> {
+
+ private final PackageManager mPackageManager;
+ private int mCurrentDataVersion;
+
+ AppListLiveData(@NonNull Context context) {
+ mPackageManager = context.getPackageManager();
+ loadData();
+ }
+
+ protected void loadData() {
+ int loadDataVersion = ++mCurrentDataVersion;
+
+ new AsyncTask<Void, Void, List<AppEntry>>() {
+ @Override
+ protected List<AppEntry> doInBackground(Void... voids) {
+ Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
+ mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+
+ List<ResolveInfo> apps = mPackageManager.queryIntentActivities(mainIntent,
+ PackageManager.GET_META_DATA);
+
+ if (apps == null) return Collections.emptyList();
+
+ List<AppEntry> entries = new ArrayList(apps.size());
+ apps.stream().forEach(app -> entries.add(new AppEntry(app, mPackageManager)));
+
+ return entries;
+ }
+
+ @Override
+ protected void onPostExecute(List<AppEntry> data) {
+ if (mCurrentDataVersion == loadDataVersion) {
+ setValue(data);
+ }
+ }
+ }.execute();
+ }
+}
+
diff --git a/tests/SecondaryHomeApp/src/com/android/car/secondaryhome/launcher/AppListViewModel.java b/tests/SecondaryHomeApp/src/com/android/car/secondaryhome/launcher/AppListViewModel.java
new file mode 100644
index 0000000..f94b96d
--- /dev/null
+++ b/tests/SecondaryHomeApp/src/com/android/car/secondaryhome/launcher/AppListViewModel.java
@@ -0,0 +1,51 @@
+/**
+ * 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.android.car.secondaryhome.launcher;
+
+import android.annotation.NonNull;
+import android.app.Application;
+
+import androidx.lifecycle.AndroidViewModel;
+import androidx.lifecycle.LiveData;
+
+import java.util.List;
+
+/**
+ * A view model that provides a list of activities that can be launched.
+ */
+public final class AppListViewModel extends AndroidViewModel {
+
+ @NonNull
+ private final AppListLiveData mAppList;
+ @NonNull
+ private final PackageIntentReceiver mPackageIntentReceiver;
+
+ public AppListViewModel(Application application) {
+ super(application);
+ mAppList = new AppListLiveData(application);
+ mPackageIntentReceiver = new PackageIntentReceiver(mAppList, application);
+ }
+
+ public LiveData<List<AppEntry>> getAppList() {
+ return mAppList;
+ }
+
+ @Override
+ protected void onCleared() {
+ getApplication().unregisterReceiver(mPackageIntentReceiver);
+ }
+}
diff --git a/tests/SecondaryHomeApp/src/com/android/car/secondaryhome/launcher/AppPickedCallback.java b/tests/SecondaryHomeApp/src/com/android/car/secondaryhome/launcher/AppPickedCallback.java
new file mode 100644
index 0000000..fb06350
--- /dev/null
+++ b/tests/SecondaryHomeApp/src/com/android/car/secondaryhome/launcher/AppPickedCallback.java
@@ -0,0 +1,27 @@
+/**
+ * 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.android.car.secondaryhome.launcher;
+
+import android.annotation.NonNull;
+
+/**
+ * Callback to be invoked when an app was picked.
+ */
+interface AppPickedCallback {
+ void onAppPicked(@NonNull AppEntry appEntry);
+}
+
diff --git a/tests/SecondaryHomeApp/src/com/android/car/secondaryhome/launcher/HomeFragment.java b/tests/SecondaryHomeApp/src/com/android/car/secondaryhome/launcher/HomeFragment.java
new file mode 100644
index 0000000..1a8ef85
--- /dev/null
+++ b/tests/SecondaryHomeApp/src/com/android/car/secondaryhome/launcher/HomeFragment.java
@@ -0,0 +1,90 @@
+/**
+ * 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.android.car.secondaryhome.launcher;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.GridView;
+
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.lifecycle.ViewModelProviders;
+
+import com.android.car.secondaryhome.R;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * {@link Fragment} that shows a grid of app installed.
+ * It will launch app into AppFragment.
+ * Note: a new task will be launched every time for app to run in multiple display.
+ */
+public final class HomeFragment extends Fragment {
+ private static final String TAG = "SecHome.HomeFragment";
+
+ private final Set<String> mHiddenApps = new HashSet<>();
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+
+ View view = inflater.inflate(R.layout.home_fragment, container, false);
+ GridView gridView = view.findViewById(R.id.app_grid);
+
+ FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
+ AppListAdapter appListAdapter = new AppListAdapter(getActivity());
+
+ gridView.setAdapter(appListAdapter);
+ gridView.setOnItemClickListener((adapterView, v, position, id) -> {
+ AppEntry entry = appListAdapter.getItem(position);
+ AppFragment mAppFragment = (AppFragment) fragmentManager
+ .findFragmentByTag(((LauncherActivity) getActivity()).APP_FRAGMENT_TAG);
+
+ if (mAppFragment != null) {
+ // Equivalent to setting in AndroidManifest: documentLaunchMode="always"
+ Intent newIntent = entry.getLaunchIntent()
+ .setFlags(~Intent.FLAG_ACTIVITY_SINGLE_TOP)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT
+ | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+ mAppFragment.addLaunchIntentToStack(newIntent);
+ ((LauncherActivity) getActivity()).navigateApp();
+
+ } else if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "AppFragment is not found...");
+ }
+ });
+
+ AppListViewModel appListViewModel = ViewModelProviders.of(this)
+ .get(AppListViewModel.class);
+
+ mHiddenApps.addAll(Arrays.asList(getResources().getStringArray(R.array.hidden_apps)));
+ appListViewModel.getAppList().observe(this, data ->
+ appListAdapter.setData(data, mHiddenApps));
+ return view;
+ }
+}
diff --git a/tests/SecondaryHomeApp/src/com/android/car/secondaryhome/launcher/LauncherActivity.java b/tests/SecondaryHomeApp/src/com/android/car/secondaryhome/launcher/LauncherActivity.java
new file mode 100644
index 0000000..4f0f6a5
--- /dev/null
+++ b/tests/SecondaryHomeApp/src/com/android/car/secondaryhome/launcher/LauncherActivity.java
@@ -0,0 +1,100 @@
+/**
+ * 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.android.car.secondaryhome.launcher;
+
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.ImageButton;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.android.car.secondaryhome.R;
+
+/**
+ * Main launcher activity.
+ * It contains a navigation bar with BACK and HOME buttons.
+ */
+public final class LauncherActivity extends AppCompatActivity {
+ public static final String APP_FRAGMENT_TAG = "app";
+ public static final String HOME_FRAGMENT_TAG = "home";
+
+ private static final String TAG = "SecHome.LauncherActivity";
+
+ private final AppFragment mAppFragment = new AppFragment();
+ private final HomeFragment mHomeFragment = new HomeFragment();
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.activity_main);
+
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Secondary home activity created...");
+ }
+
+ getSupportFragmentManager().beginTransaction()
+ .add(R.id.container, mAppFragment, APP_FRAGMENT_TAG)
+ .commit();
+
+ getSupportFragmentManager().beginTransaction()
+ .add(R.id.container, mHomeFragment, HOME_FRAGMENT_TAG)
+ .commit();
+
+ ImageButton backButton = findViewById(R.id.nav_back);
+ backButton.setOnClickListener(view -> onBackButtonPressed());
+ ImageButton homeButton = findViewById(R.id.nav_home);
+ homeButton.setOnClickListener(view -> navigateHome());
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ navigateHome();
+ }
+
+ private void onBackButtonPressed() {
+ // When BACK is pressed, if HomeFragment is shown
+ // and AppFragment's has valid and non-empty task stack, navigate to AppFragment;
+ // if AppFragment is shown, pop from stack on AppFragment.
+ if (mHomeFragment.isVisible()
+ && mAppFragment.getTaskStackId() != AppFragment.INVALID_TASK_STACK_ID) {
+ navigateApp();
+ } else if (mAppFragment.isVisible()) {
+ mAppFragment.onBackButtonPressed();
+ }
+ }
+
+ public void navigateHome() {
+ getSupportFragmentManager().beginTransaction()
+ .show(mHomeFragment)
+ .commit();
+ getSupportFragmentManager().beginTransaction()
+ .hide(mAppFragment)
+ .commit();
+ }
+
+ public void navigateApp() {
+ getSupportFragmentManager().beginTransaction()
+ .hide(mHomeFragment)
+ .commit();
+ getSupportFragmentManager().beginTransaction()
+ .show(mAppFragment)
+ .commit();
+ }
+}
diff --git a/tests/SecondaryHomeApp/src/com/android/car/secondaryhome/launcher/PackageIntentReceiver.java b/tests/SecondaryHomeApp/src/com/android/car/secondaryhome/launcher/PackageIntentReceiver.java
new file mode 100644
index 0000000..cca2fec
--- /dev/null
+++ b/tests/SecondaryHomeApp/src/com/android/car/secondaryhome/launcher/PackageIntentReceiver.java
@@ -0,0 +1,50 @@
+/**
+ * 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.android.car.secondaryhome.launcher;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+
+/**
+ * Receiver used to notify live data about app list changes.
+ */
+public final class PackageIntentReceiver extends BroadcastReceiver {
+
+ private final AppListLiveData mLiveData;
+
+ PackageIntentReceiver(AppListLiveData liveData, Context context) {
+ mLiveData = liveData;
+ IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ filter.addDataScheme("package");
+ context.registerReceiver(this, filter);
+
+ // Register for events related to sdcard installation.
+ IntentFilter sdFilter = new IntentFilter();
+ sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+ sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+ context.registerReceiver(this, sdFilter);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mLiveData.loadData();
+ }
+}
diff --git a/tests/carservice_unit_test/src/android/car/userlib/CarUserManagerHelperTest.java b/tests/carservice_unit_test/src/android/car/userlib/CarUserManagerHelperTest.java
index afcf51c..c95fd9e 100644
--- a/tests/carservice_unit_test/src/android/car/userlib/CarUserManagerHelperTest.java
+++ b/tests/carservice_unit_test/src/android/car/userlib/CarUserManagerHelperTest.java
@@ -124,35 +124,6 @@
}
@Test
- public void testGetAllSwitchableUsers() {
- // Create two non-foreground users.
- UserInfo user1 = createUserInfoForId(mForegroundUserId + 1);
- UserInfo user2 = createUserInfoForId(mForegroundUserId + 2);
-
- mockGetUsers(mForegroundUser, user1, user2);
-
- // Should return all non-foreground users.
- assertThat(mCarUserManagerHelper.getAllSwitchableUsers()).containsExactly(user1, user2);
- }
-
- @Test
- public void testGetAllPersistentUsers() {
- // Create two non-ephemeral users.
- UserInfo user1 = createUserInfoForId(mForegroundUserId);
- UserInfo user2 = createUserInfoForId(mForegroundUserId + 1);
- // Create two ephemeral users.
- UserInfo user3 = new UserInfo(
- /* id= */mForegroundUserId + 2, /* name = */ "user3", UserInfo.FLAG_EPHEMERAL);
- UserInfo user4 = new UserInfo(
- /* id= */mForegroundUserId + 3, /* name = */ "user4", UserInfo.FLAG_EPHEMERAL);
-
- mockGetUsers(user1, user2, user3, user4);
-
- // Should return all non-ephemeral users.
- assertThat(mCarUserManagerHelper.getAllPersistentUsers()).containsExactly(user1, user2);
- }
-
- @Test
public void testGetAllAdminUsers() {
// Create two admin, and two non-admin users.
UserInfo user1 = new UserInfo(/* id= */ 10, /* name = */ "user10", UserInfo.FLAG_ADMIN);
diff --git a/tests/carservice_unit_test/src/com/android/car/pm/VendorServiceControllerTest.java b/tests/carservice_unit_test/src/com/android/car/pm/VendorServiceControllerTest.java
index 904b2c7..34581c0 100644
--- a/tests/carservice_unit_test/src/com/android/car/pm/VendorServiceControllerTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/pm/VendorServiceControllerTest.java
@@ -90,7 +90,7 @@
public void setUp() {
mContext = new ServiceLauncherContext(ApplicationProvider.getApplicationContext());
mUserManagerHelper = Mockito.spy(new CarUserManagerHelper(mContext));
- mCarUserService = new CarUserService(mContext, mUserManager,
+ mCarUserService = new CarUserService(mContext, mUserManagerHelper, mUserManager,
ActivityManager.getService(), 2 /* max running users */);
CarLocalServices.addService(CarUserService.class, mCarUserService);
diff --git a/tests/carservice_unit_test/src/com/android/car/trust/BleMessageStreamV1Test.java b/tests/carservice_unit_test/src/com/android/car/trust/BleMessageStreamV1Test.java
index ec0a1ba..dd1c905 100644
--- a/tests/carservice_unit_test/src/com/android/car/trust/BleMessageStreamV1Test.java
+++ b/tests/carservice_unit_test/src/com/android/car/trust/BleMessageStreamV1Test.java
@@ -90,7 +90,7 @@
mBleMessageStream = new BleMessageStreamV1(
mHandlerMock, mBlePeripheralManager, mBluetoothDevice, mWriteCharacteristicMock,
mReadCharacteristicMock);
- mBleMessageStream.setCallback(mCallbackMock);
+ mBleMessageStream.registerCallback(mCallbackMock);
}
@Test
@@ -192,9 +192,9 @@
ArgumentCaptor<byte[]> messageCaptor = ArgumentCaptor.forClass(byte[].class);
// Because there is no ACK, the write should be retried up to the limit.
- verify(mWriteCharacteristicMock, times(BleMessageStreamV1Kt.BLE_MESSAGE_RETRY_LIMIT))
+ verify(mWriteCharacteristicMock, times(BleMessageStreamV1.BLE_MESSAGE_RETRY_LIMIT))
.setValue(messageCaptor.capture());
- verify(mBlePeripheralManager, times(BleMessageStreamV1Kt.BLE_MESSAGE_RETRY_LIMIT))
+ verify(mBlePeripheralManager, times(BleMessageStreamV1.BLE_MESSAGE_RETRY_LIMIT))
.notifyCharacteristicChanged(mBluetoothDevice, mWriteCharacteristicMock, false);
List<byte[]> writtenBytes = messageCaptor.getAllValues();
@@ -237,9 +237,9 @@
mBleMessageStream.writeMessage(message, operationType, isPayloadEncrypted);
// Because there is no ACK, the write should be retried up to the limit.
- verify(mWriteCharacteristicMock, times(BleMessageStreamV1Kt.BLE_MESSAGE_RETRY_LIMIT))
+ verify(mWriteCharacteristicMock, times(BleMessageStreamV1.BLE_MESSAGE_RETRY_LIMIT))
.setValue(any(byte[].class));
- verify(mBlePeripheralManager, times(BleMessageStreamV1Kt.BLE_MESSAGE_RETRY_LIMIT))
+ verify(mBlePeripheralManager, times(BleMessageStreamV1.BLE_MESSAGE_RETRY_LIMIT))
.notifyCharacteristicChanged(mBluetoothDevice, mWriteCharacteristicMock, false);
// But the callback should only be notified once.
diff --git a/tests/carservice_unit_test/src/com/android/car/user/CarUserServiceTest.java b/tests/carservice_unit_test/src/com/android/car/user/CarUserServiceTest.java
index c2fe86d..da9f7c9 100644
--- a/tests/carservice_unit_test/src/com/android/car/user/CarUserServiceTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/user/CarUserServiceTest.java
@@ -34,6 +34,7 @@
import android.app.ActivityManager;
import android.app.IActivityManager;
import android.car.settings.CarSettings;
+import android.car.userlib.CarUserManagerHelper;
import android.content.Context;
import android.content.pm.UserInfo;
import android.content.res.Resources;
@@ -85,6 +86,7 @@
@Mock private Context mMockContext;
@Mock private Context mApplicationContext;
@Mock private LocationManager mLocationManager;
+ @Mock private CarUserManagerHelper mMockedCarUserManagerHelper;
@Mock private IActivityManager mMockedIActivityManager;
@Mock private UserManager mMockedUserManager;
@Mock private Resources mMockedResources;
@@ -112,6 +114,7 @@
mCarUserService =
new CarUserService(
mMockContext,
+ mMockedCarUserManagerHelper,
mMockedUserManager,
mMockedIActivityManager,
3);
@@ -172,10 +175,7 @@
mCarUserService.onSwitchUser(lastActiveUserId);
- int writtenId = Settings.Global.getInt(
- InstrumentationRegistry.getTargetContext().getContentResolver(),
- Settings.Global.LAST_ACTIVE_USER_ID, 0);
- assertEquals(lastActiveUserId, writtenId);
+ verify(mMockedCarUserManagerHelper).setLastActiveUser(lastActiveUserId);
}
/**
@@ -242,7 +242,7 @@
doReturn(user4GuestInfo).when(mMockedUserManager).getUserInfo(user4Guest);
doReturn(user5Info).when(mMockedUserManager).getUserInfo(user5);
- doReturn(user1Info).when(mMockedIActivityManager).getCurrentUser();
+ doReturn(user1).when(mMockedCarUserManagerHelper).getCurrentForegroundUserId();
mCarUserService.setUserLockStatus(UserHandle.USER_SYSTEM, true);
// user 0 should never go to that list.
assertTrue(mCarUserService.getBackgroundUsersToRestart().isEmpty());
@@ -257,19 +257,19 @@
assertEquals(new Integer[]{user1},
mCarUserService.getBackgroundUsersToRestart().toArray());
- doReturn(user3Info).when(mMockedIActivityManager).getCurrentUser();
+ doReturn(user3).when(mMockedCarUserManagerHelper).getCurrentForegroundUserId();
mCarUserService.setUserLockStatus(user3, true);
mCarUserService.setUserLockStatus(user2, false);
assertEquals(new Integer[]{user3, user1},
mCarUserService.getBackgroundUsersToRestart().toArray());
- doReturn(user4GuestInfo).when(mMockedIActivityManager).getCurrentUser();
+ doReturn(user4Guest).when(mMockedCarUserManagerHelper).getCurrentForegroundUserId();
mCarUserService.setUserLockStatus(user4Guest, true);
mCarUserService.setUserLockStatus(user3, false);
assertEquals(new Integer[]{user3, user1},
mCarUserService.getBackgroundUsersToRestart().toArray());
- doReturn(user5Info).when(mMockedIActivityManager).getCurrentUser();
+ doReturn(user5).when(mMockedCarUserManagerHelper).getCurrentForegroundUserId();
mCarUserService.setUserLockStatus(user5, true);
mCarUserService.setUserLockStatus(user4Guest, false);
assertEquals(new Integer[]{user5, user3},
@@ -293,13 +293,13 @@
doReturn(user2Info).when(mMockedUserManager).getUserInfo(user2);
doReturn(user3Info).when(mMockedUserManager).getUserInfo(user3);
- doReturn(user1Info).when(mMockedIActivityManager).getCurrentUser();
+ doReturn(user1).when(mMockedCarUserManagerHelper).getCurrentForegroundUserId();
mCarUserService.setUserLockStatus(UserHandle.USER_SYSTEM, true);
mCarUserService.setUserLockStatus(user1, true);
- doReturn(user2Info).when(mMockedIActivityManager).getCurrentUser();
+ doReturn(user2).when(mMockedCarUserManagerHelper).getCurrentForegroundUserId();
mCarUserService.setUserLockStatus(user2, true);
mCarUserService.setUserLockStatus(user1, false);
- doReturn(user3Info).when(mMockedIActivityManager).getCurrentUser();
+ doReturn(user3).when(mMockedCarUserManagerHelper).getCurrentForegroundUserId();
mCarUserService.setUserLockStatus(user3, true);
mCarUserService.setUserLockStatus(user2, false);
@@ -336,7 +336,7 @@
public void testStopBackgroundUserForFgUser() throws RemoteException {
int user1 = 101;
UserInfo user1Info = new UserInfo(user1, "user1", NO_USER_INFO_FLAGS);
- doReturn(user1Info).when(mMockedIActivityManager).getCurrentUser();
+ doReturn(user1).when(mMockedCarUserManagerHelper).getCurrentForegroundUserId();
assertFalse(mCarUserService.stopBackgroundUser(UserHandle.USER_SYSTEM));
}
@@ -359,7 +359,7 @@
public void testCreateNonAdminDriver() {
String userName = "testUser";
UserInfo userInfo = new UserInfo();
- doReturn(userInfo).when(mMockedUserManager).createUser(userName, NO_USER_INFO_FLAGS);
+ doReturn(userInfo).when(mMockedCarUserManagerHelper).createNewNonAdminUser(userName);
assertEquals(userInfo, mCarUserService.createDriver(userName, false));
}
@@ -408,7 +408,7 @@
int currentId = 11;
int targetId = 12;
UserInfo userInfo = new UserInfo(currentId, "test11", NO_USER_INFO_FLAGS);
- doReturn(userInfo).when(mMockedIActivityManager).getCurrentUser();
+ doReturn(currentId).when(mMockedCarUserManagerHelper).getCurrentForegroundUserId();
doReturn(true).when(mMockedIActivityManager).switchUser(targetId);
doReturn(false).when(mMockedUserManager)
.hasUserRestriction(UserManager.DISALLOW_USER_SWITCH);
@@ -420,7 +420,7 @@
int currentId = 11;
int targetId = 12;
UserInfo userInfo = new UserInfo(currentId, "test11", NO_USER_INFO_FLAGS);
- doReturn(userInfo).when(mMockedIActivityManager).getCurrentUser();
+ doReturn(currentId).when(mMockedCarUserManagerHelper).getCurrentForegroundUserId();
doReturn(true).when(mMockedIActivityManager).switchUser(targetId);
doReturn(UserManager.SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED).when(mMockedUserManager)
.getUserSwitchability();
@@ -430,7 +430,7 @@
@Test
public void testSwitchDriver_IfSwitchedToCurrentUser() throws RemoteException {
UserInfo userInfo = new UserInfo(11, "test11", NO_USER_INFO_FLAGS);
- doReturn(userInfo).when(mMockedIActivityManager).getCurrentUser();
+ doReturn(userInfo.id).when(mMockedCarUserManagerHelper).getCurrentForegroundUserId();
doReturn(false).when(mMockedUserManager)
.hasUserRestriction(UserManager.DISALLOW_USER_SWITCH);
assertTrue(mCarUserService.switchDriver(11));
diff --git a/tests/carservice_unit_test/src/com/android/car/vms/VmsClientManagerTest.java b/tests/carservice_unit_test/src/com/android/car/vms/VmsClientManagerTest.java
index b90dac4..73217e7 100644
--- a/tests/carservice_unit_test/src/com/android/car/vms/VmsClientManagerTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/vms/VmsClientManagerTest.java
@@ -97,6 +97,8 @@
private static final String HAL_CLIENT_NAME = "HalClient";
private static final String UNKNOWN_PACKAGE = "UnknownPackage";
+ private static final long MILLIS_BEFORE_REBIND = 100;
+
@Rule
public MockitoRule mMockitoRule = MockitoJUnit.rule();
@Mock
@@ -120,6 +122,12 @@
private VmsHalService mHal;
@Mock
+ private Handler mHandler;
+
+ @Captor
+ private ArgumentCaptor<Runnable> mRebindCaptor;
+
+ @Mock
private VmsPublisherService mPublisherService;
@Mock
@@ -152,7 +160,7 @@
when(mResources.getInteger(
com.android.car.R.integer.millisecondsBeforeRebindToVmsPublisher)).thenReturn(
- 5);
+ (int) MILLIS_BEFORE_REBIND);
when(mResources.getStringArray(
com.android.car.R.array.vmsPublisherSystemClients)).thenReturn(
new String[]{ SYSTEM_CLIENT });
@@ -168,7 +176,7 @@
mCallingAppUid = UserHandle.getUid(USER_ID, 0);
mClientManager = new VmsClientManager(mContext, mBrokerService, mUserService,
- mUserManagerHelper, mHal, () -> mCallingAppUid);
+ mUserManagerHelper, mHal, mHandler, () -> mCallingAppUid);
verify(mHal).setClientManager(mClientManager);
mClientManager.setPublisherService(mPublisherService);
@@ -180,11 +188,10 @@
@After
public void tearDown() throws Exception {
- Thread.sleep(10); // Time to allow for delayed rebinds to settle
verify(mContext, atLeast(0)).getSystemService(eq(Context.USER_SERVICE));
verify(mContext, atLeast(0)).getResources();
verify(mContext, atLeast(0)).getPackageManager();
- verifyNoMoreInteractions(mContext, mBrokerService, mHal, mPublisherService);
+ verifyNoMoreInteractions(mContext, mBrokerService, mHal, mPublisherService, mHandler);
}
@Test
@@ -467,7 +474,7 @@
connection.onServiceDisconnected(null);
verify(mPublisherService).onClientDisconnected(eq(SYSTEM_CLIENT_NAME));
- Thread.sleep(10);
+ verifyAndRunRebindTask();
verify(mContext).unbindService(connection);
verifySystemBind(1);
}
@@ -490,6 +497,8 @@
binder = createPublisherBinder();
connection.onServiceConnected(null, binder);
verifyOnClientConnected(SYSTEM_CLIENT_NAME, binder);
+
+ verifyAndRunRebindTask();
// No more interactions (verified by tearDown)
}
@@ -507,7 +516,7 @@
connection.onBindingDied(null);
verify(mPublisherService).onClientDisconnected(eq(SYSTEM_CLIENT_NAME));
- Thread.sleep(10);
+ verifyAndRunRebindTask();
verify(mContext).unbindService(connection);
verifySystemBind(1);
}
@@ -523,7 +532,7 @@
verifyZeroInteractions(mPublisherService);
- Thread.sleep(10);
+ verifyAndRunRebindTask();
verify(mContext).unbindService(connection);
verifySystemBind(1);
}
@@ -541,7 +550,7 @@
connection.onServiceDisconnected(null);
verify(mPublisherService).onClientDisconnected(eq(USER_CLIENT_NAME));
- Thread.sleep(10);
+ verifyAndRunRebindTask();
verify(mContext).unbindService(connection);
verifyUserBind(1);
}
@@ -564,6 +573,8 @@
binder = createPublisherBinder();
connection.onServiceConnected(null, binder);
verifyOnClientConnected(USER_CLIENT_NAME, binder);
+
+ verifyAndRunRebindTask();
// No more interactions (verified by tearDown)
}
@@ -580,7 +591,7 @@
connection.onBindingDied(null);
verify(mPublisherService).onClientDisconnected(eq(USER_CLIENT_NAME));
- Thread.sleep(10);
+ verifyAndRunRebindTask();
verify(mContext).unbindService(connection);
verifyUserBind(1);
}
@@ -596,7 +607,7 @@
verifyZeroInteractions(mPublisherService);
- Thread.sleep(10);
+ verifyAndRunRebindTask();
verify(mContext).unbindService(connection);
verifyUserBind(1);
}
@@ -975,6 +986,11 @@
eq(Context.BIND_AUTO_CREATE), any(Handler.class), eq(user));
}
+ private void verifyAndRunRebindTask() {
+ verify(mHandler).postDelayed(mRebindCaptor.capture(), eq(MILLIS_BEFORE_REBIND));
+ mRebindCaptor.getValue().run();
+ }
+
private void verifyOnClientConnected(String publisherName, IBinder binder) {
ArgumentCaptor<IVmsPublisherClient> clientCaptor =
ArgumentCaptor.forClass(IVmsPublisherClient.class);
diff --git a/user/car-user-lib/src/android/car/userlib/CarUserManagerHelper.java b/user/car-user-lib/src/android/car/userlib/CarUserManagerHelper.java
index a008222..0c4f9b7 100644
--- a/user/car-user-lib/src/android/car/userlib/CarUserManagerHelper.java
+++ b/user/car-user-lib/src/android/car/userlib/CarUserManagerHelper.java
@@ -262,21 +262,6 @@
}
/**
- * Gets all the existing users on the system that are not currently running as
- * the foreground user.
- * These are all the users that can be switched to from the foreground user.
- *
- * @return List of {@code UserInfo} for each user that is not the foreground user.
- */
- public List<UserInfo> getAllSwitchableUsers() {
- if (isHeadlessSystemUser()) {
- return getAllUsersExceptSystemUserAndSpecifiedUser(getCurrentForegroundUserId());
- } else {
- return getAllUsersExceptSpecifiedUser(getCurrentForegroundUserId());
- }
- }
-
- /**
* Gets all the users that can be brought to the foreground on the system.
*
* @return List of {@code UserInfo} for users that associated with a real person.
@@ -294,7 +279,7 @@
*
* @return List of {@code UserInfo} for non-ephemeral users that associated with a real person.
*/
- public List<UserInfo> getAllPersistentUsers() {
+ private List<UserInfo> getAllPersistentUsers() {
List<UserInfo> users = getAllUsers();
for (Iterator<UserInfo> iterator = users.iterator(); iterator.hasNext(); ) {
UserInfo userInfo = iterator.next();
@@ -579,7 +564,7 @@
* @param userInfo User to set restrictions on.
* @param enable If true, restriction is ON, If false, restriction is OFF.
*/
- private void setDefaultNonAdminRestrictions(UserInfo userInfo, boolean enable) {
+ public void setDefaultNonAdminRestrictions(UserInfo userInfo, boolean enable) {
for (String restriction : DEFAULT_NON_ADMIN_RESTRICTIONS) {
mUserManager.setUserRestriction(restriction, enable, userInfo.getUserHandle());
}
@@ -774,8 +759,13 @@
return picture;
}
- // Assigns a default icon to a user according to the user's id.
- private Bitmap assignDefaultIcon(UserInfo userInfo) {
+ /**
+ * Assigns a default icon to a user according to the user's id.
+ *
+ * @param userInfo User whose avatar is set to default icon.
+ * @return Bitmap of the user icon.
+ */
+ public Bitmap assignDefaultIcon(UserInfo userInfo) {
Bitmap bitmap = userInfo.isGuest()
? getGuestDefaultIcon() : getUserDefaultIcon(userInfo);
mUserManager.setUserIcon(userInfo.id, bitmap);