Merge "Don't enforce declaration of app op permissions without public API." into rvc-dev
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 91efb05..5052499 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -48,7 +48,6 @@
":opt-telephony-srcs",
":opt-net-voip-srcs",
":art-module-public-api-stubs-source",
- ":conscrypt.module.public.api.stubs.source",
":android_icu4j_public_api_files",
],
// TODO(b/147699819): remove below aidl includes.
@@ -69,7 +68,10 @@
stubs_defaults {
name: "metalava-full-api-stubs-default",
defaults: ["metalava-base-api-stubs-default"],
- srcs: [":framework-updatable-sources"],
+ srcs: [
+ ":conscrypt.module.public.api.stubs.source",
+ ":framework-updatable-sources",
+ ],
sdk_version: "core_platform",
}
diff --git a/apex/extservices/Android.bp b/apex/extservices/Android.bp
index 68350af..0c6c4c2 100644
--- a/apex/extservices/Android.bp
+++ b/apex/extservices/Android.bp
@@ -21,7 +21,7 @@
apex_defaults {
name: "com.android.extservices-defaults",
updatable: true,
- min_sdk_version: "R",
+ min_sdk_version: "current",
key: "com.android.extservices.key",
certificate: ":com.android.extservices.certificate",
apps: ["ExtServices"],
diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
index 6d9e3ed..887d82c 100644
--- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
@@ -45,6 +45,14 @@
boolean idle, int bucket, int reason);
/**
+ * Callback to inform listeners that the parole state has changed. This means apps are
+ * allowed to do work even if they're idle or in a low bucket.
+ */
+ public void onParoleStateChanged(boolean isParoleOn) {
+ // No-op by default
+ }
+
+ /**
* Optional callback to inform the listener that the app has transitioned into
* an active state due to user interaction.
*/
@@ -92,6 +100,11 @@
boolean isAppIdleFiltered(String packageName, int appId, int userId,
long elapsedRealtime);
+ /**
+ * @return true if currently app idle parole mode is on.
+ */
+ boolean isInParole();
+
int[] getIdleUidsForUser(int userId);
void setAppIdleAsync(String packageName, boolean idle, int userId);
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 24728dd..980372d 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -214,8 +214,7 @@
private AppIdleHistory mAppIdleHistory;
@GuardedBy("mPackageAccessListeners")
- private ArrayList<AppIdleStateChangeListener>
- mPackageAccessListeners = new ArrayList<>();
+ private final ArrayList<AppIdleStateChangeListener> mPackageAccessListeners = new ArrayList<>();
/** Whether we've queried the list of carrier privileged apps. */
@GuardedBy("mAppIdleLock")
@@ -235,6 +234,7 @@
static final int MSG_FORCE_IDLE_STATE = 4;
static final int MSG_CHECK_IDLE_STATES = 5;
static final int MSG_REPORT_CONTENT_PROVIDER_USAGE = 8;
+ static final int MSG_PAROLE_STATE_CHANGED = 9;
static final int MSG_ONE_TIME_CHECK_IDLE_STATES = 10;
/** Check the state of one app: arg1 = userId, arg2 = uid, obj = (String) packageName */
static final int MSG_CHECK_PACKAGE_IDLE_STATE = 11;
@@ -293,6 +293,13 @@
* {@link #setAppStandbyBucket(String, int, int, int, int)} will not be propagated.
*/
boolean mLinkCrossProfileApps;
+ /**
+ * Whether we should allow apps into the
+ * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket or not.
+ * If false, any attempts to put an app into the bucket will put the app into the
+ * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RARE} bucket instead.
+ */
+ private boolean mAllowRestrictedBucket;
private volatile boolean mAppIdleEnabled;
private boolean mIsCharging;
@@ -390,7 +397,16 @@
@VisibleForTesting
void setAppIdleEnabled(boolean enabled) {
- mAppIdleEnabled = enabled;
+ synchronized (mAppIdleLock) {
+ if (mAppIdleEnabled != enabled) {
+ final boolean oldParoleState = isInParole();
+ mAppIdleEnabled = enabled;
+ if (isInParole() != oldParoleState) {
+ postParoleStateChanged();
+ }
+ }
+ }
+
}
@Override
@@ -563,11 +579,23 @@
if (mIsCharging != isCharging) {
if (DEBUG) Slog.d(TAG, "Setting mIsCharging to " + isCharging);
mIsCharging = isCharging;
+ postParoleStateChanged();
}
}
}
@Override
+ public boolean isInParole() {
+ return !mAppIdleEnabled || mIsCharging;
+ }
+
+ private void postParoleStateChanged() {
+ if (DEBUG) Slog.d(TAG, "Posting MSG_PAROLE_STATE_CHANGED");
+ mHandler.removeMessages(MSG_PAROLE_STATE_CHANGED);
+ mHandler.sendEmptyMessage(MSG_PAROLE_STATE_CHANGED);
+ }
+
+ @Override
public void postCheckIdleStates(int userId) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_IDLE_STATES, userId, 0));
}
@@ -667,6 +695,10 @@
return;
}
final int oldBucket = app.currentBucket;
+ if (oldBucket == STANDBY_BUCKET_NEVER) {
+ // None of this should bring an app out of the NEVER bucket.
+ return;
+ }
int newBucket = Math.max(oldBucket, STANDBY_BUCKET_ACTIVE); // Undo EXEMPTED
boolean predictionLate = predictionTimedOut(app, elapsedRealtime);
// Compute age-based bucket
@@ -722,11 +754,18 @@
Slog.d(TAG, "Bringing down to RESTRICTED due to timeout");
}
}
+ if (newBucket == STANDBY_BUCKET_RESTRICTED && !mAllowRestrictedBucket) {
+ newBucket = STANDBY_BUCKET_RARE;
+ // Leave the reason alone.
+ if (DEBUG) {
+ Slog.d(TAG, "Bringing up from RESTRICTED to RARE due to off switch");
+ }
+ }
if (DEBUG) {
Slog.d(TAG, " Old bucket=" + oldBucket
+ ", newBucket=" + newBucket);
}
- if (oldBucket < newBucket || predictionLate) {
+ if (oldBucket != newBucket || predictionLate) {
mAppIdleHistory.setAppStandbyBucket(packageName, userId,
elapsedRealtime, newBucket, reason);
maybeInformListeners(packageName, userId, elapsedRealtime,
@@ -1176,8 +1215,8 @@
final int reason = REASON_MAIN_FORCED_BY_SYSTEM | (REASON_SUB_MASK & restrictReason);
final long nowElapsed = mInjector.elapsedRealtime();
- setAppStandbyBucket(packageName, userId, STANDBY_BUCKET_RESTRICTED, reason,
- nowElapsed, false);
+ final int bucket = mAllowRestrictedBucket ? STANDBY_BUCKET_RESTRICTED : STANDBY_BUCKET_RARE;
+ setAppStandbyBucket(packageName, userId, bucket, reason, nowElapsed, false);
}
@Override
@@ -1247,6 +1286,9 @@
Slog.e(TAG, "Tried to set bucket of uninstalled app: " + packageName);
return;
}
+ if (newBucket == STANDBY_BUCKET_RESTRICTED && !mAllowRestrictedBucket) {
+ newBucket = STANDBY_BUCKET_RARE;
+ }
AppIdleHistory.AppUsageHistory app = mAppIdleHistory.getAppUsageHistory(packageName,
userId, elapsedRealtime);
boolean predicted = (reason & REASON_MAIN_MASK) == REASON_MAIN_PREDICTED;
@@ -1365,6 +1407,7 @@
Slog.d(TAG, " Keeping at WORKING_SET due to min timeout");
}
} else if (newBucket == STANDBY_BUCKET_RARE
+ && mAllowRestrictedBucket
&& getBucketForLocked(packageName, userId, elapsedRealtime)
== STANDBY_BUCKET_RESTRICTED) {
// Prediction doesn't think the app will be used anytime soon and
@@ -1502,6 +1545,15 @@
}
}
+ private void informParoleStateChanged() {
+ final boolean paroled = isInParole();
+ synchronized (mPackageAccessListeners) {
+ for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
+ listener.onParoleStateChanged(paroled);
+ }
+ }
+ }
+
@Override
public void flushToDisk(int userId) {
synchronized (mAppIdleLock) {
@@ -1697,6 +1749,8 @@
pw.println();
pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled);
+ pw.print(" mAllowRestrictedBucket=");
+ pw.print(mAllowRestrictedBucket);
pw.print(" mIsCharging=");
pw.print(mIsCharging);
pw.println();
@@ -1798,6 +1852,12 @@
return mPowerWhitelistManager.isWhitelisted(packageName, false);
}
+ boolean isRestrictedBucketEnabled() {
+ return Global.getInt(mContext.getContentResolver(),
+ Global.ENABLE_RESTRICTED_BUCKET,
+ Global.DEFAULT_ENABLE_RESTRICTED_BUCKET) == 1;
+ }
+
File getDataSystemDirectory() {
return Environment.getDataSystemDirectory();
}
@@ -1920,6 +1980,11 @@
args.recycle();
break;
+ case MSG_PAROLE_STATE_CHANGED:
+ if (DEBUG) Slog.d(TAG, "Parole state: " + isInParole());
+ informParoleStateChanged();
+ break;
+
case MSG_CHECK_PACKAGE_IDLE_STATE:
checkAndUpdateStandbyState((String) msg.obj, msg.arg1, msg.arg2,
mInjector.elapsedRealtime());
@@ -2031,6 +2096,8 @@
final ContentResolver cr = mContext.getContentResolver();
cr.registerContentObserver(Global.getUriFor(Global.APP_IDLE_CONSTANTS), false, this);
cr.registerContentObserver(Global.getUriFor(Global.APP_STANDBY_ENABLED), false, this);
+ cr.registerContentObserver(Global.getUriFor(Global.ENABLE_RESTRICTED_BUCKET),
+ false, this);
cr.registerContentObserver(Global.getUriFor(Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED),
false, this);
}
@@ -2129,6 +2196,8 @@
mLinkCrossProfileApps = mParser.getBoolean(
KEY_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS,
DEFAULT_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS);
+
+ mAllowRestrictedBucket = mInjector.isRestrictedBucketEnabled();
}
// Check if app_idle_enabled has changed. Do this after getting the rest of the settings
diff --git a/apex/statsd/framework/java/android/app/StatsManager.java b/apex/statsd/framework/java/android/app/StatsManager.java
index 7fbfc43..d1b7d8d 100644
--- a/apex/statsd/framework/java/android/app/StatsManager.java
+++ b/apex/statsd/framework/java/android/app/StatsManager.java
@@ -28,7 +28,6 @@
import android.os.IPullAtomCallback;
import android.os.IPullAtomResultReceiver;
import android.os.IStatsManagerService;
-import android.os.IStatsd;
import android.os.RemoteException;
import android.os.StatsFrameworkInitializer;
import android.util.AndroidException;
@@ -57,9 +56,6 @@
private final Context mContext;
@GuardedBy("sLock")
- private IStatsd mService;
-
- @GuardedBy("sLock")
private IStatsManagerService mStatsManagerService;
/**
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
index 93e6c10..5cf5e0b 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
@@ -54,11 +54,11 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* Helper service for statsd (the native stats management service in cmds/statsd/).
@@ -112,17 +112,8 @@
private final HashMap<Long, String> mDeletedFiles = new HashMap<>();
private final CompanionHandler mHandler;
- // Flag that is set when PHASE_BOOT_COMPLETED is triggered in the StatsCompanion lifecycle. This
- // and the flag mSentBootComplete below is used for synchronization to ensure that the boot
- // complete signal is only ever sent once to statsd. Two signals are needed because
- // #sayHiToStatsd can be called from both statsd and #onBootPhase
- // PHASE_THIRD_PARTY_APPS_CAN_START.
- @GuardedBy("sStatsdLock")
- private boolean mBootCompleted = false;
- // Flag that is set when IStatsd#bootCompleted is called. This flag ensures that boot complete
- // signal is only ever sent once.
- @GuardedBy("sStatsdLock")
- private boolean mSentBootComplete = false;
+ // Flag that is set when PHASE_BOOT_COMPLETED is triggered in the StatsCompanion lifecycle.
+ private AtomicBoolean mBootCompleted = new AtomicBoolean(false);
public StatsCompanionService(Context context) {
super();
@@ -607,27 +598,35 @@
// Statsd related code
/**
- * Fetches the statsd IBinder service. This is a blocking call.
+ * Fetches the statsd IBinder service. This is a blocking call that always refetches statsd
+ * instead of returning the cached sStatsd.
* Note: This should only be called from {@link #sayHiToStatsd()}. All other clients should use
* the cached sStatsd via {@link #getStatsdNonblocking()}.
*/
- private IStatsd fetchStatsdService(StatsdDeathRecipient deathRecipient) {
- synchronized (sStatsdLock) {
- if (sStatsd == null) {
- sStatsd = IStatsd.Stub.asInterface(StatsFrameworkInitializer
- .getStatsServiceManager()
- .getStatsdServiceRegisterer()
- .get());
- if (sStatsd != null) {
- try {
- sStatsd.asBinder().linkToDeath(deathRecipient, /* flags */ 0);
- } catch (RemoteException e) {
- Log.e(TAG, "linkToDeath(StatsdDeathRecipient) failed");
- statsdNotReadyLocked();
- }
+ private IStatsd fetchStatsdServiceLocked() {
+ sStatsd = IStatsd.Stub.asInterface(StatsFrameworkInitializer
+ .getStatsServiceManager()
+ .getStatsdServiceRegisterer()
+ .get());
+ return sStatsd;
+ }
+
+ private void registerStatsdDeathRecipient(IStatsd statsd, List<BroadcastReceiver> receivers) {
+ StatsdDeathRecipient deathRecipient = new StatsdDeathRecipient(statsd, receivers);
+
+ try {
+ statsd.asBinder().linkToDeath(deathRecipient, /*flags=*/0);
+ } catch (RemoteException e) {
+ Log.e(TAG, "linkToDeath (StatsdDeathRecipient) failed");
+ // Statsd has already died. Unregister receivers ourselves.
+ for (BroadcastReceiver receiver : receivers) {
+ mContext.unregisterReceiver(receiver);
+ }
+ synchronized (sStatsdLock) {
+ if (statsd == sStatsd) {
+ statsdNotReadyLocked();
}
}
- return sStatsd;
}
}
@@ -648,22 +647,23 @@
* statsd.
*/
private void sayHiToStatsd() {
- if (getStatsdNonblocking() != null) {
- Log.e(TAG, "Trying to fetch statsd, but it was already fetched",
- new IllegalStateException(
- "sStatsd is not null when being fetched"));
- return;
+ IStatsd statsd;
+ synchronized (sStatsdLock) {
+ if (sStatsd != null && sStatsd.asBinder().isBinderAlive()) {
+ Log.e(TAG, "statsd has already been fetched before",
+ new IllegalStateException("IStatsd object should be null or dead"));
+ return;
+ }
+ statsd = fetchStatsdServiceLocked();
}
- StatsdDeathRecipient deathRecipient = new StatsdDeathRecipient();
- IStatsd statsd = fetchStatsdService(deathRecipient);
+
if (statsd == null) {
- Log.i(TAG,
- "Could not yet find statsd to tell it that StatsCompanion is "
- + "alive.");
+ Log.i(TAG, "Could not yet find statsd to tell it that StatsCompanion is alive.");
return;
}
- mStatsManagerService.statsdReady(statsd);
+
if (DEBUG) Log.d(TAG, "Saying hi to statsd");
+ mStatsManagerService.statsdReady(statsd);
try {
statsd.statsCompanionReady();
@@ -682,8 +682,7 @@
mContext.registerReceiverForAllUsers(appUpdateReceiver, filter, null, null);
// Setup receiver for user initialize (which happens once for a new user)
- // and
- // if a user is removed.
+ // and if a user is removed.
filter = new IntentFilter(Intent.ACTION_USER_INITIALIZE);
filter.addAction(Intent.ACTION_USER_REMOVED);
mContext.registerReceiverForAllUsers(userUpdateReceiver, filter, null, null);
@@ -691,27 +690,20 @@
// Setup receiver for device reboots or shutdowns.
filter = new IntentFilter(Intent.ACTION_REBOOT);
filter.addAction(Intent.ACTION_SHUTDOWN);
- mContext.registerReceiverForAllUsers(
- shutdownEventReceiver, filter, null, null);
+ mContext.registerReceiverForAllUsers(shutdownEventReceiver, filter, null, null);
- // Only add the receivers if the registration is successful.
- deathRecipient.addRegisteredBroadcastReceivers(
- List.of(appUpdateReceiver, userUpdateReceiver, shutdownEventReceiver));
+ // Register death recipient.
+ List<BroadcastReceiver> broadcastReceivers =
+ List.of(appUpdateReceiver, userUpdateReceiver, shutdownEventReceiver);
+ registerStatsdDeathRecipient(statsd, broadcastReceivers);
- // Used so we can call statsd.bootComplete() outside of the lock.
- boolean shouldSendBootComplete = false;
- synchronized (sStatsdLock) {
- if (mBootCompleted && !mSentBootComplete) {
- mSentBootComplete = true;
- shouldSendBootComplete = true;
- }
- }
- if (shouldSendBootComplete) {
+ // Tell statsd that boot has completed. The signal may have already been sent, but since
+ // the signal-receiving function is idempotent, that's ok.
+ if (mBootCompleted.get()) {
statsd.bootCompleted();
}
- // Pull the latest state of UID->app name, version mapping when
- // statsd starts.
+ // Pull the latest state of UID->app name, version mapping when statsd starts.
informAllUids(mContext);
Log.i(TAG, "Told statsd that StatsCompanionService is alive.");
@@ -722,18 +714,16 @@
private class StatsdDeathRecipient implements IBinder.DeathRecipient {
- private List<BroadcastReceiver> mReceiversToUnregister;
+ private final IStatsd mStatsd;
+ private final List<BroadcastReceiver> mReceiversToUnregister;
- StatsdDeathRecipient() {
- mReceiversToUnregister = new ArrayList<>();
+ StatsdDeathRecipient(IStatsd statsd, List<BroadcastReceiver> receivers) {
+ mStatsd = statsd;
+ mReceiversToUnregister = receivers;
}
- public void addRegisteredBroadcastReceivers(List<BroadcastReceiver> receivers) {
- synchronized (sStatsdLock) {
- mReceiversToUnregister.addAll(receivers);
- }
- }
-
+ // It is possible for binderDied to be called after a restarted statsd calls statsdReady,
+ // but that's alright because the code does not assume an ordering of the two calls.
@Override
public void binderDied() {
Log.i(TAG, "Statsd is dead - erase all my knowledge, except pullers");
@@ -762,13 +752,19 @@
}
}
}
- // We only unregister in binder death becaseu receivers can only be unregistered
- // once, or an IllegalArgumentException is thrown.
+
+ // Unregister receivers on death because receivers can only be unregistered once.
+ // Otherwise, an IllegalArgumentException is thrown.
for (BroadcastReceiver receiver: mReceiversToUnregister) {
mContext.unregisterReceiver(receiver);
}
- statsdNotReadyLocked();
- mSentBootComplete = false;
+
+ // It's possible for statsd to have restarted and called statsdReady, causing a new
+ // sStatsd binder object to be fetched, before the binderDied callback runs. Only
+ // call #statsdNotReadyLocked if that hasn't happened yet.
+ if (mStatsd == sStatsd) {
+ statsdNotReadyLocked();
+ }
}
}
}
@@ -779,19 +775,12 @@
}
void bootCompleted() {
+ mBootCompleted.set(true);
IStatsd statsd = getStatsdNonblocking();
- synchronized (sStatsdLock) {
- mBootCompleted = true;
- if (mSentBootComplete) {
- // do not send a boot complete a second time.
- return;
- }
- if (statsd == null) {
- // Statsd is not yet ready.
- // Delay the boot completed ping to {@link #sayHiToStatsd()}
- return;
- }
- mSentBootComplete = true;
+ if (statsd == null) {
+ // Statsd is not yet ready.
+ // Delay the boot completed ping to {@link #sayHiToStatsd()}
+ return;
}
try {
statsd.bootCompleted();
@@ -808,8 +797,7 @@
}
synchronized (sStatsdLock) {
- writer.println(
- "Number of configuration files deleted: " + mDeletedFiles.size());
+ writer.println("Number of configuration files deleted: " + mDeletedFiles.size());
if (mDeletedFiles.size() > 0) {
writer.println(" timestamp, deleted file name");
}
@@ -817,8 +805,7 @@
SystemClock.currentThreadTimeMillis() - SystemClock.elapsedRealtime();
for (Long elapsedMillis : mDeletedFiles.keySet()) {
long deletionMillis = lastBootMillis + elapsedMillis;
- writer.println(
- " " + deletionMillis + ", " + mDeletedFiles.get(elapsedMillis));
+ writer.println(" " + deletionMillis + ", " + mDeletedFiles.get(elapsedMillis));
}
}
}
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java b/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
index 90764b0..97846f2 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
@@ -172,6 +172,10 @@
public void registerPullAtomCallback(int atomTag, long coolDownMillis, long timeoutMillis,
int[] additiveFields, IPullAtomCallback pullerCallback) {
enforceRegisterStatsPullAtomPermission();
+ if (pullerCallback == null) {
+ Log.w(TAG, "Puller callback is null for atom " + atomTag);
+ return;
+ }
int callingUid = Binder.getCallingUid();
PullerKey key = new PullerKey(callingUid, atomTag);
PullerValue val =
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index bd9f7a5..a65f5f7 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -343,9 +343,11 @@
if (!utf8Args[0].compare(String8("print-logs"))) {
return cmd_print_logs(out, utf8Args);
}
+
if (!utf8Args[0].compare(String8("send-active-configs"))) {
return cmd_trigger_active_config_broadcast(out, utf8Args);
}
+
if (!utf8Args[0].compare(String8("data-subscribe"))) {
{
std::lock_guard<std::mutex> lock(mShellSubscriberMutex);
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 650545f..03061bb 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -428,6 +428,7 @@
266 [(module) = "framework"];
AccessibilityServiceReported accessibility_service_reported = 267 [(module) = "settings"];
DocsUIDragAndDropReported docs_ui_drag_and_drop_reported = 268 [(module) = "docsui"];
+ AppUsageEventOccurred app_usage_event_occurred = 269 [(module) = "framework"];
SdkExtensionStatus sdk_extension_status = 354;
// StatsdStats tracks platform atoms with ids upto 500.
@@ -3021,14 +3022,14 @@
optional string component_name = 11;
// (x, y) coordinate and the index information of the target on the container
- optional int32 grid_x = 12;
- optional int32 grid_y = 13;
- optional int32 page_id = 14;
+ optional int32 grid_x = 12 [default = -1];
+ optional int32 grid_y = 13 [default = -1];
+ optional int32 page_id = 14 [default = -2];
// e.g., folder icon's (x, y) location and index information on the workspace
- optional int32 grid_x_parent = 15;
- optional int32 grid_y_parent = 16;
- optional int32 page_id_parent = 17;
+ optional int32 grid_x_parent = 15 [default = -1];
+ optional int32 grid_y_parent = 16 [default = -1];
+ optional int32 page_id_parent = 17 [default = -2];
// e.g., SEARCHBOX_ALLAPPS, FOLDER_WORKSPACE
optional int32 hierarchy = 18;
@@ -3036,7 +3037,7 @@
optional bool is_work_profile = 19;
// Used to store the predicted rank of the target
- optional int32 rank = 20;
+ optional int32 rank = 20 [default = -1];
// e.g., folderLabelState can be captured in the following two fields
optional int32 from_state = 21;
@@ -3044,6 +3045,9 @@
// e.g., autofilled or suggested texts that are not user entered
optional string edittext = 23;
+
+ // e.g., number of contents inside a container (e.g., icons inside a folder)
+ optional int32 cardinality = 24;
}
/**
@@ -3064,22 +3068,34 @@
optional string component_name = 6;
// (x, y) coordinate and the index information of the target on the container
- optional int32 grid_x = 7;
- optional int32 grid_y = 8;
- optional int32 page_id = 9;
+ optional int32 grid_x = 7 [default = -1];
+ optional int32 grid_y = 8 [default = -1];
+ optional int32 page_id = 9 [default = -2];
// e.g., folder icon's (x, y) location and index information on the workspace
- optional int32 grid_x_parent = 10;
- optional int32 grid_y_parent = 11;
- optional int32 page_id_parent = 12;
+ // e.g., when used with widgets target, use these values for (span_x, span_y)
+ optional int32 grid_x_parent = 10 [default = -1];
+ optional int32 grid_y_parent = 11 [default = -1];
+ optional int32 page_id_parent = 12 [default = -2];
- // e.g., WORKSPACE, HOTSEAT, FOLDER_WORKSPACE, FOLDER_HOTSEAT
+ // UNKNOWN = 0
+ // HOTSEAT = 1
+ // WORKSPACE = 2
+ // FOLDER_HOTSEAT = 3
+ // FOLDER_WORKSPACE = 4
optional int32 hierarchy = 13;
optional bool is_work_profile = 14;
// e.g., PIN, WIDGET TRAY, APPS TRAY, PREDICTION
optional int32 origin = 15;
+
+ // e.g., number of icons inside a folder
+ optional int32 cardinality = 16;
+
+ // e.g., (x, y) span of the widget inside homescreen grid system
+ optional int32 span_x = 17 [default = 1];
+ optional int32 span_y = 18 [default = 1];
}
/**
@@ -9513,3 +9529,19 @@
// From frameworks/base/core/proto/android/stats/accessibility/accessibility_enums.proto.
optional android.stats.accessibility.ServiceStatus service_status = 2;
}
+
+/**
+ * Logs app usage events.
+ */
+message AppUsageEventOccurred {
+ optional int32 uid = 1 [(is_uid) = true];
+ optional string package_name = 2;
+ optional string class_name = 3;
+
+ enum EventType {
+ NONE = 0;
+ MOVE_TO_FOREGROUND = 1;
+ MOVE_TO_BACKGROUND = 2;
+ }
+ optional EventType event_type = 4;
+}
diff --git a/cmds/statsd/src/external/StatsCallbackPuller.cpp b/cmds/statsd/src/external/StatsCallbackPuller.cpp
index 3618bb0..78e6f09 100644
--- a/cmds/statsd/src/external/StatsCallbackPuller.cpp
+++ b/cmds/statsd/src/external/StatsCallbackPuller.cpp
@@ -86,6 +86,7 @@
// in unit tests. In process calls are not oneway.
Status status = mCallback->onPullAtom(mTagId, resultReceiver);
if (!status.isOk()) {
+ StatsdStats::getInstance().notePullBinderCallFailed(mTagId);
return false;
}
diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp
index 5192ddf..829a603 100644
--- a/cmds/statsd/src/external/StatsPuller.cpp
+++ b/cmds/statsd/src/external/StatsPuller.cpp
@@ -82,6 +82,11 @@
mapAndMergeIsolatedUidsToHostUid(mCachedData, mUidMap, mTagId, mAdditiveFields);
}
+ if (mCachedData.empty()) {
+ VLOG("Data pulled is empty");
+ StatsdStats::getInstance().noteEmptyData(mTagId);
+ }
+
(*data) = mCachedData;
return mHasGoodData;
}
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index cfd5d14..1a52eb9 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -111,12 +111,14 @@
if (uidProviderIt == mPullUidProviders.end()) {
ALOGE("Error pulling tag %d. No pull uid provider for config key %s", tagId,
configKey.ToString().c_str());
+ StatsdStats::getInstance().notePullUidProviderNotFound(tagId);
return false;
}
sp<PullUidProvider> pullUidProvider = uidProviderIt->second.promote();
if (pullUidProvider == nullptr) {
ALOGE("Error pulling tag %d, pull uid provider for config %s is gone.", tagId,
configKey.ToString().c_str());
+ StatsdStats::getInstance().notePullUidProviderNotFound(tagId);
return false;
}
uids = pullUidProvider->getPullAtomUids(tagId);
@@ -140,6 +142,7 @@
return ret;
}
}
+ StatsdStats::getInstance().notePullerNotFound(tagId);
ALOGW("StatsPullerManager: Unknown tagId %d", tagId);
return false; // Return early since we don't know what to pull.
} else {
@@ -288,10 +291,7 @@
for (const auto& pullInfo : needToPull) {
vector<shared_ptr<LogEvent>> data;
bool pullSuccess = PullLocked(pullInfo.first->atomTag, pullInfo.first->configKey, &data);
- if (pullSuccess) {
- StatsdStats::getInstance().notePullDelay(pullInfo.first->atomTag,
- getElapsedRealtimeNs() - elapsedTimeNs);
- } else {
+ if (!pullSuccess) {
VLOG("pull failed at %lld, will try again later", (long long)elapsedTimeNs);
}
@@ -354,6 +354,11 @@
std::lock_guard<std::mutex> _l(mLock);
VLOG("RegisterPullerCallback: adding puller for tag %d", atomTag);
+ if (callback == nullptr) {
+ ALOGW("SetPullAtomCallback called with null callback for atom %d.", atomTag);
+ return;
+ }
+
StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag, /*registered=*/true);
int64_t actualCoolDownNs = coolDownNs < kMinCoolDownNs ? kMinCoolDownNs : coolDownNs;
int64_t actualTimeoutNs = timeoutNs > kMaxTimeoutNs ? kMaxTimeoutNs : timeoutNs;
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index 46f5dbd..c027fff 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -472,14 +472,19 @@
mPulledAtomStats[atomId].pullFailed++;
}
-void StatsdStats::noteStatsCompanionPullFailed(int atomId) {
+void StatsdStats::notePullUidProviderNotFound(int atomId) {
lock_guard<std::mutex> lock(mLock);
- mPulledAtomStats[atomId].statsCompanionPullFailed++;
+ mPulledAtomStats[atomId].pullUidProviderNotFound++;
}
-void StatsdStats::noteStatsCompanionPullBinderTransactionFailed(int atomId) {
+void StatsdStats::notePullerNotFound(int atomId) {
lock_guard<std::mutex> lock(mLock);
- mPulledAtomStats[atomId].statsCompanionPullBinderTransactionFailed++;
+ mPulledAtomStats[atomId].pullerNotFound++;
+}
+
+void StatsdStats::notePullBinderCallFailed(int atomId) {
+ lock_guard<std::mutex> lock(mLock);
+ mPulledAtomStats[atomId].binderCallFailCount++;
}
void StatsdStats::noteEmptyData(int atomId) {
@@ -608,6 +613,7 @@
for (auto& pullStats : mPulledAtomStats) {
pullStats.second.totalPull = 0;
pullStats.second.totalPullFromCache = 0;
+ pullStats.second.minPullIntervalSec = LONG_MAX;
pullStats.second.avgPullTimeNs = 0;
pullStats.second.maxPullTimeNs = 0;
pullStats.second.numPullTime = 0;
@@ -617,9 +623,13 @@
pullStats.second.dataError = 0;
pullStats.second.pullTimeout = 0;
pullStats.second.pullExceedMaxDelay = 0;
+ pullStats.second.pullFailed = 0;
+ pullStats.second.pullUidProviderNotFound = 0;
+ pullStats.second.pullerNotFound = 0;
pullStats.second.registeredCount = 0;
pullStats.second.unregisteredCount = 0;
pullStats.second.atomErrorCount = 0;
+ pullStats.second.binderCallFailCount = 0;
}
mAtomMetricStats.clear();
mActivationBroadcastGuardrailStats.clear();
@@ -764,14 +774,16 @@
" (average pull time nanos)%lld, (max pull time nanos)%lld, (average pull delay "
"nanos)%lld, "
" (max pull delay nanos)%lld, (data error)%ld\n"
- " (pull timeout)%ld, (pull exceed max delay)%ld\n"
- " (registered count) %ld, (unregistered count) %ld\n"
+ " (pull timeout)%ld, (pull exceed max delay)%ld"
+ " (no uid provider count)%ld, (no puller found count)%ld\n"
+ " (registered count) %ld, (unregistered count) %ld"
" (atom error count) %d\n",
(int)pair.first, (long)pair.second.totalPull, (long)pair.second.totalPullFromCache,
(long)pair.second.pullFailed, (long)pair.second.minPullIntervalSec,
(long long)pair.second.avgPullTimeNs, (long long)pair.second.maxPullTimeNs,
(long long)pair.second.avgPullDelayNs, (long long)pair.second.maxPullDelayNs,
pair.second.dataError, pair.second.pullTimeout, pair.second.pullExceedMaxDelay,
+ pair.second.pullUidProviderNotFound, pair.second.pullerNotFound,
pair.second.registeredCount, pair.second.unregisteredCount,
pair.second.atomErrorCount);
}
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 805281c..3d0eeb8 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -371,21 +371,30 @@
int32_t lastAtomTag, int32_t uid, int32_t pid);
/**
- * Records that the pull of an atom has failed
+ * Records that the pull of an atom has failed. Eg, if the client indicated the pull failed, if
+ * the pull timed out, or if the outgoing binder call failed.
+ * This count will only increment if the puller was actually invoked.
+ *
+ * It does not include a pull not occurring due to not finding the appropriate
+ * puller. These cases are covered in other counts.
*/
void notePullFailed(int atomId);
/**
- * Records that the pull of StatsCompanionService atom has failed
+ * Records that the pull of an atom has failed due to not having a uid provider.
*/
- void noteStatsCompanionPullFailed(int atomId);
+ void notePullUidProviderNotFound(int atomId);
/**
- * Records that the pull of a StatsCompanionService atom has failed due to a failed binder
- * transaction. This can happen when StatsCompanionService returns too
- * much data (the max Binder parcel size is 1MB)
+ * Records that the pull of an atom has failed due not finding a puller registered by a
+ * trusted uid.
*/
- void noteStatsCompanionPullBinderTransactionFailed(int atomId);
+ void notePullerNotFound(int atomId);
+
+ /**
+ * Records that the pull has failed due to the outgoing binder call failing.
+ */
+ void notePullBinderCallFailed(int atomId);
/**
* A pull with no data occurred
@@ -503,12 +512,13 @@
long pullTimeout = 0;
long pullExceedMaxDelay = 0;
long pullFailed = 0;
- long statsCompanionPullFailed = 0;
- long statsCompanionPullBinderTransactionFailed = 0;
+ long pullUidProviderNotFound = 0;
+ long pullerNotFound = 0;
long emptyData = 0;
long registeredCount = 0;
long unregisteredCount = 0;
int32_t atomErrorCount = 0;
+ long binderCallFailCount = 0;
} PulledAtomStats;
typedef struct {
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index c4bd054..cc4c565 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -326,13 +326,12 @@
return;
}
const int64_t pullDelayNs = getElapsedRealtimeNs() - timestampNs;
+ StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
if (pullDelayNs > mMaxPullDelayNs) {
ALOGE("Pull finish too late for atom %d", mPullTagId);
StatsdStats::getInstance().notePullExceedMaxDelay(mPullTagId);
- StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
return;
}
- StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
for (const auto& data : allData) {
LogEvent localCopy = data->makeCopy();
localCopy.setElapsedTimestampNs(timestampNs);
@@ -415,6 +414,13 @@
if (!pullSuccess || allData.size() == 0) {
return;
}
+ const int64_t pullDelayNs = getElapsedRealtimeNs() - originalPullTimeNs;
+ StatsdStats::getInstance().notePullDelay(mPullTagId, pullDelayNs);
+ if (pullDelayNs > mMaxPullDelayNs) {
+ ALOGE("Pull finish too late for atom %d", mPullTagId);
+ StatsdStats::getInstance().notePullExceedMaxDelay(mPullTagId);
+ return;
+ }
for (const auto& data : allData) {
if (mEventMatcherWizard->matchLogEvent(
*data, mWhatMatcherIndex) == MatchingState::kMatched) {
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index f34423a..e5ec72e 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -584,11 +584,6 @@
return;
}
- if (allData.size() == 0) {
- VLOG("Data pulled is empty");
- StatsdStats::getInstance().noteEmptyData(mPullTagId);
- }
-
mMatchedMetricDimensionKeys.clear();
for (const auto& data : allData) {
LogEvent localCopy = data->makeCopy();
diff --git a/cmds/statsd/src/shell/ShellSubscriber.cpp b/cmds/statsd/src/shell/ShellSubscriber.cpp
index bed836a..7b68721 100644
--- a/cmds/statsd/src/shell/ShellSubscriber.cpp
+++ b/cmds/statsd/src/shell/ShellSubscriber.cpp
@@ -19,6 +19,7 @@
#include "ShellSubscriber.h"
#include <android-base/file.h>
+
#include "matchers/matcher_util.h"
#include "stats_log_util.h"
@@ -32,41 +33,52 @@
void ShellSubscriber::startNewSubscription(int in, int out, int timeoutSec) {
int myToken = claimToken();
+ VLOG("ShellSubscriber: new subscription %d has come in", myToken);
mSubscriptionShouldEnd.notify_one();
shared_ptr<SubscriptionInfo> mySubscriptionInfo = make_shared<SubscriptionInfo>(in, out);
- if (!readConfig(mySubscriptionInfo)) {
- return;
- }
+ if (!readConfig(mySubscriptionInfo)) return;
- // critical-section
- std::unique_lock<std::mutex> lock(mMutex);
- if (myToken != mToken) {
- // Some other subscription has already come in. Stop.
- return;
- }
- mSubscriptionInfo = mySubscriptionInfo;
+ {
+ std::unique_lock<std::mutex> lock(mMutex);
+ if (myToken != mToken) {
+ // Some other subscription has already come in. Stop.
+ return;
+ }
+ mSubscriptionInfo = mySubscriptionInfo;
- if (mySubscriptionInfo->mPulledInfo.size() > 0 && mySubscriptionInfo->mPullIntervalMin > 0) {
- // This thread terminates after it detects that mToken has changed.
+ spawnHelperThreadsLocked(mySubscriptionInfo, myToken);
+ waitForSubscriptionToEndLocked(mySubscriptionInfo, myToken, lock, timeoutSec);
+
+ if (mSubscriptionInfo == mySubscriptionInfo) {
+ mSubscriptionInfo = nullptr;
+ }
+
+ }
+}
+
+void ShellSubscriber::spawnHelperThreadsLocked(shared_ptr<SubscriptionInfo> myInfo, int myToken) {
+ if (!myInfo->mPulledInfo.empty() && myInfo->mPullIntervalMin > 0) {
std::thread puller([this, myToken] { startPull(myToken); });
puller.detach();
}
- // Block until subscription has ended.
- if (timeoutSec > 0) {
- mSubscriptionShouldEnd.wait_for(
- lock, timeoutSec * 1s, [this, myToken, &mySubscriptionInfo] {
- return mToken != myToken || !mySubscriptionInfo->mClientAlive;
- });
- } else {
- mSubscriptionShouldEnd.wait(lock, [this, myToken, &mySubscriptionInfo] {
- return mToken != myToken || !mySubscriptionInfo->mClientAlive;
- });
- }
+ std::thread heartbeatSender([this, myToken] { sendHeartbeats(myToken); });
+ heartbeatSender.detach();
+}
- if (mSubscriptionInfo == mySubscriptionInfo) {
- mSubscriptionInfo = nullptr;
+void ShellSubscriber::waitForSubscriptionToEndLocked(shared_ptr<SubscriptionInfo> myInfo,
+ int myToken,
+ std::unique_lock<std::mutex>& lock,
+ int timeoutSec) {
+ if (timeoutSec > 0) {
+ mSubscriptionShouldEnd.wait_for(lock, timeoutSec * 1s, [this, myToken, &myInfo] {
+ return mToken != myToken || !myInfo->mClientAlive;
+ });
+ } else {
+ mSubscriptionShouldEnd.wait(lock, [this, myToken, &myInfo] {
+ return mToken != myToken || !myInfo->mClientAlive;
+ });
}
}
@@ -129,51 +141,55 @@
return true;
}
-void ShellSubscriber::startPull(int64_t myToken) {
+void ShellSubscriber::startPull(int myToken) {
+ VLOG("ShellSubscriber: pull thread %d starting", myToken);
while (true) {
- std::lock_guard<std::mutex> lock(mMutex);
- if (!mSubscriptionInfo || mToken != myToken) {
- VLOG("Pulling thread %lld done!", (long long)myToken);
- return;
- }
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (!mSubscriptionInfo || mToken != myToken) {
+ VLOG("ShellSubscriber: pulling thread %d done!", myToken);
+ return;
+ }
- int64_t nowMillis = getElapsedRealtimeMillis();
- for (auto& pullInfo : mSubscriptionInfo->mPulledInfo) {
- if (pullInfo.mPrevPullElapsedRealtimeMs + pullInfo.mInterval < nowMillis) {
- vector<std::shared_ptr<LogEvent>> data;
+ int64_t nowMillis = getElapsedRealtimeMillis();
+ for (PullInfo& pullInfo : mSubscriptionInfo->mPulledInfo) {
+ if (pullInfo.mPrevPullElapsedRealtimeMs + pullInfo.mInterval >= nowMillis) {
+ continue;
+ }
+
vector<int32_t> uids;
- uids.insert(uids.end(), pullInfo.mPullUids.begin(), pullInfo.mPullUids.end());
- // This is slow. Consider storing the uids per app and listening to uidmap updates.
- for (const string& pkg : pullInfo.mPullPackages) {
- set<int32_t> uidsForPkg = mUidMap->getAppUid(pkg);
- uids.insert(uids.end(), uidsForPkg.begin(), uidsForPkg.end());
- }
- uids.push_back(DEFAULT_PULL_UID);
- mPullerMgr->Pull(pullInfo.mPullerMatcher.atom_id(), uids, &data);
- VLOG("pulled %zu atoms with id %d", data.size(), pullInfo.mPullerMatcher.atom_id());
+ getUidsForPullAtom(&uids, pullInfo);
- if (!writePulledAtomsLocked(data, pullInfo.mPullerMatcher)) {
- mSubscriptionInfo->mClientAlive = false;
- mSubscriptionShouldEnd.notify_one();
- return;
- }
+ vector<std::shared_ptr<LogEvent>> data;
+ mPullerMgr->Pull(pullInfo.mPullerMatcher.atom_id(), uids, &data);
+ VLOG("Pulled %zu atoms with id %d", data.size(), pullInfo.mPullerMatcher.atom_id());
+ writePulledAtomsLocked(data, pullInfo.mPullerMatcher);
+
pullInfo.mPrevPullElapsedRealtimeMs = nowMillis;
}
}
- VLOG("Pulling thread %lld sleep....", (long long)myToken);
+ VLOG("ShellSubscriber: pulling thread %d sleeping for %d ms", myToken,
+ mSubscriptionInfo->mPullIntervalMin);
std::this_thread::sleep_for(std::chrono::milliseconds(mSubscriptionInfo->mPullIntervalMin));
}
}
-// \return boolean indicating if writes were successful (will return false if
-// client dies)
-bool ShellSubscriber::writePulledAtomsLocked(const vector<std::shared_ptr<LogEvent>>& data,
+void ShellSubscriber::getUidsForPullAtom(vector<int32_t>* uids, const PullInfo& pullInfo) {
+ uids->insert(uids->end(), pullInfo.mPullUids.begin(), pullInfo.mPullUids.end());
+ // This is slow. Consider storing the uids per app and listening to uidmap updates.
+ for (const string& pkg : pullInfo.mPullPackages) {
+ set<int32_t> uidsForPkg = mUidMap->getAppUid(pkg);
+ uids->insert(uids->end(), uidsForPkg.begin(), uidsForPkg.end());
+ }
+ uids->push_back(DEFAULT_PULL_UID);
+}
+
+void ShellSubscriber::writePulledAtomsLocked(const vector<std::shared_ptr<LogEvent>>& data,
const SimpleAtomMatcher& matcher) {
mProto.clear();
int count = 0;
for (const auto& event : data) {
- VLOG("%s", event->ToString().c_str());
if (matchesSimple(*mUidMap, matcher, *event)) {
count++;
uint64_t atomToken = mProto.start(util::FIELD_TYPE_MESSAGE |
@@ -183,55 +199,67 @@
}
}
- if (count > 0) {
- // First write the payload size.
- size_t bufferSize = mProto.size();
- if (!android::base::WriteFully(mSubscriptionInfo->mOutputFd, &bufferSize,
- sizeof(bufferSize))) {
- return false;
- }
-
- VLOG("%d atoms, proto size: %zu", count, bufferSize);
- // Then write the payload.
- if (!mProto.flush(mSubscriptionInfo->mOutputFd)) {
- return false;
- }
- }
-
- return true;
+ if (count > 0) attemptWriteToSocketLocked(mProto.size());
}
void ShellSubscriber::onLogEvent(const LogEvent& event) {
std::lock_guard<std::mutex> lock(mMutex);
- if (!mSubscriptionInfo) {
- return;
- }
+ if (!mSubscriptionInfo) return;
mProto.clear();
for (const auto& matcher : mSubscriptionInfo->mPushedMatchers) {
if (matchesSimple(*mUidMap, matcher, event)) {
- VLOG("%s", event.ToString().c_str());
uint64_t atomToken = mProto.start(util::FIELD_TYPE_MESSAGE |
util::FIELD_COUNT_REPEATED | FIELD_ID_ATOM);
event.ToProto(mProto);
mProto.end(atomToken);
+ attemptWriteToSocketLocked(mProto.size());
+ }
+ }
+}
- // First write the payload size.
- size_t bufferSize = mProto.size();
- if (!android::base::WriteFully(mSubscriptionInfo->mOutputFd, &bufferSize,
- sizeof(bufferSize))) {
- mSubscriptionInfo->mClientAlive = false;
- mSubscriptionShouldEnd.notify_one();
+// Tries to write the atom encoded in mProto to the socket. If the write fails
+// because the read end of the pipe has closed, signals to other threads that
+// the subscription should end.
+void ShellSubscriber::attemptWriteToSocketLocked(size_t dataSize) {
+ // First write the payload size.
+ if (!android::base::WriteFully(mSubscriptionInfo->mOutputFd, &dataSize, sizeof(dataSize))) {
+ mSubscriptionInfo->mClientAlive = false;
+ mSubscriptionShouldEnd.notify_one();
+ return;
+ }
+
+ if (dataSize == 0) return;
+
+ // Then, write the payload.
+ if (!mProto.flush(mSubscriptionInfo->mOutputFd)) {
+ mSubscriptionInfo->mClientAlive = false;
+ mSubscriptionShouldEnd.notify_one();
+ return;
+ }
+
+ mLastWriteMs = getElapsedRealtimeMillis();
+}
+
+// Send a heartbeat, consisting solely of a data size of 0, if perfd has not
+// recently received any writes from statsd. When it receives the data size of
+// 0, perfd will not expect any data and recheck whether the shell command is
+// still running.
+void ShellSubscriber::sendHeartbeats(int myToken) {
+ while (true) {
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (!mSubscriptionInfo || myToken != mToken) {
+ VLOG("ShellSubscriber: heartbeat thread %d done!", myToken);
return;
}
- // Then write the payload.
- if (!mProto.flush(mSubscriptionInfo->mOutputFd)) {
- mSubscriptionInfo->mClientAlive = false;
- mSubscriptionShouldEnd.notify_one();
- return;
+ if (getElapsedRealtimeMillis() - mLastWriteMs > kMsBetweenHeartbeats) {
+ VLOG("ShellSubscriber: sending a heartbeat to perfd");
+ attemptWriteToSocketLocked(/*dataSize=*/0);
}
}
+ std::this_thread::sleep_for(std::chrono::milliseconds(kMsBetweenHeartbeats));
}
}
diff --git a/cmds/statsd/src/shell/ShellSubscriber.h b/cmds/statsd/src/shell/ShellSubscriber.h
index 61457d8..26c8a2a 100644
--- a/cmds/statsd/src/shell/ShellSubscriber.h
+++ b/cmds/statsd/src/shell/ShellSubscriber.h
@@ -38,11 +38,11 @@
*
* A shell subscription lasts *until shell exits*. Unlike config based clients, a shell client
* communicates with statsd via file descriptors. They can subscribe pushed and pulled atoms.
- * The atoms are sent back to the client in real time, as opposed to
- * keeping the data in memory. Shell clients do not subscribe aggregated metrics, as they are
- * responsible for doing the aggregation after receiving the atom events.
+ * The atoms are sent back to the client in real time, as opposed to keeping the data in memory.
+ * Shell clients do not subscribe aggregated metrics, as they are responsible for doing the
+ * aggregation after receiving the atom events.
*
- * Shell client pass ShellSubscription in the proto binary format. Client can update the
+ * Shell clients pass ShellSubscription in the proto binary format. Clients can update the
* subscription by sending a new subscription. The new subscription would replace the old one.
* Input data stream format is:
*
@@ -54,7 +54,7 @@
* The stream would be in the following format:
* |size_t|shellData proto|size_t|shellData proto|....
*
- * Only one shell subscriber allowed at a time, because each shell subscriber blocks one thread
+ * Only one shell subscriber is allowed at a time because each shell subscriber blocks one thread
* until it exits.
*/
class ShellSubscriber : public virtual RefBase {
@@ -100,11 +100,28 @@
bool readConfig(std::shared_ptr<SubscriptionInfo> subscriptionInfo);
- void startPull(int64_t myToken);
+ void spawnHelperThreadsLocked(std::shared_ptr<SubscriptionInfo> myInfo, int myToken);
- bool writePulledAtomsLocked(const vector<std::shared_ptr<LogEvent>>& data,
+ void waitForSubscriptionToEndLocked(std::shared_ptr<SubscriptionInfo> myInfo,
+ int myToken,
+ std::unique_lock<std::mutex>& lock,
+ int timeoutSec);
+
+ void startPull(int myToken);
+
+ void writePulledAtomsLocked(const vector<std::shared_ptr<LogEvent>>& data,
const SimpleAtomMatcher& matcher);
+ void getUidsForPullAtom(vector<int32_t>* uids, const PullInfo& pullInfo);
+
+ void attemptWriteToSocketLocked(size_t dataSize);
+
+ // Send ocassional heartbeats for two reasons: (a) for statsd to detect when
+ // the read end of the pipe has closed and (b) for perfd to escape a
+ // blocking read call and recheck if the user has terminated the
+ // subscription.
+ void sendHeartbeats(int myToken);
+
sp<UidMap> mUidMap;
sp<StatsPullerManager> mPullerMgr;
@@ -120,6 +137,11 @@
int mToken = 0;
const int32_t DEFAULT_PULL_UID = AID_SYSTEM;
+
+ // Tracks when we last send data to perfd. We need that time to determine
+ // when next to send a heartbeat.
+ int64_t mLastWriteMs = 0;
+ const int64_t kMsBetweenHeartbeats = 1000;
};
} // namespace statsd
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 868247b..1121392 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -456,12 +456,15 @@
optional int64 pull_timeout = 10;
optional int64 pull_exceed_max_delay = 11;
optional int64 pull_failed = 12;
- optional int64 stats_companion_pull_failed = 13;
- optional int64 stats_companion_pull_binder_transaction_failed = 14;
+ optional int64 stats_companion_pull_failed = 13 [deprecated = true];
+ optional int64 stats_companion_pull_binder_transaction_failed = 14 [deprecated = true];
optional int64 empty_data = 15;
optional int64 registered_count = 16;
optional int64 unregistered_count = 17;
optional int32 atom_error_count = 18;
+ optional int64 binder_call_failed = 19;
+ optional int64 failed_uid_provider_not_found = 20;
+ optional int64 puller_not_found = 21;
}
repeated PulledAtomStats pulled_atom_stats = 10;
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index 2acffee..bafdfcb 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -74,12 +74,13 @@
const int FIELD_ID_PULL_TIMEOUT = 10;
const int FIELD_ID_PULL_EXCEED_MAX_DELAY = 11;
const int FIELD_ID_PULL_FAILED = 12;
-const int FIELD_ID_STATS_COMPANION_FAILED = 13;
-const int FIELD_ID_STATS_COMPANION_BINDER_TRANSACTION_FAILED = 14;
const int FIELD_ID_EMPTY_DATA = 15;
const int FIELD_ID_PULL_REGISTERED_COUNT = 16;
const int FIELD_ID_PULL_UNREGISTERED_COUNT = 17;
const int FIELD_ID_ATOM_ERROR_COUNT = 18;
+const int FIELD_ID_BINDER_CALL_FAIL_COUNT = 19;
+const int FIELD_ID_PULL_UID_PROVIDER_NOT_FOUND = 20;
+const int FIELD_ID_PULLER_NOT_FOUND = 21;
// for AtomMetricStats proto
const int FIELD_ID_ATOM_METRIC_STATS = 17;
@@ -483,10 +484,6 @@
(long long)pair.second.pullExceedMaxDelay);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_FAILED,
(long long)pair.second.pullFailed);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_STATS_COMPANION_FAILED,
- (long long)pair.second.statsCompanionPullFailed);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_STATS_COMPANION_BINDER_TRANSACTION_FAILED,
- (long long)pair.second.statsCompanionPullBinderTransactionFailed);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_EMPTY_DATA,
(long long)pair.second.emptyData);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_REGISTERED_COUNT,
@@ -494,6 +491,12 @@
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_UNREGISTERED_COUNT,
(long long) pair.second.unregisteredCount);
protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_ERROR_COUNT, pair.second.atomErrorCount);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BINDER_CALL_FAIL_COUNT,
+ (long long)pair.second.binderCallFailCount);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_UID_PROVIDER_NOT_FOUND,
+ (long long)pair.second.pullUidProviderNotFound);
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULLER_NOT_FOUND,
+ (long long)pair.second.pullerNotFound);
protoOutput->end(token);
}
diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
index cdde603..948d587 100644
--- a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
+++ b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
@@ -302,7 +302,10 @@
stats.notePullerCallbackRegistrationChanged(util::DISK_SPACE, true);
stats.notePullerCallbackRegistrationChanged(util::DISK_SPACE, false);
stats.notePullerCallbackRegistrationChanged(util::DISK_SPACE, true);
-
+ stats.notePullBinderCallFailed(util::DISK_SPACE);
+ stats.notePullUidProviderNotFound(util::DISK_SPACE);
+ stats.notePullerNotFound(util::DISK_SPACE);
+ stats.notePullerNotFound(util::DISK_SPACE);
vector<uint8_t> output;
stats.dumpStats(&output, false);
@@ -322,6 +325,9 @@
EXPECT_EQ(3335L, report.pulled_atom_stats(0).max_pull_delay_nanos());
EXPECT_EQ(2L, report.pulled_atom_stats(0).registered_count());
EXPECT_EQ(1L, report.pulled_atom_stats(0).unregistered_count());
+ EXPECT_EQ(1L, report.pulled_atom_stats(0).binder_call_failed());
+ EXPECT_EQ(1L, report.pulled_atom_stats(0).failed_uid_provider_not_found());
+ EXPECT_EQ(2L, report.pulled_atom_stats(0).puller_not_found());
}
TEST(StatsdStatsTest, TestAtomMetricsStats) {
diff --git a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
index 7b952d7..363fcb4 100644
--- a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
+++ b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
@@ -86,28 +86,34 @@
// wait for the data to be written.
std::this_thread::sleep_for(100ms);
- int expected_data_size = expectedData.ByteSize();
+ // Because we might receive heartbeats from statsd, consisting of data sizes
+ // of 0, encapsulate reads within a while loop.
+ bool readAtom = false;
+ while (!readAtom) {
+ // Read the atom size.
+ size_t dataSize = 0;
+ read(fds_data[0], &dataSize, sizeof(dataSize));
+ if (dataSize == 0) continue;
+ EXPECT_EQ(expectedData.ByteSize(), int(dataSize));
- // now read from the pipe. firstly read the atom size.
- size_t dataSize = 0;
- EXPECT_EQ((int)sizeof(dataSize), read(fds_data[0], &dataSize, sizeof(dataSize)));
+ // Read that much data in proto binary format.
+ vector<uint8_t> dataBuffer(dataSize);
+ EXPECT_EQ((int)dataSize, read(fds_data[0], dataBuffer.data(), dataSize));
- EXPECT_EQ(expected_data_size, (int)dataSize);
+ // Make sure the received bytes can be parsed to an atom.
+ ShellData receivedAtom;
+ EXPECT_TRUE(receivedAtom.ParseFromArray(dataBuffer.data(), dataSize) != 0);
- // then read that much data which is the atom in proto binary format
- vector<uint8_t> dataBuffer(dataSize);
- EXPECT_EQ((int)dataSize, read(fds_data[0], dataBuffer.data(), dataSize));
+ // Serialize the expected atom to byte array and compare to make sure
+ // they are the same.
+ vector<uint8_t> expectedAtomBuffer(expectedData.ByteSize());
+ expectedData.SerializeToArray(expectedAtomBuffer.data(), expectedData.ByteSize());
+ EXPECT_EQ(expectedAtomBuffer, dataBuffer);
- // make sure the received bytes can be parsed to an atom
- ShellData receivedAtom;
- EXPECT_TRUE(receivedAtom.ParseFromArray(dataBuffer.data(), dataSize) != 0);
+ readAtom = true;
+ }
- // serialze the expected atom to bytes. and compare. to make sure they are the same.
- vector<uint8_t> atomBuffer(expected_data_size);
- expectedData.SerializeToArray(&atomBuffer[0], expected_data_size);
- EXPECT_EQ(atomBuffer, dataBuffer);
close(fds_data[0]);
-
if (reader.joinable()) {
reader.join();
}
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 9372c95..cb29431 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -396,8 +396,13 @@
}
}
- pkg.setVolumeUuid(volumeUuid)
- .setSigningDetails(SigningDetails.UNKNOWN);
+ pkg.setVolumeUuid(volumeUuid);
+
+ if ((flags & PackageParser.PARSE_COLLECT_CERTIFICATES) != 0) {
+ pkg.setSigningDetails(getSigningDetails(pkg, false));
+ } else {
+ pkg.setSigningDetails(SigningDetails.UNKNOWN);
+ }
return input.success(pkg);
} catch (Exception e) {
@@ -449,7 +454,7 @@
*/
private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, String apkPath,
String codePath, Resources res, XmlResourceParser parser, int flags)
- throws XmlPullParserException, IOException {
+ throws XmlPullParserException, IOException, PackageParserException {
final String splitName;
final String pkgName;
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 187274a..7912dac 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -635,10 +635,11 @@
/**
* Specifies if a user is disallowed from adding new users. This can only be set by device
- * owners, profile owners on the primary user or profile owners of organization-owned managed
- * profiles on the parent profile. The default value is <code>false</code>.
+ * owners or profile owners on the primary user. The default value is <code>false</code>.
* <p>This restriction has no effect on secondary users and managed profiles since only the
* primary user can add other users.
+ * <p> When the device is an organization-owned device provisioned with a managed profile,
+ * this restriction will be set as a base restriction which cannot be removed by any admin.
*
* <p>Key for user restrictions.
* <p>Type: Boolean
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index ed429dd..06caa03 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -56,10 +56,15 @@
import com.android.internal.infra.AndroidFuture;
import com.android.internal.infra.RemoteStream;
import com.android.internal.infra.ServiceConnector;
+import com.android.internal.os.TransferPipe;
import com.android.internal.util.CollectionUtils;
import libcore.util.EmptyArray;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -67,7 +72,9 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
/**
@@ -476,6 +483,36 @@
}
/**
+ * Dump permission controller state.
+ *
+ * @hide
+ */
+ public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @Nullable String[] args) {
+ CompletableFuture<Throwable> dumpResult = new CompletableFuture<>();
+ mRemoteService.postForResult(
+ service -> TransferPipe.dumpAsync(service.asBinder(), args))
+ .whenComplete(
+ (dump, err) -> {
+ try (FileOutputStream out = new FileOutputStream(fd)) {
+ out.write(dump);
+ } catch (IOException | NullPointerException e) {
+ Log.e(TAG, "Could for forwards permission controller dump", e);
+ }
+
+ dumpResult.complete(err);
+ });
+
+ try {
+ Throwable err = dumpResult.get(UNBIND_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ if (err != null) {
+ throw err;
+ }
+ } catch (Throwable e) {
+ Log.e(TAG, "Could not dump permission controller state", e);
+ }
+ }
+
+ /**
* Gets the runtime permissions for an app.
*
* @param packageName The package for which to query.
diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java
index 82a7d78..c6ede32 100644
--- a/core/java/android/permission/PermissionControllerService.java
+++ b/core/java/android/permission/PermissionControllerService.java
@@ -50,9 +50,11 @@
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.Preconditions;
+import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -494,6 +496,11 @@
"packageName cannot be null");
onOneTimePermissionSessionTimeout(packageName);
}
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ PermissionControllerService.this.dump(fd, writer, args);
+ }
};
}
}
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 2e00c0c..327bca2 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -1274,8 +1274,6 @@
out.putParcelable(DocumentsContract.EXTRA_RESULT, path);
} else if (METHOD_GET_DOCUMENT_METADATA.equals(method)) {
- enforceReadPermissionInner(documentUri, getCallingPackage(),
- getCallingAttributionTag(), null);
return getDocumentMetadata(documentId);
} else {
throw new UnsupportedOperationException("Method not supported " + method);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index c0d0c21..ae88ba5 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11956,8 +11956,24 @@
"adaptive_battery_management_enabled";
/**
+ * Whether or not apps are allowed into the
+ * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket.
+ * Type: int (0 for false, 1 for true)
+ * Default: {@value #DEFAULT_ENABLE_RESTRICTED_BUCKET}
+ *
+ * @hide
+ */
+ public static final String ENABLE_RESTRICTED_BUCKET = "enable_restricted_bucket";
+
+ /**
+ * @see #ENABLE_RESTRICTED_BUCKET
+ * @hide
+ */
+ public static final int DEFAULT_ENABLE_RESTRICTED_BUCKET = 1;
+
+ /**
* Whether or not app auto restriction is enabled. When it is enabled, settings app will
- * auto restrict the app if it has bad behavior(e.g. hold wakelock for long time).
+ * auto restrict the app if it has bad behavior (e.g. hold wakelock for long time).
*
* Type: boolean (0 for false, 1 for true)
* Default: 1
diff --git a/core/java/android/service/autofill/IInlineSuggestionUi.aidl b/core/java/android/service/autofill/IInlineSuggestionUi.aidl
new file mode 100644
index 0000000..7289853
--- /dev/null
+++ b/core/java/android/service/autofill/IInlineSuggestionUi.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 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.service.autofill;
+
+import android.service.autofill.ISurfacePackageResultCallback;
+
+/**
+ * Interface to interact with a remote inline suggestion UI.
+ *
+ * @hide
+ */
+oneway interface IInlineSuggestionUi {
+ void getSurfacePackage(ISurfacePackageResultCallback callback);
+ void releaseSurfaceControlViewHost();
+}
diff --git a/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl b/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl
index 172cfef..97eb790 100644
--- a/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl
+++ b/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl
@@ -18,17 +18,19 @@
import android.content.IntentSender;
import android.os.IBinder;
+import android.service.autofill.IInlineSuggestionUi;
import android.view.SurfaceControlViewHost;
/**
- * Interface to receive events from inline suggestions.
+ * Interface to receive events from a remote inline suggestion UI.
*
* @hide
*/
oneway interface IInlineSuggestionUiCallback {
void onClick();
void onLongClick();
- void onContent(in SurfaceControlViewHost.SurfacePackage surface, int width, int height);
+ void onContent(in IInlineSuggestionUi content, in SurfaceControlViewHost.SurfacePackage surface,
+ int width, int height);
void onError();
void onTransferTouchFocusToImeWindow(in IBinder sourceInputToken, int displayId);
void onStartIntentSender(in IntentSender intentSender);
diff --git a/core/java/android/service/autofill/ISurfacePackageResultCallback.aidl b/core/java/android/service/autofill/ISurfacePackageResultCallback.aidl
new file mode 100644
index 0000000..0c2c624
--- /dev/null
+++ b/core/java/android/service/autofill/ISurfacePackageResultCallback.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 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.service.autofill;
+
+import android.view.SurfaceControlViewHost;
+
+/**
+ * Interface to receive a SurfaceControlViewHost.SurfacePackage.
+ *
+ * @hide
+ */
+oneway interface ISurfacePackageResultCallback {
+ void onResult(in SurfaceControlViewHost.SurfacePackage result);
+}
diff --git a/core/java/android/service/autofill/InlineSuggestionRenderService.java b/core/java/android/service/autofill/InlineSuggestionRenderService.java
index 6c22b19..3ea443b 100644
--- a/core/java/android/service/autofill/InlineSuggestionRenderService.java
+++ b/core/java/android/service/autofill/InlineSuggestionRenderService.java
@@ -33,6 +33,7 @@
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.util.Log;
+import android.util.LruCache;
import android.util.Size;
import android.view.Display;
import android.view.SurfaceControlViewHost;
@@ -40,6 +41,8 @@
import android.view.ViewGroup;
import android.view.WindowManager;
+import java.lang.ref.WeakReference;
+
/**
* A service that renders an inline presentation view given the {@link InlinePresentation}.
*
@@ -65,6 +68,27 @@
private IInlineSuggestionUiCallback mCallback;
+
+ /**
+ * A local LRU cache keeping references to the inflated {@link SurfaceControlViewHost}s, so
+ * they can be released properly when no longer used. Each view needs to be tracked separately,
+ * therefore for simplicity we use the hash code of the value object as key in the cache.
+ */
+ private final LruCache<InlineSuggestionUiImpl, Boolean> mActiveInlineSuggestions =
+ new LruCache<InlineSuggestionUiImpl, Boolean>(30) {
+ @Override
+ public void entryRemoved(boolean evicted, InlineSuggestionUiImpl key,
+ Boolean oldValue,
+ Boolean newValue) {
+ if (evicted) {
+ Log.w(TAG,
+ "Hit max=100 entries in the cache. Releasing oldest one to make "
+ + "space.");
+ key.releaseSurfaceControlViewHost();
+ }
+ }
+ };
+
/**
* If the specified {@code width}/{@code height} is an exact value, then it will be returned as
* is, otherwise the method tries to measure a size that is just large enough to fit the view
@@ -169,8 +193,14 @@
return true;
});
- sendResult(callback, host.getSurfacePackage(), measuredSize.getWidth(),
- measuredSize.getHeight());
+ try {
+ InlineSuggestionUiImpl uiImpl = new InlineSuggestionUiImpl(host, mHandler);
+ mActiveInlineSuggestions.put(uiImpl, true);
+ callback.onContent(new InlineSuggestionUiWrapper(uiImpl), host.getSurfacePackage(),
+ measuredSize.getWidth(), measuredSize.getHeight());
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException calling onContent()");
+ }
} finally {
updateDisplay(Display.DEFAULT_DISPLAY);
}
@@ -181,12 +211,87 @@
callback.sendResult(rendererInfo);
}
- private void sendResult(@NonNull IInlineSuggestionUiCallback callback,
- @Nullable SurfaceControlViewHost.SurfacePackage surface, int width, int height) {
- try {
- callback.onContent(surface, width, height);
- } catch (RemoteException e) {
- Log.w(TAG, "RemoteException calling onContent(" + surface + ")");
+ /**
+ * A wrapper class around the {@link InlineSuggestionUiImpl} to ensure it's not strongly
+ * reference by the remote system server process.
+ */
+ private static final class InlineSuggestionUiWrapper extends
+ android.service.autofill.IInlineSuggestionUi.Stub {
+
+ private final WeakReference<InlineSuggestionUiImpl> mUiImpl;
+
+ InlineSuggestionUiWrapper(InlineSuggestionUiImpl uiImpl) {
+ mUiImpl = new WeakReference<>(uiImpl);
+ }
+
+ @Override
+ public void releaseSurfaceControlViewHost() {
+ final InlineSuggestionUiImpl uiImpl = mUiImpl.get();
+ if (uiImpl != null) {
+ uiImpl.releaseSurfaceControlViewHost();
+ }
+ }
+
+ @Override
+ public void getSurfacePackage(ISurfacePackageResultCallback callback) {
+ final InlineSuggestionUiImpl uiImpl = mUiImpl.get();
+ if (uiImpl != null) {
+ uiImpl.getSurfacePackage(callback);
+ }
+ }
+ }
+
+ /**
+ * Keeps track of a SurfaceControlViewHost to ensure it's released when its lifecycle ends.
+ *
+ * <p>This class is thread safe, because all the outside calls are piped into a single
+ * handler thread to be processed.
+ */
+ private final class InlineSuggestionUiImpl {
+
+ @Nullable
+ private SurfaceControlViewHost mViewHost;
+ @NonNull
+ private final Handler mHandler;
+
+ InlineSuggestionUiImpl(SurfaceControlViewHost viewHost, Handler handler) {
+ this.mViewHost = viewHost;
+ this.mHandler = handler;
+ }
+
+ /**
+ * Call {@link SurfaceControlViewHost#release()} to release it. After this, this view is
+ * not usable, and any further calls to the
+ * {@link #getSurfacePackage(ISurfacePackageResultCallback)} will get {@code null} result.
+ */
+ public void releaseSurfaceControlViewHost() {
+ mHandler.post(() -> {
+ if (mViewHost == null) {
+ return;
+ }
+ Log.v(TAG, "Releasing inline suggestion view host");
+ mViewHost.release();
+ mViewHost = null;
+ InlineSuggestionRenderService.this.mActiveInlineSuggestions.remove(
+ InlineSuggestionUiImpl.this);
+ Log.v(TAG, "Removed the inline suggestion from the cache, current size="
+ + InlineSuggestionRenderService.this.mActiveInlineSuggestions.size());
+ });
+ }
+
+ /**
+ * Sends back a new {@link android.view.SurfaceControlViewHost.SurfacePackage} if the view
+ * is not released, {@code null} otherwise.
+ */
+ public void getSurfacePackage(ISurfacePackageResultCallback callback) {
+ Log.d(TAG, "getSurfacePackage");
+ mHandler.post(() -> {
+ try {
+ callback.onResult(mViewHost == null ? null : mViewHost.getSurfacePackage());
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException calling onSurfacePackage");
+ }
+ });
}
}
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 4e9e3d4..d2dfb29 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -1075,8 +1075,12 @@
@Override
public void onViewDetachedFromWindow(View v) {
- mActivity = null;
- finish();
+ if (mActivity == null || !mActivity.isChangingConfigurations()) {
+ // Only stop the dream if the view is not detached by relaunching
+ // activity for configuration changes.
+ mActivity = null;
+ finish();
+ }
}
});
}
diff --git a/core/java/android/speech/SpeechRecognizer.java b/core/java/android/speech/SpeechRecognizer.java
index 92f3538..aea94bf 100644
--- a/core/java/android/speech/SpeechRecognizer.java
+++ b/core/java/android/speech/SpeechRecognizer.java
@@ -283,8 +283,8 @@
} else {
serviceIntent.setComponent(mServiceComponent);
}
-
- if (!mContext.bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE)) {
+ if (!mContext.bindService(serviceIntent, mConnection,
+ Context.BIND_AUTO_CREATE | Context.BIND_INCLUDE_CAPABILITIES)) {
Log.e(TAG, "bind to recognition service failed");
mConnection = null;
mService = null;
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index a135b0c..2d17b6d 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -667,7 +667,7 @@
if (hideTypes[0] != 0) {
applyAnimation(hideTypes[0], false /* show */, false /* fromIme */);
}
- if (hasControl) {
+ if (hasControl && mRequestedState.getSourcesCount() > 0) {
// We might have changed our requested visibilities while we don't have the control,
// so we need to update our requested state once we have control. Otherwise, our
// requested state at the server side might be incorrect.
@@ -1065,9 +1065,16 @@
if (consumer.getControl() != null) {
final InsetsSource localSource = mState.getSource(type);
if (!localSource.equals(mRequestedState.peekSource(type))) {
+ // Our requested state is stale. Update it here and send it to window manager.
mRequestedState.addSource(new InsetsSource(localSource));
changed = true;
}
+ if (!localSource.equals(mLastDispatchedState.peekSource(type))) {
+ // The server state is not what we expected. This can happen while we don't have
+ // the control. Since we have the control now, we need to send our request again
+ // to modify the server state.
+ changed = true;
+ }
}
}
if (!changed) {
@@ -1117,7 +1124,7 @@
* Cancel on-going animation to show/hide {@link InsetsType}.
*/
@VisibleForTesting
- public void cancelExistingAnimation() {
+ public void cancelExistingAnimations() {
cancelExistingControllers(all());
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 42f11c1..a17af6c 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -4624,6 +4624,8 @@
setAccessibilityFocus(null, null);
+ mInsetsController.cancelExistingAnimations();
+
mView.assignParent(null);
mView = null;
mAttachInfo.mRootView = null;
diff --git a/core/java/android/view/ViewRootInsetsControllerHost.java b/core/java/android/view/ViewRootInsetsControllerHost.java
index d8bf58f..9674a80 100644
--- a/core/java/android/view/ViewRootInsetsControllerHost.java
+++ b/core/java/android/view/ViewRootInsetsControllerHost.java
@@ -104,10 +104,10 @@
@Override
public void applySurfaceParams(SyncRtSurfaceTransactionApplier.SurfaceParams... params) {
+ if (mViewRoot.mView == null) {
+ throw new IllegalStateException("View of the ViewRootImpl is not initiated.");
+ }
if (mApplier == null) {
- if (mViewRoot.mView == null) {
- throw new IllegalStateException("View of the ViewRootImpl is not initiated.");
- }
mApplier = new SyncRtSurfaceTransactionApplier(mViewRoot.mView);
}
if (mViewRoot.mView.isHardwareAccelerated()) {
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 1773ec2..76fe6b5f 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -1894,7 +1894,13 @@
final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
mService.addClient(mServiceClient, client.autofillClientGetComponentName(),
userId, receiver);
- final int flags = receiver.getIntResult();
+ int flags = 0;
+ try {
+ flags = receiver.getIntResult();
+ } catch (SyncResultReceiver.TimeoutException e) {
+ Log.w(TAG, "Failed to initialize autofill: " + e);
+ return;
+ }
mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
sDebug = (flags & FLAG_ADD_CLIENT_DEBUG) != 0;
sVerbose = (flags & FLAG_ADD_CLIENT_VERBOSE) != 0;
diff --git a/core/java/android/view/inputmethod/InlineSuggestion.java b/core/java/android/view/inputmethod/InlineSuggestion.java
index 6b1a480..4c72474 100644
--- a/core/java/android/view/inputmethod/InlineSuggestion.java
+++ b/core/java/android/view/inputmethod/InlineSuggestion.java
@@ -18,11 +18,13 @@
import android.annotation.BinderThread;
import android.annotation.CallbackExecutor;
+import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.content.Context;
-import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.Looper;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
@@ -42,26 +44,26 @@
import java.util.function.Consumer;
/**
- * This class represents an inline suggestion which is made by one app
- * and can be embedded into the UI of another. Suggestions may contain
- * sensitive information not known to the host app which needs to be
- * protected from spoofing. To address that the suggestion view inflated
- * on demand for embedding is created in such a way that the hosting app
- * cannot introspect its content and cannot interact with it.
+ * This class represents an inline suggestion which is made by one app and can be embedded into the
+ * UI of another. Suggestions may contain sensitive information not known to the host app which
+ * needs to be protected from spoofing. To address that the suggestion view inflated on demand for
+ * embedding is created in such a way that the hosting app cannot introspect its content and cannot
+ * interact with it.
*/
-@DataClass(
- genEqualsHashCode = true,
- genToString = true,
- genHiddenConstDefs = true,
+@DataClass(genEqualsHashCode = true, genToString = true, genHiddenConstDefs = true,
genHiddenConstructor = true)
-@DataClass.Suppress({"getContentProvider"})
public final class InlineSuggestion implements Parcelable {
private static final String TAG = "InlineSuggestion";
- private final @NonNull InlineSuggestionInfo mInfo;
+ @NonNull
+ private final InlineSuggestionInfo mInfo;
- private final @Nullable IInlineContentProvider mContentProvider;
+ /**
+ * @hide
+ */
+ @Nullable
+ private final IInlineContentProvider mContentProvider;
/**
* Used to keep a strong reference to the callback so it doesn't get garbage collected.
@@ -69,7 +71,8 @@
* @hide
*/
@DataClass.ParcelWith(InlineContentCallbackImplParceling.class)
- private @Nullable InlineContentCallbackImpl mInlineContentCallback;
+ @Nullable
+ private InlineContentCallbackImpl mInlineContentCallback;
/**
* Creates a new {@link InlineSuggestion}, for testing purpose.
@@ -87,8 +90,7 @@
*
* @hide
*/
- public InlineSuggestion(
- @NonNull InlineSuggestionInfo info,
+ public InlineSuggestion(@NonNull InlineSuggestionInfo info,
@Nullable IInlineContentProvider contentProvider) {
this(info, contentProvider, /* inlineContentCallback */ null);
}
@@ -96,25 +98,30 @@
/**
* Inflates a view with the content of this suggestion at a specific size.
*
- * <p> The size must be either 1) between the
- * {@link android.widget.inline.InlinePresentationSpec#getMinSize() min size} and the
- * {@link android.widget.inline.InlinePresentationSpec#getMaxSize() max size} of the
- * presentation spec returned by {@link InlineSuggestionInfo#getInlinePresentationSpec()},
- * or 2) {@link ViewGroup.LayoutParams#WRAP_CONTENT}. If the size is set to
- * {@link ViewGroup.LayoutParams#WRAP_CONTENT}, then the size of the inflated view will be just
- * large enough to fit the content, while still conforming to the min / max size specified by
- * the {@link android.widget.inline.InlinePresentationSpec}.
+ * <p> Each dimension of the size must satisfy one of the following conditions:
+ *
+ * <ol>
+ * <li>between {@link android.widget.inline.InlinePresentationSpec#getMinSize()} and
+ * {@link android.widget.inline.InlinePresentationSpec#getMaxSize()} of the presentation spec
+ * from {@code mInfo}
+ * <li>{@link ViewGroup.LayoutParams#WRAP_CONTENT}
+ * </ol>
+ *
+ * If the size is set to {@link
+ * ViewGroup.LayoutParams#WRAP_CONTENT}, then the size of the inflated view will be just large
+ * enough to fit the content, while still conforming to the min / max size specified by the
+ * {@link android.widget.inline.InlinePresentationSpec}.
*
* <p> The caller can attach an {@link android.view.View.OnClickListener} and/or an
- * {@link android.view.View.OnLongClickListener} to the view in the
- * {@code callback} to receive click and long click events on the view.
+ * {@link android.view.View.OnLongClickListener} to the view in the {@code callback} to receive
+ * click and long click events on the view.
*
* @param context Context in which to inflate the view.
- * @param size The size at which to inflate the suggestion. For each dimension, it maybe
- * an exact value or {@link ViewGroup.LayoutParams#WRAP_CONTENT}.
- * @param callback Callback for receiving the inflated view, where the
- * {@link ViewGroup.LayoutParams} of the view is set as the actual size of
- * the underlying remote view.
+ * @param size The size at which to inflate the suggestion. For each dimension, it maybe an
+ * exact value or {@link ViewGroup.LayoutParams#WRAP_CONTENT}.
+ * @param callback Callback for receiving the inflated view, where the {@link
+ * ViewGroup.LayoutParams} of the view is set as the actual size of the
+ * underlying remote view.
* @throws IllegalArgumentException If an invalid argument is passed.
* @throws IllegalStateException If this method is already called.
*/
@@ -130,19 +137,17 @@
+ ", nor wrap_content");
}
mInlineContentCallback = getInlineContentCallback(context, callbackExecutor, callback);
- AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
- if (mContentProvider == null) {
- callback.accept(/* view */ null);
- return;
- }
- try {
- mContentProvider.provideContent(size.getWidth(), size.getHeight(),
- new InlineContentCallbackWrapper(mInlineContentCallback));
- } catch (RemoteException e) {
- Slog.w(TAG, "Error creating suggestion content surface: " + e);
- callback.accept(/* view */ null);
- }
- });
+ if (mContentProvider == null) {
+ callbackExecutor.execute(() -> callback.accept(/* view */ null));
+ return;
+ }
+ try {
+ mContentProvider.provideContent(size.getWidth(), size.getHeight(),
+ new InlineContentCallbackWrapper(mInlineContentCallback));
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error creating suggestion content surface: " + e);
+ callbackExecutor.execute(() -> callback.accept(/* view */ null));
+ }
}
/**
@@ -161,9 +166,14 @@
if (mInlineContentCallback != null) {
throw new IllegalStateException("Already called #inflate()");
}
- return new InlineContentCallbackImpl(context, callbackExecutor, callback);
+ return new InlineContentCallbackImpl(context, mContentProvider, callbackExecutor,
+ callback);
}
+ /**
+ * A wrapper class around the {@link InlineContentCallbackImpl} to ensure it's not strongly
+ * reference by the remote system server process.
+ */
private static final class InlineContentCallbackWrapper extends IInlineContentCallback.Stub {
private final WeakReference<InlineContentCallbackImpl> mCallbackImpl;
@@ -201,17 +211,68 @@
}
}
+ /**
+ * Handles the communication between the inline suggestion view in current (IME) process and
+ * the remote view provided from the system server.
+ *
+ * <p>This class is thread safe, because all the outside calls are piped into a single
+ * handler thread to be processed.
+ */
private static final class InlineContentCallbackImpl {
- private final @NonNull Context mContext;
- private final @NonNull Executor mCallbackExecutor;
- private final @NonNull Consumer<InlineContentView> mCallback;
- private @Nullable InlineContentView mView;
+ @NonNull
+ private final Handler mMainHandler = new Handler(Looper.getMainLooper());
+
+ @NonNull
+ private final Context mContext;
+ @Nullable
+ private final IInlineContentProvider mInlineContentProvider;
+ @NonNull
+ private final Executor mCallbackExecutor;
+
+ /**
+ * Callback from the client (IME) that will receive the inflated suggestion view. It'll
+ * only be called once when the view SurfacePackage is first sent back to the client. Any
+ * updates to the view due to attach to window and detach from window events will be
+ * handled under the hood, transparent from the client.
+ */
+ @NonNull
+ private final Consumer<InlineContentView> mCallback;
+
+ /**
+ * Indicates whether the first content has been received or not.
+ */
+ private boolean mFirstContentReceived = false;
+
+ /**
+ * The client (IME) side view which internally wraps a remote view. It'll be set when
+ * {@link #onContent(SurfaceControlViewHost.SurfacePackage, int, int)} is called, which
+ * should only happen once in the lifecycle of this inline suggestion instance.
+ */
+ @Nullable
+ private InlineContentView mView;
+
+ /**
+ * The SurfacePackage pointing to the remote view. It's cached here to be sent to the next
+ * available consumer.
+ */
+ @Nullable
+ private SurfaceControlViewHost.SurfacePackage mSurfacePackage;
+
+ /**
+ * The callback (from the {@link InlineContentView}) which consumes the surface package.
+ * It's cached here to be called when the SurfacePackage is returned from the remote
+ * view owning process.
+ */
+ @Nullable
+ private Consumer<SurfaceControlViewHost.SurfacePackage> mSurfacePackageConsumer;
InlineContentCallbackImpl(@NonNull Context context,
+ @Nullable IInlineContentProvider inlineContentProvider,
@NonNull @CallbackExecutor Executor callbackExecutor,
@NonNull Consumer<InlineContentView> callback) {
mContext = context;
+ mInlineContentProvider = inlineContentProvider;
mCallbackExecutor = callbackExecutor;
mCallback = callback;
}
@@ -219,28 +280,110 @@
@BinderThread
public void onContent(SurfaceControlViewHost.SurfacePackage content, int width,
int height) {
- if (content == null) {
+ mMainHandler.post(() -> handleOnContent(content, width, height));
+ }
+
+ @MainThread
+ private void handleOnContent(SurfaceControlViewHost.SurfacePackage content, int width,
+ int height) {
+ if (!mFirstContentReceived) {
+ handleOnFirstContentReceived(content, width, height);
+ mFirstContentReceived = true;
+ } else {
+ handleOnSurfacePackage(content);
+ }
+ }
+
+ /**
+ * Called when the view content is returned for the first time.
+ */
+ @MainThread
+ private void handleOnFirstContentReceived(SurfaceControlViewHost.SurfacePackage content,
+ int width, int height) {
+ mSurfacePackage = content;
+ if (mSurfacePackage == null) {
mCallbackExecutor.execute(() -> mCallback.accept(/* view */null));
} else {
mView = new InlineContentView(mContext);
mView.setLayoutParams(new ViewGroup.LayoutParams(width, height));
- mView.setChildSurfacePackage(content);
+ mView.setChildSurfacePackageUpdater(getSurfacePackageUpdater());
mCallbackExecutor.execute(() -> mCallback.accept(mView));
}
}
+ /**
+ * Called when any subsequent SurfacePackage is returned from the remote view owning
+ * process.
+ */
+ @MainThread
+ private void handleOnSurfacePackage(SurfaceControlViewHost.SurfacePackage surfacePackage) {
+ mSurfacePackage = surfacePackage;
+ if (mSurfacePackage != null && mSurfacePackageConsumer != null) {
+ mSurfacePackageConsumer.accept(mSurfacePackage);
+ mSurfacePackageConsumer = null;
+ }
+ }
+
+ @MainThread
+ private void handleOnSurfacePackageReleased() {
+ mSurfacePackage = null;
+ try {
+ mInlineContentProvider.onSurfacePackageReleased();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error calling onSurfacePackageReleased(): " + e);
+ }
+ }
+
+ @MainThread
+ private void handleGetSurfacePackage(
+ Consumer<SurfaceControlViewHost.SurfacePackage> consumer) {
+ if (mSurfacePackage != null) {
+ consumer.accept(mSurfacePackage);
+ } else {
+ mSurfacePackageConsumer = consumer;
+ try {
+ mInlineContentProvider.requestSurfacePackage();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error calling getSurfacePackage(): " + e);
+ consumer.accept(null);
+ mSurfacePackageConsumer = null;
+ }
+ }
+ }
+
+ private InlineContentView.SurfacePackageUpdater getSurfacePackageUpdater() {
+ return new InlineContentView.SurfacePackageUpdater() {
+ @Override
+ public void onSurfacePackageReleased() {
+ mMainHandler.post(
+ () -> InlineContentCallbackImpl.this.handleOnSurfacePackageReleased());
+ }
+
+ @Override
+ public void getSurfacePackage(
+ Consumer<SurfaceControlViewHost.SurfacePackage> consumer) {
+ mMainHandler.post(
+ () -> InlineContentCallbackImpl.this.handleGetSurfacePackage(consumer));
+ }
+ };
+ }
+
@BinderThread
public void onClick() {
- if (mView != null && mView.hasOnClickListeners()) {
- mView.callOnClick();
- }
+ mMainHandler.post(() -> {
+ if (mView != null && mView.hasOnClickListeners()) {
+ mView.callOnClick();
+ }
+ });
}
@BinderThread
public void onLongClick() {
- if (mView != null && mView.hasOnLongClickListeners()) {
- mView.performLongClick();
- }
+ mMainHandler.post(() -> {
+ if (mView != null && mView.hasOnLongClickListeners()) {
+ mView.performLongClick();
+ }
+ });
}
}
@@ -262,6 +405,7 @@
+
// Code below generated by codegen v1.0.15.
//
// DO NOT MODIFY!
@@ -302,6 +446,14 @@
}
/**
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public @Nullable IInlineContentProvider getContentProvider() {
+ return mContentProvider;
+ }
+
+ /**
* Used to keep a strong reference to the callback so it doesn't get garbage collected.
*
* @hide
@@ -421,7 +573,7 @@
};
@DataClass.Generated(
- time = 1587771173367L,
+ time = 1588308946517L,
codegenVersion = "1.0.15",
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestion.java",
inputSignatures = "private static final java.lang.String TAG\nprivate final @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo mInfo\nprivate final @android.annotation.Nullable com.android.internal.view.inline.IInlineContentProvider mContentProvider\nprivate @com.android.internal.util.DataClass.ParcelWith(android.view.inputmethod.InlineSuggestion.InlineContentCallbackImplParceling.class) @android.annotation.Nullable android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl mInlineContentCallback\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestion newInlineSuggestion(android.view.inputmethod.InlineSuggestionInfo)\npublic void inflate(android.content.Context,android.util.Size,java.util.concurrent.Executor,java.util.function.Consumer<android.widget.inline.InlineContentView>)\nprivate static boolean isValid(int,int,int)\nprivate synchronized android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl getInlineContentCallback(android.content.Context,java.util.concurrent.Executor,java.util.function.Consumer<android.widget.inline.InlineContentView>)\nclass InlineSuggestion extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 3cf6109..71dd665 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -645,6 +645,11 @@
@Override
public void setCurrentRootView(ViewRootImpl rootView) {
synchronized (mH) {
+ if (mCurRootView != null) {
+ // Reset the last served view and restart window focus state of the root view.
+ mCurRootView.getImeFocusController().setServedView(null);
+ mRestartOnNextWindowFocus = true;
+ }
mCurRootView = rootView;
}
}
diff --git a/core/java/android/widget/inline/InlineContentView.java b/core/java/android/widget/inline/InlineContentView.java
index 4f2af63..8657e82 100644
--- a/core/java/android/widget/inline/InlineContentView.java
+++ b/core/java/android/widget/inline/InlineContentView.java
@@ -21,40 +21,45 @@
import android.content.Context;
import android.graphics.PixelFormat;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.ViewGroup;
+import java.util.function.Consumer;
+
/**
- * This class represents a view that holds opaque content from another app that
- * you can inline in your UI.
+ * This class represents a view that holds opaque content from another app that you can inline in
+ * your UI.
*
* <p>Since the content presented by this view is from another security domain,it is
- * shown on a remote surface preventing the host application from accessing that content.
- * Also the host application cannot interact with the inlined content by injecting touch
- * events or clicking programmatically.
+ * shown on a remote surface preventing the host application from accessing that content. Also the
+ * host application cannot interact with the inlined content by injecting touch events or clicking
+ * programmatically.
*
* <p>This view can be overlaid by other windows, i.e. redressed, but if this is the case
- * the inined UI would not be interactive. Sometimes this is desirable, e.g. animating
- * transitions.
+ * the inlined UI would not be interactive. Sometimes this is desirable, e.g. animating transitions.
*
* <p>By default the surface backing this view is shown on top of the hosting window such
- * that the inlined content is interactive. However, you can temporarily move the surface
- * under the hosting window which could be useful in some cases, e.g. animating transitions.
- * At this point the inlined content will not be interactive and the touch events would
- * be delivered to your app.
- * <p>
- * Instances of this class are created by the platform and can be programmatically attached
- * to your UI. Once you attach and detach this view it can not longer be reused and you
- * should obtain a new view from the platform via the dedicated APIs.
+ * that the inlined content is interactive. However, you can temporarily move the surface under the
+ * hosting window which could be useful in some cases, e.g. animating transitions. At this point the
+ * inlined content will not be interactive and the touch events would be delivered to your app.
+ *
+ * <p> Instances of this class are created by the platform and can be programmatically attached to
+ * your UI. Once the view is attached to the window, you may detach and reattach it to the window.
+ * It should work seamlessly from the hosting process's point of view.
*/
public class InlineContentView extends ViewGroup {
+ private static final String TAG = "InlineContentView";
+
+ private static final boolean DEBUG = false;
+
/**
- * Callback for observing the lifecycle of the surface control
- * that manipulates the backing secure embedded UI surface.
+ * Callback for observing the lifecycle of the surface control that manipulates the backing
+ * secure embedded UI surface.
*/
public interface SurfaceControlCallback {
/**
@@ -72,15 +77,41 @@
void onDestroyed(@NonNull SurfaceControl surfaceControl);
}
- private final @NonNull SurfaceHolder.Callback mSurfaceCallback = new SurfaceHolder.Callback() {
+ /**
+ * Callback for sending an updated surface package in case the previous one is released
+ * from the detached from window event, and for getting notified of such event.
+ *
+ * This is expected to be provided to the {@link InlineContentView} so it can get updates
+ * from and send updates to the remote content (i.e. surface package) provider.
+ *
+ * @hide
+ */
+ public interface SurfacePackageUpdater {
+
+ /**
+ * Called when the previous surface package is released due to view being detached
+ * from the window.
+ */
+ void onSurfacePackageReleased();
+
+ /**
+ * Called to request an updated surface package.
+ *
+ * @param consumer consumes the updated surface package.
+ */
+ void getSurfacePackage(Consumer<SurfaceControlViewHost.SurfacePackage> consumer);
+ }
+
+ @NonNull
+ private final SurfaceHolder.Callback mSurfaceCallback = new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(@NonNull SurfaceHolder holder) {
mSurfaceControlCallback.onCreated(mSurfaceView.getSurfaceControl());
}
@Override
- public void surfaceChanged(@NonNull SurfaceHolder holder,
- int format, int width, int height) {
+ public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width,
+ int height) {
/* do nothing */
}
@@ -90,13 +121,17 @@
}
};
- private final @NonNull SurfaceView mSurfaceView;
+ @NonNull
+ private final SurfaceView mSurfaceView;
- private @Nullable SurfaceControlCallback mSurfaceControlCallback;
+ @Nullable
+ private SurfaceControlCallback mSurfaceControlCallback;
+
+ @Nullable
+ private SurfacePackageUpdater mSurfacePackageUpdater;
/**
* @inheritDoc
- *
* @hide
*/
public InlineContentView(@NonNull Context context) {
@@ -105,7 +140,6 @@
/**
* @inheritDoc
- *
* @hide
*/
public InlineContentView(@NonNull Context context, @Nullable AttributeSet attrs) {
@@ -114,7 +148,6 @@
/**
* @inheritDoc
- *
* @hide
*/
public InlineContentView(@NonNull Context context, @Nullable AttributeSet attrs,
@@ -123,20 +156,18 @@
}
/**
- * Gets the surface control. If the surface is not created this method
- * returns {@code null}.
+ * Gets the surface control. If the surface is not created this method returns {@code null}.
*
* @return The surface control.
- *
* @see #setSurfaceControlCallback(SurfaceControlCallback)
*/
- public @Nullable SurfaceControl getSurfaceControl() {
+ @Nullable
+ public SurfaceControl getSurfaceControl() {
return mSurfaceView.getSurfaceControl();
}
/**
* @inheritDoc
- *
* @hide
*/
public InlineContentView(@NonNull Context context, @Nullable AttributeSet attrs,
@@ -149,14 +180,35 @@
}
/**
- * Sets the embedded UI.
- * @param surfacePackage The embedded UI.
+ * Sets the embedded UI provider.
*
* @hide
*/
- public void setChildSurfacePackage(
- @Nullable SurfaceControlViewHost.SurfacePackage surfacePackage) {
- mSurfaceView.setChildSurfacePackage(surfacePackage);
+ public void setChildSurfacePackageUpdater(
+ @Nullable SurfacePackageUpdater surfacePackageUpdater) {
+ mSurfacePackageUpdater = surfacePackageUpdater;
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ if (DEBUG) Log.v(TAG, "onAttachedToWindow");
+ super.onAttachedToWindow();
+ if (mSurfacePackageUpdater != null) {
+ mSurfacePackageUpdater.getSurfacePackage(
+ sp -> {
+ if (DEBUG) Log.v(TAG, "Received new SurfacePackage");
+ mSurfaceView.setChildSurfacePackage(sp);
+ });
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ if (DEBUG) Log.v(TAG, "onDetachedFromWindow");
+ super.onDetachedFromWindow();
+ if (mSurfacePackageUpdater != null) {
+ mSurfacePackageUpdater.onSurfacePackageReleased();
+ }
}
@Override
@@ -165,8 +217,8 @@
}
/**
- * Sets a callback to observe the lifecycle of the surface control for
- * managing the backing surface.
+ * Sets a callback to observe the lifecycle of the surface control for managing the backing
+ * surface.
*
* @param callback The callback to set or {@code null} to clear.
*/
@@ -182,7 +234,6 @@
/**
* @return Whether the surface backing this view appears on top of its parent.
- *
* @see #setZOrderedOnTop(boolean)
*/
public boolean isZOrderedOnTop() {
@@ -190,17 +241,15 @@
}
/**
- * Controls whether the backing surface is placed on top of this view's window.
- * Normally, it is placed on top of the window, to allow interaction
- * with the inlined UI. Via this method, you can place the surface below the
- * window. This means that all of the contents of the window this view is in
- * will be visible on top of its surface.
+ * Controls whether the backing surface is placed on top of this view's window. Normally, it is
+ * placed on top of the window, to allow interaction with the inlined UI. Via this method, you
+ * can place the surface below the window. This means that all of the contents of the window
+ * this view is in will be visible on top of its surface.
*
* <p> The Z ordering can be changed dynamically if the backing surface is
* created, otherwise the ordering would be applied at surface construction time.
*
* @param onTop Whether to show the surface on top of this view's window.
- *
* @see #isZOrderedOnTop()
*/
public boolean setZOrderedOnTop(boolean onTop) {
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 11ac388..3fc3f3e 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -158,6 +158,7 @@
private static final String TAG = "ChooserActivity";
private AppPredictor mPersonalAppPredictor;
private AppPredictor mWorkAppPredictor;
+ private boolean mShouldDisplayLandscape;
@UnsupportedAppUsage
public ChooserActivity() {
@@ -716,6 +717,8 @@
mCallerChooserTargets = targets;
}
+ mShouldDisplayLandscape = shouldDisplayLandscape(
+ getResources().getConfiguration().orientation);
setRetainInOnStop(intent.getBooleanExtra(EXTRA_PRIVATE_RETAIN_IN_ON_STOP, false));
super.onCreate(savedInstanceState, target, title, defaultTitleRes, initialIntents,
null, false);
@@ -1073,6 +1076,7 @@
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
+ mShouldDisplayLandscape = shouldDisplayLandscape(newConfig.orientation);
adjustPreviewWidth(newConfig.orientation, null);
updateStickyContentPreview();
}
@@ -1086,7 +1090,7 @@
private void adjustPreviewWidth(int orientation, View parent) {
int width = -1;
- if (shouldDisplayLandscape(orientation)) {
+ if (mShouldDisplayLandscape) {
width = getResources().getDimensionPixelSize(R.dimen.chooser_preview_width);
}
@@ -2940,6 +2944,19 @@
.setSubtype(previewType));
}
+ class ViewHolderBase extends RecyclerView.ViewHolder {
+ private int mViewType;
+
+ ViewHolderBase(View itemView, int viewType) {
+ super(itemView);
+ this.mViewType = viewType;
+ }
+
+ int getViewType() {
+ return mViewType;
+ }
+ }
+
/**
* Used to bind types of individual item including
* {@link ChooserGridAdapter#VIEW_TYPE_NORMAL},
@@ -2947,12 +2964,12 @@
* {@link ChooserGridAdapter#VIEW_TYPE_PROFILE},
* and {@link ChooserGridAdapter#VIEW_TYPE_AZ_LABEL}.
*/
- final class ItemViewHolder extends RecyclerView.ViewHolder {
+ final class ItemViewHolder extends ViewHolderBase {
ResolverListAdapter.ViewHolder mWrappedViewHolder;
int mListPosition = ChooserListAdapter.NO_POSITION;
- ItemViewHolder(View itemView, boolean isClickable) {
- super(itemView);
+ ItemViewHolder(View itemView, boolean isClickable, int viewType) {
+ super(itemView, viewType);
mWrappedViewHolder = new ResolverListAdapter.ViewHolder(itemView);
if (isClickable) {
itemView.setOnClickListener(v -> startSelected(mListPosition,
@@ -2970,9 +2987,9 @@
/**
* Add a footer to the list, to support scrolling behavior below the navbar.
*/
- final class FooterViewHolder extends RecyclerView.ViewHolder {
- FooterViewHolder(View itemView) {
- super(itemView);
+ final class FooterViewHolder extends ViewHolderBase {
+ FooterViewHolder(View itemView, int viewType) {
+ super(itemView, viewType);
}
}
@@ -3083,7 +3100,7 @@
int getMaxTargetsPerRow() {
int maxTargets = MAX_TARGETS_PER_ROW_PORTRAIT;
- if (shouldDisplayLandscape(getResources().getConfiguration().orientation)) {
+ if (mShouldDisplayLandscape) {
maxTargets = MAX_TARGETS_PER_ROW_LANDSCAPE;
}
return maxTargets;
@@ -3191,13 +3208,14 @@
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case VIEW_TYPE_CONTENT_PREVIEW:
- return new ItemViewHolder(createContentPreviewView(parent), false);
+ return new ItemViewHolder(createContentPreviewView(parent), false, viewType);
case VIEW_TYPE_PROFILE:
- return new ItemViewHolder(createProfileView(parent), false);
+ return new ItemViewHolder(createProfileView(parent), false, viewType);
case VIEW_TYPE_AZ_LABEL:
- return new ItemViewHolder(createAzLabelView(parent), false);
+ return new ItemViewHolder(createAzLabelView(parent), false, viewType);
case VIEW_TYPE_NORMAL:
- return new ItemViewHolder(mChooserListAdapter.createView(parent), true);
+ return new ItemViewHolder(
+ mChooserListAdapter.createView(parent), true, viewType);
case VIEW_TYPE_DIRECT_SHARE:
case VIEW_TYPE_CALLER_AND_RANK:
return createItemGroupViewHolder(viewType, parent);
@@ -3205,7 +3223,7 @@
Space sp = new Space(parent.getContext());
sp.setLayoutParams(new RecyclerView.LayoutParams(
LayoutParams.MATCH_PARENT, mFooterHeight));
- return new FooterViewHolder(sp);
+ return new FooterViewHolder(sp, viewType);
default:
// Since we catch all possible viewTypes above, no chance this is being called.
return null;
@@ -3214,7 +3232,7 @@
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
- int viewType = getItemViewType(position);
+ int viewType = ((ViewHolderBase) holder).getViewType();
switch (viewType) {
case VIEW_TYPE_DIRECT_SHARE:
case VIEW_TYPE_CALLER_AND_RANK:
@@ -3325,7 +3343,6 @@
}
viewGroup.setTag(holder);
-
return holder;
}
@@ -3352,14 +3369,15 @@
parentGroup.addView(row2);
mDirectShareViewHolder = new DirectShareViewHolder(parentGroup,
- Lists.newArrayList(row1, row2), getMaxTargetsPerRow());
+ Lists.newArrayList(row1, row2), getMaxTargetsPerRow(), viewType);
loadViewsIntoGroup(mDirectShareViewHolder);
return mDirectShareViewHolder;
} else {
ViewGroup row = (ViewGroup) mLayoutInflater.inflate(R.layout.chooser_row, parent,
false);
- ItemGroupViewHolder holder = new SingleRowViewHolder(row, getMaxTargetsPerRow());
+ ItemGroupViewHolder holder =
+ new SingleRowViewHolder(row, getMaxTargetsPerRow(), viewType);
loadViewsIntoGroup(holder);
return holder;
@@ -3521,14 +3539,14 @@
* {@link ChooserGridAdapter#VIEW_TYPE_DIRECT_SHARE},
* and {@link ChooserGridAdapter#VIEW_TYPE_CALLER_AND_RANK}.
*/
- abstract class ItemGroupViewHolder extends RecyclerView.ViewHolder {
+ abstract class ItemGroupViewHolder extends ViewHolderBase {
protected int mMeasuredRowHeight;
private int[] mItemIndices;
protected final View[] mCells;
private final int mColumnCount;
- ItemGroupViewHolder(int cellCount, View itemView) {
- super(itemView);
+ ItemGroupViewHolder(int cellCount, View itemView, int viewType) {
+ super(itemView, viewType);
this.mCells = new View[cellCount];
this.mItemIndices = new int[cellCount];
this.mColumnCount = cellCount;
@@ -3574,8 +3592,8 @@
class SingleRowViewHolder extends ItemGroupViewHolder {
private final ViewGroup mRow;
- SingleRowViewHolder(ViewGroup row, int cellCount) {
- super(cellCount, row);
+ SingleRowViewHolder(ViewGroup row, int cellCount, int viewType) {
+ super(cellCount, row, viewType);
this.mRow = row;
}
@@ -3617,8 +3635,9 @@
private final boolean[] mCellVisibility;
- DirectShareViewHolder(ViewGroup parent, List<ViewGroup> rows, int cellCountPerRow) {
- super(rows.size() * cellCountPerRow, parent);
+ DirectShareViewHolder(ViewGroup parent, List<ViewGroup> rows, int cellCountPerRow,
+ int viewType) {
+ super(rows.size() * cellCountPerRow, parent, viewType);
this.mParent = parent;
this.mRows = rows;
diff --git a/core/java/com/android/internal/app/ChooserGridLayoutManager.java b/core/java/com/android/internal/app/ChooserGridLayoutManager.java
new file mode 100644
index 0000000..317a987
--- /dev/null
+++ b/core/java/com/android/internal/app/ChooserGridLayoutManager.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 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.internal.app;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import com.android.internal.widget.GridLayoutManager;
+import com.android.internal.widget.RecyclerView;
+
+/**
+ * For a11y and per {@link RecyclerView#onInitializeAccessibilityNodeInfo}, override
+ * methods to ensure proper row counts.
+ */
+public class ChooserGridLayoutManager extends GridLayoutManager {
+
+ /**
+ * Constructor used when layout manager is set in XML by RecyclerView attribute
+ * "layoutManager". If spanCount is not specified in the XML, it defaults to a
+ * single column.
+ *
+ */
+ public ChooserGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ /**
+ * Creates a vertical GridLayoutManager
+ *
+ * @param context Current context, will be used to access resources.
+ * @param spanCount The number of columns in the grid
+ */
+ public ChooserGridLayoutManager(Context context, int spanCount) {
+ super(context, spanCount);
+ }
+
+ /**
+ * @param context Current context, will be used to access resources.
+ * @param spanCount The number of columns or rows in the grid
+ * @param orientation Layout orientation. Should be {@link #HORIZONTAL} or {@link
+ * #VERTICAL}.
+ * @param reverseLayout When set to true, layouts from end to start.
+ */
+ public ChooserGridLayoutManager(Context context, int spanCount, int orientation,
+ boolean reverseLayout) {
+ super(context, spanCount, orientation, reverseLayout);
+ }
+
+ @Override
+ public int getRowCountForAccessibility(RecyclerView.Recycler recycler,
+ RecyclerView.State state) {
+ // Do not count the footer view in the official count
+ return super.getRowCountForAccessibility(recycler, state) - 1;
+ }
+}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 2f62f8e..83dabe8 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -182,6 +182,8 @@
private BroadcastReceiver mWorkProfileStateReceiver;
private UserHandle mHeaderCreatorUser;
+ private UserHandle mWorkProfileUserHandle;
+
/**
* Get the string resource to be used as a label for the link to the resolver activity for an
* action.
@@ -363,6 +365,7 @@
// a more complicated UI that the current voice interaction flow is not able
// to handle.
boolean filterLastUsed = mSupportsAlwaysUseOption && !isVoiceInteraction();
+ mWorkProfileUserHandle = fetchWorkProfileUserProfile();
mMultiProfilePagerAdapter = createMultiProfilePagerAdapter(initialIntents, rList, filterLastUsed);
if (configureContentView()) {
return;
@@ -527,13 +530,18 @@
return UserHandle.of(ActivityManager.getCurrentUser());
}
protected @Nullable UserHandle getWorkProfileUserHandle() {
+ return mWorkProfileUserHandle;
+ }
+
+ protected @Nullable UserHandle fetchWorkProfileUserProfile() {
+ mWorkProfileUserHandle = null;
UserManager userManager = getSystemService(UserManager.class);
for (final UserInfo userInfo : userManager.getProfiles(ActivityManager.getCurrentUser())) {
if (userInfo.isManagedProfile()) {
- return userInfo.getUserHandle();
+ mWorkProfileUserHandle = userInfo.getUserHandle();
}
}
- return null;
+ return mWorkProfileUserHandle;
}
private boolean hasWorkProfile() {
diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java
index 2fd938f..24bf98b 100644
--- a/core/java/com/android/internal/app/ResolverListAdapter.java
+++ b/core/java/com/android/internal/app/ResolverListAdapter.java
@@ -54,6 +54,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
import com.android.internal.app.chooser.DisplayResolveInfo;
+import com.android.internal.app.chooser.SelectableTargetInfo;
import com.android.internal.app.chooser.TargetInfo;
import java.util.ArrayList;
@@ -549,6 +550,15 @@
getLoadLabelTask((DisplayResolveInfo) info, holder).execute();
} else {
holder.bindLabel(info.getDisplayLabel(), info.getExtendedInfo());
+ if (info instanceof SelectableTargetInfo) {
+ // direct share targets should append the application name for a better readout
+ DisplayResolveInfo rInfo = ((SelectableTargetInfo) info).getDisplayResolveInfo();
+ CharSequence appName = rInfo != null ? rInfo.getDisplayLabel() : "";
+ CharSequence extendedInfo = info.getExtendedInfo();
+ String contentDescription = String.join(" ", info.getDisplayLabel(),
+ extendedInfo != null ? extendedInfo : "", appName);
+ holder.updateContentDescription(contentDescription);
+ }
}
if (info.isSuspended()) {
@@ -697,6 +707,12 @@
text2.setVisibility(View.VISIBLE);
text2.setText(subLabel);
}
+
+ itemView.setContentDescription(null);
+ }
+
+ public void updateContentDescription(String description) {
+ itemView.setContentDescription(description);
}
}
diff --git a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
index 246a07d..900e18d 100644
--- a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
+++ b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
@@ -44,7 +44,6 @@
import com.android.internal.app.SimpleIconFactory;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
/**
@@ -136,6 +135,10 @@
return mIsSuspended;
}
+ public DisplayResolveInfo getDisplayResolveInfo() {
+ return mSourceInfo;
+ }
+
private Drawable getChooserTargetIconDrawable(ChooserTarget target,
@Nullable ShortcutInfo shortcutInfo) {
Drawable directShareIcon = null;
diff --git a/core/java/com/android/internal/policy/TaskResizingAlgorithm.java b/core/java/com/android/internal/policy/TaskResizingAlgorithm.java
index 1fce098..1ec0206 100644
--- a/core/java/com/android/internal/policy/TaskResizingAlgorithm.java
+++ b/core/java/com/android/internal/policy/TaskResizingAlgorithm.java
@@ -91,14 +91,14 @@
int width = right - left;
int height = bottom - top;
if ((ctrlType & CTRL_LEFT) != 0) {
- width = Math.max(minVisibleWidth, width - deltaX);
+ width = Math.max(minVisibleWidth, Math.min(width - deltaX, maxVisibleSize.x));
} else if ((ctrlType & CTRL_RIGHT) != 0) {
- width = Math.max(minVisibleWidth, width + deltaX);
+ width = Math.max(minVisibleWidth, Math.min(width + deltaX, maxVisibleSize.x));
}
if ((ctrlType & CTRL_TOP) != 0) {
- height = Math.max(minVisibleHeight, height - deltaY);
+ height = Math.max(minVisibleHeight, Math.min(height - deltaY, maxVisibleSize.y));
} else if ((ctrlType & CTRL_BOTTOM) != 0) {
- height = Math.max(minVisibleHeight, height + deltaY);
+ height = Math.max(minVisibleHeight, Math.min(height + deltaY, maxVisibleSize.y));
}
// If we have to preserve the orientation - check that we are doing so.
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 38f5f32..8c5fdf5 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -137,7 +137,7 @@
// Used to show the authentication dialog (Biometrics, Device Credential)
void showAuthenticationDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver,
int biometricModality, boolean requireConfirmation, int userId, String opPackageName,
- long operationId);
+ long operationId, int sysUiSessionId);
// Used to notify the authentication dialog that a biometric has been authenticated
void onBiometricAuthenticated();
// Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 24fe063..c320824 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -106,7 +106,7 @@
// Used to show the authentication dialog (Biometrics, Device Credential)
void showAuthenticationDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver,
int biometricModality, boolean requireConfirmation, int userId, String opPackageName,
- long operationId);
+ long operationId, int sysUiSessionId);
// Used to notify the authentication dialog that a biometric has been authenticated
void onBiometricAuthenticated();
// Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
diff --git a/core/java/com/android/internal/view/inline/IInlineContentProvider.aidl b/core/java/com/android/internal/view/inline/IInlineContentProvider.aidl
index 08a349c..78df3eb 100644
--- a/core/java/com/android/internal/view/inline/IInlineContentProvider.aidl
+++ b/core/java/com/android/internal/view/inline/IInlineContentProvider.aidl
@@ -24,4 +24,6 @@
*/
oneway interface IInlineContentProvider {
void provideContent(int width, int height, in IInlineContentCallback callback);
+ void requestSurfacePackage();
+ void onSurfacePackageReleased();
}
diff --git a/core/java/com/android/internal/widget/GridLayoutManager.java b/core/java/com/android/internal/widget/GridLayoutManager.java
index e0502f1..09e6a99 100644
--- a/core/java/com/android/internal/widget/GridLayoutManager.java
+++ b/core/java/com/android/internal/widget/GridLayoutManager.java
@@ -153,13 +153,11 @@
if (mOrientation == HORIZONTAL) {
info.setCollectionItemInfo(AccessibilityNodeInfo.CollectionItemInfo.obtain(
glp.getSpanIndex(), glp.getSpanSize(),
- spanGroupIndex, 1,
- mSpanCount > 1 && glp.getSpanSize() == mSpanCount, false));
+ spanGroupIndex, 1, false, false));
} else { // VERTICAL
info.setCollectionItemInfo(AccessibilityNodeInfo.CollectionItemInfo.obtain(
spanGroupIndex, 1,
- glp.getSpanIndex(), glp.getSpanSize(),
- mSpanCount > 1 && glp.getSpanSize() == mSpanCount, false));
+ glp.getSpanIndex(), glp.getSpanSize(), false, false));
}
}
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index fb2ecf3..3f708f8 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -825,18 +825,6 @@
return true;
}
break;
- case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
- case R.id.accessibilityActionScrollUp:
- if (mCollapseOffset < mCollapsibleHeight) {
- smoothScrollTo(mCollapsibleHeight, 0);
- return true;
- } else if ((mCollapseOffset < mCollapsibleHeight + mUncollapsibleHeight)
- && isDismissable()) {
- smoothScrollTo(mCollapsibleHeight + mUncollapsibleHeight, 0);
- mDismissOnScrollerFinished = true;
- return true;
- }
- break;
case AccessibilityNodeInfo.ACTION_COLLAPSE:
if (mCollapseOffset < mCollapsibleHeight) {
smoothScrollTo(mCollapsibleHeight, 0);
@@ -886,7 +874,6 @@
}
if ((mCollapseOffset < mCollapsibleHeight + mUncollapsibleHeight)
&& ((mCollapseOffset < mCollapsibleHeight) || isDismissable())) {
- info.addAction(AccessibilityAction.ACTION_SCROLL_BACKWARD);
info.addAction(AccessibilityAction.ACTION_SCROLL_UP);
info.setScrollable(true);
}
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index c5a4588..22bb210 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -1149,6 +1149,21 @@
return N;
}
+static jintArray convertEncapsulationInfoFromNative(JNIEnv *env, uint32_t encapsulationInfo) {
+ std::vector<int> encapsulation;
+ // Ignore the first bit, which is ENCAPSULATION_.*_NONE, as an empty array
+ // should be returned if no encapsulation is supported.
+ encapsulationInfo >>= 1;
+ for (int bitPosition = 1; encapsulationInfo; encapsulationInfo >>= 1, bitPosition++) {
+ if (encapsulationInfo & 1) {
+ encapsulation.push_back(bitPosition);
+ }
+ }
+ jintArray result = env->NewIntArray(encapsulation.size());
+ env->SetIntArrayRegion(result, 0, encapsulation.size(), (jint *)encapsulation.data());
+ return result;
+}
+
static jint convertAudioPortFromNative(JNIEnv *env,
jobject *jAudioPort, const struct audio_port *nAudioPort)
{
@@ -1156,6 +1171,8 @@
jintArray jSamplingRates = NULL;
jintArray jChannelMasks = NULL;
jintArray jChannelIndexMasks = NULL;
+ jintArray jEncapsulationModes = NULL;
+ jintArray jEncapsulationMetadataTypes = NULL;
int* cFormats = NULL;
jintArray jFormats = NULL;
jobjectArray jGains = NULL;
@@ -1316,11 +1333,16 @@
if (nAudioPort->type == AUDIO_PORT_TYPE_DEVICE) {
ALOGV("convertAudioPortFromNative is a device %08x", nAudioPort->ext.device.type);
jstring jAddress = env->NewStringUTF(nAudioPort->ext.device.address);
- *jAudioPort = env->NewObject(gAudioDevicePortClass, gAudioDevicePortCstor,
- jHandle, jDeviceName,
- jSamplingRates, jChannelMasks, jChannelIndexMasks,
- jFormats, jGains,
- nAudioPort->ext.device.type, jAddress);
+ jEncapsulationModes =
+ convertEncapsulationInfoFromNative(env, nAudioPort->ext.device.encapsulation_modes);
+ jEncapsulationMetadataTypes =
+ convertEncapsulationInfoFromNative(env,
+ nAudioPort->ext.device
+ .encapsulation_metadata_types);
+ *jAudioPort = env->NewObject(gAudioDevicePortClass, gAudioDevicePortCstor, jHandle,
+ jDeviceName, jSamplingRates, jChannelMasks, jChannelIndexMasks,
+ jFormats, jGains, nAudioPort->ext.device.type, jAddress,
+ jEncapsulationModes, jEncapsulationMetadataTypes);
env->DeleteLocalRef(jAddress);
} else if (nAudioPort->type == AUDIO_PORT_TYPE_MIX) {
ALOGV("convertAudioPortFromNative is a mix");
@@ -1362,6 +1384,12 @@
if (jChannelIndexMasks != NULL) {
env->DeleteLocalRef(jChannelIndexMasks);
}
+ if (jEncapsulationModes != NULL) {
+ env->DeleteLocalRef(jEncapsulationModes);
+ }
+ if (jEncapsulationMetadataTypes != NULL) {
+ env->DeleteLocalRef(jEncapsulationMetadataTypes);
+ }
if (cFormats != NULL) {
delete[] cFormats;
}
@@ -2615,8 +2643,10 @@
jclass audioDevicePortClass = FindClassOrDie(env, "android/media/AudioDevicePort");
gAudioDevicePortClass = MakeGlobalRefOrDie(env, audioDevicePortClass);
- gAudioDevicePortCstor = GetMethodIDOrDie(env, audioDevicePortClass, "<init>",
- "(Landroid/media/AudioHandle;Ljava/lang/String;[I[I[I[I[Landroid/media/AudioGain;ILjava/lang/String;)V");
+ gAudioDevicePortCstor =
+ GetMethodIDOrDie(env, audioDevicePortClass, "<init>",
+ "(Landroid/media/AudioHandle;Ljava/lang/String;[I[I[I[I"
+ "[Landroid/media/AudioGain;ILjava/lang/String;[I[I)V");
// When access AudioPort as AudioDevicePort
gAudioPortFields.mType = GetFieldIDOrDie(env, audioDevicePortClass, "mType", "I");
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 3a5720fd..c5bc083 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -1552,8 +1552,8 @@
}
}
-static void BindMountStorageToLowerFs(const userid_t user_id, const char* dir_name,
- const char* package, fail_fn_t fail_fn) {
+static void BindMountStorageToLowerFs(const userid_t user_id, const uid_t uid,
+ const char* dir_name, const char* package, fail_fn_t fail_fn) {
bool hasSdcardFs = IsFilesystemSupported("sdcardfs");
std::string source;
@@ -1565,6 +1565,9 @@
}
std::string target = StringPrintf("/storage/emulated/%d/%s/%s", user_id, dir_name, package);
+ // As the parent is mounted as tmpfs, we need to create the target dir here.
+ PrepareDirIfNotPresent(target, 0700, uid, uid, fail_fn);
+
if (access(source.c_str(), F_OK) != 0) {
fail_fn(CREATE_ERROR("Error accessing %s: %s", source.c_str(), strerror(errno)));
}
@@ -1574,9 +1577,8 @@
BindMount(source, target, fail_fn);
}
-// Bind mount all obb & data directories that are visible to this app.
-// If app data isolation is not enabled for this process, bind mount the whole obb
-// and data directory instead.
+// Mount tmpfs on Android/data and Android/obb, then bind mount all app visible package
+// directories in data and obb directories.
static void BindMountStorageDirs(JNIEnv* env, jobjectArray pkg_data_info_list,
uid_t uid, const char* process_name, jstring managed_nice_name, fail_fn_t fail_fn) {
@@ -1590,12 +1592,18 @@
fail_fn(CREATE_ERROR("Data package list cannot be empty"));
}
+ // Create tmpfs on Android/obb and Android/data so these 2 dirs won't enter fuse anymore.
+ std::string androidObbDir = StringPrintf("/storage/emulated/%d/Android/obb", user_id);
+ MountAppDataTmpFs(androidObbDir, fail_fn);
+ std::string androidDataDir = StringPrintf("/storage/emulated/%d/Android/data", user_id);
+ MountAppDataTmpFs(androidDataDir, fail_fn);
+
// Bind mount each package obb directory
for (int i = 0; i < size; i += 3) {
jstring package_str = (jstring) (env->GetObjectArrayElement(pkg_data_info_list, i));
std::string packageName = extract_fn(package_str).value();
- BindMountStorageToLowerFs(user_id, "Android/obb", packageName.c_str(), fail_fn);
- BindMountStorageToLowerFs(user_id, "Android/data", packageName.c_str(), fail_fn);
+ BindMountStorageToLowerFs(user_id, uid, "Android/obb", packageName.c_str(), fail_fn);
+ BindMountStorageToLowerFs(user_id, uid, "Android/data", packageName.c_str(), fail_fn);
}
}
@@ -1648,9 +1656,10 @@
uid, process_name, managed_nice_name, fail_fn);
isolateJitProfile(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
}
- if (mount_external != MOUNT_EXTERNAL_INSTALLER &&
- mount_external != MOUNT_EXTERNAL_PASS_THROUGH &&
- mount_storage_dirs) {
+ // MOUNT_EXTERNAL_INSTALLER, MOUNT_EXTERNAL_PASS_THROUGH, MOUNT_EXTERNAL_ANDROID_WRITABLE apps
+ // will have mount_storage_dirs == false here (set by ProcessList.needsStorageDataIsolation()),
+ // and hence they won't bind mount storage dirs.
+ if (mount_storage_dirs) {
BindMountStorageDirs(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
}
diff --git a/core/res/res/layout/chooser_list_per_profile.xml b/core/res/res/layout/chooser_list_per_profile.xml
index 6b1b002..86dc71c 100644
--- a/core/res/res/layout/chooser_list_per_profile.xml
+++ b/core/res/res/layout/chooser_list_per_profile.xml
@@ -20,7 +20,7 @@
<com.android.internal.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layoutManager="com.android.internal.widget.GridLayoutManager"
+ android:layoutManager="com.android.internal.app.ChooserGridLayoutManager"
android:id="@+id/resolver_list"
android:clipToPadding="false"
android:background="?attr/colorBackgroundFloating"
@@ -29,4 +29,4 @@
android:nestedScrollingEnabled="true" />
<include layout="@layout/resolver_empty_states" />
-</RelativeLayout>
\ No newline at end of file
+</RelativeLayout>
diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml
index 4d0837f..446ce3f 100644
--- a/core/res/res/layout/resolver_list.xml
+++ b/core/res/res/layout/resolver_list.xml
@@ -83,6 +83,7 @@
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
+ android:accessibilityTraversalAfter="@id/title"
android:background="?attr/colorBackgroundFloating">
<LinearLayout
android:orientation="vertical"
diff --git a/core/res/res/values-mcc334-mnc020/config.xml b/core/res/res/values-mcc334-mnc020/config.xml
index 0970517..c64acc7 100644
--- a/core/res/res/values-mcc334-mnc020/config.xml
+++ b/core/res/res/values-mcc334-mnc020/config.xml
@@ -18,4 +18,7 @@
-->
<resources>
<bool name="config_use_sim_language_file">false</bool>
+
+ <bool name="config_pdp_rejeect_enable_retry">true</bool>
+ <integer name="config_pdp_reject_retry_delay_ms">45000</integer>
</resources>
\ No newline at end of file
diff --git a/core/res/res/values-mcc334-mnc020/strings.xml b/core/res/res/values-mcc334-mnc020/strings.xml
new file mode 100644
index 0000000..a8a78d5
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2020, 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title"></string>
+ <string name="config_pdp_reject_user_authentication_failed">AUTHENTICATION FAILURE -29-</string>
+ <string name="config_pdp_reject_service_not_subscribed">NOT SUBSCRIBED TO SERVICE -33-</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed">Multiple PDN connections for a given APN not allowed -55-</string>
+</resources>
\ No newline at end of file
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index fa4c25a..003c0da 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3899,7 +3899,7 @@
<string name="config_managed_provisioning_package" translatable="false">com.android.managedprovisioning</string>
<!-- Whether or not swipe up gesture's opt-in setting is available on this device -->
- <bool name="config_swipe_up_gesture_setting_available">false</bool>
+ <bool name="config_swipe_up_gesture_setting_available">true</bool>
<!-- Applications which are disabled unless matching a particular sku -->
<string-array name="config_disableApksUnlessMatchedSku_apk_list" translatable="false" />
@@ -4421,4 +4421,8 @@
<!-- Set to true to make assistant show in front of the dream/screensaver. -->
<bool name="config_assistantOnTopOfDream">false</bool>
+ <!-- pdp data retry for cause 29, 33 and 55 -->
+ <bool name="config_pdp_reject_enable_retry">false</bool>
+ <!-- pdp data reject retry delay in ms -->
+ <integer name="config_pdp_reject_retry_delay_ms">-1</integer>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index bbe547b..2ac61ec 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -559,6 +559,10 @@
<dimen name="resolver_max_width">480dp</dimen>
+ <!-- Tile Stroke width -->
+ <dimen name="config_qsTileStrokeWidthActive">-1dp</dimen>
+ <dimen name="config_qsTileStrokeWidthInactive">-1dp</dimen>
+
<!-- Amount to reduce the size of the circular mask by (to compensate for
aliasing effects). This is only used on circular displays. -->
<dimen name="circular_display_mask_thickness">1px</dimen>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 51b23db..35a7857 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5747,4 +5747,13 @@
<string name="PERSOSUBSTATE_SIM_IMPI_SUCCESS">IMPI unlock successful.</string>
<!-- Success message displayed on SIM NS_SP Depersonalization panel [CHAR LIMIT=none] -->
<string name="PERSOSUBSTATE_SIM_NS_SP_SUCCESS">Network subset service provider unlock successful.</string>
+
+ <!-- pdp data reject dialog string for cause 29, 33 and 55 [CHAR LIMIT=100] -->
+ <string name="config_pdp_reject_dialog_title"></string>
+ <!-- pdp data reject dialog string for cause 29 (USER_AUTHENTICATION) [CHAR LIMIT=100] -->
+ <string name="config_pdp_reject_user_authentication_failed"></string>
+ <!-- pdp data reject dialog string for cause 33 (SERVICE_OPTION_NOT_SUBSCRIBED) [CHAR LIMIT=100] -->
+ <string name="config_pdp_reject_service_not_subscribed"></string>
+ <!-- pdp data reject dialog string for cause 55 (MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED) [CHAR LIMIT=100] -->
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed"></string>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index bcfa1fe..9a7f07f 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -361,6 +361,8 @@
<java-symbol type="bool" name="config_disableUsbPermissionDialogs"/>
<java-symbol type="dimen" name="config_highResTaskSnapshotScale" />
<java-symbol type="dimen" name="config_lowResTaskSnapshotScale" />
+ <java-symbol type="dimen" name="config_qsTileStrokeWidthInactive" />
+ <java-symbol type="dimen" name="config_qsTileStrokeWidthActive" />
<java-symbol type="bool" name="config_use16BitTaskSnapshotPixelFormat" />
<java-symbol type="bool" name="config_hasRecents" />
<java-symbol type="string" name="config_recentsComponentName" />
@@ -4001,4 +4003,11 @@
<java-symbol type="string" name="notification_channel_network_alerts" />
<java-symbol type="string" name="notification_channel_network_available" />
+ <!-- For Pdn throttle feature -->
+ <java-symbol type="bool" name="config_pdp_reject_enable_retry" />
+ <java-symbol type="integer" name="config_pdp_reject_retry_delay_ms" />
+ <java-symbol type="string" name="config_pdp_reject_dialog_title" />
+ <java-symbol type="string" name="config_pdp_reject_user_authentication_failed" />
+ <java-symbol type="string" name="config_pdp_reject_service_not_subscribed" />
+ <java-symbol type="string" name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" />
</resources>
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index 6720ed6..0490678 100644
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -2548,9 +2548,18 @@
} else {
installFromRawResource(apk2Name, apk2, 0, false, false, -1,
PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
- int match = mContext.getPackageManager().checkSignatures(pkg1.getPackageName(),
- pkg2.getPackageName());
- assertEquals(expMatchResult, match);
+ // TODO: All checkSignatures tests should return the same result regardless of
+ // querying by package name or uid; however if there are any edge cases where
+ // individual packages within a shareduid are compared with signatures that do not
+ // match the full lineage of the shareduid this method should be overloaded to
+ // accept the expected response for the uid query.
+ PackageManager pm = getPm();
+ int matchByName = pm.checkSignatures(pkg1.getPackageName(), pkg2.getPackageName());
+ int pkg1Uid = pm.getApplicationInfo(pkg1.getPackageName(), 0).uid;
+ int pkg2Uid = pm.getApplicationInfo(pkg2.getPackageName(), 0).uid;
+ int matchByUid = pm.checkSignatures(pkg1Uid, pkg2Uid);
+ assertEquals(expMatchResult, matchByName);
+ assertEquals(expMatchResult, matchByUid);
}
} finally {
if (cleanUp) {
diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
index 164c372..bfcf52a 100644
--- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
@@ -100,12 +100,12 @@
// test if setVisibility can show IME
mImeConsumer.onWindowFocusGained();
mImeConsumer.applyImeVisibility(true);
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertTrue(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
// test if setVisibility can hide IME
mImeConsumer.applyImeVisibility(false);
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
});
}
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index cc85332..d4c2569 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -245,14 +245,14 @@
mController.applyImeVisibility(true /* setVisible */);
mController.show(Type.all());
// quickly jump to final state by cancelling it.
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
assertTrue(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
mController.applyImeVisibility(false /* setVisible */);
mController.hide(Type.all());
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
@@ -268,10 +268,10 @@
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
mController.getSourceConsumer(ITYPE_IME).onWindowFocusGained();
mController.applyImeVisibility(true);
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertTrue(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
mController.applyImeVisibility(false);
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
mController.getSourceConsumer(ITYPE_IME).onWindowFocusLost();
});
@@ -291,7 +291,7 @@
mController.hide(types);
assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_NAVIGATION_BAR));
assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_STATUS_BAR));
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertEquals(ANIMATION_TYPE_NONE, mController.getAnimationType(ITYPE_NAVIGATION_BAR));
assertEquals(ANIMATION_TYPE_NONE, mController.getAnimationType(ITYPE_STATUS_BAR));
assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
@@ -302,7 +302,7 @@
mController.show(types);
assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ITYPE_NAVIGATION_BAR));
assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ITYPE_STATUS_BAR));
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
@@ -321,21 +321,21 @@
int types = Type.navigationBars() | Type.systemBars();
// test show select types.
mController.show(types);
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
// test hide all
mController.hide(Type.all());
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
// test single show
mController.show(Type.navigationBars());
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
@@ -363,7 +363,7 @@
mController.hide(Type.systemBars());
assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_NAVIGATION_BAR));
assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_STATUS_BAR));
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
@@ -372,7 +372,7 @@
mController.show(Type.systemBars());
assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ITYPE_NAVIGATION_BAR));
assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ITYPE_STATUS_BAR));
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertTrue(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
@@ -383,7 +383,7 @@
mController.hide(Type.navigationBars());
assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_NAVIGATION_BAR));
assertEquals(ANIMATION_TYPE_NONE, mController.getAnimationType(ITYPE_STATUS_BAR));
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
@@ -391,7 +391,7 @@
mController.hide(Type.systemBars());
assertEquals(ANIMATION_TYPE_NONE, mController.getAnimationType(ITYPE_NAVIGATION_BAR));
assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_STATUS_BAR));
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
@@ -411,13 +411,13 @@
// show two at a time and hide one by one.
mController.show(types);
mController.hide(Type.navigationBars());
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
assertTrue(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
mController.hide(Type.systemBars());
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertFalse(mController.getSourceConsumer(navBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(statusBar.getType()).isRequestedVisible());
assertFalse(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
@@ -431,7 +431,7 @@
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
mController.hide(Type.statusBars());
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertFalse(mController.getSourceConsumer(ITYPE_STATUS_BAR).isRequestedVisible());
assertFalse(mController.getState().getSource(ITYPE_STATUS_BAR).isVisible());
@@ -446,7 +446,7 @@
// Gaining control
mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR));
assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_STATUS_BAR));
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertFalse(mController.getSourceConsumer(ITYPE_STATUS_BAR).isRequestedVisible());
assertFalse(mController.getState().getSource(ITYPE_STATUS_BAR).isVisible());
});
@@ -468,7 +468,7 @@
mController.onControlsChanged(createSingletonControl(ITYPE_IME));
assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ITYPE_IME));
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertTrue(mController.getSourceConsumer(ITYPE_IME).isRequestedVisible());
assertTrue(mController.getState().getSource(ITYPE_IME).isVisible());
});
@@ -489,7 +489,7 @@
mController.show(ime(), true /* fromIme */);
assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ITYPE_IME));
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertTrue(mController.getSourceConsumer(ITYPE_IME).isRequestedVisible());
assertTrue(mController.getState().getSource(ITYPE_IME).isVisible());
});
@@ -658,7 +658,7 @@
mController.getState().getSource(ITYPE_IME).getFrame());
assertNotEquals(new Rect(4, 5, 6, 7),
mController.getState().getSource(ITYPE_IME).getVisibleFrame());
- mController.cancelExistingAnimation();
+ mController.cancelExistingAnimations();
assertEquals(new Rect(0, 1, 2, 3),
mController.getState().getSource(ITYPE_IME).getFrame());
assertEquals(new Rect(4, 5, 6, 7),
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 6af887d..9cd7cc6 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -197,6 +197,9 @@
<split-permission name="android.permission.WRITE_EXTERNAL_STORAGE">
<new-permission name="android.permission.READ_EXTERNAL_STORAGE" />
</split-permission>
+ <split-permission name="android.permission.READ_PRIVILEGED_PHONE_STATE">
+ <new-permission name="android.permission.READ_PHONE_STATE" />
+ </split-permission>
<split-permission name="android.permission.READ_CONTACTS"
targetSdk="16">
<new-permission name="android.permission.READ_CALL_LOG" />
diff --git a/libs/hwui/jni/pdf/PdfEditor.cpp b/libs/hwui/jni/pdf/PdfEditor.cpp
index 828d6e3..e65921a 100644
--- a/libs/hwui/jni/pdf/PdfEditor.cpp
+++ b/libs/hwui/jni/pdf/PdfEditor.cpp
@@ -110,7 +110,7 @@
jlong transformPtr, jint clipLeft, jint clipTop, jint clipRight, jint clipBottom) {
FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr);
- FPDF_PAGE* page = (FPDF_PAGE*) FPDF_LoadPage(document, pageIndex);
+ FPDF_PAGE page = FPDF_LoadPage(document, pageIndex);
if (!page) {
jniThrowException(env, "java/lang/IllegalStateException",
"cannot open page");
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 8d5acc6..24a6228 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -157,21 +157,7 @@
}
}
-static void setBufferCount(ANativeWindow* window, uint32_t extraBuffers) {
- int query_value;
- int err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value);
- if (err != 0 || query_value < 0) {
- ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, query_value);
- return;
- }
- auto min_undequeued_buffers = static_cast<uint32_t>(query_value);
-
- int bufferCount = min_undequeued_buffers + 2 + extraBuffers;
- native_window_set_buffer_count(window, bufferCount);
-}
-
-bool SkiaOpenGLPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior,
- uint32_t extraBuffers) {
+bool SkiaOpenGLPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior) {
if (mEglSurface != EGL_NO_SURFACE) {
mEglManager.destroySurface(mEglSurface);
mEglSurface = EGL_NO_SURFACE;
@@ -189,7 +175,6 @@
if (mEglSurface != EGL_NO_SURFACE) {
const bool preserveBuffer = (swapBehavior != SwapBehavior::kSwap_discardBuffer);
mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer);
- setBufferCount(surface, extraBuffers);
return true;
}
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
index fc6e114..fddd97f 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
@@ -45,8 +45,7 @@
bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
FrameInfo* currentFrameInfo, bool* requireSwap) override;
DeferredLayerUpdater* createTextureLayer() override;
- bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior,
- uint32_t extraBuffers) override;
+ bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior) override;
void onStop() override;
bool isSurfaceReady() override;
bool isContextReady() override;
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index 535a199..212a428 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -116,8 +116,7 @@
void SkiaVulkanPipeline::onStop() {}
-bool SkiaVulkanPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior,
- uint32_t extraBuffers) {
+bool SkiaVulkanPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior) {
if (mVkSurface) {
mVkManager.destroySurface(mVkSurface);
mVkSurface = nullptr;
@@ -127,7 +126,7 @@
mRenderThread.requireVkContext();
mVkSurface =
mVkManager.createSurface(surface, mColorMode, mSurfaceColorSpace, mSurfaceColorType,
- mRenderThread.getGrContext(), extraBuffers);
+ mRenderThread.getGrContext(), 0);
}
return mVkSurface != nullptr;
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
index c8bf233..6268daa 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
@@ -42,8 +42,7 @@
bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
FrameInfo* currentFrameInfo, bool* requireSwap) override;
DeferredLayerUpdater* createTextureLayer() override;
- bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior,
- uint32_t extraBuffers) override;
+ bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior) override;
void onStop() override;
bool isSurfaceReady() override;
bool isContextReady() override;
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 335bcdc..a362bd2 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -139,20 +139,22 @@
mAnimationContext->destroy();
}
+static void setBufferCount(ANativeWindow* window, uint32_t extraBuffers) {
+ int query_value;
+ int err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value);
+ if (err != 0 || query_value < 0) {
+ ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, query_value);
+ return;
+ }
+ auto min_undequeued_buffers = static_cast<uint32_t>(query_value);
+
+ int bufferCount = min_undequeued_buffers + 2 + extraBuffers;
+ native_window_set_buffer_count(window, bufferCount);
+}
+
void CanvasContext::setSurface(ANativeWindow* window, bool enableTimeout) {
ATRACE_CALL();
- if (window) {
- mNativeSurface = std::make_unique<ReliableSurface>(window);
- mNativeSurface->init();
- if (enableTimeout) {
- // TODO: Fix error handling & re-shorten timeout
- ANativeWindow_setDequeueTimeout(window, 4000_ms);
- }
- } else {
- mNativeSurface = nullptr;
- }
-
if (mRenderAheadDepth == 0 && DeviceInfo::get()->getMaxRefreshRate() > 66.6f) {
mFixedRenderAhead = false;
mRenderAheadCapacity = 1;
@@ -161,9 +163,24 @@
mRenderAheadCapacity = mRenderAheadDepth;
}
+ if (window) {
+ mNativeSurface = std::make_unique<ReliableSurface>(window);
+ mNativeSurface->init();
+ if (enableTimeout) {
+ // TODO: Fix error handling & re-shorten timeout
+ ANativeWindow_setDequeueTimeout(window, 4000_ms);
+ }
+ mNativeSurface->setExtraBufferCount(mRenderAheadCapacity);
+ } else {
+ mNativeSurface = nullptr;
+ }
+
bool hasSurface = mRenderPipeline->setSurface(
- mNativeSurface ? mNativeSurface->getNativeWindow() : nullptr, mSwapBehavior,
- mRenderAheadCapacity);
+ mNativeSurface ? mNativeSurface->getNativeWindow() : nullptr, mSwapBehavior);
+
+ if (mNativeSurface && !mNativeSurface->didSetExtraBuffers()) {
+ setBufferCount(mNativeSurface->getNativeWindow(), mRenderAheadCapacity);
+ }
mFrameNumber = -1;
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index ba0d64c..c3c2286 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -66,8 +66,7 @@
virtual bool swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty,
FrameInfo* currentFrameInfo, bool* requireSwap) = 0;
virtual DeferredLayerUpdater* createTextureLayer() = 0;
- virtual bool setSurface(ANativeWindow* window, SwapBehavior swapBehavior,
- uint32_t extraBuffers) = 0;
+ virtual bool setSurface(ANativeWindow* window, SwapBehavior swapBehavior) = 0;
virtual void onStop() = 0;
virtual bool isSurfaceReady() = 0;
virtual bool isContextReady() = 0;
diff --git a/libs/hwui/renderthread/ReliableSurface.cpp b/libs/hwui/renderthread/ReliableSurface.cpp
index 8a0b4e8..dcf1fc1 100644
--- a/libs/hwui/renderthread/ReliableSurface.cpp
+++ b/libs/hwui/renderthread/ReliableSurface.cpp
@@ -19,6 +19,7 @@
#include <log/log_main.h>
#include <private/android/AHardwareBufferHelpers.h>
// TODO: this should be including apex instead.
+#include <system/window.h>
#include <vndk/window.h>
namespace android::uirenderer::renderthread {
@@ -44,6 +45,7 @@
ANativeWindow_setDequeueBufferInterceptor(mWindow, nullptr, nullptr);
ANativeWindow_setQueueBufferInterceptor(mWindow, nullptr, nullptr);
ANativeWindow_setPerformInterceptor(mWindow, nullptr, nullptr);
+ ANativeWindow_setQueryInterceptor(mWindow, nullptr, nullptr);
ANativeWindow_release(mWindow);
}
@@ -63,6 +65,10 @@
result = ANativeWindow_setPerformInterceptor(mWindow, hook_perform, this);
LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set perform interceptor: error = %d",
result);
+
+ result = ANativeWindow_setQueryInterceptor(mWindow, hook_query, this);
+ LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set query interceptor: error = %d",
+ result);
}
int ReliableSurface::reserveNext() {
@@ -249,9 +255,29 @@
case ANATIVEWINDOW_PERFORM_SET_BUFFERS_FORMAT:
rs->mFormat = static_cast<AHardwareBuffer_Format>(va_arg(args, int32_t));
break;
+ case NATIVE_WINDOW_SET_BUFFER_COUNT:
+ size_t bufferCount = va_arg(args, size_t);
+ if (bufferCount >= rs->mExpectedBufferCount) {
+ rs->mDidSetExtraBuffers = true;
+ } else {
+ ALOGD("HOOK FAILED! Expected %zd got = %zd", rs->mExpectedBufferCount, bufferCount);
+ }
+ break;
}
}
return result;
}
+int ReliableSurface::hook_query(const ANativeWindow *window, ANativeWindow_queryFn query,
+ void *data, int what, int *value) {
+ ReliableSurface* rs = reinterpret_cast<ReliableSurface*>(data);
+ int result = query(window, what, value);
+ if (what == ANATIVEWINDOW_QUERY_MIN_UNDEQUEUED_BUFFERS && result == OK) {
+ std::lock_guard _lock{rs->mMutex};
+ *value += rs->mExtraBuffers;
+ rs->mExpectedBufferCount = *value + 2;
+ }
+ return result;
+}
+
}; // namespace android::uirenderer::renderthread
diff --git a/libs/hwui/renderthread/ReliableSurface.h b/libs/hwui/renderthread/ReliableSurface.h
index 58cd067..f699eb1 100644
--- a/libs/hwui/renderthread/ReliableSurface.h
+++ b/libs/hwui/renderthread/ReliableSurface.h
@@ -17,6 +17,7 @@
#pragma once
#include <android-base/unique_fd.h>
+#include <system/window.h>
#include <apex/window.h>
#include <utils/Errors.h>
#include <utils/Macros.h>
@@ -49,6 +50,16 @@
return ret;
}
+ void setExtraBufferCount(size_t extraBuffers) {
+ std::lock_guard _lock{mMutex};
+ mExtraBuffers = extraBuffers;
+ }
+
+ bool didSetExtraBuffers() const {
+ std::lock_guard _lock{mMutex};
+ return mDidSetExtraBuffers;
+ }
+
private:
ANativeWindow* mWindow;
@@ -62,6 +73,9 @@
base::unique_fd mReservedFenceFd;
bool mHasDequeuedBuffer = false;
int mBufferQueueState = OK;
+ size_t mExtraBuffers = 0;
+ size_t mExpectedBufferCount = 0;
+ bool mDidSetExtraBuffers = false;
bool isFallbackBuffer(const ANativeWindowBuffer* windowBuffer) const;
ANativeWindowBuffer* acquireFallbackBuffer(int error);
@@ -81,6 +95,8 @@
static int hook_perform(ANativeWindow* window, ANativeWindow_performFn perform, void* data,
int operation, va_list args);
+ static int hook_query(const ANativeWindow* window, ANativeWindow_queryFn query, void* data,
+ int what, int* value);
};
}; // namespace android::uirenderer::renderthread
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 1208062..e7a889d 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -398,7 +398,7 @@
auto surface = context.surface();
auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
EXPECT_FALSE(pipeline->isSurfaceReady());
- EXPECT_TRUE(pipeline->setSurface(surface.get(), SwapBehavior::kSwap_default, 0));
+ EXPECT_TRUE(pipeline->setSurface(surface.get(), SwapBehavior::kSwap_default));
EXPECT_TRUE(pipeline->isSurfaceReady());
renderThread.destroyRenderingContext();
EXPECT_FALSE(pipeline->isSurfaceReady());
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index 6b0e17d..2cca669 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -455,8 +455,7 @@
* may be an empty array if no encapsulation modes are supported.
*/
public @NonNull @AudioTrack.EncapsulationMode int[] getEncapsulationModes() {
- // Implement a getter in r-dev or r-tv-dev as needed.
- return new int[0]; // be careful of returning a copy of any internal data.
+ return mPort.encapsulationModes();
}
/**
@@ -474,8 +473,7 @@
* may be an empty array if no metadata types are supported.
*/
public @NonNull @AudioTrack.EncapsulationMetadataType int[] getEncapsulationMetadataTypes() {
- // Implement a getter in r-dev or r-tv-dev as needed.
- return new int[0]; // be careful of returning a copy of any internal data.
+ return mPort.encapsulationMetadataTypes();
}
/**
diff --git a/media/java/android/media/AudioDevicePort.java b/media/java/android/media/AudioDevicePort.java
index 51909db..42d0f0c 100644
--- a/media/java/android/media/AudioDevicePort.java
+++ b/media/java/android/media/AudioDevicePort.java
@@ -16,8 +16,11 @@
package android.media;
+import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
+import java.util.Arrays;
+
/**
* The AudioDevicePort is a specialized type of AudioPort
* describing an input (e.g microphone) or output device (e.g speaker)
@@ -35,17 +38,22 @@
private final int mType;
private final String mAddress;
+ private final int[] mEncapsulationModes;
+ private final int[] mEncapsulationMetadataTypes;
@UnsupportedAppUsage
AudioDevicePort(AudioHandle handle, String deviceName,
int[] samplingRates, int[] channelMasks, int[] channelIndexMasks,
- int[] formats, AudioGain[] gains, int type, String address) {
+ int[] formats, AudioGain[] gains, int type, String address, int[] encapsulationModes,
+ @AudioTrack.EncapsulationMetadataType int[] encapsulationMetadataTypes) {
super(handle,
(AudioManager.isInputDevice(type) == true) ?
AudioPort.ROLE_SOURCE : AudioPort.ROLE_SINK,
deviceName, samplingRates, channelMasks, channelIndexMasks, formats, gains);
mType = type;
mAddress = address;
+ mEncapsulationModes = encapsulationModes;
+ mEncapsulationMetadataTypes = encapsulationMetadataTypes;
}
/**
@@ -72,6 +80,31 @@
}
/**
+ * Get supported encapsulation modes.
+ */
+ public @NonNull @AudioTrack.EncapsulationMode int[] encapsulationModes() {
+ if (mEncapsulationModes == null) {
+ return new int[0];
+ }
+ return Arrays.stream(mEncapsulationModes).boxed()
+ .filter(mode -> mode != AudioTrack.ENCAPSULATION_MODE_HANDLE)
+ .mapToInt(Integer::intValue).toArray();
+ }
+
+ /**
+ * Get supported encapsulation metadata types.
+ */
+ public @NonNull @AudioTrack.EncapsulationMetadataType int[] encapsulationMetadataTypes() {
+ if (mEncapsulationMetadataTypes == null) {
+ return new int[0];
+ }
+ int[] encapsulationMetadataTypes = new int[mEncapsulationMetadataTypes.length];
+ System.arraycopy(mEncapsulationMetadataTypes, 0,
+ encapsulationMetadataTypes, 0, mEncapsulationMetadataTypes.length);
+ return encapsulationMetadataTypes;
+ }
+
+ /**
* Build a specific configuration of this audio device port for use by methods
* like AudioManager.connectAudioPatch().
*/
diff --git a/media/java/android/media/AudioMetadata.java b/media/java/android/media/AudioMetadata.java
index c91ff0d..ff9fd41 100644
--- a/media/java/android/media/AudioMetadata.java
+++ b/media/java/android/media/AudioMetadata.java
@@ -166,10 +166,25 @@
*
* A Boolean value which is true if Atmos is present in an E-AC3 stream.
*/
+
+ // Since Boolean isn't handled by Parceling, we translate
+ // internally to KEY_HAS_ATMOS when sending through JNI.
+ // Consider deprecating this key for KEY_HAS_ATMOS in the future.
+ //
@NonNull public static final Key<Boolean> KEY_ATMOS_PRESENT =
createKey("atmos-present", Boolean.class);
/**
+ * A key representing the presence of Atmos in an E-AC3 stream.
+ *
+ * An Integer value which is nonzero if Atmos is present in an E-AC3 stream.
+ * The integer representation is used for communication to the native side.
+ * @hide
+ */
+ @NonNull public static final Key<Integer> KEY_HAS_ATMOS =
+ createKey("has-atmos", Integer.class);
+
+ /**
* A key representing the audio encoding used for the stream.
* This is the same encoding used in {@link AudioFormat#getEncoding()}.
*
@@ -731,6 +746,15 @@
Log.e(TAG, "Failed to unpack value for map");
return null;
}
+
+ // Special handling of KEY_ATMOS_PRESENT.
+ if (key.equals(Format.KEY_HAS_ATMOS.getName())
+ && value.first == Format.KEY_HAS_ATMOS.getValueClass()) {
+ ret.set(Format.KEY_ATMOS_PRESENT,
+ (Boolean) ((int) value.second != 0)); // Translate Integer to Boolean
+ continue; // Should we store both keys in the java table?
+ }
+
ret.set(createKey(key, value.first), value.first.cast(value.second));
}
return ret;
@@ -746,11 +770,19 @@
return false;
}
for (Key<?> key : obj.keySet()) {
+ Object value = obj.get(key);
+
+ // Special handling of KEY_ATMOS_PRESENT.
+ if (key == Format.KEY_ATMOS_PRESENT) {
+ key = Format.KEY_HAS_ATMOS;
+ value = (Integer) ((boolean) value ? 1 : 0); // Translate Boolean to Integer
+ }
+
if (!strDataPackage.pack(output, key.getName())) {
Log.i(TAG, "Failed to pack key: " + key.getName());
return false;
}
- if (!OBJECT_PACKAGE.pack(output, new Pair<>(key.getValueClass(), obj.get(key)))) {
+ if (!OBJECT_PACKAGE.pack(output, new Pair<>(key.getValueClass(), value))) {
Log.i(TAG, "Failed to pack value: " + obj.get(key));
return false;
}
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 6c9013f..0dc019c 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -170,8 +170,7 @@
public MediaController getMediaControllerForRoutingSession(
@NonNull RoutingSessionInfo sessionInfo) {
for (MediaController controller : mMediaSessionManager.getActiveSessions(null)) {
- String volumeControlId = controller.getPlaybackInfo().getVolumeControlId();
- if (TextUtils.equals(sessionInfo.getId(), volumeControlId)) {
+ if (areSessionsMatched(controller, sessionInfo)) {
return controller;
}
}
@@ -206,6 +205,37 @@
}
/**
+ * Gets available routes for the given routing session.
+ * The returned routes can be passed to
+ * {@link #transfer(RoutingSessionInfo, MediaRoute2Info)} for transferring the routing session.
+ *
+ * @param sessionInfo the routing session that would be transferred
+ */
+ @NonNull
+ public List<MediaRoute2Info> getAvailableRoutesForRoutingSession(
+ @NonNull RoutingSessionInfo sessionInfo) {
+ Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
+
+ List<MediaRoute2Info> routes = new ArrayList<>();
+
+ String packageName = sessionInfo.getClientPackageName();
+ List<String> preferredFeatures = mPreferredFeaturesMap.get(packageName);
+ if (preferredFeatures == null) {
+ preferredFeatures = Collections.emptyList();
+ }
+ synchronized (mRoutesLock) {
+ for (MediaRoute2Info route : mRoutes.values()) {
+ if (route.isSystemRoute() || route.hasAnyFeatures(preferredFeatures)
+ || sessionInfo.getSelectedRoutes().contains(route.getId())
+ || sessionInfo.getTransferableRoutes().contains(route.getId())) {
+ routes.add(route);
+ }
+ }
+ }
+ return routes;
+ }
+
+ /**
* Gets the system routing session associated with no specific application.
*/
@NonNull
@@ -219,6 +249,33 @@
}
/**
+ * Gets the routing session of a media session.
+ * If the session is using {#link PlaybackInfo#PLAYBACK_TYPE_LOCAL local playback},
+ * the system routing session is returned.
+ * If the session is using {#link PlaybackInfo#PLAYBACK_TYPE_REMOTE remote playback},
+ * it returns the corresponding routing session or {@code null} if it's unavailable.
+ */
+ @Nullable
+ public RoutingSessionInfo getRoutingSessionForMediaController(MediaController mediaController) {
+ MediaController.PlaybackInfo playbackInfo = mediaController.getPlaybackInfo();
+ if (playbackInfo == null) {
+ return null;
+ }
+ if (playbackInfo.getPlaybackType() == MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL) {
+ return new RoutingSessionInfo.Builder(getSystemRoutingSession())
+ .setClientPackageName(mediaController.getPackageName())
+ .build();
+ }
+ for (RoutingSessionInfo sessionInfo : getActiveSessions()) {
+ if (!sessionInfo.isSystemSession()
+ && areSessionsMatched(mediaController, sessionInfo)) {
+ return sessionInfo;
+ }
+ }
+ return null;
+ }
+
+ /**
* Gets routing sessions of an application with the given package name.
* The first element of the returned list is the system routing session.
*
@@ -578,8 +635,11 @@
public List<MediaRoute2Info> getSelectedRoutes(@NonNull RoutingSessionInfo sessionInfo) {
Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
- List<String> routeIds = sessionInfo.getSelectedRoutes();
- return getRoutesWithIds(routeIds);
+ synchronized (sLock) {
+ return sessionInfo.getSelectedRoutes().stream().map(mRoutes::get)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ }
}
/**
@@ -589,8 +649,15 @@
public List<MediaRoute2Info> getSelectableRoutes(@NonNull RoutingSessionInfo sessionInfo) {
Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
- List<String> routeIds = sessionInfo.getSelectableRoutes();
- return getRoutesWithIds(routeIds);
+ List<String> selectedRouteIds = sessionInfo.getSelectedRoutes();
+
+ synchronized (sLock) {
+ return sessionInfo.getSelectableRoutes().stream()
+ .filter(routeId -> !selectedRouteIds.contains(routeId))
+ .map(mRoutes::get)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ }
}
/**
@@ -600,8 +667,15 @@
public List<MediaRoute2Info> getDeselectableRoutes(@NonNull RoutingSessionInfo sessionInfo) {
Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
- List<String> routeIds = sessionInfo.getDeselectableRoutes();
- return getRoutesWithIds(routeIds);
+ List<String> selectedRouteIds = sessionInfo.getSelectedRoutes();
+
+ synchronized (sLock) {
+ return sessionInfo.getDeselectableRoutes().stream()
+ .filter(routeId -> selectedRouteIds.contains(routeId))
+ .map(mRoutes::get)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ }
}
/**
@@ -762,12 +836,25 @@
}
}
- private List<MediaRoute2Info> getRoutesWithIds(List<String> routeIds) {
- synchronized (sLock) {
- return routeIds.stream().map(mRoutes::get)
- .filter(Objects::nonNull)
- .collect(Collectors.toList());
+ private boolean areSessionsMatched(MediaController mediaController,
+ RoutingSessionInfo sessionInfo) {
+ MediaController.PlaybackInfo playbackInfo = mediaController.getPlaybackInfo();
+ if (playbackInfo == null) {
+ return false;
}
+
+ String volumeControlId = playbackInfo.getVolumeControlId();
+ if (volumeControlId == null) {
+ return false;
+ }
+
+ if (TextUtils.equals(volumeControlId, sessionInfo.getId())) {
+ return true;
+ }
+ // Workaround for provider not being able to know the unique session ID.
+ return TextUtils.equals(volumeControlId, sessionInfo.getOriginalId())
+ && TextUtils.equals(mediaController.getPackageName(),
+ sessionInfo.getOwnerPackageName());
}
/**
diff --git a/media/java/android/media/RoutingSessionInfo.java b/media/java/android/media/RoutingSessionInfo.java
index 608e29a..edf1fc5 100644
--- a/media/java/android/media/RoutingSessionInfo.java
+++ b/media/java/android/media/RoutingSessionInfo.java
@@ -50,6 +50,7 @@
final String mId;
final CharSequence mName;
+ final String mOwnerPackageName;
final String mClientPackageName;
@Nullable
final String mProviderId;
@@ -71,6 +72,7 @@
mId = builder.mId;
mName = builder.mName;
+ mOwnerPackageName = builder.mOwnerPackageName;
mClientPackageName = builder.mClientPackageName;
mProviderId = builder.mProviderId;
@@ -96,6 +98,7 @@
mId = ensureString(src.readString());
mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(src);
+ mOwnerPackageName = src.readString();
mClientPackageName = ensureString(src.readString());
mProviderId = src.readString();
@@ -159,6 +162,15 @@
}
/**
+ * Gets the package name of the session owner.
+ * @hide
+ */
+ @Nullable
+ public String getOwnerPackageName() {
+ return mOwnerPackageName;
+ }
+
+ /**
* Gets the client package name of the session
*/
@NonNull
@@ -263,6 +275,7 @@
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(mId);
dest.writeCharSequence(mName);
+ dest.writeString(mOwnerPackageName);
dest.writeString(mClientPackageName);
dest.writeString(mProviderId);
dest.writeStringList(mSelectedRoutes);
@@ -288,6 +301,7 @@
RoutingSessionInfo other = (RoutingSessionInfo) obj;
return Objects.equals(mId, other.mId)
&& Objects.equals(mName, other.mName)
+ && Objects.equals(mOwnerPackageName, other.mOwnerPackageName)
&& Objects.equals(mClientPackageName, other.mClientPackageName)
&& Objects.equals(mProviderId, other.mProviderId)
&& Objects.equals(mSelectedRoutes, other.mSelectedRoutes)
@@ -301,7 +315,7 @@
@Override
public int hashCode() {
- return Objects.hash(mId, mName, mClientPackageName, mProviderId,
+ return Objects.hash(mId, mName, mOwnerPackageName, mClientPackageName, mProviderId,
mSelectedRoutes, mSelectableRoutes, mDeselectableRoutes, mTransferableRoutes,
mVolumeMax, mVolumeHandling, mVolume);
}
@@ -356,6 +370,7 @@
// TODO: Reorder these (important ones first)
final String mId;
CharSequence mName;
+ String mOwnerPackageName;
String mClientPackageName;
String mProviderId;
final List<String> mSelectedRoutes;
@@ -440,6 +455,17 @@
}
/**
+ * Sets the package name of the session owner. It is expected to be called by the system.
+ *
+ * @hide
+ */
+ @NonNull
+ public Builder setOwnerPackageName(@Nullable String packageName) {
+ mOwnerPackageName = packageName;
+ return this;
+ }
+
+ /**
* Sets the client package name of the session.
*
* @hide
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
index 6a1e965..eee797a 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
@@ -26,6 +26,7 @@
import static com.android.mediaroutertest.StubMediaRoute2ProviderService.FEATURE_SPECIAL;
import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_ID1;
import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_ID2;
+import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_ID4_TO_SELECT_AND_DESELECT;
import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_ID5_TO_TRANSFER_TO;
import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_ID_FIXED_VOLUME;
import static com.android.mediaroutertest.StubMediaRoute2ProviderService.ROUTE_ID_SPECIAL_FEATURE;
@@ -68,6 +69,7 @@
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
+import java.util.stream.Collectors;
@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -566,6 +568,41 @@
assertFalse(failureLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
}
+ /**
+ * Tests if getSelectableRoutes and getDeselectableRoutes filter routes based on
+ * selected routes
+ */
+ @Test
+ public void testGetSelectableRoutes_notReturnsSelectedRoutes() throws Exception {
+ Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
+ addRouterCallback(new RouteCallback() {});
+
+ CountDownLatch onSessionCreatedLatch = new CountDownLatch(1);
+
+ addManagerCallback(new MediaRouter2Manager.Callback() {
+ @Override
+ public void onTransferred(RoutingSessionInfo oldSessionInfo,
+ RoutingSessionInfo newSessionInfo) {
+ assertNotNull(newSessionInfo);
+ List<String> selectedRoutes = mManager.getSelectedRoutes(newSessionInfo).stream()
+ .map(MediaRoute2Info::getId)
+ .collect(Collectors.toList());
+ for (MediaRoute2Info selectableRoute :
+ mManager.getSelectableRoutes(newSessionInfo)) {
+ assertFalse(selectedRoutes.contains(selectableRoute.getId()));
+ }
+ for (MediaRoute2Info deselectableRoute :
+ mManager.getDeselectableRoutes(newSessionInfo)) {
+ assertTrue(selectedRoutes.contains(deselectableRoute.getId()));
+ }
+ onSessionCreatedLatch.countDown();
+ }
+ });
+
+ mManager.selectRoute(mPackageName, routes.get(ROUTE_ID4_TO_SELECT_AND_DESELECT));
+ assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+
Map<String, MediaRoute2Info> waitAndGetRoutesWithManager(List<String> routeFeatures)
throws Exception {
CountDownLatch addedLatch = new CountDownLatch(1);
diff --git a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
index 3389a7a..60e0d7e 100644
--- a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
@@ -76,7 +76,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@null"
- systemui:intent="intent:#Intent;component=com.android.car.settings/.common.CarSettingActivity;launchFlags=0x24000000;end"
+ systemui:intent="intent:#Intent;component=com.android.car.settings/.common.CarSettingActivities$QuickSettingActivity;launchFlags=0x24000000;end"
/>
<com.android.systemui.statusbar.policy.Clock
android:id="@+id/clock"
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
index ab7bf5e..f8729c3 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
@@ -20,8 +20,11 @@
import static com.android.systemui.Dependency.LEAK_REPORT_EMAIL_NAME;
import android.content.Context;
+import android.os.Handler;
+import android.os.PowerManager;
import com.android.keyguard.KeyguardViewController;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.car.CarDeviceProvisionedController;
import com.android.systemui.car.CarDeviceProvisionedControllerImpl;
import com.android.systemui.car.keyguard.CarKeyguardViewController;
@@ -30,6 +33,8 @@
import com.android.systemui.car.statusbar.DummyNotificationShadeWindowController;
import com.android.systemui.car.volume.CarVolumeDialogComponent;
import com.android.systemui.dagger.SystemUIRootComponent;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerImpl;
import com.android.systemui.plugins.qs.QSFactory;
@@ -113,10 +118,17 @@
abstract NotificationLockscreenUserManager bindNotificationLockscreenUserManager(
NotificationLockscreenUserManagerImpl notificationLockscreenUserManager);
- @Binds
+ @Provides
@Singleton
- public abstract BatteryController provideBatteryController(
- BatteryControllerImpl controllerImpl);
+ static BatteryController provideBatteryController(Context context,
+ EnhancedEstimates enhancedEstimates, PowerManager powerManager,
+ BroadcastDispatcher broadcastDispatcher, @Main Handler mainHandler,
+ @Background Handler bgHandler) {
+ BatteryController bC = new BatteryControllerImpl(context, enhancedEstimates, powerManager,
+ broadcastDispatcher, mainHandler, bgHandler);
+ bC.init();
+ return bC;
+ }
@Binds
@Singleton
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
index baa6ac9..aee7643 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
@@ -171,6 +171,7 @@
mKeyguardStateController.notifyKeyguardState(mShowing, /* occluded= */ false);
mCarNavigationBarController.showAllKeyguardButtons(/* isSetUp= */ true);
start();
+ getOverlayViewGlobalStateController().setWindowFocusable(/* focusable= */ true);
reset(/* hideBouncerWhenShowing= */ false);
notifyKeyguardUpdateMonitor();
}
@@ -185,6 +186,7 @@
mBouncer.hide(/* destroyView= */ true);
mCarNavigationBarController.hideAllKeyguardButtons(/* isSetUp= */ true);
stop();
+ getOverlayViewGlobalStateController().setWindowFocusable(/* focusable= */ false);
mKeyguardStateController.notifyKeyguardDoneFading();
mViewMediatorCallback.keyguardGone();
notifyKeyguardUpdateMonitor();
@@ -213,7 +215,7 @@
@Override
public void onCancelClicked() {
- if (!mShowing) return;
+ if (mBouncer == null) return;
getOverlayViewGlobalStateController().setWindowFocusable(/* focusable= */ false);
getOverlayViewGlobalStateController().setWindowNeedsInput(/* needsInput= */ false);
@@ -229,19 +231,20 @@
@Override
public void dismissAndCollapse() {
- hide(/* startTime= */ 0, /* fadeoutDuration= */ 0);
+ if (!mBouncer.isSecure()) {
+ hide(/* startTime= */ 0, /* fadeoutDuration= */ 0);
+ }
}
@Override
public void startPreHideAnimation(Runnable finishRunnable) {
- if (!mShowing) return;
+ if (mBouncer == null) return;
mBouncer.startPreHideAnimation(finishRunnable);
}
@Override
public void setNeedsInput(boolean needsInput) {
- getOverlayViewGlobalStateController().setWindowFocusable(needsInput);
getOverlayViewGlobalStateController().setWindowNeedsInput(needsInput);
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java
index 9e194fb..288e5cf 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java
@@ -73,7 +73,7 @@
}
/**
- * Hides all navigation bars.
+ * Hides all system bars.
*/
public void hideBars() {
if (mTopView != null) {
@@ -85,7 +85,7 @@
}
/**
- * Shows all navigation bars.
+ * Shows all system bars.
*/
public void showBars() {
if (mTopView != null) {
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java
index 20fcca0..aeb1d39 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java
@@ -29,41 +29,40 @@
import com.android.car.notification.R;
import com.android.car.notification.headsup.CarHeadsUpNotificationContainer;
import com.android.systemui.car.CarDeviceProvisionedController;
+import com.android.systemui.car.window.OverlayViewGlobalStateController;
import com.android.systemui.dagger.qualifiers.Main;
import javax.inject.Inject;
import javax.inject.Singleton;
-import dagger.Lazy;
-
/**
* A controller for SysUI's HUN display.
*/
@Singleton
public class CarHeadsUpNotificationSystemContainer implements CarHeadsUpNotificationContainer {
private final CarDeviceProvisionedController mCarDeviceProvisionedController;
- private final Lazy<NotificationPanelViewController> mNotificationPanelViewControllerLazy;
+ private final OverlayViewGlobalStateController mOverlayViewGlobalStateController;
private final ViewGroup mWindow;
private final FrameLayout mHeadsUpContentFrame;
- private final boolean mEnableHeadsUpNotificationWhenNotificationShadeOpen;
-
@Inject
CarHeadsUpNotificationSystemContainer(Context context,
@Main Resources resources,
CarDeviceProvisionedController deviceProvisionedController,
WindowManager windowManager,
- Lazy<NotificationPanelViewController> notificationPanelViewControllerLazy) {
+ OverlayViewGlobalStateController overlayViewGlobalStateController) {
mCarDeviceProvisionedController = deviceProvisionedController;
- mNotificationPanelViewControllerLazy = notificationPanelViewControllerLazy;
+ mOverlayViewGlobalStateController = overlayViewGlobalStateController;
boolean showOnBottom = resources.getBoolean(R.bool.config_showHeadsUpNotificationOnBottom);
+ // Use TYPE_STATUS_BAR_SUB_PANEL window type since we need to find a window that is above
+ // status bar but below navigation bar.
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.WRAP_CONTENT,
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
+ WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
PixelFormat.TRANSLUCENT);
@@ -78,15 +77,11 @@
windowManager.addView(mWindow, lp);
mWindow.setVisibility(View.INVISIBLE);
mHeadsUpContentFrame = mWindow.findViewById(R.id.headsup_content);
-
- mEnableHeadsUpNotificationWhenNotificationShadeOpen = resources.getBoolean(
- R.bool.config_enableHeadsUpNotificationWhenNotificationShadeOpen);
}
private void animateShow() {
- if ((mEnableHeadsUpNotificationWhenNotificationShadeOpen
- || !mNotificationPanelViewControllerLazy.get().isPanelExpanded())
- && mCarDeviceProvisionedController.isCurrentUserFullySetup()) {
+ if (mCarDeviceProvisionedController.isCurrentUserFullySetup()
+ && mOverlayViewGlobalStateController.shouldShowHUN()) {
mWindow.setVisibility(View.VISIBLE);
}
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
index cb9539a..1738091 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
@@ -73,6 +73,7 @@
private final CarNotificationListener mCarNotificationListener;
private final NotificationClickHandlerFactory mNotificationClickHandlerFactory;
private final StatusBarStateController mStatusBarStateController;
+ private final boolean mEnableHeadsUpNotificationWhenNotificationShadeOpen;
private float mInitialBackgroundAlpha;
private float mBackgroundAlphaDiff;
@@ -144,6 +145,10 @@
+ " percentage");
}
mBackgroundAlphaDiff = finalBackgroundAlpha - mInitialBackgroundAlpha;
+
+ mEnableHeadsUpNotificationWhenNotificationShadeOpen = mResources.getBoolean(
+ com.android.car.notification.R.bool
+ .config_enableHeadsUpNotificationWhenNotificationShadeOpen);
}
@Override
@@ -151,6 +156,16 @@
reinflate();
}
+ @Override
+ protected boolean shouldShowNavigationBar() {
+ return true;
+ }
+
+ @Override
+ protected boolean shouldShowHUN() {
+ return mEnableHeadsUpNotificationWhenNotificationShadeOpen;
+ }
+
/** Reinflates the view. */
public void reinflate() {
ViewGroup container = (ViewGroup) getLayout();
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java
index 8f52638..41349b2 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java
@@ -26,8 +26,15 @@
import com.android.systemui.car.window.OverlayViewMediator;
import com.android.systemui.statusbar.policy.ConfigurationController;
-/** The view mediator which attaches the view controller to other elements of the system ui. */
-public abstract class NotificationPanelViewMediator implements OverlayViewMediator,
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * The view mediator which attaches the view controller to other elements of the system ui. Disables
+ * drag open behavior of the notification panel from any navigation bar.
+ */
+@Singleton
+public class NotificationPanelViewMediator implements OverlayViewMediator,
ConfigurationController.ConfigurationListener {
private final CarNavigationBarController mCarNavigationBarController;
@@ -36,6 +43,7 @@
private final CarDeviceProvisionedController mCarDeviceProvisionedController;
private final ConfigurationController mConfigurationController;
+ @Inject
public NotificationPanelViewMediator(
CarNavigationBarController carNavigationBarController,
NotificationPanelViewController notificationPanelViewController,
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java
index 0fe9856..45808a8 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java
@@ -375,10 +375,10 @@
}
if (visible && !getOverlayViewGlobalStateController().isWindowVisible()) {
- getOverlayViewGlobalStateController().setWindowVisible(true);
+ getOverlayViewGlobalStateController().showView(/* panelViewController= */ this);
}
if (!visible && getOverlayViewGlobalStateController().isWindowVisible()) {
- getOverlayViewGlobalStateController().setWindowVisible(false);
+ getOverlayViewGlobalStateController().hideView(/* panelViewController= */ this);
}
getLayout().setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
getOverlayViewGlobalStateController().setWindowFocusable(visible);
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
index 87f2020..30e2657 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
@@ -54,7 +54,6 @@
mOverlayViewGlobalStateController.hideView(/* viewController= */ this, this::hide);
}
-
/**
* Inflate layout owned by controller.
*/
@@ -72,7 +71,7 @@
}
/**
- * Returns [@code true} if layout owned by controller has been inflated.
+ * Returns {@code true} if layout owned by controller has been inflated.
*/
public final boolean isInflated() {
return mLayout != null;
@@ -125,4 +124,18 @@
protected final OverlayViewGlobalStateController getOverlayViewGlobalStateController() {
return mOverlayViewGlobalStateController;
}
+
+ /**
+ * Returns {@code true} if heads up notifications should be displayed over this view.
+ */
+ protected boolean shouldShowHUN() {
+ return true;
+ }
+
+ /**
+ * Returns {@code true} if navigation bar should be displayed over this view.
+ */
+ protected boolean shouldShowNavigationBar() {
+ return false;
+ }
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
index 290505f..70260b0 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
@@ -16,14 +16,17 @@
package com.android.systemui.car.window;
+import android.annotation.Nullable;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import com.android.systemui.car.navigationbar.CarNavigationBarController;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -39,11 +42,17 @@
*/
@Singleton
public class OverlayViewGlobalStateController {
+ private static final boolean DEBUG = false;
private static final String TAG = OverlayViewGlobalStateController.class.getSimpleName();
+ private static final int UNKNOWN_Z_ORDER = -1;
private final SystemUIOverlayWindowController mSystemUIOverlayWindowController;
private final CarNavigationBarController mCarNavigationBarController;
@VisibleForTesting
- Set<String> mShownSet;
+ Map<OverlayViewController, Integer> mZOrderMap;
+ @VisibleForTesting
+ SortedMap<Integer, OverlayViewController> mZOrderVisibleSortedMap;
+ @VisibleForTesting
+ OverlayViewController mHighestZOrder;
@Inject
public OverlayViewGlobalStateController(
@@ -52,7 +61,8 @@
mSystemUIOverlayWindowController = systemUIOverlayWindowController;
mSystemUIOverlayWindowController.attach();
mCarNavigationBarController = carNavigationBarController;
- mShownSet = new HashSet<>();
+ mZOrderMap = new HashMap<>();
+ mZOrderVisibleSortedMap = new TreeMap<>();
}
/**
@@ -66,51 +76,127 @@
}
/**
- * Show content in Overlay Window.
+ * Show content in Overlay Window using {@link OverlayPanelViewController}.
+ *
+ * This calls {@link OverlayViewGlobalStateController#showView(OverlayViewController, Runnable)}
+ * where the runnable is nullified since the actual showing of the panel is handled by the
+ * controller itself.
*/
- public void showView(OverlayViewController viewController, Runnable show) {
- if (mShownSet.isEmpty()) {
- mCarNavigationBarController.hideBars();
- setWindowVisible(true);
- }
-
- inflateView(viewController);
-
- show.run();
- mShownSet.add(viewController.getClass().getName());
-
- Log.d(TAG, "Content shown: " + viewController.getClass().getName());
+ public void showView(OverlayPanelViewController panelViewController) {
+ showView(panelViewController, /* show= */ null);
}
/**
- * Hide content in Overlay Window.
+ * Show content in Overlay Window using {@link OverlayViewController}.
*/
- public void hideView(OverlayViewController viewController, Runnable hide) {
+ public void showView(OverlayViewController viewController, @Nullable Runnable show) {
+ debugLog();
+ if (mZOrderVisibleSortedMap.isEmpty()) {
+ setWindowVisible(true);
+ }
+ if (!(viewController instanceof OverlayPanelViewController)) {
+ inflateView(viewController);
+ }
+
+ if (show != null) {
+ show.run();
+ }
+
+ updateInternalsWhenShowingView(viewController);
+ refreshNavigationBarVisibility();
+
+ Log.d(TAG, "Content shown: " + viewController.getClass().getName());
+ debugLog();
+ }
+
+ private void updateInternalsWhenShowingView(OverlayViewController viewController) {
+ int zOrder;
+ if (mZOrderMap.containsKey(viewController)) {
+ zOrder = mZOrderMap.get(viewController);
+ } else {
+ zOrder = mSystemUIOverlayWindowController.getBaseLayout().indexOfChild(
+ viewController.getLayout());
+ mZOrderMap.put(viewController, zOrder);
+ }
+
+ mZOrderVisibleSortedMap.put(zOrder, viewController);
+
+ refreshHighestZOrderWhenShowingView(viewController);
+ }
+
+ private void refreshHighestZOrderWhenShowingView(OverlayViewController viewController) {
+ if (mZOrderMap.getOrDefault(mHighestZOrder, UNKNOWN_Z_ORDER) < mZOrderMap.get(
+ viewController)) {
+ mHighestZOrder = viewController;
+ }
+ }
+
+ /**
+ * Hide content in Overlay Window using {@link OverlayPanelViewController}.
+ *
+ * This calls {@link OverlayViewGlobalStateController#hideView(OverlayViewController, Runnable)}
+ * where the runnable is nullified since the actual hiding of the panel is handled by the
+ * controller itself.
+ */
+ public void hideView(OverlayPanelViewController panelViewController) {
+ hideView(panelViewController, /* hide= */ null);
+ }
+
+ /**
+ * Hide content in Overlay Window using {@link OverlayViewController}.
+ */
+ public void hideView(OverlayViewController viewController, @Nullable Runnable hide) {
+ debugLog();
if (!viewController.isInflated()) {
Log.d(TAG, "Content cannot be hidden since it isn't inflated: "
+ viewController.getClass().getName());
return;
}
- if (!mShownSet.contains(viewController.getClass().getName())) {
- Log.d(TAG, "Content cannot be hidden since it isn't shown: "
+ if (!mZOrderMap.containsKey(viewController)) {
+ Log.d(TAG, "Content cannot be hidden since it has never been shown: "
+ + viewController.getClass().getName());
+ return;
+ }
+ if (!mZOrderVisibleSortedMap.containsKey(mZOrderMap.get(viewController))) {
+ Log.d(TAG, "Content cannot be hidden since it isn't currently shown: "
+ viewController.getClass().getName());
return;
}
- hide.run();
- mShownSet.remove(viewController.getClass().getName());
+ if (hide != null) {
+ hide.run();
+ }
- if (mShownSet.isEmpty()) {
- mCarNavigationBarController.showBars();
+ mZOrderVisibleSortedMap.remove(mZOrderMap.get(viewController));
+ refreshHighestZOrderWhenHidingView(viewController);
+ refreshNavigationBarVisibility();
+
+ if (mZOrderVisibleSortedMap.isEmpty()) {
setWindowVisible(false);
}
Log.d(TAG, "Content hidden: " + viewController.getClass().getName());
+ debugLog();
}
- /** Sets the window visibility state. */
- public void setWindowVisible(boolean expanded) {
- mSystemUIOverlayWindowController.setWindowVisible(expanded);
+ private void refreshHighestZOrderWhenHidingView(OverlayViewController viewController) {
+ if (mZOrderVisibleSortedMap.isEmpty()) {
+ mHighestZOrder = null;
+ return;
+ }
+ if (!mHighestZOrder.equals(viewController)) {
+ return;
+ }
+
+ mHighestZOrder = mZOrderVisibleSortedMap.get(mZOrderVisibleSortedMap.lastKey());
+ }
+
+ private void refreshNavigationBarVisibility() {
+ if (mZOrderVisibleSortedMap.isEmpty() || mHighestZOrder.shouldShowNavigationBar()) {
+ mCarNavigationBarController.showBars();
+ } else {
+ mCarNavigationBarController.hideBars();
+ }
}
/** Returns {@code true} is the window is visible. */
@@ -118,13 +204,14 @@
return mSystemUIOverlayWindowController.isWindowVisible();
}
- /** Sets the focusable flag of the sysui overlawy window. */
- public void setWindowFocusable(boolean focusable) {
- mSystemUIOverlayWindowController.setWindowFocusable(focusable);
+ private void setWindowVisible(boolean visible) {
+ mSystemUIOverlayWindowController.setWindowVisible(visible);
}
- /** Sets the {@link android.view.WindowManager.LayoutParams#FLAG_ALT_FOCUSABLE_IM} flag of the
- * sysui overlay window */
+ /**
+ * Sets the {@link android.view.WindowManager.LayoutParams#FLAG_ALT_FOCUSABLE_IM} flag of the
+ * sysui overlay window.
+ */
public void setWindowNeedsInput(boolean needsInput) {
mSystemUIOverlayWindowController.setWindowNeedsInput(needsInput);
}
@@ -134,10 +221,34 @@
return mSystemUIOverlayWindowController.isWindowFocusable();
}
+ /** Sets the focusable flag of the sysui overlawy window. */
+ public void setWindowFocusable(boolean focusable) {
+ mSystemUIOverlayWindowController.setWindowFocusable(focusable);
+ }
+
/** Inflates the view controlled by the given view controller. */
public void inflateView(OverlayViewController viewController) {
if (!viewController.isInflated()) {
viewController.inflate(mSystemUIOverlayWindowController.getBaseLayout());
}
}
+
+ /**
+ * Return {@code true} if OverlayWindow is in a state where HUNs should be displayed above it.
+ */
+ public boolean shouldShowHUN() {
+ return mZOrderVisibleSortedMap.isEmpty() || mHighestZOrder.shouldShowHUN();
+ }
+
+ private void debugLog() {
+ if (!DEBUG) {
+ return;
+ }
+
+ Log.d(TAG, "mHighestZOrder: " + mHighestZOrder);
+ Log.d(TAG, "mZOrderVisibleSortedMap.size(): " + mZOrderVisibleSortedMap.size());
+ Log.d(TAG, "mZOrderVisibleSortedMap: " + mZOrderVisibleSortedMap);
+ Log.d(TAG, "mZOrderMap.size(): " + mZOrderMap.size());
+ Log.d(TAG, "mZOrderMap: " + mZOrderMap);
+ }
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayWindowModule.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayWindowModule.java
index e1918ce..484aa63 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayWindowModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayWindowModule.java
@@ -18,6 +18,7 @@
import com.android.systemui.car.keyguard.CarKeyguardViewMediator;
import com.android.systemui.car.notification.BottomNotificationPanelViewMediator;
+import com.android.systemui.car.notification.NotificationPanelViewMediator;
import com.android.systemui.car.notification.TopNotificationPanelViewMediator;
import com.android.systemui.car.userswitcher.FullscreenUserSwitcherViewMediator;
@@ -32,6 +33,13 @@
@Module
public abstract class OverlayWindowModule {
+ /** Injects NotificationPanelViewMediator. */
+ @Binds
+ @IntoMap
+ @ClassKey(NotificationPanelViewMediator.class)
+ public abstract OverlayViewMediator bindNotificationPanelViewMediator(
+ NotificationPanelViewMediator notificationPanelViewMediator);
+
/** Injects TopNotificationPanelViewMediator. */
@Binds
@IntoMap
diff --git a/packages/CarSystemUI/tests/res/layout/overlay_view_global_state_controller_test.xml b/packages/CarSystemUI/tests/res/layout/overlay_view_global_state_controller_test.xml
new file mode 100644
index 0000000..03fe0e4
--- /dev/null
+++ b/packages/CarSystemUI/tests/res/layout/overlay_view_global_state_controller_test.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<!-- Fullscreen views in sysui should be listed here in increasing Z order. -->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:background="@android:color/transparent"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ViewStub android:id="@+id/overlay_view_controller_stub_1"
+ android:inflatedId="@+id/overlay_view_controller_1"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout="@layout/overlay_view_controller_stub"/>
+
+ <ViewStub android:id="@+id/overlay_view_controller_stub_2"
+ android:inflatedId="@+id/overlay_view_controller_2"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout="@layout/overlay_view_controller_stub"/>
+
+ <ViewStub android:id="@+id/overlay_view_controller_stub_3"
+ android:inflatedId="@+id/overlay_view_controller_3"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout="@layout/overlay_view_controller_stub"/>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java
index a2192af..1b4621f 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java
@@ -16,9 +16,10 @@
package com.android.systemui.car.keyguard;
-import static com.google.common.truth.Truth.assertThat;
-
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -29,7 +30,6 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.LayoutInflater;
-import android.view.View;
import android.view.ViewGroup;
import com.android.internal.widget.LockPatternUtils;
@@ -40,7 +40,6 @@
import com.android.systemui.car.CarServiceProvider;
import com.android.systemui.car.navigationbar.CarNavigationBarController;
import com.android.systemui.car.window.OverlayViewGlobalStateController;
-import com.android.systemui.car.window.SystemUIOverlayWindowController;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
@@ -51,6 +50,7 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -61,28 +61,20 @@
public class CarKeyguardViewControllerTest extends SysuiTestCase {
private TestableCarKeyguardViewController mCarKeyguardViewController;
- private OverlayViewGlobalStateController mOverlayViewGlobalStateController;
- private ViewGroup mBaseLayout;
@Mock
+ private OverlayViewGlobalStateController mOverlayViewGlobalStateController;
+ @Mock
private KeyguardBouncer mBouncer;
@Mock
private CarNavigationBarController mCarNavigationBarController;
@Mock
- private SystemUIOverlayWindowController mSystemUIOverlayWindowController;
- @Mock
private CarKeyguardViewController.OnKeyguardCancelClickedListener mCancelClickedListener;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mOverlayViewGlobalStateController = new OverlayViewGlobalStateController(
- mCarNavigationBarController, mSystemUIOverlayWindowController);
- mBaseLayout = (ViewGroup) LayoutInflater.from(mContext).inflate(
- R.layout.sysui_overlay_window, /* root= */ null);
- when(mSystemUIOverlayWindowController.getBaseLayout()).thenReturn(mBaseLayout);
-
mCarKeyguardViewController = new TestableCarKeyguardViewController(
mContext,
Handler.getMain(),
@@ -98,6 +90,8 @@
mock(FalsingManager.class),
() -> mock(KeyguardBypassController.class)
);
+ mCarKeyguardViewController.inflate((ViewGroup) LayoutInflater.from(mContext).inflate(
+ R.layout.sysui_overlay_window, /* root= */ null));
}
@Test
@@ -113,8 +107,7 @@
when(mBouncer.isSecure()).thenReturn(true);
mCarKeyguardViewController.show(/* options= */ null);
- assertThat(mBaseLayout.findViewById(R.id.keyguard_container).getVisibility()).isEqualTo(
- View.VISIBLE);
+ verify(mOverlayViewGlobalStateController).showView(eq(mCarKeyguardViewController), any());
}
@Test
@@ -130,8 +123,17 @@
when(mBouncer.isSecure()).thenReturn(false);
mCarKeyguardViewController.show(/* options= */ null);
- assertThat(mBaseLayout.findViewById(R.id.keyguard_container).getVisibility()).isEqualTo(
- View.GONE);
+ // Here we check for both showView and hideView since the current implementation of show
+ // with bouncer being not secure has the following method execution orders:
+ // 1) show -> start -> showView
+ // 2) show -> reset -> dismissAndCollapse -> hide -> stop -> hideView
+ // Hence, we want to make sure that showView is called before hideView and not in any
+ // other combination.
+ InOrder inOrder = inOrder(mOverlayViewGlobalStateController);
+ inOrder.verify(mOverlayViewGlobalStateController).showView(eq(mCarKeyguardViewController),
+ any());
+ inOrder.verify(mOverlayViewGlobalStateController).hideView(eq(mCarKeyguardViewController),
+ any());
}
@Test
@@ -156,8 +158,11 @@
mCarKeyguardViewController.show(/* options= */ null);
mCarKeyguardViewController.hide(/* startTime= */ 0, /* fadeoutDelay= */ 0);
- assertThat(mBaseLayout.findViewById(R.id.keyguard_container).getVisibility()).isEqualTo(
- View.GONE);
+ InOrder inOrder = inOrder(mOverlayViewGlobalStateController);
+ inOrder.verify(mOverlayViewGlobalStateController).showView(eq(mCarKeyguardViewController),
+ any());
+ inOrder.verify(mOverlayViewGlobalStateController).hideView(eq(mCarKeyguardViewController),
+ any());
}
@Test
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainerTest.java
index 6ac72a6..ccaeb45 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainerTest.java
@@ -28,9 +28,9 @@
import androidx.test.filters.SmallTest;
-import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.car.CarDeviceProvisionedController;
+import com.android.systemui.car.window.OverlayViewGlobalStateController;
import org.junit.Before;
import org.junit.Test;
@@ -42,12 +42,11 @@
@TestableLooper.RunWithLooper
@SmallTest
public class CarHeadsUpNotificationSystemContainerTest extends SysuiTestCase {
- private CarHeadsUpNotificationSystemContainer mDefaultController;
- private CarHeadsUpNotificationSystemContainer mOverrideEnabledController;
+ private CarHeadsUpNotificationSystemContainer mCarHeadsUpNotificationSystemContainer;
@Mock
private CarDeviceProvisionedController mCarDeviceProvisionedController;
@Mock
- private NotificationPanelViewController mNotificationPanelViewController;
+ private OverlayViewGlobalStateController mOverlayViewGlobalStateController;
@Mock
private WindowManager mWindowManager;
@@ -58,76 +57,63 @@
@Before
public void setUp() {
- MockitoAnnotations.initMocks(this);
+ MockitoAnnotations.initMocks(/* testClass= */this);
- when(mNotificationPanelViewController.isPanelExpanded()).thenReturn(false);
- when(mCarDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true);
- when(mCarDeviceProvisionedController.isCurrentUserSetupInProgress()).thenReturn(false);
+ when(mOverlayViewGlobalStateController.shouldShowHUN()).thenReturn(true);
+ when(mCarDeviceProvisionedController.isCurrentUserFullySetup()).thenReturn(true);
TestableResources testableResources = mContext.getOrCreateTestableResources();
- testableResources.addOverride(
- R.bool.config_enableHeadsUpNotificationWhenNotificationShadeOpen, false);
-
- mDefaultController = new CarHeadsUpNotificationSystemContainer(mContext,
+ mCarHeadsUpNotificationSystemContainer = new CarHeadsUpNotificationSystemContainer(mContext,
testableResources.getResources(), mCarDeviceProvisionedController, mWindowManager,
- () -> mNotificationPanelViewController);
-
- testableResources.addOverride(
- R.bool.config_enableHeadsUpNotificationWhenNotificationShadeOpen, true);
-
- mOverrideEnabledController = new CarHeadsUpNotificationSystemContainer(mContext,
- testableResources.getResources(), mCarDeviceProvisionedController, mWindowManager,
- () -> mNotificationPanelViewController);
+ mOverlayViewGlobalStateController);
}
@Test
public void testDisplayNotification_firstNotification_isVisible() {
- mDefaultController.displayNotification(mNotificationView);
- assertThat(mDefaultController.isVisible()).isTrue();
+ mCarHeadsUpNotificationSystemContainer.displayNotification(mNotificationView);
+ assertThat(mCarHeadsUpNotificationSystemContainer.isVisible()).isTrue();
}
@Test
public void testRemoveNotification_lastNotification_isInvisible() {
- mDefaultController.displayNotification(mNotificationView);
- mDefaultController.removeNotification(mNotificationView);
- assertThat(mDefaultController.isVisible()).isFalse();
+ mCarHeadsUpNotificationSystemContainer.displayNotification(mNotificationView);
+ mCarHeadsUpNotificationSystemContainer.removeNotification(mNotificationView);
+ assertThat(mCarHeadsUpNotificationSystemContainer.isVisible()).isFalse();
}
@Test
public void testRemoveNotification_nonLastNotification_isVisible() {
- mDefaultController.displayNotification(mNotificationView);
- mDefaultController.displayNotification(mNotificationView2);
- mDefaultController.removeNotification(mNotificationView);
- assertThat(mDefaultController.isVisible()).isTrue();
+ mCarHeadsUpNotificationSystemContainer.displayNotification(mNotificationView);
+ mCarHeadsUpNotificationSystemContainer.displayNotification(mNotificationView2);
+ mCarHeadsUpNotificationSystemContainer.removeNotification(mNotificationView);
+ assertThat(mCarHeadsUpNotificationSystemContainer.isVisible()).isTrue();
}
@Test
- public void testDisplayNotification_userSetupInProgress_isInvisible() {
- when(mCarDeviceProvisionedController.isCurrentUserSetupInProgress()).thenReturn(true);
- mDefaultController.displayNotification(mNotificationView);
- assertThat(mDefaultController.isVisible()).isFalse();
+ public void testDisplayNotification_userFullySetupTrue_isInvisible() {
+ mCarHeadsUpNotificationSystemContainer.displayNotification(mNotificationView);
+ assertThat(mCarHeadsUpNotificationSystemContainer.isVisible()).isTrue();
}
@Test
- public void testDisplayNotification_userSetupIncomplete_isInvisible() {
- when(mCarDeviceProvisionedController.isCurrentUserSetup()).thenReturn(false);
- mDefaultController.displayNotification(mNotificationView);
- assertThat(mDefaultController.isVisible()).isFalse();
+ public void testDisplayNotification_userFullySetupFalse_isInvisible() {
+ when(mCarDeviceProvisionedController.isCurrentUserFullySetup()).thenReturn(false);
+ mCarHeadsUpNotificationSystemContainer.displayNotification(mNotificationView);
+ assertThat(mCarHeadsUpNotificationSystemContainer.isVisible()).isFalse();
}
@Test
- public void testDisplayNotification_notificationPanelExpanded_isInvisible() {
- when(mNotificationPanelViewController.isPanelExpanded()).thenReturn(true);
- mDefaultController.displayNotification(mNotificationView);
- assertThat(mDefaultController.isVisible()).isFalse();
+ public void testDisplayNotification_overlayWindowStateShouldShowHUNFalse_isInvisible() {
+ when(mOverlayViewGlobalStateController.shouldShowHUN()).thenReturn(false);
+ mCarHeadsUpNotificationSystemContainer.displayNotification(mNotificationView);
+ assertThat(mCarHeadsUpNotificationSystemContainer.isVisible()).isFalse();
}
@Test
- public void testDisplayNotification_notificationPanelExpandedEnabledHUNWhenOpen_isVisible() {
- when(mNotificationPanelViewController.isPanelExpanded()).thenReturn(true);
- mOverrideEnabledController.displayNotification(mNotificationView);
- assertThat(mOverrideEnabledController.isVisible()).isTrue();
+ public void testDisplayNotification_overlayWindowStateShouldShowHUNTrue_isVisible() {
+ mCarHeadsUpNotificationSystemContainer.displayNotification(mNotificationView);
+ assertThat(mCarHeadsUpNotificationSystemContainer.isVisible()).isTrue();
}
}
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java
index 8d705a8..45a05ac 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java
@@ -339,7 +339,7 @@
mOverlayPanelViewController.setPanelVisible(true);
- verify(mOverlayViewGlobalStateController).setWindowVisible(true);
+ verify(mOverlayViewGlobalStateController).showView(mOverlayPanelViewController);
}
@Test
@@ -349,7 +349,7 @@
mOverlayPanelViewController.setPanelVisible(true);
- verify(mOverlayViewGlobalStateController, never()).setWindowVisible(true);
+ verify(mOverlayViewGlobalStateController, never()).showView(mOverlayPanelViewController);
}
@Test
@@ -377,7 +377,7 @@
mOverlayPanelViewController.setPanelVisible(false);
- verify(mOverlayViewGlobalStateController).setWindowVisible(false);
+ verify(mOverlayViewGlobalStateController).hideView(mOverlayPanelViewController);
}
@Test
@@ -387,7 +387,7 @@
mOverlayPanelViewController.setPanelVisible(false);
- verify(mOverlayViewGlobalStateController, never()).setWindowVisible(false);
+ verify(mOverlayViewGlobalStateController, never()).hideView(mOverlayPanelViewController);
}
@Test
@@ -428,10 +428,6 @@
private static class TestOverlayPanelViewController extends OverlayPanelViewController {
- private boolean mShouldAnimateCollapsePanel;
- private boolean mShouldAnimateExpandPanel;
- private boolean mShouldAllowClosingScroll;
-
boolean mOnAnimateCollapsePanelCalled;
boolean mAnimateCollapsePanelCalled;
boolean mOnAnimateExpandPanelCalled;
@@ -440,6 +436,9 @@
boolean mOnExpandAnimationEndCalled;
boolean mOnOpenScrollStartEnd;
List<Integer> mOnScrollHeights;
+ private boolean mShouldAnimateCollapsePanel;
+ private boolean mShouldAnimateExpandPanel;
+ private boolean mShouldAllowClosingScroll;
TestOverlayPanelViewController(
Context context,
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
index 25dd4f5..9e6e616 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
@@ -24,25 +24,33 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.view.LayoutInflater;
+import android.view.View;
import android.view.ViewGroup;
-import android.widget.FrameLayout;
+import android.view.ViewStub;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.car.navigationbar.CarNavigationBarController;
+import com.android.systemui.tests.R;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import java.util.Arrays;
+
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
- private static final String MOCK_OVERLAY_VIEW_CONTROLLER_NAME = "OverlayViewController";
+ private static final int OVERLAY_VIEW_CONTROLLER_1_Z_ORDER = 0;
+ private static final int OVERLAY_VIEW_CONTROLLER_2_Z_ORDER = 1;
+ private static final int OVERLAY_PANEL_VIEW_CONTROLLER_Z_ORDER = 2;
private OverlayViewGlobalStateController mOverlayViewGlobalStateController;
private ViewGroup mBaseLayout;
@@ -54,7 +62,11 @@
@Mock
private OverlayViewMediator mOverlayViewMediator;
@Mock
- private OverlayViewController mOverlayViewController;
+ private OverlayViewController mOverlayViewController1;
+ @Mock
+ private OverlayViewController mOverlayViewController2;
+ @Mock
+ private OverlayPanelViewController mOverlayPanelViewController;
@Mock
private Runnable mRunnable;
@@ -62,14 +74,15 @@
public void setUp() {
MockitoAnnotations.initMocks(/* testClass= */ this);
+ mBaseLayout = (ViewGroup) LayoutInflater.from(mContext).inflate(
+ R.layout.overlay_view_global_state_controller_test, /* root= */ null);
+
+ when(mSystemUIOverlayWindowController.getBaseLayout()).thenReturn(mBaseLayout);
+
mOverlayViewGlobalStateController = new OverlayViewGlobalStateController(
mCarNavigationBarController, mSystemUIOverlayWindowController);
verify(mSystemUIOverlayWindowController).attach();
-
- mBaseLayout = new FrameLayout(mContext);
-
- when(mSystemUIOverlayWindowController.getBaseLayout()).thenReturn(mBaseLayout);
}
@Test
@@ -87,182 +100,445 @@
}
@Test
- public void showView_nothingAlreadyShown_navigationBarsHidden() {
- mOverlayViewGlobalStateController.showView(mOverlayViewController, mRunnable);
+ public void showView_nothingAlreadyShown_shouldShowNavBarFalse_navigationBarsHidden() {
+ setupOverlayViewController1();
+ when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(false);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
verify(mCarNavigationBarController).hideBars();
}
@Test
- public void showView_nothingAlreadyShown_windowIsExpanded() {
- mOverlayViewGlobalStateController.showView(mOverlayViewController, mRunnable);
+ public void showView_nothingAlreadyShown_shouldShowNavBarTrue_navigationBarsShown() {
+ setupOverlayViewController1();
+ when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(true);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mCarNavigationBarController).showBars();
+ }
+
+ @Test
+ public void showView_nothingAlreadyShown_windowIsSetVisible() {
+ setupOverlayViewController1();
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
verify(mSystemUIOverlayWindowController).setWindowVisible(true);
}
@Test
- public void showView_somethingAlreadyShown_navigationBarsHidden() {
- mOverlayViewGlobalStateController.mShownSet.add(MOCK_OVERLAY_VIEW_CONTROLLER_NAME);
+ public void showView_nothingAlreadyShown_newHighestZOrder() {
+ setupOverlayViewController1();
- mOverlayViewGlobalStateController.showView(mOverlayViewController, mRunnable);
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
- verify(mCarNavigationBarController, never()).hideBars();
+ assertThat(mOverlayViewGlobalStateController.mHighestZOrder).isEqualTo(
+ mOverlayViewController1);
}
@Test
- public void showView_somethingAlreadyShown_windowIsExpanded() {
- mOverlayViewGlobalStateController.mShownSet.add(MOCK_OVERLAY_VIEW_CONTROLLER_NAME);
+ public void showView_nothingAlreadyShown_newHighestZOrder_isVisible() {
+ setupOverlayViewController1();
- mOverlayViewGlobalStateController.showView(mOverlayViewController, mRunnable);
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ assertThat(mOverlayViewGlobalStateController.mZOrderVisibleSortedMap.containsKey(
+ OVERLAY_VIEW_CONTROLLER_1_Z_ORDER)).isTrue();
+ }
+
+ @Test
+ public void showView_newHighestZOrder() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
+
+ assertThat(mOverlayViewGlobalStateController.mHighestZOrder).isEqualTo(
+ mOverlayViewController2);
+ }
+
+ @Test
+ public void showView_newHighestZOrder_shouldShowNavBarFalse_navigationBarsHidden() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(false);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
+
+ verify(mCarNavigationBarController).hideBars();
+ }
+
+ @Test
+ public void showView_newHighestZOrder_shouldShowNavBarTrue_navigationBarsShown() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(true);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
+
+ verify(mCarNavigationBarController).showBars();
+ }
+
+ @Test
+ public void showView_newHighestZOrder_correctViewsShown() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
+
+ assertThat(mOverlayViewGlobalStateController.mZOrderVisibleSortedMap.keySet().toArray())
+ .isEqualTo(Arrays.asList(OVERLAY_VIEW_CONTROLLER_1_Z_ORDER,
+ OVERLAY_VIEW_CONTROLLER_2_Z_ORDER).toArray());
+ }
+
+ @Test
+ public void showView_oldHighestZOrder() {
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ assertThat(mOverlayViewGlobalStateController.mHighestZOrder).isEqualTo(
+ mOverlayViewController2);
+ }
+
+ @Test
+ public void showView_oldHighestZOrder_shouldShowNavBarFalse_navigationBarsHidden() {
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(true);
+ when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(false);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mCarNavigationBarController).hideBars();
+ }
+
+ @Test
+ public void showView_oldHighestZOrder_shouldShowNavBarTrue_navigationBarsShown() {
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(false);
+ when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(true);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ verify(mCarNavigationBarController).showBars();
+ }
+
+ @Test
+ public void showView_oldHighestZOrder_correctViewsShown() {
+ setupOverlayViewController1();
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+ assertThat(mOverlayViewGlobalStateController.mZOrderVisibleSortedMap.keySet().toArray())
+ .isEqualTo(Arrays.asList(OVERLAY_VIEW_CONTROLLER_1_Z_ORDER,
+ OVERLAY_VIEW_CONTROLLER_2_Z_ORDER).toArray());
+ }
+
+ @Test
+ public void showView_somethingAlreadyShown_windowVisibleNotCalled() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
verify(mSystemUIOverlayWindowController, never()).setWindowVisible(true);
}
@Test
public void showView_viewControllerNotInflated_inflateViewController() {
- when(mOverlayViewController.isInflated()).thenReturn(false);
+ setupOverlayViewController2();
+ when(mOverlayViewController2.isInflated()).thenReturn(false);
- mOverlayViewGlobalStateController.showView(mOverlayViewController, mRunnable);
+ mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
- verify(mOverlayViewController).inflate(mBaseLayout);
+ verify(mOverlayViewController2).inflate(mBaseLayout);
}
@Test
public void showView_viewControllerInflated_inflateViewControllerNotCalled() {
- when(mOverlayViewController.isInflated()).thenReturn(true);
+ setupOverlayViewController2();
- mOverlayViewGlobalStateController.showView(mOverlayViewController, mRunnable);
+ mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
- verify(mOverlayViewController, never()).inflate(mBaseLayout);
+ verify(mOverlayViewController2, never()).inflate(mBaseLayout);
+ }
+
+ @Test
+ public void showView_panelViewController_inflateViewControllerNotCalled() {
+ setupOverlayPanelViewController();
+
+ mOverlayViewGlobalStateController.showView(mOverlayPanelViewController, mRunnable);
+
+ verify(mOverlayPanelViewController, never()).inflate(mBaseLayout);
+ verify(mOverlayPanelViewController, never()).isInflated();
}
@Test
public void showView_showRunnableCalled() {
- mOverlayViewGlobalStateController.showView(mOverlayViewController, mRunnable);
+ setupOverlayViewController1();
+
+ mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
verify(mRunnable).run();
}
@Test
- public void showView_overlayViewControllerAddedToShownSet() {
- mOverlayViewGlobalStateController.showView(mOverlayViewController, mRunnable);
-
- assertThat(mOverlayViewGlobalStateController.mShownSet.contains(
- mOverlayViewController.getClass().getName())).isTrue();
- }
-
- @Test
public void hideView_viewControllerNotInflated_hideRunnableNotCalled() {
- when(mOverlayViewController.isInflated()).thenReturn(false);
+ when(mOverlayViewController2.isInflated()).thenReturn(false);
- mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable);
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
verify(mRunnable, never()).run();
}
@Test
public void hideView_nothingShown_hideRunnableNotCalled() {
- when(mOverlayViewController.isInflated()).thenReturn(true);
- mOverlayViewGlobalStateController.mShownSet.clear();
+ when(mOverlayViewController2.isInflated()).thenReturn(true);
+ mOverlayViewGlobalStateController.mZOrderMap.clear();
- mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable);
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
verify(mRunnable, never()).run();
}
@Test
public void hideView_viewControllerNotShown_hideRunnableNotCalled() {
- when(mOverlayViewController.isInflated()).thenReturn(true);
- mOverlayViewGlobalStateController.mShownSet.add(MOCK_OVERLAY_VIEW_CONTROLLER_NAME);
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ when(mOverlayViewController2.isInflated()).thenReturn(true);
- mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable);
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
verify(mRunnable, never()).run();
}
@Test
public void hideView_viewControllerShown_hideRunnableCalled() {
- when(mOverlayViewController.isInflated()).thenReturn(true);
- mOverlayViewGlobalStateController.mShownSet.add(
- mOverlayViewController.getClass().getName());
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
- mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable);
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
verify(mRunnable).run();
}
@Test
- public void hideView_viewControllerOnlyShown_nothingShown() {
- when(mOverlayViewController.isInflated()).thenReturn(true);
- mOverlayViewGlobalStateController.mShownSet.add(
- mOverlayViewController.getClass().getName());
+ public void hideView_viewControllerOnlyShown_noHighestZOrder() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
- mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable);
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
- assertThat(mOverlayViewGlobalStateController.mShownSet.isEmpty()).isTrue();
+ assertThat(mOverlayViewGlobalStateController.mHighestZOrder).isNull();
}
@Test
- public void hideView_viewControllerNotOnlyShown_navigationBarNotShown() {
- when(mOverlayViewController.isInflated()).thenReturn(true);
- mOverlayViewGlobalStateController.mShownSet.add(
- mOverlayViewController.getClass().getName());
- mOverlayViewGlobalStateController.mShownSet.add(MOCK_OVERLAY_VIEW_CONTROLLER_NAME);
+ public void hideView_viewControllerOnlyShown_nothingShown() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
- mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable);
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
- verify(mCarNavigationBarController, never()).showBars();
+ assertThat(mOverlayViewGlobalStateController.mZOrderVisibleSortedMap.isEmpty()).isTrue();
+ }
+
+ @Test
+ public void hideView_viewControllerOnlyShown_viewControllerNotShown() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+ assertThat(mOverlayViewGlobalStateController.mZOrderVisibleSortedMap.containsKey(
+ OVERLAY_VIEW_CONTROLLER_1_Z_ORDER)).isFalse();
+ }
+
+ @Test
+ public void hideView_newHighestZOrder_twoViewsShown() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
+
+ assertThat(mOverlayViewGlobalStateController.mHighestZOrder).isEqualTo(
+ mOverlayViewController1);
+ }
+
+ @Test
+ public void hideView_newHighestZOrder_threeViewsShown() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ setupOverlayPanelViewController();
+ setOverlayViewControllerAsShowing(mOverlayPanelViewController);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayPanelViewController, mRunnable);
+
+ assertThat(mOverlayViewGlobalStateController.mHighestZOrder).isEqualTo(
+ mOverlayViewController2);
+ }
+
+ @Test
+ public void hideView_newHighestZOrder_shouldShowNavBarFalse_navigationBarHidden() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(false);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
+
+ verify(mCarNavigationBarController).hideBars();
+ }
+
+ @Test
+ public void hideView_newHighestZOrder_shouldShowNavBarTrue_navigationBarShown() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(true);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
+
+ verify(mCarNavigationBarController).showBars();
+ }
+
+ @Test
+ public void hideView_oldHighestZOrder() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+ assertThat(mOverlayViewGlobalStateController.mHighestZOrder).isEqualTo(
+ mOverlayViewController2);
+ }
+
+ @Test
+ public void hideView_oldHighestZOrder_shouldShowNavBarFalse_navigationBarHidden() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(false);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+ verify(mCarNavigationBarController).hideBars();
+ }
+
+ @Test
+ public void hideView_oldHighestZOrder_shouldShowNavBarTrue_navigationBarShown() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
+ when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(true);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+ verify(mCarNavigationBarController).showBars();
}
@Test
public void hideView_viewControllerNotOnlyShown_windowNotCollapsed() {
- when(mOverlayViewController.isInflated()).thenReturn(true);
- mOverlayViewGlobalStateController.mShownSet.add(
- mOverlayViewController.getClass().getName());
- mOverlayViewGlobalStateController.mShownSet.add(MOCK_OVERLAY_VIEW_CONTROLLER_NAME);
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ setupOverlayViewController2();
+ setOverlayViewControllerAsShowing(mOverlayViewController2);
- mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable);
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
verify(mSystemUIOverlayWindowController, never()).setWindowVisible(false);
}
@Test
public void hideView_viewControllerOnlyShown_navigationBarShown() {
- when(mOverlayViewController.isInflated()).thenReturn(true);
- mOverlayViewGlobalStateController.mShownSet.add(
- mOverlayViewController.getClass().getName());
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
- mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable);
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
verify(mCarNavigationBarController).showBars();
}
@Test
public void hideView_viewControllerOnlyShown_windowCollapsed() {
- when(mOverlayViewController.isInflated()).thenReturn(true);
- mOverlayViewGlobalStateController.mShownSet.add(
- mOverlayViewController.getClass().getName());
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
- mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable);
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
verify(mSystemUIOverlayWindowController).setWindowVisible(false);
}
@Test
public void inflateView_notInflated_inflates() {
- when(mOverlayViewController.isInflated()).thenReturn(false);
+ when(mOverlayViewController2.isInflated()).thenReturn(false);
- mOverlayViewGlobalStateController.inflateView(mOverlayViewController);
+ mOverlayViewGlobalStateController.inflateView(mOverlayViewController2);
- verify(mOverlayViewController).inflate(mBaseLayout);
+ verify(mOverlayViewController2).inflate(mBaseLayout);
}
@Test
public void inflateView_alreadyInflated_doesNotInflate() {
- when(mOverlayViewController.isInflated()).thenReturn(true);
+ when(mOverlayViewController2.isInflated()).thenReturn(true);
- mOverlayViewGlobalStateController.inflateView(mOverlayViewController);
+ mOverlayViewGlobalStateController.inflateView(mOverlayViewController2);
- verify(mOverlayViewController, never()).inflate(mBaseLayout);
+ verify(mOverlayViewController2, never()).inflate(mBaseLayout);
+ }
+
+ private void setupOverlayViewController1() {
+ setupOverlayViewController(mOverlayViewController1, R.id.overlay_view_controller_stub_1,
+ R.id.overlay_view_controller_1);
+ }
+
+ private void setupOverlayViewController2() {
+ setupOverlayViewController(mOverlayViewController2, R.id.overlay_view_controller_stub_2,
+ R.id.overlay_view_controller_2);
+ }
+
+ private void setupOverlayPanelViewController() {
+ setupOverlayViewController(mOverlayPanelViewController, R.id.overlay_view_controller_stub_3,
+ R.id.overlay_view_controller_3);
+ }
+
+ private void setupOverlayViewController(OverlayViewController overlayViewController,
+ int stubId, int inflatedId) {
+ ViewStub viewStub = mBaseLayout.findViewById(stubId);
+ View layout;
+ if (viewStub == null) {
+ layout = mBaseLayout.findViewById(inflatedId);
+ } else {
+ layout = viewStub.inflate();
+ }
+ when(overlayViewController.getLayout()).thenReturn(layout);
+ when(overlayViewController.isInflated()).thenReturn(true);
+ }
+
+ private void setOverlayViewControllerAsShowing(OverlayViewController overlayViewController) {
+ mOverlayViewGlobalStateController.showView(overlayViewController, /* show= */ null);
+ Mockito.reset(mCarNavigationBarController, mSystemUIOverlayWindowController);
+ when(mSystemUIOverlayWindowController.getBaseLayout()).thenReturn(mBaseLayout);
}
}
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index c16949a..1729027 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -549,4 +549,10 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Voeg gas by"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Verwyder gas"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gas"</string>
+ <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Toestelverstek"</string>
+ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Gedeaktiveer"</string>
+ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Geaktiveer"</string>
+ <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Jou toestel moet herselflaai om hierdie verandering toe te pas. Herselflaai nou of kanselleer."</string>
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 4c6c7b8..fad7a82 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -549,4 +549,10 @@
<string name="guest_new_guest" msgid="3482026122932643557">"እንግዳን አክል"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"እንግዳን አስወግድ"</string>
<string name="guest_nickname" msgid="6332276931583337261">"እንግዳ"</string>
+ <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"የመሣሪያ ነባሪ"</string>
+ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ተሰናክሏል"</string>
+ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ነቅቷል"</string>
+ <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"የእርስዎን መሣሪያ ይህ ለው ለማመልከት እንደገና መነሣት አለበት። አሁን እንደገና ያስነሡ ወይም ይተዉት።"</string>
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 38b7179..60f4c6b 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -235,7 +235,7 @@
<string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"يُرجى الاتصال بشبكة Wi-Fi."</string>
<string name="keywords_adb_wireless" msgid="6507505581882171240">"adb، تصحيح الأخطاء، مطور برامج"</string>
<string name="bugreport_in_power" msgid="8664089072534638709">"اختصار تقرير الأخطاء"</string>
- <string name="bugreport_in_power_summary" msgid="1885529649381831775">"عرض زر في قائمة خيارات التشغيل لإعداد تقرير بالأخطاء"</string>
+ <string name="bugreport_in_power_summary" msgid="1885529649381831775">"عرض زر في قائمة زر التشغيل لإعداد تقرير بالأخطاء"</string>
<string name="keep_screen_on" msgid="1187161672348797558">"البقاء في الوضع النشط"</string>
<string name="keep_screen_on_summary" msgid="1510731514101925829">"لا يتم مطلقًا دخول الشاشة في وضع السكون أثناء الشحن"</string>
<string name="bt_hci_snoop_log" msgid="7291287955649081448">"تفعيل سجلّ تطفل بواجهة وحدة تحكم المضيف في بلوتوث"</string>
@@ -284,8 +284,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"عرض خيارات شهادة عرض شاشة لاسلكي"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"زيادة مستوى تسجيل Wi-Fi، وعرض لكل SSID RSSI في منتقي Wi-Fi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"لتقليل استنفاد البطارية وتحسين أداء الشبكة."</string>
- <!-- no translation found for wifi_enhanced_mac_randomization_summary (1210663439867489931) -->
- <skip />
+ <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"عند تفعيل هذا الوضع، قد يتم تغيير عنوان MAC لهذا الجهاز في كل مرة تتصل فيها بشبكة تم تفعيل التوزيع العشوائي لعناوين MAC عليها."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"تفرض تكلفة استخدام"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"بدون قياس"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"أحجام ذاكرة التخزين المؤقت للتسجيل"</string>
@@ -554,4 +553,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"إضافة ضيف"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"إزالة جلسة الضيف"</string>
<string name="guest_nickname" msgid="6332276931583337261">"ضيف"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index ef36490..ad344c3 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -549,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"অতিথি যোগ কৰক"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"অতিথি আঁতৰাওক"</string>
<string name="guest_nickname" msgid="6332276931583337261">"অতিথি"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 62bb248..5bf468f 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -458,7 +458,7 @@
<string name="disabled" msgid="8017887509554714950">"Deaktiv"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"İcazə verilib"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"İcazə verilməyib"</string>
- <string name="install_other_apps" msgid="3232595082023199454">"Naməlum tətbiqlərin quraşdırılması"</string>
+ <string name="install_other_apps" msgid="3232595082023199454">"Tanınmayan tətbiqlərin quraşdırılması"</string>
<string name="home" msgid="973834627243661438">"Ayarların əsas səhifəsi"</string>
<string-array name="battery_labels">
<item msgid="7878690469765357158">"0%"</item>
@@ -549,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Qonaq əlavə edin"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Qonağı silin"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Qonaq"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 40432f8..85bb91c 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -143,11 +143,11 @@
<string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"Uklonjene aplikacije"</string>
<string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"Uklonjene aplikacije i korisnici"</string>
<string name="data_usage_ota" msgid="7984667793701597001">"Ažuriranja sistema"</string>
- <string name="tether_settings_title_usb" msgid="3728686573430917722">"USB Internet povezivanje"</string>
+ <string name="tether_settings_title_usb" msgid="3728686573430917722">"USB privezivanje"</string>
<string name="tether_settings_title_wifi" msgid="4803402057533895526">"Prenosni hotspot"</string>
<string name="tether_settings_title_bluetooth" msgid="916519902721399656">"Bluetooth privezivanje"</string>
- <string name="tether_settings_title_usb_bluetooth" msgid="1727111807207577322">"Povezivanje sa internetom"</string>
- <string name="tether_settings_title_all" msgid="8910259483383010470">"Povezivanje i prenosni hotspot"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="1727111807207577322">"Privezivanje"</string>
+ <string name="tether_settings_title_all" msgid="8910259483383010470">"Privezivanje i prenosni hotspot"</string>
<string name="managed_user_title" msgid="449081789742645723">"Sve radne aplikacije"</string>
<string name="user_guest" msgid="6939192779649870792">"Gost"</string>
<string name="unknown" msgid="3544487229740637809">"Nepoznato"</string>
@@ -550,4 +550,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gosta"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Ukloni gosta"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gost"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 3e2bbfe..95e1bdb 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -153,7 +153,7 @@
<string name="unknown" msgid="3544487229740637809">"Невядома"</string>
<string name="running_process_item_user_label" msgid="3988506293099805796">"Карыстальнiк: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
<string name="launch_defaults_some" msgid="3631650616557252926">"Усталяваны некаторыя стандартныя налады"</string>
- <string name="launch_defaults_none" msgid="8049374306261262709">"Параметры па змаўчанні не ўсталяваныя"</string>
+ <string name="launch_defaults_none" msgid="8049374306261262709">"Стандартныя налады не зададзены"</string>
<string name="tts_settings" msgid="8130616705989351312">"Налады Text-to-speech"</string>
<string name="tts_settings_title" msgid="7602210956640483039">"Сінтэз маўлення"</string>
<string name="tts_default_rate_title" msgid="3964187817364304022">"Хуткасць маўлення"</string>
@@ -284,8 +284,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Паказаць опцыі сертыфікацыі бесправаднога экрана"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Пры выбары сеткі Wi-Fi указваць у журнале RSSI для кожнага SSID"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Зніжае расход зараду акумулятара і павышае прадукцыйнасць мабільных сетак"</string>
- <!-- no translation found for wifi_enhanced_mac_randomization_summary (1210663439867489931) -->
- <skip />
+ <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Калі ўключаны гэты рэжым, MAC-адрас гэтай прылады можа змяняцца падчас кожнага падключэння да сеткі з актыўнай рандамізацыяй MAC-адрасоў."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Сетка з улікам трафіка"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Сетка без уліку трафіка"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Памеры буфера журнала"</string>
@@ -552,4 +551,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Дадаць госця"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Выдаліць госця"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Госць"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 19ad534..e426625 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -549,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Добавяне на гост"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Премахване на госта"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Гост"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 94e1f23..7bae6ee 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -153,7 +153,7 @@
<string name="unknown" msgid="3544487229740637809">"অজানা"</string>
<string name="running_process_item_user_label" msgid="3988506293099805796">"ব্যবহারকারী: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
<string name="launch_defaults_some" msgid="3631650616557252926">"কিছু ডিফল্ট সেট করা রয়েছে"</string>
- <string name="launch_defaults_none" msgid="8049374306261262709">"কোনো ডিফল্ট সেট করা নেই"</string>
+ <string name="launch_defaults_none" msgid="8049374306261262709">"কোনও ডিফল্ট সেট করা নেই"</string>
<string name="tts_settings" msgid="8130616705989351312">"পাঠ্য থেকে ভাষ্য আউটপুট সেটিংস"</string>
<string name="tts_settings_title" msgid="7602210956640483039">"টেক্সট-টু-স্পিচ"</string>
<string name="tts_default_rate_title" msgid="3964187817364304022">"কথা বলার হার"</string>
@@ -284,8 +284,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"ওয়্যারলেস প্রদর্শন সার্টিফিকেশন জন্য বিকল্পগুলি দেখান"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"ওয়াই-ফাই লগিং স্তর বাড়ান, ওয়াই-ফাই চয়নকারীতে SSID RSSI অনুযায়ী দেখান"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"ব্যাটারির খরচ কমায় এবং নেটওয়ার্কের পারফর্ম্যান্স উন্নত করে"</string>
- <!-- no translation found for wifi_enhanced_mac_randomization_summary (1210663439867489931) -->
- <skip />
+ <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"এই মোডটি চালু থাকার সময়, MAC র্যান্ডমাইজেশন চালু হওয়া এমন কোনও নেটওয়ার্কে কানেক্ট করার সময় এই ডিভাইসের MAC অ্যাড্রেস পরিবর্তিত হতে পারে।"</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"মিটার্ড"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"পরিমাপ করা নয়"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"লগার বাফারের আকারগুলি"</string>
@@ -550,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"অতিথি যোগ করুন"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"অতিথি সরান"</string>
<string name="guest_nickname" msgid="6332276931583337261">"অতিথি"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 4ef8871..e965e5f 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -550,4 +550,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gosta"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Ukloni gosta"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gost"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index baee151..2378d3a 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -284,8 +284,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Mostra les opcions per a la certificació de pantalla sense fil"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Augmenta nivell de registre Wi‑Fi, mostra\'l per SSID RSSI al selector de Wi‑Fi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Redueix el consum de bateria i millora el rendiment de la xarxa"</string>
- <!-- no translation found for wifi_enhanced_mac_randomization_summary (1210663439867489931) -->
- <skip />
+ <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Quan aquest mode està activat, és possible que l’adreça MAC d’aquest dispositiu canviï cada vegada que es connecti a una xarxa amb l\'aleatorització d\'adreces MAC activada."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"D\'ús mesurat"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"D\'ús no mesurat"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Mides de la mem. intermèdia del registrador"</string>
@@ -550,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Afegeix un convidat"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Suprimeix el convidat"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Convidat"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 26fb72ea..aa78612 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -284,8 +284,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Zobrazit možnosti certifikace bezdrátového displeje"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Zvýšit úroveň protokolování Wi‑Fi zobrazenou v SSID a RSSI při výběru sítě Wi‑Fi."</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Snižuje vyčerpávání baterie a vylepšuje výkon sítě"</string>
- <!-- no translation found for wifi_enhanced_mac_randomization_summary (1210663439867489931) -->
- <skip />
+ <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Když je tento režim aktivován, adresa MAC tohoto zařízení se může změnit pokaždé, když se zařízení připojí k síti s aktivovanou randomizací adres MAC."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Měřená"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Neměřená"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Vyrovnávací paměť protokol. nástroje"</string>
@@ -434,7 +433,7 @@
<string name="power_discharge_by" msgid="4113180890060388350">"Vydrží asi do <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="92545648425937000">"Vydrží asi do <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Baterie se může vybít do <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Baterie se může vybít kolem <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Zbývá méně než <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="318215464914990578">"Zbývá méně než <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Zbývá více než <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -552,4 +551,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Přidat hosta"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Odstranit hosta"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Host"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 9ab80da..366bae3 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -211,10 +211,10 @@
<string name="adb_wireless_error" msgid="721958772149779856">"Fejl"</string>
<string name="adb_wireless_settings" msgid="2295017847215680229">"Trådløs fejlretning"</string>
<string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"Du kan se og bruge tilgængelige enheder ved at aktivere trådløs fejlretning"</string>
- <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"Dan par med en enhed ved hjælp af en QR-kode"</string>
- <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"Dan par med nye enheder ved hjælp af QR-kodescanneren"</string>
- <string name="adb_pair_method_code_title" msgid="1122590300445142904">"Dan par med en enhed ved hjælp af en parringskode"</string>
- <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"Dan par med nye enheder ved hjælp af den sekscifrede kode"</string>
+ <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"Par enhed ved hjælp af en QR-kode"</string>
+ <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"Par nye enheder ved hjælp af QR-kodescanneren"</string>
+ <string name="adb_pair_method_code_title" msgid="1122590300445142904">"Par enhed ved hjælp af en parringskode"</string>
+ <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"Par nye enheder ved hjælp af den sekscifrede kode"</string>
<string name="adb_paired_devices_title" msgid="5268997341526217362">"Parrede enheder"</string>
<string name="adb_wireless_device_connected_summary" msgid="3039660790249148713">"Forbundet lige nu"</string>
<string name="adb_wireless_device_details_title" msgid="7129369670526565786">"Enhedsoplysninger"</string>
@@ -222,16 +222,16 @@
<string name="adb_device_fingerprint_title_format" msgid="291504822917843701">"Fingeraftryk for enhed: <xliff:g id="FINGERPRINT_PARAM">%1$s</xliff:g>"</string>
<string name="adb_wireless_connection_failed_title" msgid="664211177427438438">"Forbindelsen mislykkedes"</string>
<string name="adb_wireless_connection_failed_message" msgid="9213896700171602073">"Sørg for, at <xliff:g id="DEVICE_NAME">%1$s</xliff:g> har forbindelse til det rigtige netværk."</string>
- <string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"Dan par med enhed"</string>
+ <string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"Par med enhed"</string>
<string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Parringskode til Wi-Fi"</string>
- <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"Parringen mislykkedes"</string>
+ <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"Pardannelse mislykkedes"</string>
<string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"Sørg for, at enheden er forbundet til det samme netværk."</string>
- <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Dan par med en enhed via Wi-Fi ved at scanne en QR-kode"</string>
+ <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Par en enhed via Wi-Fi ved at scanne en QR-kode"</string>
<string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"Parrer enhed…"</string>
<string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Enheden blev ikke parret. Det skyldes enten, at QR-koden var forkert, eller at enheden ikke er forbundet til det samme netværk."</string>
<string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"IP-adresse og port"</string>
<string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Scan QR-kode"</string>
- <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Dan par med en enhed via Wi-Fi ved at scanne en QR-kode"</string>
+ <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Par en enhed via Wi-Fi ved at scanne en QR-kode"</string>
<string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Opret forbindelse til et Wi-Fi-netværk"</string>
<string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, fejlfinding, dev"</string>
<string name="bugreport_in_power" msgid="8664089072534638709">"Genvej til fejlrapportering"</string>
@@ -549,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Tilføj gæsten"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Fjern gæsten"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gæst"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 73a6245..74f59e2 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -284,8 +284,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Optionen zur Zertifizierung für kabellose Übertragung anzeigen"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"WLAN-Protokollierungsebene erhöhen, pro SSID RSSI in WiFi Picker anzeigen"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Verringert den Akkuverbrauch und verbessert die Netzwerkleistung"</string>
- <!-- no translation found for wifi_enhanced_mac_randomization_summary (1210663439867489931) -->
- <skip />
+ <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Wenn dieser Modus aktiviert ist, kann sich die MAC-Adresse dieses Geräts bei jeder Verbindung mit einem Netzwerk ändern, bei dem die MAC-Adressen randomisiert werden."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Kostenpflichtig"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Kostenlos"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Logger-Puffergrößen"</string>
@@ -550,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Gast hinzufügen"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Gast entfernen"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gast"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index ceabc74..c2502ef 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -549,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Προσθήκη επισκέπτη"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Κατάργηση επισκέπτη"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Επισκέπτης"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index fca1c50..5fa9d45 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -549,4 +549,10 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Guest"</string>
+ <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Device default"</string>
+ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Disabled"</string>
+ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string>
+ <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Your device must be rebooted for this change to apply. Reboot now or cancel."</string>
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index fca1c50..5fa9d45 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -549,4 +549,10 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Guest"</string>
+ <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Device default"</string>
+ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Disabled"</string>
+ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string>
+ <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Your device must be rebooted for this change to apply. Reboot now or cancel."</string>
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index fca1c50..5fa9d45 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -549,4 +549,10 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Guest"</string>
+ <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Device default"</string>
+ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Disabled"</string>
+ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string>
+ <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Your device must be rebooted for this change to apply. Reboot now or cancel."</string>
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index fca1c50..5fa9d45 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -549,4 +549,10 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Guest"</string>
+ <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Device default"</string>
+ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Disabled"</string>
+ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string>
+ <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Your device must be rebooted for this change to apply. Reboot now or cancel."</string>
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 95bc936..1cd2d84 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -549,4 +549,10 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Guest"</string>
+ <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Device default"</string>
+ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Disabled"</string>
+ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string>
+ <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Your device must be rebooted for this change to apply. Reboot now or cancel."</string>
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index e2d488c..025cfc4 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -223,8 +223,8 @@
<string name="adb_wireless_connection_failed_title" msgid="664211177427438438">"Error de conexión"</string>
<string name="adb_wireless_connection_failed_message" msgid="9213896700171602073">"Asegúrate de que <xliff:g id="DEVICE_NAME">%1$s</xliff:g> esté conectado a la red correcta."</string>
<string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"Vincular con dispositivo"</string>
- <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Código de sincroniz. de Wi‑Fi"</string>
- <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"Error de sincronización"</string>
+ <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Código de vinculación de Wi‑Fi"</string>
+ <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"Error de vinculación"</string>
<string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"Asegúrate de que el dispositivo esté conectado a la misma red."</string>
<string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Escanea un código QR para vincular el dispositivo mediante Wi‑Fi"</string>
<string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"Vinculando dispositivo…"</string>
@@ -285,7 +285,7 @@
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Aumentar nivel de registro Wi-Fi; mostrar por SSID RSSI en el selector de Wi-Fi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Reduce el consumo de batería y mejora el rendimiento de la red"</string>
<string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Si este modo está habilitado, es posible que la dirección MAC del dispositivo cambie cada vez que se conecte a una red que tenga habilitada la aleatorización de MAC."</string>
- <string name="wifi_metered_label" msgid="8737187690304098638">"Con uso medido"</string>
+ <string name="wifi_metered_label" msgid="8737187690304098638">"De uso medido"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Sin tarifa plana"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Tamaños de búfer de Logger"</string>
<string name="select_logd_size_dialog_title" msgid="2105401994681013578">"Selecciona el tamaño del Logger por búfer"</string>
@@ -549,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Agregar invitado"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Quitar invitado"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Invitado"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index e7d53f6..03b0495 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -549,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Añadir invitado"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Quitar invitado"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Invitado"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index f6d2c01..4857b1e 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -549,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Lisa külaline"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Eemalda külaline"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Külaline"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 685facb..4d79747 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -284,8 +284,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Erakutsi hari gabe bistaratzeko ziurtagiriaren aukerak"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Erakutsi datu gehiago wifi-sareetan saioa hastean. Erakutsi sarearen identifikatzailea eta seinalearen indarra wifi-sareen hautatzailean."</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Bateria gutxiago kontsumituko da, eta sarearen errendimendua hobetuko."</string>
- <!-- no translation found for wifi_enhanced_mac_randomization_summary (1210663439867489931) -->
- <skip />
+ <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Modu hau gaituta dagoenean, baliteke gailuaren MAC helbidea aldatzea MAC helbideak ausaz antolatzeko aukera gaituta daukan sare batera konektatzen den bakoitzean."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Sare neurtua"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Neurtu gabeko sarea"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Erregistroen buffer-tamainak"</string>
@@ -550,4 +549,10 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Gehitu gonbidatua"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Kendu gonbidatua"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gonbidatua"</string>
+ <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Gailuaren balio lehenetsia"</string>
+ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desgaituta"</string>
+ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Gaituta"</string>
+ <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Aldaketa aplikatzeko, berrabiarazi egin behar da gailua. Berrabiaraz ezazu orain, edo utzi bertan behera."</string>
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index a893c75..d176114 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -153,7 +153,7 @@
<string name="unknown" msgid="3544487229740637809">"ناشناس"</string>
<string name="running_process_item_user_label" msgid="3988506293099805796">"کاربر: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
<string name="launch_defaults_some" msgid="3631650616557252926">"بعضی پیشفرضها تنظیم شدهاند"</string>
- <string name="launch_defaults_none" msgid="8049374306261262709">"هیچ پیشفرضی تنظیم نشده است"</string>
+ <string name="launch_defaults_none" msgid="8049374306261262709">"پیشفرضی تنظیم نشده است"</string>
<string name="tts_settings" msgid="8130616705989351312">"تنظیمات متن به گفتار"</string>
<string name="tts_settings_title" msgid="7602210956640483039">"خروجی تبدیل متن به گفتار"</string>
<string name="tts_default_rate_title" msgid="3964187817364304022">"سرعت گفتار"</string>
@@ -549,4 +549,10 @@
<string name="guest_new_guest" msgid="3482026122932643557">"افزودن مهمان"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"حذف مهمان"</string>
<string name="guest_nickname" msgid="6332276931583337261">"مهمان"</string>
+ <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"پیشفرض دستگاه"</string>
+ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"غیرفعال"</string>
+ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"فعال"</string>
+ <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"برای اعمال این تغییر، دستگاهتان باید راهاندازی مجدد شود. اکنون راهاندازی مجدد کنید یا لغو کنید."</string>
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-fi/arrays.xml b/packages/SettingsLib/res/values-fi/arrays.xml
index c899d9c..af278cb 100644
--- a/packages/SettingsLib/res/values-fi/arrays.xml
+++ b/packages/SettingsLib/res/values-fi/arrays.xml
@@ -61,7 +61,7 @@
<string-array name="bt_hci_snoop_log_entries">
<item msgid="695678520785580527">"Ei käytössä"</item>
<item msgid="6336372935919715515">"Suodatus käytössä"</item>
- <item msgid="2779123106632690576">"Käytössä"</item>
+ <item msgid="2779123106632690576">"Päällä"</item>
</string-array>
<string-array name="bluetooth_avrcp_versions">
<item msgid="8036025277512210160">"AVRCP 1.4 (oletus)"</item>
@@ -242,7 +242,7 @@
<item msgid="1212561935004167943">"Korosta testatut piirtokom. vihreällä"</item>
</string-array>
<string-array name="track_frame_time_entries">
- <item msgid="634406443901014984">"Pois käytöstä"</item>
+ <item msgid="634406443901014984">"Pois päältä"</item>
<item msgid="1288760936356000927">"Ruudulla palkkeina"</item>
<item msgid="5023908510820531131">"Kohteessa <xliff:g id="AS_TYPED_COMMAND">adb shell dumpsys gfxinfo</xliff:g>"</item>
</string-array>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 5fae210..db5e957 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -24,7 +24,7 @@
<string name="wifi_security_none" msgid="7392696451280611452">"Ei mitään"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"Tallennettu"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"Yhteys katkaistu"</string>
- <string name="wifi_disabled_generic" msgid="2651916945380294607">"Pois käytöstä"</string>
+ <string name="wifi_disabled_generic" msgid="2651916945380294607">"Pois päältä"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP-kokoonpanovirhe"</string>
<string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Ei yhteyttä – verkko huonolaatuinen"</string>
<string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Wi-Fi-yhteysvirhe"</string>
@@ -276,7 +276,7 @@
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="2040810756832027227">"Striimaus: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
<string name="select_private_dns_configuration_title" msgid="7887550926056143018">"Yksityinen DNS"</string>
<string name="select_private_dns_configuration_dialog_title" msgid="3731422918335951912">"Valitse yksityinen DNS-tila"</string>
- <string name="private_dns_mode_off" msgid="7065962499349997041">"Pois käytöstä"</string>
+ <string name="private_dns_mode_off" msgid="7065962499349997041">"Pois päältä"</string>
<string name="private_dns_mode_opportunistic" msgid="1947864819060442354">"Automaattinen"</string>
<string name="private_dns_mode_provider" msgid="3619040641762557028">"Yksityisen DNS-tarjoajan isäntänimi"</string>
<string name="private_dns_mode_provider_hostname_hint" msgid="6564868953748514595">"Anna isäntänimi tai DNS-tarjoaja"</string>
@@ -284,8 +284,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Näytä langattoman näytön sertifiointiin liittyvät asetukset."</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Lisää Wi‑Fin lokikirjaustasoa, näytä SSID RSSI -kohtaisesti Wi‑Fi-valitsimessa."</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Vähentää virrankulutusta ja parantaa verkon toimintaa"</string>
- <!-- no translation found for wifi_enhanced_mac_randomization_summary (1210663439867489931) -->
- <skip />
+ <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Kun tämä tila on päällä, laitteen MAC-osoite voi muuttua aina, kun laite yhdistää verkkoon, jossa MAC-satunnaistaminen on käytössä."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Maksullinen"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Maksuton"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Lokipuskurien koot"</string>
@@ -399,7 +398,7 @@
</string-array>
<string name="inactive_apps_title" msgid="5372523625297212320">"Valmiustilasovellukset"</string>
<string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Ei käytössä. Ota käyttöön koskettamalla."</string>
- <string name="inactive_app_active_summary" msgid="8047630990208722344">"Käytössä. Poista käytöstä koskettamalla."</string>
+ <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktiivinen. Vaihda koskettamalla."</string>
<string name="standby_bucket_summary" msgid="5128193447550429600">"Sovelluksen valmiusluokka: <xliff:g id="BUCKET"> %s</xliff:g>"</string>
<string name="runningservices_settings_title" msgid="6460099290493086515">"Käynnissä olevat palvelut"</string>
<string name="runningservices_settings_summary" msgid="1046080643262665743">"Tarkastele ja hallitse käynnissä olevia palveluita."</string>
@@ -456,7 +455,7 @@
<string name="battery_info_status_not_charging" msgid="8330015078868707899">"Kytketty virtalähteeseen, lataaminen ei onnistu"</string>
<string name="battery_info_status_full" msgid="4443168946046847468">"Täynnä"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Järjestelmänvalvoja hallinnoi tätä asetusta."</string>
- <string name="disabled" msgid="8017887509554714950">"Pois käytöstä"</string>
+ <string name="disabled" msgid="8017887509554714950">"Pois päältä"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Sallittu"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Ei sallittu"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Tuntemattomien sovellusten asentaminen"</string>
@@ -550,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Lisää vieras"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Poista vieras"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Vieras"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 689f267..b8ff6598 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -549,4 +549,10 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Ajouter un invité"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Supprimer l\'invité"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Invité"</string>
+ <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Valeur par défaut de l\'appareil"</string>
+ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Désactivé"</string>
+ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activé"</string>
+ <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Votre appareil doit être redémarré pour que ce changement prenne effet. Redémarrez-le maintenant ou annulez la modification."</string>
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 6a88f63..064e59f 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -284,8 +284,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Afficher les options pour la certification de l\'affichage sans fil"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Détailler les infos Wi-Fi, afficher par RSSI de SSID dans l\'outil de sélection Wi-Fi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Réduit la décharge de la batterie et améliore les performances du réseau"</string>
- <!-- no translation found for wifi_enhanced_mac_randomization_summary (1210663439867489931) -->
- <skip />
+ <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Lorsque ce mode est activé, l\'adresse e-mail MAC de cet appareil peut changer lors de chaque connexion à un réseau pour lequel le changement aléatoire d\'adresse MAC est activé."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Facturé à l\'usage"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Non facturé à l\'usage"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Tailles des tampons de l\'enregistreur"</string>
@@ -550,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Ajouter un invité"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Supprimer l\'invité"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Invité"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index c1d5548..8bb04f3 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -549,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Engadir convidado"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Quitar convidado"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index b6845a0..b88126b 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -195,7 +195,7 @@
</string-array>
<string name="choose_profile" msgid="343803890897657450">"પ્રોફાઇલ પસંદ કરો"</string>
<string name="category_personal" msgid="6236798763159385225">"વ્યક્તિગત"</string>
- <string name="category_work" msgid="4014193632325996115">"કાર્યાલય"</string>
+ <string name="category_work" msgid="4014193632325996115">"ઑફિસ"</string>
<string name="development_settings_title" msgid="140296922921597393">"વિકાસકર્તાનાં વિકલ્પો"</string>
<string name="development_settings_enable" msgid="4285094651288242183">"વિકાસકર્તાનાં વિકલ્પો સક્ષમ કરો"</string>
<string name="development_settings_summary" msgid="8718917813868735095">"ઍપ્લિકેશન વિકાસ માટે વિકલ્પો સેટ કરો"</string>
@@ -284,8 +284,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"વાયરલેસ ડિસ્પ્લે પ્રમાણપત્ર માટેના વિકલ્પો બતાવો"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"વાઇ-ફાઇ લોગિંગ સ્તર વધારો, વાઇ-ફાઇ પીકરમાં SSID RSSI દીઠ બતાવો"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"બૅટરીનો ચાર્જ ઝડપથી ઓછો થવાનું ટાળે છે અને નેટવર્કની કાર્યક્ષમતામાં સુધારો કરે છે"</string>
- <!-- no translation found for wifi_enhanced_mac_randomization_summary (1210663439867489931) -->
- <skip />
+ <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"આ મોડ ચાલુ કરેલો હશે, ત્યારે MAC રેન્ડમાઇઝેશન ચાલુ કરેલું હોય તેવા નેટવર્ક સાથે આ ડિવાઇસ જોડાશે ત્યારે દર વખતે તેનું MAC ઍડ્રેસ બદલાય તેમ બની શકે છે."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"મીટર કરેલ"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"મીટર ન કરેલ"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"લોગર બફર કદ"</string>
@@ -550,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"અતિથિ ઉમેરો"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"અતિથિને કાઢી નાખો"</string>
<string name="guest_nickname" msgid="6332276931583337261">"અતિથિ"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index aae58c4..d8aa092 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -284,8 +284,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"वायरलेस डिसप्ले सर्टिफ़िकेशन के विकल्प दिखाएं"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"वाई-फ़ाई लॉगिंग का स्तर बढ़ाएं, वाई-फ़ाई पिकर में प्रति SSID RSSI दिखाएं"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"बैटरी की खपत कम और नेटवर्क की परफ़ॉर्मेंस बेहतर होती है"</string>
- <!-- no translation found for wifi_enhanced_mac_randomization_summary (1210663439867489931) -->
- <skip />
+ <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"जब यह मोड चालू होता है, तब नेटवर्क से कनेक्ट होने पर हर बार इस डिवाइस का MAC पता बदल सकता है. ऐसा तब होता है, जब डिवाइस किसी ऐसे नेटवर्क से जुड़ता है जिस पर MAC पते को बिना किसी तय नियम के बदलने की सुविधा चालू होती है."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"डेटा इस्तेमाल करने की सीमा तय की गई है"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"डेटा इस्तेमाल करने की सीमा तय नहीं की गई है"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"लॉगर बफ़र आकार"</string>
@@ -550,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"मेहमान जोड़ें"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"मेहमान हटाएं"</string>
<string name="guest_nickname" msgid="6332276931583337261">"मेहमान"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 64cd891..548fc20 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -196,7 +196,7 @@
<string name="choose_profile" msgid="343803890897657450">"Odabir profila"</string>
<string name="category_personal" msgid="6236798763159385225">"Osobno"</string>
<string name="category_work" msgid="4014193632325996115">"Posao"</string>
- <string name="development_settings_title" msgid="140296922921597393">"Za razvojne programere"</string>
+ <string name="development_settings_title" msgid="140296922921597393">"Opcije za razvojne programere"</string>
<string name="development_settings_enable" msgid="4285094651288242183">"Omogući opcije za razvojne programere"</string>
<string name="development_settings_summary" msgid="8718917813868735095">"Postavljanje opcija za razvoj aplikacije"</string>
<string name="development_settings_not_available" msgid="355070198089140951">"Opcije razvojnih programera nisu dostupne za ovog korisnika"</string>
@@ -550,4 +550,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Dodavanje gosta"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Uklanjanje gosta"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gost"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index ca410f2..9e51cfd 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -284,8 +284,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Vezeték nélküli kijelző tanúsítványával kapcsolatos lehetőségek megjelenítése"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Wi‑Fi-naplózási szint növelése, RSSI/SSID megjelenítése a Wi‑Fi-választóban"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Csökkenti az akkumulátorhasználatot, és javítja a hálózat teljesítményét"</string>
- <!-- no translation found for wifi_enhanced_mac_randomization_summary (1210663439867489931) -->
- <skip />
+ <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Ha ez a mód be van kapcsolva, akkor ennek az eszköznek a MAC-címe minden alkalommal módosulhat, amikor olyan hálózathoz csatlakozik, amelyen engedélyezve van a MAC-címek randomizálása."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Forgalomkorlátos"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Nem forgalomkorlátos"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Naplózási puffer mérete"</string>
@@ -550,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Vendég hozzáadása"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Vendég munkamenet eltávolítása"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Vendég"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 22c01ac..52e9175 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -549,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Ավելացնել հյուր"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Հեռացնել հյուրին"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Հյուր"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index a578d31..561b7c4 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -549,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Tambahkan tamu"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Hapus tamu"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Tamu"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 862dabd..e99faaf 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -549,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Bæta gesti við"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Fjarlægja gest"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gestur"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index ff3df16..ece0ba4 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -207,11 +207,11 @@
<string name="enable_adb_summary" msgid="3711526030096574316">"Modalità debug quando è connesso tramite USB"</string>
<string name="clear_adb_keys" msgid="3010148733140369917">"Revoca autorizzazioni debug USB"</string>
<string name="enable_adb_wireless" msgid="6973226350963971018">"Debug wireless"</string>
- <string name="enable_adb_wireless_summary" msgid="7344391423657093011">"Modalità Debug quando il Wi-Fi è connesso"</string>
+ <string name="enable_adb_wireless_summary" msgid="7344391423657093011">"Modalità debug quando il Wi-Fi è connesso"</string>
<string name="adb_wireless_error" msgid="721958772149779856">"Errore"</string>
<string name="adb_wireless_settings" msgid="2295017847215680229">"Debug wireless"</string>
<string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"Per trovare e utilizzare i dispositivi disponibili, attiva il debug wireless"</string>
- <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"Accoppia il dispositivo con il codice QR"</string>
+ <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"Accoppia dispositivo con codice QR"</string>
<string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"Accoppia i nuovi dispositivi utilizzando lo scanner di codici QR"</string>
<string name="adb_pair_method_code_title" msgid="1122590300445142904">"Accoppia dispositivo con codice di accoppiamento"</string>
<string name="adb_pair_method_code_summary" msgid="6370414511333685185">"Accoppia i nuovi dispositivi utilizzando un codice di sei cifre"</string>
@@ -284,7 +284,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Mostra opzioni per la certificazione display wireless"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Aumenta livello di logging Wi-Fi, mostra SSID RSSI nel selettore Wi-Fi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Riduce il consumo della batteria e migliora le prestazioni della rete"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Quando questa modalità è attiva, l\'indirizzo MAC del dispositivo potrebbe cambiare ogni volta che si connette a una rete con randomizzazione MAC attivata"</string>
+ <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Quando questa modalità è attiva, l\'indirizzo MAC del dispositivo potrebbe cambiare ogni volta che si connette a una rete con randomizzazione MAC attivata."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"A consumo"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Non a consumo"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Dimensioni buffer logger"</string>
@@ -359,7 +359,7 @@
<string name="track_frame_time" msgid="522674651937771106">"Rendering HWUI profilo"</string>
<string name="enable_gpu_debug_layers" msgid="4986675516188740397">"Attiva livelli debug GPU"</string>
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Consenti caricamento livelli debug GPU per app di debug"</string>
- <string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Attiva reg. dettagl. fornitori"</string>
+ <string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Attiva log dettagliati fornitori"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Includi log aggiuntivi di fornitori relativi a un dispositivo specifico nelle segnalazioni di bug che potrebbero contenere informazioni private, causare un maggior consumo della batteria e/o utilizzare più spazio di archiviazione."</string>
<string name="window_animation_scale_title" msgid="5236381298376812508">"Scala animazione finestra"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Scala animazione transizione"</string>
@@ -549,4 +549,10 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Aggiungi ospite"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Rimuovi ospite"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Ospite"</string>
+ <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Predefinito dispositivo"</string>
+ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Non attivo"</string>
+ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Attivo"</string>
+ <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Devi riavviare il dispositivo per applicare questa modifica. Riavvia ora o annulla."</string>
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index ca0849e..06111b6 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -284,8 +284,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"הצג אפשרויות עבור אישור של תצוגת WiFi"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"העלה את רמת הרישום של Wi‑Fi ביומן, הצג לכל SSID RSSI ב-Wi‑Fi Picker"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"מפחית את קצב התרוקנות הסוללה ומשפר את ביצועי הרשת"</string>
- <!-- no translation found for wifi_enhanced_mac_randomization_summary (1210663439867489931) -->
- <skip />
+ <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"כשמצב זה מופעל, כתובת ה-MAC של המכשיר הזה עשויה להשתנות בכל פעם שהוא מתחבר לרשת שפועלת בה רנדומיזציה של כתובות MAC."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"נמדדת"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"לא נמדדת"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"גדלי מאגר של יומן רישום"</string>
@@ -552,4 +551,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"הוספת אורח"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"הסרת אורח"</string>
<string name="guest_nickname" msgid="6332276931583337261">"אורח"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 604ceb2..850fd97 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -284,8 +284,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"ワイヤレス ディスプレイ認証のオプションを表示"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Wi-Fi ログレベルを上げて、Wi-Fi 選択ツールで SSID RSSI ごとに表示します"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"電池の消耗が軽減され、ネットワーク パフォーマンスが改善されます"</string>
- <!-- no translation found for wifi_enhanced_mac_randomization_summary (1210663439867489931) -->
- <skip />
+ <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"このモードが有効な場合、このデバイスは、MAC アドレスのランダム化が有効なネットワークに接続するたびに MAC アドレスが変わる可能性があります。"</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"従量制"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"定額制"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"ログバッファのサイズ"</string>
@@ -550,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"ゲストを追加"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"ゲストを削除"</string>
<string name="guest_nickname" msgid="6332276931583337261">"ゲスト"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 754d4a0..025cd94 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -549,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"სტუმრის დამატება"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"სტუმრის ამოშლა"</string>
<string name="guest_nickname" msgid="6332276931583337261">"სტუმარი"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 50ff04d..9bcc0a2 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -284,8 +284,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Сымсыз дисплей сертификаты опцияларын көрсету"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Wi‑Fi тіркеу деңгейін арттыру, Wi‑Fi таңдағанда әр SSID RSSI бойынша көрсету"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Батарея зарядының шығынын азайтады және желі жұмысын жақсартады."</string>
- <!-- no translation found for wifi_enhanced_mac_randomization_summary (1210663439867489931) -->
- <skip />
+ <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Бұл режим қосулы болса, құрылғының MAC мекенжайы MAC рандомизациясы қосулы желіге жалғанған сайын өзгеруі мүмкін."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Трафик саналады"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Трафик саналмайды"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Журналға тіркеуші буферінің өлшемдері"</string>
@@ -550,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Қонақты енгізу"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Қонақты өшіру"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Қонақ"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 92ea817..ce03f22 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -284,8 +284,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"បង្ហាញជម្រើសសម្រាប់សេចក្តីបញ្ជាក់ការបង្ហាញឥតខ្សែ"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"បង្កើនកម្រិតកំណត់ហេតុ Wi-Fi បង្ហាញក្នុង SSID RSSI ក្នុងកម្មវិធីជ្រើសរើស Wi-Fi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"កាត់បន្ថយការប្រើប្រាស់ថ្ម និងកែលម្អប្រតិបត្តិការបណ្ដាញ"</string>
- <!-- no translation found for wifi_enhanced_mac_randomization_summary (1210663439867489931) -->
- <skip />
+ <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"នៅពេលបើកមុខងារនេះ អាសយដ្ឋាន MAC របស់ឧបករណ៍នេះអាចផ្លាស់ប្ដូរ រាល់ពេលដែលវាភ្ជាប់ជាមួយបណ្ដាញដែលបានបើកការតម្រៀប MAC តាមលំដាប់ចៃដន្យ។"</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"មានការកំណត់"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"មិនមានការកំណត់"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"ទំហំកន្លែងផ្ទុករបស់ logger"</string>
@@ -550,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"បញ្ចូលភ្ញៀវ"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"លុបភ្ញៀវ"</string>
<string name="guest_nickname" msgid="6332276931583337261">"ភ្ញៀវ"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index f495857..52e3b99 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -549,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"ಅತಿಥಿಯನ್ನು ಸೇರಿಸಿ"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"ಅತಿಥಿಯನ್ನು ತೆಗೆದುಹಾಕಿ"</string>
<string name="guest_nickname" msgid="6332276931583337261">"ಅತಿಥಿ"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 782791a..b95ad9b 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -210,7 +210,7 @@
<string name="enable_adb_wireless_summary" msgid="7344391423657093011">"Wi‑Fi가 연결되었을 때 디버그 모드 사용"</string>
<string name="adb_wireless_error" msgid="721958772149779856">"오류"</string>
<string name="adb_wireless_settings" msgid="2295017847215680229">"무선 디버깅"</string>
- <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"사용 가능한 기기를 보고 사용하려면 무선 디버깅을 사용 설정하세요."</string>
+ <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"사용 가능한 기기를 확인하고 사용하려면 무선 디버깅을 사용 설정하세요."</string>
<string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"QR 코드로 기기 페어링"</string>
<string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"QR 코드 스캐너를 사용하여 새 기기 페어링"</string>
<string name="adb_pair_method_code_title" msgid="1122590300445142904">"페어링 코드로 기기 페어링"</string>
@@ -549,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"게스트 추가"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"게스트 삭제"</string>
<string name="guest_nickname" msgid="6332276931583337261">"게스트"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 3d93110..45fc6cb 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -203,29 +203,29 @@
<string name="vpn_settings_not_available" msgid="2894137119965668920">"Бул колдонуучу VPN жөндөөлөрүн колдоно албайт"</string>
<string name="tethering_settings_not_available" msgid="266821736434699780">"Бул колдонуучу модем режиминин жөндөөлөрүн өзгөртө албайт"</string>
<string name="apn_settings_not_available" msgid="1147111671403342300">"Бул колдонуучу мүмкүндүк алуу түйүнүнүн аталышынын жөндөөлөрүн колдоно албайт"</string>
- <string name="enable_adb" msgid="8072776357237289039">"USB аркылуу мүчүлүштүктөрдү оңдоо"</string>
+ <string name="enable_adb" msgid="8072776357237289039">"USB аркылуу мүчүлүштүктөрдү аныктоо"</string>
<string name="enable_adb_summary" msgid="3711526030096574316">"USB компьютерге сайылганда мүчүлүштүктөрдү оңдоо режими иштейт"</string>
- <string name="clear_adb_keys" msgid="3010148733140369917">"USB аркылуу мүчүлүштүктөрдү оңдоо уруксатын артка кайтаруу"</string>
- <string name="enable_adb_wireless" msgid="6973226350963971018">"Мүчүлүштүктөрдү Wi-Fi аркылуу оңдоо"</string>
- <string name="enable_adb_wireless_summary" msgid="7344391423657093011">"Wi‑Fi\'га туташтырылганда мүчүлүштүктөрдү оңдоо режими"</string>
+ <string name="clear_adb_keys" msgid="3010148733140369917">"USB аркылуу мүчүлүштүктөрдү аныктоо уруксатын артка кайтаруу"</string>
+ <string name="enable_adb_wireless" msgid="6973226350963971018">"Мүчүлүштүктөрдү Wi-Fi аркылуу аныктоо"</string>
+ <string name="enable_adb_wireless_summary" msgid="7344391423657093011">"Wi‑Fi\'га туташканда, мүчүлүштүктөрдү аныктоо режими иштейт оңдоо режими"</string>
<string name="adb_wireless_error" msgid="721958772149779856">"Ката"</string>
- <string name="adb_wireless_settings" msgid="2295017847215680229">"Мүчүлүштүктөрдү Wi-Fi аркылуу оңдоо"</string>
- <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"Жеткиликтүү түзмөктөрдү көрүү үчүн мүчүлүштүктөрдү Wi-Fi аркылуу оңдоону күйгүзүңүз"</string>
- <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"QR кодун колдонуп, түзмөктү жупташтырыңыз"</string>
+ <string name="adb_wireless_settings" msgid="2295017847215680229">"Мүчүлүштүктөрдү Wi-Fi аркылуу аныктоо"</string>
+ <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"Жеткиликтүү түзмөктөрдү көрүү үчүн, мүчүлүштүктөрдү Wi-Fi аркылуу аныктоону күйгүзүңүз"</string>
+ <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"Түзмөктү QR коду аркылуу жупташтыруу"</string>
<string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"QR кодунун сканерин колдонуп, жаңы түзмөктөрдү жупташтырыңыз"</string>
- <string name="adb_pair_method_code_title" msgid="1122590300445142904">"Жупташтыруучу код менен түзмөктү жупташтырыңыз"</string>
- <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"Алты сандан турган кодду колдонуп, жаңы түзмөктөрдү жупташтырыңыз"</string>
+ <string name="adb_pair_method_code_title" msgid="1122590300445142904">"Түзмөктү атайын код аркылуу жупташтыруу"</string>
+ <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"Жаңы түзмөктөрдү алты сандан турган код аркылуу жупташтырасыз"</string>
<string name="adb_paired_devices_title" msgid="5268997341526217362">"Жупташтырылган түзмөктөр"</string>
<string name="adb_wireless_device_connected_summary" msgid="3039660790249148713">"Учурда туташып турган түзмөктөр"</string>
<string name="adb_wireless_device_details_title" msgid="7129369670526565786">"Түзмөктүн чоо-жайы"</string>
<string name="adb_device_forget" msgid="193072400783068417">"Унутулсун"</string>
<string name="adb_device_fingerprint_title_format" msgid="291504822917843701">"Түзмөктөгү манжа изи: <xliff:g id="FINGERPRINT_PARAM">%1$s</xliff:g>"</string>
- <string name="adb_wireless_connection_failed_title" msgid="664211177427438438">"Туташкан жок"</string>
+ <string name="adb_wireless_connection_failed_title" msgid="664211177427438438">"Байланышкан жок"</string>
<string name="adb_wireless_connection_failed_message" msgid="9213896700171602073">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> туура тармакка туташып турганын текшериңиз"</string>
<string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"Түзмөктү жупташтыруу"</string>
<string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Wi‑Fi жупташтыруучу коду"</string>
<string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"Жупташтырылган жок"</string>
- <string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"Түзмөк бир тармакка туташканын текшериңиз."</string>
+ <string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"Түзмөк бир тармакка туташып турушу керек."</string>
<string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"QR кодун скандап, түзмөктү Wi‑Fi аркылуу жупташтырыңыз"</string>
<string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"Түзмөк жупташтырылууда…"</string>
<string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Түзмөк жупташтырылган жок. QR коду туура эмес же түзмөк бир тармакка туташпай турат."</string>
@@ -303,7 +303,7 @@
<string name="adb_warning_title" msgid="7708653449506485728">"USB аркылуу жөндөөгө уруксат бересизби?"</string>
<string name="adb_warning_message" msgid="8145270656419669221">"USB-жөндөө - өндүрүү максатында гана түзүлгөн. Аны компүтериңиз менен түзмөгүңүздүн ортосунда берилиштерди алмашуу, түзмөгүңүзгө колдонмолорду эскертүүсүз орнотуу жана лог берилиштерин окуу үчүн колдонсоңуз болот."</string>
<string name="adbwifi_warning_title" msgid="727104571653031865">"Мүчүлүштүктөрдү Wi-Fi аркылуу оңдоого уруксат берилсинби?"</string>
- <string name="adbwifi_warning_message" msgid="8005936574322702388">"Мүчүлүштүктөрдү Wi-Fi аркылуу оңдоо – өндүрүү максатында гана түзүлгөн. Аны компьютериңиз менен түзмөгүңүздүн ортосунда маалыматты алмашуу, колдонмолорду түзмөгүңүзгө эскертүүсүз орнотуу жана маалыматтар таржымалын окуу үчүн колдонсоңуз болот."</string>
+ <string name="adbwifi_warning_message" msgid="8005936574322702388">"Мүчүлүштүктөрдү Wi-Fi аркылуу аныктоо – өндүрүү максатында гана түзүлгөн. Аны компьютериңиз менен түзмөгүңүздүн ортосунда маалыматты алмашуу, колдонмолорду түзмөгүңүзгө эскертүүсүз орнотуу жана маалыматтар таржымалын окуу үчүн колдонсоңуз болот."</string>
<string name="adb_keys_warning_message" msgid="2968555274488101220">"Сиз мурун USB жөндөөлөрүнө уруксат берген бардык компүтерлердин жеткиси жокко чыгарылсынбы?"</string>
<string name="dev_settings_warning_title" msgid="8251234890169074553">"Жөндөөлөрдү өзгөртүү"</string>
<string name="dev_settings_warning_message" msgid="37741686486073668">"Бул орнотуулар өндүрүүчүлөр үчүн гана берилген. Булар түзмөгүңүздүн колдонмолорун бузулушуна же туура эмес иштешине алып келиши мүмкүн."</string>
@@ -317,7 +317,7 @@
<string name="enable_terminal_summary" msgid="2481074834856064500">"Жергиликтүү буйрук кабыгын сунуштаган терминалга уруксат берүү"</string>
<string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP текшерүү"</string>
<string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP текшерүү тартиби"</string>
- <string name="debug_debugging_category" msgid="535341063709248842">"Мүчүлүштүктөрдү оңдоо"</string>
+ <string name="debug_debugging_category" msgid="535341063709248842">"Мүчүлүштүктөрдү аныктоо"</string>
<string name="debug_app" msgid="8903350241392391766">"Мүчүлүштүктөрдү оңдоочу колдонмону тандоо"</string>
<string name="debug_app_not_set" msgid="1934083001283807188">"Бир дагы колдонмо орнотула элек."</string>
<string name="debug_app_set" msgid="6599535090477753651">"Жөндөөчү колдонмо: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
@@ -360,7 +360,7 @@
<string name="enable_gpu_debug_layers" msgid="4986675516188740397">"GPU мүчүлүштүктөрдү оңдоо катмарларын иштетүү"</string>
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"GPU мүчүлүштүктөрдү оңдоо катмарларын иштетет"</string>
<string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"Кызмат көрсөтүүчүнү оозеки киргизүүнү иштетет"</string>
- <string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Түзмөккө байланыштуу кызмат көрсөтүүчүнүн кирүүлөрү боюнча мүчүлүштүк тууралуу кабар берүү камтылсын. Анда купуя маалымат көрсөтүлүп, батарея тезирээк отуруп жана/же сактагычтан көбүрөөк орун ээлениши мүмкүн."</string>
+ <string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"Түзмөккө байланыштуу кызмат көрсөтүүчүнүн кирүүлөрү боюнча мүчүлүштүк тууралуу кабарлоо камтылсын. Анда купуя маалымат көрсөтүлүп, батарея тезирээк отуруп жана/же сактагычтан көбүрөөк орун ээлениши мүмкүн."</string>
<string name="window_animation_scale_title" msgid="5236381298376812508">"Терезелердин анимациясы"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Өткөрүү анимацснн шкаласы"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Анимациянын узактыгы"</string>
@@ -549,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Конок кошуу"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Конокту өчүрүү"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Конок"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 856f26c..4e39674 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -549,4 +549,10 @@
<string name="guest_new_guest" msgid="3482026122932643557">"ເພີ່ມແຂກ"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"ລຶບແຂກອອກ"</string>
<string name="guest_nickname" msgid="6332276931583337261">"ແຂກ"</string>
+ <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ຄ່າເລີ່ມຕົ້ນອຸປະກອນ"</string>
+ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ປິດການນຳໃຊ້ແລ້ວ"</string>
+ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ເປີດການນຳໃຊ້ແລ້ວ"</string>
+ <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ທ່ານຕ້ອງປິດເປີດອຸປະກອນຄືນໃໝ່ເພື່ອນຳໃຊ້ການປ່ຽນແປງນີ້. ປິດເປີດໃໝ່ດຽວນີ້ ຫຼື ຍົກເລີກ."</string>
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index eefc709..81357cc 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -284,8 +284,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Rodyti belaidžio rodymo sertifikavimo parinktis"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Padidinti „Wi‑Fi“ įrašymo į žurnalą lygį, rodyti SSID RSSI „Wi-Fi“ rinkiklyje"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Sumažinamas akumuliatoriaus eikvojimas ir patobulinamas tinklo našumas"</string>
- <!-- no translation found for wifi_enhanced_mac_randomization_summary (1210663439867489931) -->
- <skip />
+ <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Kai įgalintas šis režimas, šio įrenginio MAC adresas gali keistis kas kartą prisijungus prie tinklo, kuriame įgalintas atsitiktinis MAC parinkimas."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Matuojamas"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Neišmatuotas"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Registruotuvo buferio dydžiai"</string>
@@ -552,4 +551,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Pridėti svečią"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Pašalinti svečią"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Svečias"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 758fba7..c63f312 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -550,4 +550,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Pievienot viesi"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Noņemt viesi"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Viesis"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index a662cd3..e0cc38d 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -549,4 +549,10 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Додај гостин"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Отстрани гостин"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Гостин"</string>
+ <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Стандардно за уредот"</string>
+ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Оневозможено"</string>
+ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Овозможено"</string>
+ <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"За да се примени променава, уредот мора да се рестартира. Рестартирајте сега или откажете."</string>
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 1af22b2..1a3f068 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -153,7 +153,7 @@
<string name="unknown" msgid="3544487229740637809">"അജ്ഞാതം"</string>
<string name="running_process_item_user_label" msgid="3988506293099805796">"ഉപയോക്താവ്: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
<string name="launch_defaults_some" msgid="3631650616557252926">"സ്ഥിരമായ ചിലത് സജ്ജീകരിച്ചു"</string>
- <string name="launch_defaults_none" msgid="8049374306261262709">"സ്ഥിരമായൊന്നും സജ്ജീകരിച്ചിട്ടില്ല"</string>
+ <string name="launch_defaults_none" msgid="8049374306261262709">"ഡിഫോൾട്ടുകളൊന്നും സജ്ജീകരിച്ചിട്ടില്ല"</string>
<string name="tts_settings" msgid="8130616705989351312">"ടെക്സ്റ്റ്-ടു-സ്പീച്ച് ക്രമീകരണങ്ങൾ"</string>
<string name="tts_settings_title" msgid="7602210956640483039">"ടെക്സ്റ്റ്-ടു-സ്പീച്ച് ഔട്ട്പുട്ട്"</string>
<string name="tts_default_rate_title" msgid="3964187817364304022">"വായനയുടെ വേഗത"</string>
@@ -207,7 +207,7 @@
<string name="enable_adb_summary" msgid="3711526030096574316">"USB കണക്റ്റുചെയ്തിരിക്കുമ്പോഴുള്ള ഡീബഗ് മോഡ്"</string>
<string name="clear_adb_keys" msgid="3010148733140369917">"USB ഡീബഗ്ഗിംഗ് അംഗീകാരം പിൻവലിക്കുക"</string>
<string name="enable_adb_wireless" msgid="6973226350963971018">"വയർലെസ് ഡീബഗ്ഗിംഗ്"</string>
- <string name="enable_adb_wireless_summary" msgid="7344391423657093011">"വൈഫൈ കണക്റ്റ് ചെയ്തിരിക്കുമ്പോൾ ഡീബഗ് ചെയ്യൽ മോഡിലാക്കുക"</string>
+ <string name="enable_adb_wireless_summary" msgid="7344391423657093011">"വൈഫൈ കണക്റ്റ് ചെയ്തിരിക്കുമ്പോൾ ഡീബഗ് മോഡിലാക്കുക"</string>
<string name="adb_wireless_error" msgid="721958772149779856">"പിശക്"</string>
<string name="adb_wireless_settings" msgid="2295017847215680229">"വയർലെസ് ഡീബഗ്ഗിംഗ്"</string>
<string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"ലഭ്യമായ ഉപകരണങ്ങൾ കാണാനും ഉപയോഗിക്കാനും വയർലെസ് ഡീബഗ്ഗിംഗ് ഓണാക്കുക"</string>
@@ -284,8 +284,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"വയർലെസ് ഡിസ്പ്ലേ സർട്ടിഫിക്കേഷനായി ഓപ്ഷനുകൾ ദൃശ്യമാക്കുക"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"വൈഫൈ പിക്കറിൽ ഓരോ SSID RSSI പ്രകാരം കാണിക്കാൻ വൈഫൈ ലോഗിംഗ് നില വർദ്ധിപ്പിക്കുക"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"ബാറ്ററി ചാർജ് വേഗത്തിൽ തീരുന്ന അവസ്ഥ കുറച്ച് നെറ്റ്വർക്ക് പ്രകടനം മെച്ചപ്പെടുത്തുന്നു"</string>
- <!-- no translation found for wifi_enhanced_mac_randomization_summary (1210663439867489931) -->
- <skip />
+ <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"ഈ മോഡ് പ്രവർത്തനക്ഷമമാക്കുമ്പോൾ, MAC ക്രമരഹിതമാക്കൽ പ്രവർത്തനക്ഷമമാക്കിയിരിക്കുന്ന നെറ്റ്വർക്കിലേക്ക് കണക്റ്റ് ചെയ്യുമ്പോഴെല്ലാം ഈ ഉപകരണത്തിന്റെ MAC വിലാസം മാറിയേക്കാം."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"മീറ്റർ ചെയ്തത്"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"മീറ്റർമാപകമല്ലാത്തത്"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"ലോഗർ ബഫർ വലുപ്പം"</string>
@@ -550,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"അതിഥിയെ ചേർക്കുക"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"അതിഥിയെ നീക്കം ചെയ്യുക"</string>
<string name="guest_nickname" msgid="6332276931583337261">"അതിഥി"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 05e1393..81733f2 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -549,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Зочин нэмэх"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Зочин хасах"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Зочин"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index e5f6764..0895cd4 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -284,8 +284,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"वायरलेस डिस्प्ले प्रमाणिकरणाचे पर्याय दाखवा"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"वाय-फाय लॉगिंग स्तर वाढवा, वाय-फाय सिलेक्टरमध्ये प्रति SSID RSSI दर्शवा"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"बॅटरी जलदरीतीने संपण्यापासून रोखते आणि नेटवर्क परफॉर्मन्समध्ये सुधारणा करते"</string>
- <!-- no translation found for wifi_enhanced_mac_randomization_summary (1210663439867489931) -->
- <skip />
+ <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"हा मोड सुरू केला असल्यास, या डिव्हाइसचा MAC अॅड्रेस प्रत्येक वेळी MAC रँडमायझेशन सुरू असलेल्या नेटवर्कशी कनेक्ट झाल्यास, कदाचित बदलू शकतो."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"मीटरने मोजलेले"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"मीटरने न मोजलेले"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"लॉगर बफर आकार"</string>
@@ -550,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"अतिथी जोडा"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"अतिथी काढून टाका"</string>
<string name="guest_nickname" msgid="6332276931583337261">"अतिथी"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index c6be11f..bb70a86 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -284,8 +284,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Tunjukkan pilihan untuk pensijilan paparan wayarles"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Tingkatkan tahap pengelogan Wi-Fi, tunjuk setiap SSID RSSI dalam Pemilih Wi-Fi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Mengurangkan penyusutan bateri & meningkatkan prestasi rangkaian"</string>
- <!-- no translation found for wifi_enhanced_mac_randomization_summary (1210663439867489931) -->
- <skip />
+ <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Apabila mod ini didayakan, alamat MAC peranti ini mungkin berubah setiap kali peranti bersambung kepada rangkaian yang telah mendayakan perawakan MAC."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Bermeter"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Tidak bermeter"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Saiz penimbal pengelog"</string>
@@ -550,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Tambah tetamu"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Alih keluar tetamu"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Tetamu"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 037436e..920d659 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -549,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"ဧည့်သည့် ထည့်ရန်"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"ဧည့်သည်ကို ဖယ်ထုတ်ရန်"</string>
<string name="guest_nickname" msgid="6332276931583337261">"ဧည့်သည်"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 1ab840c..8dfa9a1 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -549,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Legg til en gjest"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Fjern gjesten"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gjest"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 7c6785b..42ef162 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -284,8 +284,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"ताररहित प्रदर्शन प्रमाणीकरणका लागि विकल्पहरू देखाउनुहोस्"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Wi-Fi लग स्तर बढाउनुहोस्, Wi-Fi चयनकर्तामा प्रति SSID RSSI देखाइन्छ"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"ब्याट्रीको खपत कम गरी नेटवर्कको कार्यसम्पादनमा सुधार गर्दछ"</string>
- <!-- no translation found for wifi_enhanced_mac_randomization_summary (1210663439867489931) -->
- <skip />
+ <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"यो मोड अन गरिएका बेला यो यन्त्र MAC ठेगाना बदल्ने सुविधा अन गरिएको कुनै इन्टरनेटसँग जति पटक कनेक्ट हुन्छ त्यति नै पटक यस यन्त्रको MAC ठेगाना पनि परिवर्तन हुन सक्छ।"</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"सशुल्क वाइफाइ"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"मिटर नगरिएको"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"लगर बफर आकारहरू"</string>
@@ -550,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"अतिथि थप्नुहोस्"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"अतिथि हटाउनुहोस्"</string>
<string name="guest_nickname" msgid="6332276931583337261">"अतिथि"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 417cd95..6391690 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -549,4 +549,10 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Gast toevoegen"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Gast verwijderen"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gast"</string>
+ <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Apparaatstandaard"</string>
+ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Uitgeschakeld"</string>
+ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ingeschakeld"</string>
+ <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Je apparaat moet opnieuw worden opgestart om deze wijziging toe te passen. Start nu opnieuw op of annuleer de wijziging."</string>
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 74c1477..aea13b5c 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -153,7 +153,7 @@
<string name="unknown" msgid="3544487229740637809">"ଅଜଣା"</string>
<string name="running_process_item_user_label" msgid="3988506293099805796">"ଉପଯୋଗକର୍ତ୍ତା: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
<string name="launch_defaults_some" msgid="3631650616557252926">"କିଛି ପୂର୍ବ-ନିର୍ଦ୍ଧାରିତ ମାନ ସେଟ୍ ହୋଇଛି"</string>
- <string name="launch_defaults_none" msgid="8049374306261262709">"କୌଣସି ପୂର୍ବ-ନିର୍ଦ୍ଧାରଣ ସେଟ୍ ହୋଇନାହିଁ"</string>
+ <string name="launch_defaults_none" msgid="8049374306261262709">"କୌଣସି ଡିଫଲ୍ଟ ସେଟ୍ ହୋଇନାହିଁ"</string>
<string name="tts_settings" msgid="8130616705989351312">"ଟେକ୍ସଟ-ରୁ-ସ୍ପିଚ୍ ସେଟିଂସ୍"</string>
<string name="tts_settings_title" msgid="7602210956640483039">"ଟେକ୍ସଟ-ରୁ-ସ୍ପିଚ୍ ଆଉଟପୁଟ୍"</string>
<string name="tts_default_rate_title" msgid="3964187817364304022">"ସ୍ପିଚ୍ ରେଟ୍"</string>
@@ -203,9 +203,9 @@
<string name="vpn_settings_not_available" msgid="2894137119965668920">"VPN ସେଟିଙ୍ଗ ଏହି ଉପଯୋଗକର୍ତ୍ତାଙ୍କ ପାଇଁ ଉପଲବ୍ଧ ନୁହେଁ"</string>
<string name="tethering_settings_not_available" msgid="266821736434699780">"ଏହି ଉପଯୋଗକର୍ତ୍ତାଙ୍କ ପାଇଁ ଟିଥରିଙ୍ଗ ସେଟିଙ୍ଗ ଉପଲବ୍ଧ ନାହିଁ"</string>
<string name="apn_settings_not_available" msgid="1147111671403342300">"ଆକ୍ସେସ୍ ପଏଣ୍ଟ ନାମର ସେଟିଙ୍ଗ ଏହି ଉପଯୋଗକର୍ତ୍ତାଙ୍କ ପାଇଁ ଉପଲବ୍ଧ ନାହିଁ"</string>
- <string name="enable_adb" msgid="8072776357237289039">"USB ଡିବଗ୍ ହେଉଛି"</string>
+ <string name="enable_adb" msgid="8072776357237289039">"USB ଡିବଗିଂ"</string>
<string name="enable_adb_summary" msgid="3711526030096574316">"USB ସଂଯୁକ୍ତ ହେବାବେଳେ ଡିବଗ୍ ମୋଡ୍"</string>
- <string name="clear_adb_keys" msgid="3010148733140369917">"USB ଡିବଗିଙ୍ଗ ଅଧିକାରକୁ କାଢ଼ିଦିଅନ୍ତୁ"</string>
+ <string name="clear_adb_keys" msgid="3010148733140369917">"USB ଡିବଗିଂ ଅଧିକାରକୁ ବାତିଲ୍ କରନ୍ତୁ"</string>
<string name="enable_adb_wireless" msgid="6973226350963971018">"ୱାୟାରଲେସ୍ ଡିବଗିଂ"</string>
<string name="enable_adb_wireless_summary" msgid="7344391423657093011">"ୱାଇ-ଫାଇ ସଂଯୁକ୍ତ ଥିବା ବେଳେ ଡିବଗ୍ ମୋଡ୍"</string>
<string name="adb_wireless_error" msgid="721958772149779856">"ତ୍ରୁଟି"</string>
@@ -222,7 +222,7 @@
<string name="adb_device_fingerprint_title_format" msgid="291504822917843701">"ଡିଭାଇସ୍ ଫିଙ୍ଗରପ୍ରିଣ୍ଟ: <xliff:g id="FINGERPRINT_PARAM">%1$s</xliff:g>"</string>
<string name="adb_wireless_connection_failed_title" msgid="664211177427438438">"ସଂଯୋଗ ବିଫଳ ହେଲା"</string>
<string name="adb_wireless_connection_failed_message" msgid="9213896700171602073">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ଫୋନ୍ ନେଟୱାର୍କ ସହ ସଂଯୁକ୍ତ ଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ"</string>
- <string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"ଡିଭାଇସରୁ ପେୟାର୍ କରନ୍ତୁ"</string>
+ <string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"ଡିଭାଇସରେ ପେୟାର୍ କରନ୍ତୁ"</string>
<string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"ୱାଇ-ଫାଇ ପେୟାରିଂ କୋଡ୍"</string>
<string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"ପେୟାରିଂ ବିଫଳ ହେଲା"</string>
<string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"ଡିଭାଇସଟି ସମାନ ନେଟୱାର୍କରେ ସଂଯୋଗ ହୋଇଥିବା ସୁନିଶ୍ଚିତ କରନ୍ତୁ।"</string>
@@ -284,8 +284,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"ୱେୟାରଲେସ୍ ଡିସ୍ପ୍ଲେ ସାର୍ଟିଫିକେସନ୍ ପାଇଁ ବିକଳ୍ପ ଦେଖାନ୍ତୁ"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"ୱାଇ-ଫାଇ ଲଗିଙ୍ଗ ସ୍ତର ବଢ଼ାନ୍ତୁ, ୱାଇ-ଫାଇ ପିକର୍ରେ ପ୍ରତି SSID RSSI ଦେଖାନ୍ତୁ"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"ବ୍ୟାଟେରୀ ଖର୍ଚ୍ଚ କମ୍ ଏବଂ ନେଟ୍ୱାର୍କ କାର୍ଯ୍ୟକ୍ଷମତା ଉନ୍ନତ କରିଥାଏ"</string>
- <!-- no translation found for wifi_enhanced_mac_randomization_summary (1210663439867489931) -->
- <skip />
+ <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"ଯେତେବେଳେ ଏହି ମୋଡ୍ ସକ୍ଷମ ହୁଏ, ପ୍ରତ୍ୟେକ ଥର MAC ରେଣ୍ଡୋମାଇଜେସନ୍ ସକ୍ଷମ ଥିବା କୌଣସି ନେଟୱାର୍କ ସହ ଏହି ଡିଭାଇସ୍ ସଂଯୋଗ ହେଲେ ଏହାର MAC ଠିକଣା ବଦଳିପାରେ।"</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"ମପାଯାଉଥିବା"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"ମପାଯାଉନଥିବା"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"ଲଗର୍ ବଫର୍ ସାଇଜ୍"</string>
@@ -550,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"ଅତିଥି ଯୋଗ କରନ୍ତୁ"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"ଅତିଥିଙ୍କୁ କାଢ଼ି ଦିଅନ୍ତୁ"</string>
<string name="guest_nickname" msgid="6332276931583337261">"ଅତିଥି"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 68cc061..b502f3f 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -549,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"ਮਹਿਮਾਨ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"ਮਹਿਮਾਨ ਹਟਾਓ"</string>
<string name="guest_nickname" msgid="6332276931583337261">"ਮਹਿਮਾਨ"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 9db0f5e..cdcb651 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -195,7 +195,7 @@
</string-array>
<string name="choose_profile" msgid="343803890897657450">"Wybierz profil"</string>
<string name="category_personal" msgid="6236798763159385225">"Osobiste"</string>
- <string name="category_work" msgid="4014193632325996115">"Służbowe"</string>
+ <string name="category_work" msgid="4014193632325996115">"Do pracy"</string>
<string name="development_settings_title" msgid="140296922921597393">"Opcje programistyczne"</string>
<string name="development_settings_enable" msgid="4285094651288242183">"Włącz opcje dla programistów"</string>
<string name="development_settings_summary" msgid="8718917813868735095">"Ustaw opcje związane z programowaniem aplikacji."</string>
@@ -551,4 +551,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gościa"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Usuń gościa"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gość"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index b33b2b5..d685802 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -223,7 +223,7 @@
<string name="adb_wireless_connection_failed_title" msgid="664211177427438438">"Falha na conexão"</string>
<string name="adb_wireless_connection_failed_message" msgid="9213896700171602073">"Verifique se o <xliff:g id="DEVICE_NAME">%1$s</xliff:g> está conectado à rede correta"</string>
<string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"Parear com o dispositivo"</string>
- <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Código de pareamento de Wi‑Fi"</string>
+ <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Código de pareamento por Wi‑Fi"</string>
<string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"Falha no pareamento"</string>
<string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"Verifique se o dispositivo está conectado à mesma rede."</string>
<string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Parear dispositivo na rede Wi‑Fi fazendo a leitura do código QR"</string>
@@ -549,4 +549,10 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Adicionar convidado"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Remover convidado"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string>
+ <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Padrão do dispositivo"</string>
+ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desativado"</string>
+ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ativado"</string>
+ <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"É necessário reinicializar o dispositivo para que a mudança seja aplicada. Faça isso agora ou cancele."</string>
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 36b8468..ca808b9 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -549,4 +549,10 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Adicionar convidado"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Remover convidado"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string>
+ <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Predefinição do dispositivo"</string>
+ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desativada"</string>
+ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ativada"</string>
+ <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"É necessário reiniciar o dispositivo para aplicar esta alteração. Reinicie agora ou cancele."</string>
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index b33b2b5..d685802 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -223,7 +223,7 @@
<string name="adb_wireless_connection_failed_title" msgid="664211177427438438">"Falha na conexão"</string>
<string name="adb_wireless_connection_failed_message" msgid="9213896700171602073">"Verifique se o <xliff:g id="DEVICE_NAME">%1$s</xliff:g> está conectado à rede correta"</string>
<string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"Parear com o dispositivo"</string>
- <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Código de pareamento de Wi‑Fi"</string>
+ <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Código de pareamento por Wi‑Fi"</string>
<string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"Falha no pareamento"</string>
<string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"Verifique se o dispositivo está conectado à mesma rede."</string>
<string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Parear dispositivo na rede Wi‑Fi fazendo a leitura do código QR"</string>
@@ -549,4 +549,10 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Adicionar convidado"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Remover convidado"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string>
+ <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Padrão do dispositivo"</string>
+ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desativado"</string>
+ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ativado"</string>
+ <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"É necessário reinicializar o dispositivo para que a mudança seja aplicada. Faça isso agora ou cancele."</string>
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index e784873..5954d74 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -550,4 +550,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Adăugați un invitat"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Ștergeți invitatul"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Invitat"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index bdda5ee..22b8b25 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -551,4 +551,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Добавить аккаунт гостя"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Удалить аккаунт гостя"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Гость"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index ba710e2..26677f7 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -549,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"අමුත්තා එක් කරන්න"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"අමුත්තා ඉවත් කරන්න"</string>
<string name="guest_nickname" msgid="6332276931583337261">"අමුත්තා"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 5abba6f..53c2eb0 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -211,9 +211,9 @@
<string name="adb_wireless_error" msgid="721958772149779856">"Chyba"</string>
<string name="adb_wireless_settings" msgid="2295017847215680229">"Bezdrôtové ladenie"</string>
<string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"Ak chcete zobraziť a používať dostupné zariadenia, zapnite bezdrôtové ladenie"</string>
- <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"Spárovať zariadenie pomocou QR kódu"</string>
+ <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"Spárovať zariadenie QR kódom"</string>
<string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"Spárujte nové zariadenia pomocou skenera QR kódov"</string>
- <string name="adb_pair_method_code_title" msgid="1122590300445142904">"Spárovať zariadenie pomocou párovacieho kódu"</string>
+ <string name="adb_pair_method_code_title" msgid="1122590300445142904">"Spárovať zariadenie párovacím kódom"</string>
<string name="adb_pair_method_code_summary" msgid="6370414511333685185">"Spárujte nové zariadenia pomocou šesťmiestneho kódu"</string>
<string name="adb_paired_devices_title" msgid="5268997341526217362">"Spárované zariadenia"</string>
<string name="adb_wireless_device_connected_summary" msgid="3039660790249148713">"Aktuálne pripojené"</string>
@@ -226,15 +226,15 @@
<string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Párovací kód siete Wi-Fi"</string>
<string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"Párovanie zlyhalo"</string>
<string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"Skontrolujte, či je zariadenie pripojené k rovnakej sieti."</string>
- <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Spárujte zariadenie cez sieť Wi-Fi naskenovaním QR kódu"</string>
+ <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Spárujte zariadenie cez Wi-Fi naskenovaním QR kódu"</string>
<string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"Páruje sa zariadenie…"</string>
<string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Zariadenie sa nepodarilo spárovať. Buď bol QR kód nesprávny, alebo zariadenie nie je pripojené k rovnakej sieti."</string>
<string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"Adresa IP a port"</string>
<string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Naskenujte QR kód"</string>
- <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Spárujte zariadenie cez sieť Wi-Fi naskenovaním QR kódu"</string>
+ <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Spárujte zariadenie cez Wi-Fi naskenovaním QR kódu"</string>
<string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Pripojte sa k sieti Wi‑Fi"</string>
<string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, ladenie, dev"</string>
- <string name="bugreport_in_power" msgid="8664089072534638709">"Skratka hlásenia chyby"</string>
+ <string name="bugreport_in_power" msgid="8664089072534638709">"Odkaz na hlásenie chyby"</string>
<string name="bugreport_in_power_summary" msgid="1885529649381831775">"Zobraziť v hlavnej ponuke tlačidlo na vytvorenie hlásenia chyby"</string>
<string name="keep_screen_on" msgid="1187161672348797558">"Nevypínať obrazovku"</string>
<string name="keep_screen_on_summary" msgid="1510731514101925829">"Obrazovka sa pri nabíjaní neprepne do režimu spánku"</string>
@@ -551,4 +551,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Pridať hosťa"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Odobrať hosťa"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Hosť"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index b0c6117..38ca86a 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -72,14 +72,14 @@
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Povezano (brez predstavnosti) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Povezano (brez dostopa do sporočil) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Povezano (brez telefona/predstavnosti) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Povezano, raven napolnjenosti akumulatorja je <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Povezano (brez telefona), raven napolnjenosti akumulatorja je <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_a2dp_battery_level" msgid="6499078454894324287">"Povezano (brez predstavnosti), raven napolnjenosti akumulatorja je <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="8477440576953067242">"Povezano (brez telefona ali predstavnosti), raven napolnjenosti akumulatorja je <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
- <string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Aktivna, akumulator na <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Aktivno, L: napolnjenost akumulatorja je <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, D: napolnjenost akumulatorja je <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Povezano, raven napolnjenosti baterije je <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
+ <string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Povezano (brez telefona), raven napolnjenosti baterije je <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
+ <string name="bluetooth_connected_no_a2dp_battery_level" msgid="6499078454894324287">"Povezano (brez predstavnosti), raven napolnjenosti baterije je <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
+ <string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="8477440576953067242">"Povezano (brez telefona ali predstavnosti), raven napolnjenosti baterije je <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
+ <string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Aktivna, baterija na <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Aktivno, L: napolnjenost baterije je <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, D: napolnjenost baterije je <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"Baterija na <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"L: napolnjenost akumulatorja je <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, D: napolnjenost akumulatorja je <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"L: napolnjenost baterije je <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, D: napolnjenost baterije je <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Aktivna"</string>
<string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Zvok predstavnosti"</string>
<string name="bluetooth_profile_headset" msgid="5395952236133499331">"Telefonski klici"</string>
@@ -129,8 +129,8 @@
<string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string>
<string name="bluetooth_hearingaid_left_pairing_message" msgid="8561855779703533591">"Seznanjanje z levim slušnim pripomočkom …"</string>
<string name="bluetooth_hearingaid_right_pairing_message" msgid="2655347721696331048">"Seznanjanje z desnim slušnim pripomočkom …"</string>
- <string name="bluetooth_hearingaid_left_battery_level" msgid="7375621694748104876">"Levi – akumulator na <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <string name="bluetooth_hearingaid_right_battery_level" msgid="1850094448499089312">"Desni – akumulator na <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_hearingaid_left_battery_level" msgid="7375621694748104876">"Levi – baterija na <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_hearingaid_right_battery_level" msgid="1850094448499089312">"Desni – baterija na <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="accessibility_wifi_off" msgid="1195445715254137155">"Wi-Fi je izklopljen."</string>
<string name="accessibility_no_wifi" msgid="5297119459491085771">"Povezava Wi-Fi je prekinjena."</string>
<string name="accessibility_wifi_one_bar" msgid="6025652717281815212">"Ena črtica signala Wi-Fi."</string>
@@ -283,9 +283,8 @@
<string name="private_dns_mode_provider_failure" msgid="8356259467861515108">"Povezave ni bilo mogoče vzpostaviti"</string>
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Pokaži možnosti za potrdilo brezžičnega zaslona"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Povečaj raven zapisovanja dnevnika za Wi-Fi; v izbirniku Wi‑Fi-ja pokaži glede na SSID RSSI"</string>
- <string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Zmanjša porabo energije akumulatorja in izboljša delovanje omrežja"</string>
- <!-- no translation found for wifi_enhanced_mac_randomization_summary (1210663439867489931) -->
- <skip />
+ <string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Zmanjša porabo energije baterije in izboljša delovanje omrežja"</string>
+ <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Ko je ta način omogočen, se lahko naslov MAC te naprave spremeni vsakič, ko se naprava poveže v omrežje z omogočenim naključnim dodeljevanjem naslova MAC."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Omejen prenos podatkov"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Z neomejenim prenosom podatkov"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Velikosti medpomnilnikov zapisovalnika dnevnika"</string>
@@ -552,4 +551,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Dodajanje gosta"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Odstranitev gosta"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gost"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 71f62d4..17d2d50 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -284,8 +284,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Shfaq opsionet për certifikimin e ekranit valor"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Rrit nivelin regjistrues të Wi‑Fi duke shfaqur SSID RSSI-në te Zgjedhësi i Wi‑Fi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Zvogëlon shkarkimin e baterisë dhe përmirëson cilësinë e funksionimit të rrjetit"</string>
- <!-- no translation found for wifi_enhanced_mac_randomization_summary (1210663439867489931) -->
- <skip />
+ <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Kur ky modalitet është i aktivizuar, adresa MAC e kësaj pajisjeje mund të ndryshojë çdo herë që lidhet me një rrjet që ka të aktivizuar renditjen e rastësishme të adresave MAC."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Me matje"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Pa matje"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Madhësitë e regjistruesit"</string>
@@ -550,4 +549,10 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Shto të ftuar"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Hiq të ftuarin"</string>
<string name="guest_nickname" msgid="6332276931583337261">"I ftuar"</string>
+ <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Parazgjedhja e pajisjes"</string>
+ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Joaktiv"</string>
+ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiv"</string>
+ <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Pajisja jote duhet të riniset që ky ndryshim të zbatohet. Rinise tani ose anuloje."</string>
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 15df19c..ca15c2d 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -143,11 +143,11 @@
<string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"Уклоњене апликације"</string>
<string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"Уклоњене апликације и корисници"</string>
<string name="data_usage_ota" msgid="7984667793701597001">"Ажурирања система"</string>
- <string name="tether_settings_title_usb" msgid="3728686573430917722">"USB Интернет повезивање"</string>
+ <string name="tether_settings_title_usb" msgid="3728686573430917722">"USB привезивање"</string>
<string name="tether_settings_title_wifi" msgid="4803402057533895526">"Преносни хотспот"</string>
<string name="tether_settings_title_bluetooth" msgid="916519902721399656">"Bluetooth привезивање"</string>
- <string name="tether_settings_title_usb_bluetooth" msgid="1727111807207577322">"Повезивање са интернетом"</string>
- <string name="tether_settings_title_all" msgid="8910259483383010470">"Повезивање и преносни хотспот"</string>
+ <string name="tether_settings_title_usb_bluetooth" msgid="1727111807207577322">"Привезивање"</string>
+ <string name="tether_settings_title_all" msgid="8910259483383010470">"Привезивање и преносни хотспот"</string>
<string name="managed_user_title" msgid="449081789742645723">"Све радне апликације"</string>
<string name="user_guest" msgid="6939192779649870792">"Гост"</string>
<string name="unknown" msgid="3544487229740637809">"Непознато"</string>
@@ -550,4 +550,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Додај госта"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Уклони госта"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Гост"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 6b1a63c..ee48c2f 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -549,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Lägg till gäst"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Ta bort gäst"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gäst"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index e041bea..7df39d5 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -224,7 +224,7 @@
<string name="adb_wireless_connection_failed_message" msgid="9213896700171602073">"Hakikisha kuwa <xliff:g id="DEVICE_NAME">%1$s</xliff:g> kimeunganishwa kwenye mtandao sahihi"</string>
<string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"Oanisha na kifaa"</string>
<string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Msimbo wa kuoanisha wa Wi-Fi"</string>
- <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"Imeshindwa kuunganisha"</string>
+ <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"Imeshindwa kuoanisha"</string>
<string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"Hakikisha kuwa kifaa kimeunganishwa kwenye mtandao mmoja."</string>
<string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Oanisha kifaa kupitia Wi-Fi kwa kuchanganua msimbo wa QR"</string>
<string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"Inaoanisha kifaa…"</string>
@@ -549,4 +549,10 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Weka mgeni"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Ondoa mgeni"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Mgeni"</string>
+ <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Hali chaguomsingi ya kifaa"</string>
+ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Imezimwa"</string>
+ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Imewashwa"</string>
+ <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Ni lazima uwashe tena kifaa chako ili mabadiliko haya yatekelezwe. Washa tena sasa au ughairi."</string>
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index d403858..eb7fc05 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -550,4 +550,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"கெஸ்ட்டைச் சேர்"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"கெஸ்ட்டை அகற்று"</string>
<string name="guest_nickname" msgid="6332276931583337261">"கெஸ்ட்"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 3c86509..0e36c5f 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -153,7 +153,7 @@
<string name="unknown" msgid="3544487229740637809">"తెలియదు"</string>
<string name="running_process_item_user_label" msgid="3988506293099805796">"వినియోగదారు: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
<string name="launch_defaults_some" msgid="3631650616557252926">"కొన్ని డిఫాల్ట్లు సెట్ చేయబడ్డాయి"</string>
- <string name="launch_defaults_none" msgid="8049374306261262709">"డిఫాల్ట్లు ఏవీ సెట్ చేయబడలేదు"</string>
+ <string name="launch_defaults_none" msgid="8049374306261262709">"ఆటోమేటిక్ ఆప్షన్లు వేటినీ సెట్ చేయలేదు"</string>
<string name="tts_settings" msgid="8130616705989351312">"వచనం నుండి ప్రసంగం సెట్టింగ్లు"</string>
<string name="tts_settings_title" msgid="7602210956640483039">"వచనం నుండి మాట అవుట్పుట్"</string>
<string name="tts_default_rate_title" msgid="3964187817364304022">"ప్రసంగం రేట్"</string>
@@ -284,8 +284,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"వైర్లెస్ ప్రదర్శన సర్టిఫికెట్ కోసం ఎంపికలను చూపు"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Wi‑Fi ఎంపికలో SSID RSSI ప్రకారం చూపబడే Wi‑Fi లాగింగ్ స్థాయిని పెంచండి"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"బ్యాటరీ శక్తి వినియోగాన్ని తగ్గించి & నెట్వర్క్ పనితీరును మెరుగుపరుస్తుంది"</string>
- <!-- no translation found for wifi_enhanced_mac_randomization_summary (1210663439867489931) -->
- <skip />
+ <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"ఈ మోడ్ ఎనేబుల్ అయ్యాక, MAC ర్యాండమైజేషన్ను ఎనేబుల్ చేసిన నెట్వర్క్తో కనెక్ట్ అయ్యే ప్రతిసారీ ఈ పరికరం MAC అడ్రస్ మారవచ్చు."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"గణించబడుతోంది"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"గణించబడటం లేదు"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"లాగర్ బఫర్ పరిమాణాలు"</string>
@@ -550,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"అతిథిని జోడించండి"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"అతిథిని తీసివేయండి"</string>
<string name="guest_nickname" msgid="6332276931583337261">"అతిథి"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 2e809c2..b4e1eea 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -205,7 +205,7 @@
<string name="apn_settings_not_available" msgid="1147111671403342300">"การตั้งค่าจุดเข้าใช้งานไม่สามารถใช้ได้สำหรับผู้ใช้รายนี้"</string>
<string name="enable_adb" msgid="8072776357237289039">"การแก้ไขข้อบกพร่อง USB"</string>
<string name="enable_adb_summary" msgid="3711526030096574316">"โหมดแก้ไขข้อบกพร่องเมื่อเชื่อมต่อ USB"</string>
- <string name="clear_adb_keys" msgid="3010148733140369917">"ยกเลิกการให้สิทธิ์การแก้ปัญหา USB"</string>
+ <string name="clear_adb_keys" msgid="3010148733140369917">"เพิกถอนการให้สิทธิ์การแก้ไขข้อบกพร่อง USB"</string>
<string name="enable_adb_wireless" msgid="6973226350963971018">"การแก้ไขข้อบกพร่องผ่าน Wi-Fi"</string>
<string name="enable_adb_wireless_summary" msgid="7344391423657093011">"โหมดแก้ไขข้อบกพร่องเมื่อเชื่อมต่อ Wi-Fi"</string>
<string name="adb_wireless_error" msgid="721958772149779856">"ข้อผิดพลาด"</string>
@@ -284,8 +284,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"แสดงตัวเลือกสำหรับการรับรองการแสดงผล แบบไร้สาย"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"เพิ่มระดับการบันทึก Wi‑Fi แสดงต่อ SSID RSSI ในตัวเลือก Wi‑Fi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"ลดการเปลืองแบตเตอรี่และเพิ่มประสิทธิภาพเครือข่าย"</string>
- <!-- no translation found for wifi_enhanced_mac_randomization_summary (1210663439867489931) -->
- <skip />
+ <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"เมื่อเปิดใช้โหมดนี้ ที่อยู่ MAC ของอุปกรณ์นี้อาจเปลี่ยนทุกครั้งที่เชื่อมต่อกับเครือข่ายที่มีการเปิดใช้การสุ่ม MAC"</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"มีการวัดปริมาณอินเทอร์เน็ต"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"ไม่มีการวัดปริมาณอินเทอร์เน็ต"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"ขนาดบัฟเฟอร์ของตัวบันทึก"</string>
@@ -360,7 +359,7 @@
<string name="track_frame_time" msgid="522674651937771106">"การแสดงผล HWUI ตามโปรไฟล์"</string>
<string name="enable_gpu_debug_layers" msgid="4986675516188740397">"เปิดใช้เลเยอร์การแก้ไขข้อบกพร่อง GPU"</string>
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"อนุญาตให้โหลดเลเยอร์การแก้ไขข้อบกพร่อง GPU สำหรับแอปแก้ไขข้อบกพร่อง"</string>
- <string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"เปิดบันทึกเวนเดอร์เพิ่มเติม"</string>
+ <string name="enable_verbose_vendor_logging" msgid="1196698788267682072">"เปิดการบันทึกเวนเดอร์แบบละเอียด"</string>
<string name="enable_verbose_vendor_logging_summary" msgid="5426292185780393708">"รวมบันทึกเวนเดอร์เพิ่มเติมเฉพาะอุปกรณ์ไว้ในรายงานข้อบกพร่อง ซึ่งอาจมีข้อมูลส่วนตัว ใช้แบตเตอรี่มากขึ้น และ/หรือใช้พื้นที่เก็บข้อมูลมากขึ้น"</string>
<string name="window_animation_scale_title" msgid="5236381298376812508">"อัตราการเคลื่อนไหวของหน้าต่าง"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"อัตราการเคลื่อนไหวของการเปลี่ยนภาพ"</string>
@@ -550,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"เพิ่มผู้เข้าร่วม"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"นำผู้เข้าร่วมออก"</string>
<string name="guest_nickname" msgid="6332276931583337261">"ผู้ใช้ชั่วคราว"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 69b94ef..22ad220 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -284,8 +284,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Ipakita ang mga opsyon para sa certification ng wireless display"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Pataasin ang antas ng Wi‑Fi logging, ipakita sa bawat SSID RSSI sa Wi‑Fi Picker"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Binabawasan ang pagkaubos ng baterya at pinapahusay ang performance ng network"</string>
- <!-- no translation found for wifi_enhanced_mac_randomization_summary (1210663439867489931) -->
- <skip />
+ <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Kapag naka-enable ang mode na ito, puwedeng magbago ang MAC address ng device na ito sa tuwing kokonekta ito sa isang network na may naka-enable na MAC randomization."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Nakametro"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Hindi Nakametro"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Mga laki ng buffer ng Logger"</string>
@@ -550,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Magdagdag ng bisita"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Alisin ang bisita"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Bisita"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 6c71420..daa3eed 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -549,4 +549,10 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Misafir ekle"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Misafir oturumunu kaldır"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Misafir"</string>
+ <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Cihaz varsayılanı"</string>
+ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Devre dışı"</string>
+ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Etkin"</string>
+ <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Bu değişikliğin geçerli olması için cihazının yeniden başlatılması gerekir. Şimdi yeniden başlatın veya iptal edin."</string>
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 174f6fa..442c52e 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -551,4 +551,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Додати гостя"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Видалити гостя"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Гість"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 4ad4ddb..463c161 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -284,8 +284,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"وائرلیس ڈسپلے سرٹیفیکیشن کیلئے اختیارات دکھائیں"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Wi‑Fi لاگنگ لیول میں اضافہ کریں، Wi‑Fi منتخب کنندہ میں فی SSID RSSI دکھائیں"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"بیٹری ڈرین کم کرتا ہے اور نیٹ ورک کارکردگی کو بہتر بناتا ہے"</string>
- <!-- no translation found for wifi_enhanced_mac_randomization_summary (1210663439867489931) -->
- <skip />
+ <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"جب یہ وضع فعال ہوتا ہے تو، اس آلہ کا MAC پتہ ہر بار تبدیل ہو سکتا ہے جب یہ کسی نیٹ ورک سے منسلک ہوتا ہے جس میں MAC ہے رینڈمائزیشن کو فعال کرتا ہے۔"</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"میٹرڈ"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"غیر میٹر شدہ"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"لاگر بفر کے سائز"</string>
@@ -550,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"مہمان کو شامل کریں"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"مہمان کو ہٹائیں"</string>
<string name="guest_nickname" msgid="6332276931583337261">"مہمان"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 80f4157..762a246 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -148,12 +148,12 @@
<string name="tether_settings_title_bluetooth" msgid="916519902721399656">"Bluetooth modem"</string>
<string name="tether_settings_title_usb_bluetooth" msgid="1727111807207577322">"Modem"</string>
<string name="tether_settings_title_all" msgid="8910259483383010470">"Modem rejimi"</string>
- <string name="managed_user_title" msgid="449081789742645723">"Barcha ishchi ilovalar"</string>
+ <string name="managed_user_title" msgid="449081789742645723">"Barcha ishga oid ilovalar"</string>
<string name="user_guest" msgid="6939192779649870792">"Mehmon"</string>
<string name="unknown" msgid="3544487229740637809">"Noma’lum"</string>
<string name="running_process_item_user_label" msgid="3988506293099805796">"Foydalanuvchi: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
<string name="launch_defaults_some" msgid="3631650616557252926">"Ba’zi birlamchi sozlamalar o‘rnatilgan"</string>
- <string name="launch_defaults_none" msgid="8049374306261262709">"Birlamchi sozlamalar o‘rnatilmagan"</string>
+ <string name="launch_defaults_none" msgid="8049374306261262709">"Birlamchi sozlamalar belgilanmagan"</string>
<string name="tts_settings" msgid="8130616705989351312">"Nutq sintezi sozlamalari"</string>
<string name="tts_settings_title" msgid="7602210956640483039">"Nutq sintezi"</string>
<string name="tts_default_rate_title" msgid="3964187817364304022">"Nutq tezligi"</string>
@@ -284,7 +284,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Simsiz monitorlarni sertifikatlash parametrini ko‘rsatish"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Wi-Fi ulanishini tanlashda har bir SSID uchun jurnalda ko‘rsatilsin"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Batareya sarfini tejaydi va tarmoq samaradorligini oshiradi"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Bu rejim yoqilganda qurilmaning MAC manzili tasodifiy MAC manzillar yaratish imkoniyatli tarmoqqa har safar ulanganda almashishi mumkin."</string>
+ <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Bu rejim yoqilganda qurilmaning MAC manzili tasodifiy MAC manzillar yaratish imkoniyati mavjud tarmoqqa har safar ulanganda almashishi mumkin."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Trafik hisoblanadigan tarmoq"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Trafik hisobi yuritilmaydigan tarmoq"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Jurnal buferi hajmi"</string>
@@ -549,4 +549,10 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Mehmon kiritish"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Mehmon rejimini olib tashlash"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Mehmon"</string>
+ <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Qurilma standarti"</string>
+ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Yoqilmagan"</string>
+ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Yoniq"</string>
+ <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Oʻzgarishlar qurilma oʻchib yonganda bajariladi. Hoziroq oʻchib yoqish yoki bekor qilish."</string>
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index fcfbb02..4f93fe3 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -205,9 +205,9 @@
<string name="apn_settings_not_available" msgid="1147111671403342300">"Cài đặt tên điểm truy cập không khả dụng cho người dùng này"</string>
<string name="enable_adb" msgid="8072776357237289039">"Gỡ lỗi qua USB"</string>
<string name="enable_adb_summary" msgid="3711526030096574316">"Bật chế độ gỡ lỗi khi kết nối USB"</string>
- <string name="clear_adb_keys" msgid="3010148733140369917">"Thu hồi ủy quyền gỡ lỗi USB"</string>
+ <string name="clear_adb_keys" msgid="3010148733140369917">"Thu hồi các lượt ủy quyền gỡ lỗi qua USB"</string>
<string name="enable_adb_wireless" msgid="6973226350963971018">"Gỡ lỗi qua Wi-Fi"</string>
- <string name="enable_adb_wireless_summary" msgid="7344391423657093011">"Chế độ gỡ lỗi khi có kết nối Wi-Fi"</string>
+ <string name="enable_adb_wireless_summary" msgid="7344391423657093011">"Bật chế độ gỡ lỗi khi có kết nối Wi-Fi"</string>
<string name="adb_wireless_error" msgid="721958772149779856">"Lỗi"</string>
<string name="adb_wireless_settings" msgid="2295017847215680229">"Gỡ lỗi qua Wi-Fi"</string>
<string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"Để xem và sử dụng các thiết bị có sẵn, hãy bật tính năng gỡ lỗi qua Wi-Fi"</string>
@@ -284,8 +284,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"Hiển thị tùy chọn chứng nhận hiển thị không dây"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Tăng mức ghi nhật ký Wi‑Fi, hiển thị mỗi SSID RSSI trong bộ chọn Wi‑Fi"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Giảm hao pin và cải thiện hiệu suất mạng"</string>
- <!-- no translation found for wifi_enhanced_mac_randomization_summary (1210663439867489931) -->
- <skip />
+ <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Khi bật chế độ này, địa chỉ MAC của thiết bị này có thể thay đổi mỗi lần thiết bị kết nối với mạng đã bật tính năng sử dụng địa chỉ MAC ngẫu nhiên."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Đo lượng dữ liệu"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"Không đo lượng dữ liệu"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Kích thước bộ đệm của trình ghi nhật ký"</string>
@@ -550,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Thêm khách"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Xóa phiên khách"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Khách"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 58d12af..3d8b6d7 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -225,7 +225,7 @@
<string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"与设备配对"</string>
<string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"WLAN 配对码"</string>
<string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"配对失败"</string>
- <string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"确保设备已连接到同一网络。"</string>
+ <string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"请确保设备已连接到同一网络。"</string>
<string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"扫描二维码即可通过 WLAN 配对设备"</string>
<string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"正在配对设备…"</string>
<string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"无法配对设备。可能是因为二维码不正确,或者设备未连接到同一网络。"</string>
@@ -284,8 +284,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"显示无线显示认证选项"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"提升 WLAN 日志记录级别(在 WLAN 选择器中显示每个 SSID 的 RSSI)"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"降低耗电量以及改善网络性能"</string>
- <!-- no translation found for wifi_enhanced_mac_randomization_summary (1210663439867489931) -->
- <skip />
+ <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"启用此模式后,每当此设备连接到已启用随机分配 MAC 地址功能的网络时,它的 MAC 地址就可能会发生更改。"</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"按流量计费"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"不按流量计费"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"日志记录器缓冲区大小"</string>
@@ -550,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"添加访客"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"移除访客"</string>
<string name="guest_nickname" msgid="6332276931583337261">"访客"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 3396993..b4905c7 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -23,7 +23,7 @@
<string name="wifi_fail_to_scan" msgid="2333336097603822490">"無法掃瞄網絡"</string>
<string name="wifi_security_none" msgid="7392696451280611452">"無"</string>
<string name="wifi_remembered" msgid="3266709779723179188">"已儲存"</string>
- <string name="wifi_disconnected" msgid="7054450256284661757">"已解除連接"</string>
+ <string name="wifi_disconnected" msgid="7054450256284661757">"已中斷連線"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"已停用"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP 設定失敗"</string>
<string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"網絡品質欠佳,因此無法連線"</string>
@@ -284,7 +284,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"顯示無線螢幕分享認證的選項"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"讓 Wi‑Fi 記錄功能升級,在 Wi‑Fi 選擇器中依每個 SSID RSSI 顯示 Wi‑Fi 詳細紀錄"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"減低耗電量並改善網絡表現"</string>
- <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"啟用這個模式後,每次連線到啟用了 MAC 隨機化的網路時,這部裝置的 MAC 位址都可能會有所變更。"</string>
+ <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"啟用此模式後,每次連接至已啟用 MAC 隨機處理的網絡時,此裝置的 MAC 位址都可能會變更。"</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"按用量收費"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"不限數據用量收費"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"記錄器緩衝區空間"</string>
@@ -549,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"新增訪客"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"移除訪客"</string>
<string name="guest_nickname" msgid="6332276931583337261">"訪客"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index dfebe81..9a3f2f9 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -549,4 +549,14 @@
<string name="guest_new_guest" msgid="3482026122932643557">"新增訪客"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"移除訪客"</string>
<string name="guest_nickname" msgid="6332276931583337261">"訪客"</string>
+ <!-- no translation found for cached_apps_freezer_device_default (2616594131750144342) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_disabled (4816382260660472042) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_enabled (8866703500183051546) -->
+ <skip />
+ <!-- no translation found for cached_apps_freezer_reboot_dialog_text (695330563489230096) -->
+ <skip />
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 45cd737..86698f4 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -549,4 +549,10 @@
<string name="guest_new_guest" msgid="3482026122932643557">"Engeza isivakashi"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Susa isihambeli"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Isihambeli"</string>
+ <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Idivayisi ezenzakalelayo"</string>
+ <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Ikhutshaziwe"</string>
+ <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Inikwe amandla"</string>
+ <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Kufanele idivayisi yakho iqaliswe ukuze lolu shintsho lusebenze. Qalisa manje noma khansela."</string>
+ <!-- no translation found for accessibility_work_profile_app_description (5470883112342119165) -->
+ <skip />
</resources>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 934f610..e42e438 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1373,4 +1373,7 @@
<string name="cached_apps_freezer_enabled">Enabled</string>
<!-- Developer setting dialog prompting the user to reboot after changing the app freezer setting [CHAR LIMIT=NONE]-->
<string name="cached_apps_freezer_reboot_dialog_text">Your device must be rebooted for this change to apply. Reboot now or cancel.</string>
+
+ <!-- A content description for work profile app [CHAR LIMIT=35] -->
+ <string name="accessibility_work_profile_app_description">Work <xliff:g id="app_name" example="Camera">%s</xliff:g></string>
</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
index c4ff719..b1f2a39 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
@@ -26,6 +26,7 @@
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.os.UserManager;
import android.util.Log;
import com.android.settingslib.R;
@@ -129,7 +130,7 @@
*/
public static boolean isHiddenSystemModule(Context context, String packageName) {
return ApplicationsState.getInstance((Application) context.getApplicationContext())
- .isHiddenModule(packageName);
+ .isHiddenModule(packageName);
}
/**
@@ -140,4 +141,28 @@
.isSystemModule(packageName);
}
+ /**
+ * Returns a boolean indicating whether a given package is a mainline module.
+ */
+ public static boolean isMainlineModule(Context context, String packageName) {
+ final PackageManager pm = context.getPackageManager();
+ try {
+ return pm.getModuleInfo(packageName, 0 /* flags */) != null;
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Returns a content description of an app name which distinguishes a personal app from a
+ * work app for accessibility purpose.
+ * If the app is in a work profile, then add a "work" prefix to the app name.
+ */
+ public static String getAppContentDescription(Context context, String packageName,
+ int userId) {
+ final CharSequence appLabel = getApplicationLabel(context.getPackageManager(), packageName);
+ return UserManager.get(context).isManagedProfile(userId)
+ ? context.getString(R.string.accessibility_work_profile_app_description, appLabel)
+ : appLabel.toString();
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 31ea5b4..887a49b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -27,10 +27,13 @@
import androidx.annotation.IntDef;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.settingslib.bluetooth.A2dpProfile;
import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
+import com.android.settingslib.bluetooth.HearingAidProfile;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothProfile;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -430,7 +433,8 @@
cachedDeviceManager.findDevice(device);
if (cachedDevice != null) {
if (cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED
- && !cachedDevice.isConnected()) {
+ && !cachedDevice.isConnected()
+ && isA2dpOrHearingAidDevice(cachedDevice)) {
deviceCount++;
cachedBluetoothDeviceList.add(cachedDevice);
if (deviceCount >= MAX_DISCONNECTED_DEVICE_NUM) {
@@ -454,6 +458,15 @@
return new ArrayList<>(mDisconnectedMediaDevices);
}
+ private boolean isA2dpOrHearingAidDevice(CachedBluetoothDevice device) {
+ for (LocalBluetoothProfile profile : device.getConnectableProfiles()) {
+ if (profile instanceof A2dpProfile || profile instanceof HearingAidProfile) {
+ return true;
+ }
+ }
+ return false;
+ }
+
@Override
public void onDeviceRemoved(MediaDevice device) {
if (mMediaDevices.contains(device)) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index 34de152..139a12c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -31,18 +31,14 @@
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
import android.content.Context;
-import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
import android.text.TextUtils;
-import android.util.Log;
import androidx.annotation.IntDef;
import androidx.annotation.VisibleForTesting;
-import com.android.settingslib.R;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -215,30 +211,6 @@
*
* @return application label.
*/
- public String getClientAppLabel() {
- final String packageName = mRouteInfo.getClientPackageName();
- if (TextUtils.isEmpty(packageName)) {
- Log.d(TAG, "Client package name is empty");
- return mContext.getResources().getString(R.string.unknown);
- }
- try {
- final PackageManager packageManager = mContext.getPackageManager();
- final String appLabel = packageManager.getApplicationLabel(
- packageManager.getApplicationInfo(packageName, 0)).toString();
- if (!TextUtils.isEmpty(appLabel)) {
- return appLabel;
- }
- } catch (PackageManager.NameNotFoundException e) {
- Log.e(TAG, "unable to find " + packageName);
- }
- return mContext.getResources().getString(R.string.unknown);
- }
-
- /**
- * Get application label from MediaDevice.
- *
- * @return application label.
- */
public int getDeviceType() {
return mType;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
index 42f2542..8ea5ff1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
@@ -41,7 +41,10 @@
private static final String TAG = "PhoneMediaDevice";
- public static final String ID = "phone_media_device_id_1";
+ public static final String PHONE_ID = "phone_media_device_id";
+ // For 3.5 mm wired headset
+ public static final String WIRED_HEADSET_ID = "wired_headset_media_device_id";
+ public static final String USB_HEADSET_ID = "usb_headset_media_device_id";
private String mSummary = "";
@@ -109,7 +112,25 @@
@Override
public String getId() {
- return ID;
+ String id;
+ switch (mRouteInfo.getType()) {
+ case TYPE_WIRED_HEADSET:
+ case TYPE_WIRED_HEADPHONES:
+ id = WIRED_HEADSET_ID;
+ break;
+ case TYPE_USB_DEVICE:
+ case TYPE_USB_HEADSET:
+ case TYPE_USB_ACCESSORY:
+ case TYPE_DOCK:
+ case TYPE_HDMI:
+ id = USB_HEADSET_ID;
+ break;
+ case TYPE_BUILTIN_SPEAKER:
+ default:
+ id = PHONE_ID;
+ break;
+ }
+ return id;
}
@Override
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
index 77316e9..365a16c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
@@ -41,6 +41,7 @@
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.HearingAidProfile;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothProfile;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
@@ -560,6 +561,10 @@
mLocalMediaManager.mMediaDevices.add(device3);
mLocalMediaManager.mMediaDevices.add(mLocalMediaManager.mPhoneDevice);
+ final List<LocalBluetoothProfile> profiles = new ArrayList<>();
+ final A2dpProfile a2dpProfile = mock(A2dpProfile.class);
+ profiles.add(a2dpProfile);
+
final List<BluetoothDevice> bluetoothDevices = new ArrayList<>();
final BluetoothDevice bluetoothDevice = mock(BluetoothDevice.class);
final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
@@ -571,6 +576,7 @@
when(cachedManager.findDevice(bluetoothDevice)).thenReturn(cachedDevice);
when(cachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(cachedDevice.isConnected()).thenReturn(false);
+ when(cachedDevice.getConnectableProfiles()).thenReturn(profiles);
when(device1.getId()).thenReturn(TEST_DEVICE_ID_1);
when(device2.getId()).thenReturn(TEST_DEVICE_ID_2);
@@ -634,6 +640,10 @@
mLocalMediaManager.mMediaDevices.add(device3);
mLocalMediaManager.mMediaDevices.add(mLocalMediaManager.mPhoneDevice);
+ final List<LocalBluetoothProfile> profiles = new ArrayList<>();
+ final A2dpProfile a2dpProfile = mock(A2dpProfile.class);
+ profiles.add(a2dpProfile);
+
final List<BluetoothDevice> bluetoothDevices = new ArrayList<>();
final BluetoothDevice bluetoothDevice = mock(BluetoothDevice.class);
final BluetoothDevice bluetoothDevice2 = mock(BluetoothDevice.class);
@@ -662,6 +672,7 @@
when(cachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(cachedDevice.isConnected()).thenReturn(false);
when(cachedDevice.getDevice()).thenReturn(bluetoothDevice);
+ when(cachedDevice.getConnectableProfiles()).thenReturn(profiles);
when(bluetoothDevice.getBluetoothClass()).thenReturn(bluetoothClass);
when(bluetoothClass.getDeviceClass()).thenReturn(AUDIO_VIDEO_HEADPHONES);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
index 6664870..47d4beb 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
@@ -29,13 +29,9 @@
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageStats;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
-import com.android.settingslib.R;
import com.android.settingslib.bluetooth.A2dpProfile;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.HearingAidProfile;
@@ -49,8 +45,6 @@
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-import org.robolectric.Shadows;
-import org.robolectric.shadows.ShadowPackageManager;
import java.util.ArrayList;
import java.util.Collections;
@@ -70,8 +64,6 @@
private static final String ROUTER_ID_2 = "RouterId_2";
private static final String ROUTER_ID_3 = "RouterId_3";
private static final String TEST_PACKAGE_NAME = "com.test.playmusic";
- private static final String TEST_PACKAGE_NAME2 = "com.test.playmusic2";
- private static final String TEST_APPLICATION_LABEL = "playmusic";
private final BluetoothClass mHeadreeClass =
new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES);
private final BluetoothClass mCarkitClass =
@@ -125,10 +117,6 @@
private InfoMediaDevice mInfoMediaDevice3;
private List<MediaDevice> mMediaDevices = new ArrayList<>();
private PhoneMediaDevice mPhoneMediaDevice;
- private ShadowPackageManager mShadowPackageManager;
- private ApplicationInfo mAppInfo;
- private PackageInfo mPackageInfo;
- private PackageStats mPackageStats;
@Before
public void setUp() {
@@ -459,41 +447,6 @@
assertThat(mInfoMediaDevice1.getClientPackageName()).isEqualTo(TEST_PACKAGE_NAME);
}
- private void initPackage() {
- mShadowPackageManager = Shadows.shadowOf(mContext.getPackageManager());
- mAppInfo = new ApplicationInfo();
- mAppInfo.flags = ApplicationInfo.FLAG_INSTALLED;
- mAppInfo.packageName = TEST_PACKAGE_NAME;
- mAppInfo.name = TEST_APPLICATION_LABEL;
- mPackageInfo = new PackageInfo();
- mPackageInfo.packageName = TEST_PACKAGE_NAME;
- mPackageInfo.applicationInfo = mAppInfo;
- mPackageStats = new PackageStats(TEST_PACKAGE_NAME);
- }
-
- @Test
- public void getClientAppLabel_matchedPackageName_returnLabel() {
- initPackage();
- when(mRouteInfo1.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
-
- assertThat(mInfoMediaDevice1.getClientAppLabel()).isEqualTo(
- mContext.getResources().getString(R.string.unknown));
-
- mShadowPackageManager.addPackage(mPackageInfo, mPackageStats);
-
- assertThat(mInfoMediaDevice1.getClientAppLabel()).isEqualTo(TEST_APPLICATION_LABEL);
- }
-
- @Test
- public void getClientAppLabel_noMatchedPackageName_returnDefault() {
- initPackage();
- mShadowPackageManager.addPackage(mPackageInfo, mPackageStats);
- when(mRouteInfo1.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME2);
-
- assertThat(mInfoMediaDevice1.getClientAppLabel()).isEqualTo(
- mContext.getResources().getString(R.string.unknown));
- }
-
@Test
public void setState_verifyGetState() {
mInfoMediaDevice1.setState(LocalMediaManager.MediaDeviceState.STATE_CONNECTED);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java
index 6f265dd..47f6fe3 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java
@@ -21,6 +21,10 @@
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
+import static com.android.settingslib.media.PhoneMediaDevice.PHONE_ID;
+import static com.android.settingslib.media.PhoneMediaDevice.USB_HEADSET_ID;
+import static com.android.settingslib.media.PhoneMediaDevice.WIRED_HEADSET_ID;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
@@ -108,4 +112,22 @@
assertThat(mPhoneMediaDevice.getName())
.isEqualTo(mContext.getString(R.string.media_transfer_this_device_name));
}
+
+ @Test
+ public void getId_returnCorrectId() {
+ when(mInfo.getType()).thenReturn(TYPE_WIRED_HEADPHONES);
+
+ assertThat(mPhoneMediaDevice.getId())
+ .isEqualTo(WIRED_HEADSET_ID);
+
+ when(mInfo.getType()).thenReturn(TYPE_USB_DEVICE);
+
+ assertThat(mPhoneMediaDevice.getId())
+ .isEqualTo(USB_HEADSET_ID);
+
+ when(mInfo.getType()).thenReturn(TYPE_BUILTIN_SPEAKER);
+
+ assertThat(mPhoneMediaDevice.getId())
+ .isEqualTo(PHONE_ID);
+ }
}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 4a9eba2..29c31ea 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -264,6 +264,7 @@
Settings.Global.ENABLE_DELETION_HELPER_NO_THRESHOLD_TOGGLE,
Settings.Global.ENABLE_DISKSTATS_LOGGING,
Settings.Global.ENABLE_EPHEMERAL_FEATURE,
+ Settings.Global.ENABLE_RESTRICTED_BUCKET,
Settings.Global.DYNAMIC_POWER_SAVINGS_ENABLED,
Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD,
Settings.Global.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 4771c41..8f05e78 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -291,6 +291,9 @@
<uses-permission android:name="android.permission.ACCESS_TV_TUNER" />
<uses-permission android:name="android.permission.TUNER_RESOURCE_ACCESS" />
+ <!-- Permissions required for CTS test - AutoRevokeTest -->
+ <uses-permission android:name="android.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS" />
+
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/res/drawable/screenshot_actions_background_protection.xml b/packages/SystemUI/res/drawable/screenshot_actions_background_protection.xml
index 163015b7..21013c6 100644
--- a/packages/SystemUI/res/drawable/screenshot_actions_background_protection.xml
+++ b/packages/SystemUI/res/drawable/screenshot_actions_background_protection.xml
@@ -17,6 +17,6 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<gradient
android:angle="90"
- android:startColor="#1f000000"
+ android:startColor="@color/global_screenshot_background_protection_start"
android:endColor="#00000000"/>
</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/bubble_overflow_view.xml b/packages/SystemUI/res/layout/bubble_overflow_view.xml
index 88a05ec..1ed1f07 100644
--- a/packages/SystemUI/res/layout/bubble_overflow_view.xml
+++ b/packages/SystemUI/res/layout/bubble_overflow_view.xml
@@ -36,6 +36,8 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:maxLines="1"
+ android:lines="2"
+ android:ellipsize="end"
android:layout_gravity="center"
android:paddingTop="@dimen/bubble_overflow_text_padding"
android:gravity="center"/>
diff --git a/packages/SystemUI/res/layout/global_screenshot.xml b/packages/SystemUI/res/layout/global_screenshot.xml
index de19303..1dbb38d 100644
--- a/packages/SystemUI/res/layout/global_screenshot.xml
+++ b/packages/SystemUI/res/layout/global_screenshot.xml
@@ -22,7 +22,7 @@
android:layout_height="match_parent">
<ImageView
android:id="@+id/global_screenshot_actions_background"
- android:layout_height="@dimen/global_screenshot_bg_protection_height"
+ android:layout_height="@dimen/screenshot_bg_protection_height"
android:layout_width="match_parent"
android:alpha="0.0"
android:src="@drawable/screenshot_actions_background_protection"
diff --git a/packages/SystemUI/res/layout/screen_record_dialog.xml b/packages/SystemUI/res/layout/screen_record_dialog.xml
index df576d8..fd9936f 100644
--- a/packages/SystemUI/res/layout/screen_record_dialog.xml
+++ b/packages/SystemUI/res/layout/screen_record_dialog.xml
@@ -59,29 +59,16 @@
android:layout_gravity="center"
android:layout_weight="0"
android:layout_marginRight="@dimen/screenrecord_dialog_padding"/>
- <LinearLayout
+ <Spinner
+ android:id="@+id/screen_recording_options"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:layout_weight="1">
- <TextView
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center_vertical"
- android:text="@string/screenrecord_audio_label"
- android:textColor="?android:attr/textColorPrimary"
- android:textAppearance="?android:attr/textAppearanceMedium"/>
- <TextView
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:id="@+id/audio_type"
- android:text="@string/screenrecord_mic_label"
- android:textAppearance="?android:attr/textAppearanceSmall"/>
- </LinearLayout>
+ android:layout_height="48dp"
+ android:prompt="@string/screenrecord_audio_label"/>
<Switch
android:layout_width="wrap_content"
android:layout_height="48dp"
- android:layout_weight="0"
+ android:layout_weight="1"
+ android:layout_gravity="end"
android:id="@+id/screenrecord_audio_switch"/>
</LinearLayout>
@@ -102,7 +89,8 @@
android:id="@+id/screenrecord_taps_switch"
android:text="@string/screenrecord_taps_label"
android:textColor="?android:attr/textColorPrimary"
- android:textAppearance="?android:attr/textAppearanceMedium"/>
+ android:textAppearance="?android:attr/textAppearanceSmall"/>
+
</LinearLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml b/packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml
new file mode 100644
index 0000000..af6f9bb
--- /dev/null
+++ b/packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:orientation="vertical"
+ android:padding="10dp"
+ android:layout_weight="1">
+ <TextView
+ android:id="@+id/screen_recording_dialog_source_text"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_vertical"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorPrimary"/>
+ <TextView
+ android:id="@+id/screen_recording_dialog_source_description"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/screen_record_dialog_audio_source_selected.xml b/packages/SystemUI/res/layout/screen_record_dialog_audio_source_selected.xml
new file mode 100644
index 0000000..fabe9e2
--- /dev/null
+++ b/packages/SystemUI/res/layout/screen_record_dialog_audio_source_selected.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:orientation="vertical"
+ android:layout_weight="1">
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:text="@string/screenrecord_audio_label"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorPrimary"/>
+ <TextView
+ android:id="@+id/screen_recording_dialog_source_text"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textAppearance="?android:attr/textAppearanceSmall"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index 2d51011..196357c 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -79,6 +79,7 @@
<color name="global_screenshot_button_icon">@color/GM2_blue_300</color>
<color name="global_screenshot_dismiss_background">@color/GM2_grey_800</color>
<color name="global_screenshot_dismiss_foreground">#FFFFFF</color>
+ <color name="global_screenshot_background_protection_start">#80000000</color> <!-- 50% black -->
<!-- Biometric dialog colors -->
diff --git a/packages/SystemUI/res/values-night/dimens.xml b/packages/SystemUI/res/values-night/dimens.xml
index 4814839..23e3231 100644
--- a/packages/SystemUI/res/values-night/dimens.xml
+++ b/packages/SystemUI/res/values-night/dimens.xml
@@ -18,4 +18,8 @@
<resources>
<!-- The height of the divider between the individual notifications. -->
<dimen name="notification_divider_height">1dp</dimen>
+
+ <!-- Height of the background gradient behind the screenshot UI (taller in dark mode) -->
+ <dimen name="screenshot_bg_protection_height">375dp</dimen>
+
</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 82eda31..b677600 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -200,6 +200,7 @@
<color name="global_screenshot_button_icon">@color/GM2_blue_500</color>
<color name="global_screenshot_dismiss_background">#FFFFFF</color>
<color name="global_screenshot_dismiss_foreground">@color/GM2_grey_500</color>
+ <color name="global_screenshot_background_protection_start">#40000000</color> <!-- 25% black -->
<!-- GM2 colors -->
<color name="GM2_grey_50">#F8F9FA</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 2cbb498..99e347e 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -306,6 +306,7 @@
<dimen name="global_screenshot_bg_padding">20dp</dimen>
<dimen name="global_screenshot_bg_protection_height">400dp</dimen>
<dimen name="global_screenshot_x_scale">80dp</dimen>
+ <dimen name="screenshot_bg_protection_height">242dp</dimen>
<dimen name="screenshot_preview_elevation">6dp</dimen>
<dimen name="screenshot_offset_y">48dp</dimen>
<dimen name="screenshot_offset_x">16dp</dimen>
@@ -1168,7 +1169,7 @@
<!-- Default (and minimum) height of the expanded view shown when the bubble is expanded -->
<dimen name="bubble_expanded_default_height">180dp</dimen>
<!-- Default height of bubble overflow -->
- <dimen name="bubble_overflow_height">460dp</dimen>
+ <dimen name="bubble_overflow_height">480dp</dimen>
<!-- Bubble overflow padding when there are no bubbles -->
<dimen name="bubble_overflow_empty_state_padding">16dp</dimen>
<!-- Padding of container for overflow bubbles -->
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 04640f4..8156e8d 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -144,5 +144,10 @@
<!-- NotificationPanelView -->
<item type="id" name="notification_panel" />
+
+ <!-- Screen Recording -->
+ <item type="id" name="screen_recording_options" />
+ <item type="id" name="screen_recording_dialog_source_text" />
+ <item type="id" name="screen_recording_dialog_source_description" />
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 96843f1..ec29622 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -240,6 +240,8 @@
<!-- Notification title displayed for screen recording [CHAR LIMIT=50]-->
<string name="screenrecord_name">Screen Recorder</string>
+ <!-- Processing screen recoding video in the background [CHAR LIMIT=30]-->
+ <string name="screenrecord_background_processing_label">Processing screen recording</string>
<!-- Description of the screen recording notification channel [CHAR LIMIT=NONE]-->
<string name="screenrecord_channel_description">Ongoing notification for a screen record session</string>
<!-- Title for the screen prompting the user to begin recording their screen [CHAR LIMIT=NONE]-->
@@ -1253,6 +1255,9 @@
<!-- The text for the notification history link. [CHAR LIMIT=40] -->
<string name="manage_notifications_history_text">History</string>
+ <!-- Section title for notifications that have recently appeared. [CHAR LIMIT=40] -->
+ <string name="notification_section_header_incoming">Incoming</string>
+
<!-- Section title for notifications that do not vibrate or make noise. [CHAR LIMIT=40] -->
<string name="notification_section_header_gentle">Silent notifications</string>
@@ -2608,6 +2613,10 @@
<!-- Text used for content description of settings button in the header of expanded bubble
view. [CHAR_LIMIT=NONE] -->
<string name="bubbles_settings_button_description">Settings for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g> bubbles</string>
+ <!-- Content description for button that shows bubble overflow on click [CHAR LIMIT=NONE] -->
+ <string name="bubble_overflow_button_content_description">Overflow</string>
+ <!-- Action to add overflow bubble back to stack. [CHAR LIMIT=NONE] -->
+ <string name="bubble_accessibility_action_add_back">Add back to stack</string>
<!-- The text for the manage bubbles link. [CHAR LIMIT=NONE] -->
<string name="manage_bubbles_text">Manage</string>
<!-- Content description when a bubble is focused. [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index a96ef91..b37400f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1664,6 +1664,15 @@
filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
mBroadcastDispatcher.registerReceiverWithHandler(mBroadcastReceiver, filter, mHandler);
+ // Since ACTION_SERVICE_STATE is being moved to a non-sticky broadcast, trigger the
+ // listener now with the service state from the default sub.
+ mBackgroundExecutor.execute(() -> {
+ int subId = SubscriptionManager.getDefaultSubscriptionId();
+ ServiceState serviceState = mContext.getSystemService(TelephonyManager.class)
+ .getServiceStateForSubscriber(subId);
+ mHandler.sendMessage(
+ mHandler.obtainMessage(MSG_SERVICE_STATE_CHANGE, subId, 0, serviceState));
+ });
mHandler.post(this::registerRingerTracker);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index 7dea7f8..7c25d28 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -432,7 +432,7 @@
Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this);
}
- public void updateState(@BiometricState int newState) {
+ void updateState(@BiometricState int newState) {
Log.v(TAG, "newState: " + newState);
switch (newState) {
@@ -453,8 +453,10 @@
}
announceForAccessibility(getResources()
.getString(R.string.biometric_dialog_authenticated));
- mHandler.postDelayed(() -> mCallback.onAction(Callback.ACTION_AUTHENTICATED),
- getDelayAfterAuthenticatedDurationMs());
+ mHandler.postDelayed(() -> {
+ Log.d(TAG, "Sending ACTION_AUTHENTICATED");
+ mCallback.onAction(Callback.ACTION_AUTHENTICATED);
+ }, getDelayAfterAuthenticatedDurationMs());
break;
case STATE_PENDING_CONFIRMATION:
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index b736b4d..8608766 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -113,6 +113,7 @@
int mModalityMask;
boolean mSkipIntro;
long mOperationId;
+ int mSysUiSessionId;
}
public static class Builder {
@@ -158,6 +159,11 @@
return this;
}
+ public Builder setSysUiSessionId(int sysUiSessionId) {
+ mConfig.mSysUiSessionId = sysUiSessionId;
+ return this;
+ }
+
public AuthContainerView build(int modalityMask) {
mConfig.mModalityMask = modalityMask;
return new AuthContainerView(mConfig, new Injector());
@@ -203,6 +209,9 @@
final class BiometricCallback implements AuthBiometricView.Callback {
@Override
public void onAction(int action) {
+ Log.d(TAG, "onAction: " + action
+ + ", sysUiSessionId: " + mConfig.mSysUiSessionId
+ + ", state: " + mContainerState);
switch (action) {
case AuthBiometricView.Callback.ACTION_AUTHENTICATED:
animateAway(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED);
@@ -461,13 +470,13 @@
if (animate) {
animateAway(false /* sendReason */, 0 /* reason */);
} else {
- removeWindowIfAttached();
+ removeWindowIfAttached(false /* sendReason */);
}
}
@Override
public void dismissFromSystemServer() {
- removeWindowIfAttached();
+ removeWindowIfAttached(true /* sendReason */);
}
@Override
@@ -540,7 +549,7 @@
final Runnable endActionRunnable = () -> {
setVisibility(View.INVISIBLE);
- removeWindowIfAttached();
+ removeWindowIfAttached(true /* sendReason */);
};
postOnAnimation(() -> {
@@ -575,19 +584,24 @@
}
private void sendPendingCallbackIfNotNull() {
- Log.d(TAG, "pendingCallback: " + mPendingCallbackReason);
+ Log.d(TAG, "pendingCallback: " + mPendingCallbackReason
+ + " sysUISessionId: " + mConfig.mSysUiSessionId);
if (mPendingCallbackReason != null) {
mConfig.mCallback.onDismissed(mPendingCallbackReason, mCredentialAttestation);
mPendingCallbackReason = null;
}
}
- private void removeWindowIfAttached() {
- sendPendingCallbackIfNotNull();
+ private void removeWindowIfAttached(boolean sendReason) {
+ if (sendReason) {
+ sendPendingCallbackIfNotNull();
+ }
if (mContainerState == STATE_GONE) {
+ Log.w(TAG, "Container already STATE_GONE, mSysUiSessionId: " + mConfig.mSysUiSessionId);
return;
}
+ Log.d(TAG, "Removing container, mSysUiSessionId: " + mConfig.mSysUiSessionId);
mContainerState = STATE_GONE;
mWindowManager.removeView(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 0c6794c..9f0ea3e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -276,14 +276,15 @@
@Override
public void showAuthenticationDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
int biometricModality, boolean requireConfirmation, int userId, String opPackageName,
- long operationId) {
+ long operationId, int sysUiSessionId) {
final int authenticators = Utils.getAuthenticators(bundle);
if (DEBUG) {
Log.d(TAG, "showAuthenticationDialog, authenticators: " + authenticators
+ ", biometricModality: " + biometricModality
+ ", requireConfirmation: " + requireConfirmation
- + ", operationId: " + operationId);
+ + ", operationId: " + operationId
+ + ", sysUiSessionId: " + sysUiSessionId);
}
SomeArgs args = SomeArgs.obtain();
args.arg1 = bundle;
@@ -293,6 +294,7 @@
args.argi2 = userId;
args.arg4 = opPackageName;
args.arg5 = operationId;
+ args.argi3 = sysUiSessionId;
boolean skipAnimation = false;
if (mCurrentDialog != null) {
@@ -382,6 +384,7 @@
final int userId = args.argi2;
final String opPackageName = (String) args.arg4;
final long operationId = (long) args.arg5;
+ final int sysUiSessionId = args.argi3;
// Create a new dialog but do not replace the current one yet.
final AuthDialog newDialog = buildDialog(
@@ -391,7 +394,8 @@
type,
opPackageName,
skipAnimation,
- operationId);
+ operationId,
+ sysUiSessionId);
if (newDialog == null) {
Log.e(TAG, "Unsupported type: " + type);
@@ -403,7 +407,8 @@
+ " savedState: " + savedState
+ " mCurrentDialog: " + mCurrentDialog
+ " newDialog: " + newDialog
- + " type: " + type);
+ + " type: " + type
+ + " sysUiSessionId: " + sysUiSessionId);
}
if (mCurrentDialog != null) {
@@ -458,7 +463,8 @@
}
protected AuthDialog buildDialog(Bundle biometricPromptBundle, boolean requireConfirmation,
- int userId, int type, String opPackageName, boolean skipIntro, long operationId) {
+ int userId, int type, String opPackageName, boolean skipIntro, long operationId,
+ int sysUiSessionId) {
return new AuthContainerView.Builder(mContext)
.setCallback(this)
.setBiometricPromptBundle(biometricPromptBundle)
@@ -467,6 +473,7 @@
.setOpPackageName(opPackageName)
.setSkipIntro(skipIntro)
.setOperationId(operationId)
+ .setSysUiSessionId(sysUiSessionId)
.build(type);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
index 13669a6..e96bef3 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
@@ -73,12 +73,13 @@
updateIcon(mContext, parentViewGroup);
}
- // TODO(b/149146374) Propagate theme change to bubbles in overflow.
void updateIcon(Context context, ViewGroup parentViewGroup) {
mInflater = LayoutInflater.from(context);
mOverflowBtn = (BadgedImageView) mInflater.inflate(R.layout.bubble_overflow_button,
parentViewGroup /* root */,
false /* attachToRoot */);
+ mOverflowBtn.setContentDescription(mContext.getResources().getString(
+ R.string.bubble_overflow_button_content_description));
TypedArray ta = mContext.obtainStyledAttributes(
new int[]{android.R.attr.colorBackgroundFloating});
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
index de54c35..c2ca9fa 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
@@ -21,6 +21,7 @@
import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
import android.app.Activity;
+import android.app.Notification;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -32,6 +33,7 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -103,7 +105,7 @@
- res.getDimensionPixelSize(R.dimen.bubble_overflow_padding);
final int viewHeight = recyclerViewHeight / rows;
- mAdapter = new BubbleOverflowAdapter(mOverflowBubbles,
+ mAdapter = new BubbleOverflowAdapter(getApplicationContext(), mOverflowBubbles,
mBubbleController::promoteBubbleFromOverflow, viewWidth, viewHeight);
mRecyclerView.setAdapter(mAdapter);
@@ -221,13 +223,15 @@
}
class BubbleOverflowAdapter extends RecyclerView.Adapter<BubbleOverflowAdapter.ViewHolder> {
+ private Context mContext;
private Consumer<Bubble> mPromoteBubbleFromOverflow;
private List<Bubble> mBubbles;
private int mWidth;
private int mHeight;
- public BubbleOverflowAdapter(List<Bubble> list, Consumer<Bubble> promoteBubble, int width,
- int height) {
+ public BubbleOverflowAdapter(Context context, List<Bubble> list, Consumer<Bubble> promoteBubble,
+ int width, int height) {
+ mContext = context;
mBubbles = list;
mPromoteBubbleFromOverflow = promoteBubble;
mWidth = width;
@@ -260,6 +264,32 @@
mPromoteBubbleFromOverflow.accept(b);
});
+ final CharSequence titleCharSeq =
+ b.getEntry().getSbn().getNotification().extras.getCharSequence(
+ Notification.EXTRA_TITLE);
+ String titleStr = mContext.getResources().getString(R.string.notification_bubble_title);
+ if (titleCharSeq != null) {
+ titleStr = titleCharSeq.toString();
+ }
+ vh.iconView.setContentDescription(mContext.getResources().getString(
+ R.string.bubble_content_description_single, titleStr, b.getAppName()));
+
+ vh.iconView.setAccessibilityDelegate(
+ new View.AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host,
+ AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ // Talkback prompts "Double tap to add back to stack"
+ // instead of the default "Double tap to activate"
+ info.addAction(
+ new AccessibilityNodeInfo.AccessibilityAction(
+ AccessibilityNodeInfo.ACTION_CLICK,
+ mContext.getResources().getString(
+ R.string.bubble_accessibility_action_add_back)));
+ }
+ });
+
Bubble.FlyoutMessage message = b.getFlyoutMessage();
if (message != null && message.senderName != null) {
vh.textView.setText(message.senderName);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 3414587..2cb097f 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -454,7 +454,6 @@
// that means overflow was previously expanded. Set the selected bubble
// internally without going through BubbleData (which would ignore it since it's
// already selected).
- mBubbleData.setShowingOverflow(true);
setSelectedBubble(clickedBubble);
}
} else {
@@ -1344,7 +1343,10 @@
}
if (bubbleToSelect == null || bubbleToSelect.getKey() != BubbleOverflow.KEY) {
mBubbleData.setShowingOverflow(false);
+ } else {
+ mBubbleData.setShowingOverflow(true);
}
+
final BubbleViewProvider previouslySelected = mExpandedBubble;
mExpandedBubble = bubbleToSelect;
updatePointerPosition();
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index 8c572fe..6d1bf72 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -20,10 +20,15 @@
import static com.android.systemui.Dependency.LEAK_REPORT_EMAIL_NAME;
import android.content.Context;
+import android.os.Handler;
+import android.os.PowerManager;
import androidx.annotation.Nullable;
import com.android.keyguard.KeyguardViewController;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerImpl;
import com.android.systemui.plugins.qs.QSFactory;
@@ -81,10 +86,17 @@
abstract NotificationLockscreenUserManager bindNotificationLockscreenUserManager(
NotificationLockscreenUserManagerImpl notificationLockscreenUserManager);
- @Binds
+ @Provides
@Singleton
- public abstract BatteryController provideBatteryController(
- BatteryControllerImpl controllerImpl);
+ static BatteryController provideBatteryController(Context context,
+ EnhancedEstimates enhancedEstimates, PowerManager powerManager,
+ BroadcastDispatcher broadcastDispatcher, @Main Handler mainHandler,
+ @Background Handler bgHandler) {
+ BatteryController bC = new BatteryControllerImpl(context, enhancedEstimates, powerManager,
+ broadcastDispatcher, mainHandler, bgHandler);
+ bC.init();
+ return bC;
+ }
@Binds
@Singleton
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/LongRunning.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/LongRunning.java
new file mode 100644
index 0000000..e90781b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/LongRunning.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 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.systemui.dagger.qualifiers;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface LongRunning {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 10776c9..e1081cd 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -41,6 +41,9 @@
import androidx.annotation.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEvent;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.UiEventLoggerImpl;
import com.android.internal.logging.nano.MetricsProto;
import com.android.systemui.plugins.SensorManagerPlugin;
import com.android.systemui.statusbar.phone.DozeParameters;
@@ -56,8 +59,8 @@
public class DozeSensors {
private static final boolean DEBUG = DozeService.DEBUG;
-
private static final String TAG = "DozeSensors";
+ private static final UiEventLogger UI_EVENT_LOGGER = new UiEventLoggerImpl();
private final Context mContext;
private final AlarmManager mAlarmManager;
@@ -79,6 +82,23 @@
private boolean mListening;
private boolean mPaused;
+ @VisibleForTesting
+ public enum DozeSensorsUiEvent implements UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "User performs pickup gesture that activates the ambient display")
+ ACTION_AMBIENT_GESTURE_PICKUP(459);
+
+ private final int mId;
+
+ DozeSensorsUiEvent(int id) {
+ mId = id;
+ }
+
+ @Override
+ public int getId() {
+ return mId;
+ }
+ }
+
public DozeSensors(Context context, AlarmManager alarmManager, AsyncSensorManager sensorManager,
DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock,
Callback callback, Consumer<Boolean> proxCallback, DozeLog dozeLog) {
@@ -416,6 +436,7 @@
MetricsLogger.action(
mContext, MetricsProto.MetricsEvent.ACTION_AMBIENT_GESTURE,
subType);
+ UI_EVENT_LOGGER.log(DozeSensorsUiEvent.ACTION_AMBIENT_GESTURE_PICKUP);
}
mRegistered = false;
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
index f322489..f72de11 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
@@ -376,7 +376,8 @@
// NOTE: intentionally does not apply the transaction here.
// this end transaction should get executed synchronously with the final
// WindowContainerTransaction in task organizer
- getSurfaceTransactionHelper().resetScale(tx, leash, getDestinationBounds());
+ getSurfaceTransactionHelper().resetScale(tx, leash, getDestinationBounds())
+ .crop(tx, leash, getDestinationBounds());
}
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
index ec15dd1..1982227 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -27,7 +27,7 @@
import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_CONTROLLER_MESSENGER;
import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_DISMISS_FRACTION;
import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_MENU_STATE;
-import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_MOVEMENT_BOUNDS;
+import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_SHOW_MENU_WITH_DELAY;
import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_STACK_BOUNDS;
import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_WILL_RESIZE_MENU;
import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_CLOSE;
@@ -138,9 +138,9 @@
final Bundle data = (Bundle) msg.obj;
showMenu(data.getInt(EXTRA_MENU_STATE),
data.getParcelable(EXTRA_STACK_BOUNDS),
- data.getParcelable(EXTRA_MOVEMENT_BOUNDS),
data.getBoolean(EXTRA_ALLOW_TIMEOUT),
- data.getBoolean(EXTRA_WILL_RESIZE_MENU));
+ data.getBoolean(EXTRA_WILL_RESIZE_MENU),
+ data.getBoolean(EXTRA_SHOW_MENU_WITH_DELAY));
break;
}
case MESSAGE_POKE_MENU:
@@ -177,12 +177,7 @@
private Messenger mToControllerMessenger;
private Messenger mMessenger = new Messenger(mHandler);
- private final Runnable mFinishRunnable = new Runnable() {
- @Override
- public void run() {
- hideMenu();
- }
- };
+ private final Runnable mFinishRunnable = this::hideMenu;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -321,8 +316,8 @@
// Do nothing
}
- private void showMenu(int menuState, Rect stackBounds, Rect movementBounds,
- boolean allowMenuTimeout, boolean resizeMenuOnShow) {
+ private void showMenu(int menuState, Rect stackBounds, boolean allowMenuTimeout,
+ boolean resizeMenuOnShow, boolean withDelay) {
mAllowMenuTimeout = allowMenuTimeout;
if (mMenuState != menuState) {
// Disallow touches if the menu needs to resize while showing, and we are transitioning
@@ -335,7 +330,6 @@
if (mMenuContainerAnimator != null) {
mMenuContainerAnimator.cancel();
}
- notifyMenuStateChange(menuState, resizeMenuOnShow);
mMenuContainerAnimator = new AnimatorSet();
ObjectAnimator menuAnim = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
mMenuContainer.getAlpha(), 1f);
@@ -359,7 +353,13 @@
}
});
}
- mMenuContainerAnimator.start();
+ if (withDelay) {
+ // starts the menu container animation after window expansion is completed
+ notifyMenuStateChange(menuState, resizeMenuOnShow, mMenuContainerAnimator::start);
+ } else {
+ notifyMenuStateChange(menuState, resizeMenuOnShow, null /* callback */);
+ mMenuContainerAnimator.start();
+ }
} else {
// If we are already visible, then just start the delayed dismiss and unregister any
// existing input consumers from the previous drag
@@ -382,7 +382,7 @@
if (mMenuState != MENU_STATE_NONE) {
cancelDelayedFinish();
if (notifyMenuVisibility) {
- notifyMenuStateChange(MENU_STATE_NONE, mResize);
+ notifyMenuStateChange(MENU_STATE_NONE, mResize, null /* callback */);
}
mMenuContainerAnimator = new AnimatorSet();
ObjectAnimator menuAnim = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
@@ -434,10 +434,10 @@
final int menuState = intent.getIntExtra(EXTRA_MENU_STATE, MENU_STATE_NONE);
if (menuState != MENU_STATE_NONE) {
Rect stackBounds = intent.getParcelableExtra(EXTRA_STACK_BOUNDS);
- Rect movementBounds = intent.getParcelableExtra(EXTRA_MOVEMENT_BOUNDS);
boolean allowMenuTimeout = intent.getBooleanExtra(EXTRA_ALLOW_TIMEOUT, true);
boolean willResizeMenu = intent.getBooleanExtra(EXTRA_WILL_RESIZE_MENU, false);
- showMenu(menuState, stackBounds, movementBounds, allowMenuTimeout, willResizeMenu);
+ boolean withDelay = intent.getBooleanExtra(EXTRA_SHOW_MENU_WITH_DELAY, false);
+ showMenu(menuState, stackBounds, allowMenuTimeout, willResizeMenu, withDelay);
}
}
@@ -540,13 +540,14 @@
mBackgroundDrawable.setAlpha(alpha);
}
- private void notifyMenuStateChange(int menuState, boolean resize) {
+ private void notifyMenuStateChange(int menuState, boolean resize, Runnable callback) {
mMenuState = menuState;
mResize = resize;
Message m = Message.obtain();
m.what = PipMenuActivityController.MESSAGE_MENU_STATE_CHANGED;
m.arg1 = menuState;
m.arg2 = resize ? 1 : 0;
+ m.obj = callback;
sendMessage(m, "Could not notify controller of PIP menu visibility");
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index 61ed40d..1608f83 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -60,11 +60,11 @@
public static final String EXTRA_CONTROLLER_MESSENGER = "messenger";
public static final String EXTRA_ACTIONS = "actions";
public static final String EXTRA_STACK_BOUNDS = "stack_bounds";
- public static final String EXTRA_MOVEMENT_BOUNDS = "movement_bounds";
public static final String EXTRA_ALLOW_TIMEOUT = "allow_timeout";
public static final String EXTRA_WILL_RESIZE_MENU = "resize_menu_on_show";
public static final String EXTRA_DISMISS_FRACTION = "dismiss_fraction";
public static final String EXTRA_MENU_STATE = "menu_state";
+ public static final String EXTRA_SHOW_MENU_WITH_DELAY = "show_menu_with_delay";
public static final int MESSAGE_MENU_STATE_CHANGED = 100;
public static final int MESSAGE_EXPAND_PIP = 101;
@@ -89,7 +89,7 @@
* @param menuState the current state of the menu
* @param resize whether or not to resize the PiP with the state change
*/
- void onPipMenuStateChanged(int menuState, boolean resize);
+ void onPipMenuStateChanged(int menuState, boolean resize, Runnable callback);
/**
* Called when the PIP requested to be expanded.
@@ -130,7 +130,7 @@
case MESSAGE_MENU_STATE_CHANGED: {
int menuState = msg.arg1;
boolean resize = msg.arg2 != 0;
- onMenuStateChanged(menuState, resize);
+ onMenuStateChanged(menuState, resize, (Runnable) msg.obj);
break;
}
case MESSAGE_EXPAND_PIP: {
@@ -155,7 +155,7 @@
// Mark the menu as invisible once the activity finishes as well
if (mToActivityMessenger == null) {
final boolean resize = msg.arg1 != 0;
- onMenuStateChanged(MENU_STATE_NONE, resize);
+ onMenuStateChanged(MENU_STATE_NONE, resize, null /* callback */);
}
break;
}
@@ -247,21 +247,38 @@
// If we haven't requested the start activity, or if it previously took too long to
// start, then start it
startMenuActivity(MENU_STATE_NONE, null /* stackBounds */,
- null /* movementBounds */, false /* allowMenuTimeout */,
- false /* resizeMenuOnShow */);
+ false /* allowMenuTimeout */, false /* resizeMenuOnShow */,
+ false /* withDelay */);
}
}
/**
- * Shows the menu activity.
+ * Similar to {@link #showMenu(int, Rect, boolean, boolean)} but only show the menu upon
+ * PiP window transition is finished.
*/
- public void showMenu(int menuState, Rect stackBounds, Rect movementBounds,
- boolean allowMenuTimeout, boolean willResizeMenu) {
+ public void showMenuWithDelay(int menuState, Rect stackBounds, boolean allowMenuTimeout,
+ boolean willResizeMenu) {
+ showMenuInternal(menuState, stackBounds, allowMenuTimeout, willResizeMenu,
+ true /* withDelay */);
+ }
+
+ /**
+ * Shows the menu activity immediately.
+ */
+ public void showMenu(int menuState, Rect stackBounds, boolean allowMenuTimeout,
+ boolean willResizeMenu) {
+ showMenuInternal(menuState, stackBounds, allowMenuTimeout, willResizeMenu,
+ false /* withDelay */);
+ }
+
+ private void showMenuInternal(int menuState, Rect stackBounds, boolean allowMenuTimeout,
+ boolean willResizeMenu, boolean withDelay) {
if (DEBUG) {
Log.d(TAG, "showMenu() state=" + menuState
+ " hasActivity=" + (mToActivityMessenger != null)
+ " allowMenuTimeout=" + allowMenuTimeout
+ " willResizeMenu=" + willResizeMenu
+ + " withDelay=" + withDelay
+ " callers=\n" + Debug.getCallers(5, " "));
}
@@ -271,9 +288,9 @@
if (stackBounds != null) {
data.putParcelable(EXTRA_STACK_BOUNDS, stackBounds);
}
- data.putParcelable(EXTRA_MOVEMENT_BOUNDS, movementBounds);
data.putBoolean(EXTRA_ALLOW_TIMEOUT, allowMenuTimeout);
data.putBoolean(EXTRA_WILL_RESIZE_MENU, willResizeMenu);
+ data.putBoolean(EXTRA_SHOW_MENU_WITH_DELAY, withDelay);
Message m = Message.obtain();
m.what = PipMenuActivity.MESSAGE_SHOW_MENU;
m.obj = data;
@@ -285,8 +302,7 @@
} else if (!mStartActivityRequested || isStartActivityRequestedElapsed()) {
// If we haven't requested the start activity, or if it previously took too long to
// start, then start it
- startMenuActivity(menuState, stackBounds, movementBounds, allowMenuTimeout,
- willResizeMenu);
+ startMenuActivity(menuState, stackBounds, allowMenuTimeout, willResizeMenu, withDelay);
}
}
@@ -364,7 +380,7 @@
* (ie. when manually expanding or dismissing).
*/
public void hideMenuWithoutResize() {
- onMenuStateChanged(MENU_STATE_NONE, false /* resize */);
+ onMenuStateChanged(MENU_STATE_NONE, false /* resize */, null /* callback */);
}
/**
@@ -388,8 +404,8 @@
/**
* Starts the menu activity on the top task of the pinned stack.
*/
- private void startMenuActivity(int menuState, Rect stackBounds, Rect movementBounds,
- boolean allowMenuTimeout, boolean willResizeMenu) {
+ private void startMenuActivity(int menuState, Rect stackBounds, boolean allowMenuTimeout,
+ boolean willResizeMenu, boolean withDelay) {
try {
StackInfo pinnedStackInfo = ActivityTaskManager.getService().getStackInfo(
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
@@ -402,12 +418,10 @@
if (stackBounds != null) {
intent.putExtra(EXTRA_STACK_BOUNDS, stackBounds);
}
- if (movementBounds != null) {
- intent.putExtra(EXTRA_MOVEMENT_BOUNDS, movementBounds);
- }
intent.putExtra(EXTRA_MENU_STATE, menuState);
intent.putExtra(EXTRA_ALLOW_TIMEOUT, allowMenuTimeout);
intent.putExtra(EXTRA_WILL_RESIZE_MENU, willResizeMenu);
+ intent.putExtra(EXTRA_SHOW_MENU_WITH_DELAY, withDelay);
ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
options.setLaunchTaskId(
pinnedStackInfo.taskIds[pinnedStackInfo.taskIds.length - 1]);
@@ -472,14 +486,14 @@
/**
* Handles changes in menu visibility.
*/
- private void onMenuStateChanged(int menuState, boolean resize) {
+ private void onMenuStateChanged(int menuState, boolean resize, Runnable callback) {
if (DEBUG) {
Log.d(TAG, "onMenuStateChanged() mMenuState=" + mMenuState
+ " menuState=" + menuState + " resize=" + resize);
}
if (menuState != mMenuState) {
- mListeners.forEach(l -> l.onPipMenuStateChanged(menuState, resize));
+ mListeners.forEach(l -> l.onPipMenuStateChanged(menuState, resize, callback));
if (menuState == MENU_STATE_FULL) {
// Once visible, start listening for media action changes. This call will trigger
// the menu actions to be updated again.
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index b1e4d67..162cdbc 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.IActivityTaskManager;
+import android.content.ComponentName;
import android.content.Context;
import android.graphics.Rect;
import android.os.Debug;
@@ -123,6 +124,29 @@
*/
private boolean mSpringingToTouch = false;
+ /**
+ * Gets set in {@link #animateToExpandedState(Rect, Rect, Rect, Runnable)}, this callback is
+ * used to show menu activity when the expand animation is completed.
+ */
+ private Runnable mPostPipTransitionCallback;
+
+ private final PipTaskOrganizer.PipTransitionCallback mPipTransitionCallback =
+ new PipTaskOrganizer.PipTransitionCallback() {
+ @Override
+ public void onPipTransitionStarted(ComponentName activity, int direction) {}
+
+ @Override
+ public void onPipTransitionFinished(ComponentName activity, int direction) {
+ if (mPostPipTransitionCallback != null) {
+ mPostPipTransitionCallback.run();
+ mPostPipTransitionCallback = null;
+ }
+ }
+
+ @Override
+ public void onPipTransitionCanceled(ComponentName activity, int direction) {}
+ };
+
public PipMotionHelper(Context context, IActivityTaskManager activityTaskManager,
PipTaskOrganizer pipTaskOrganizer, PipMenuActivityController menuController,
PipSnapAlgorithm snapAlgorithm, FlingAnimationUtils flingAnimationUtils,
@@ -135,6 +159,7 @@
mFlingAnimationUtils = flingAnimationUtils;
mFloatingContentCoordinator = floatingContentCoordinator;
onConfigurationChanged();
+ mPipTaskOrganizer.registerPipTransitionCallback(mPipTransitionCallback);
}
@NonNull
@@ -375,9 +400,10 @@
* Animates the PiP to the expanded state to show the menu.
*/
float animateToExpandedState(Rect expandedBounds, Rect movementBounds,
- Rect expandedMovementBounds) {
+ Rect expandedMovementBounds, Runnable callback) {
float savedSnapFraction = mSnapAlgorithm.getSnapFraction(new Rect(mBounds), movementBounds);
mSnapAlgorithm.applySnapFraction(expandedBounds, expandedMovementBounds, savedSnapFraction);
+ mPostPipTransitionCallback = callback;
resizeAndAnimatePipUnchecked(expandedBounds, EXPAND_STACK_TO_MENU_DURATION);
return savedSnapFraction;
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index f5c83c1..87f0a70 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -182,8 +182,8 @@
*/
private class PipMenuListener implements PipMenuActivityController.Listener {
@Override
- public void onPipMenuStateChanged(int menuState, boolean resize) {
- setMenuState(menuState, resize);
+ public void onPipMenuStateChanged(int menuState, boolean resize, Runnable callback) {
+ setMenuState(menuState, resize, callback);
}
@Override
@@ -204,7 +204,7 @@
@Override
public void onPipShowMenu() {
mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
- mMovementBounds, true /* allowMenuTimeout */, willResizeMenu());
+ true /* allowMenuTimeout */, willResizeMenu());
}
}
@@ -234,8 +234,8 @@
new PipResizeGestureHandler(context, pipBoundsHandler, mMotionHelper,
deviceConfig, pipTaskOrganizer);
mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler,
- () -> mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
- mMovementBounds, true /* allowMenuTimeout */, willResizeMenu()));
+ () -> mMenuController.showMenuWithDelay(MENU_STATE_FULL, mMotionHelper.getBounds(),
+ true /* allowMenuTimeout */, willResizeMenu()));
Resources res = context.getResources();
mExpandedShortestEdgeSize = res.getDimensionPixelSize(
@@ -322,7 +322,7 @@
// Only show the menu if the user isn't currently interacting with the PiP
if (!mTouchState.isUserInteracting()) {
mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
- mMovementBounds, false /* allowMenuTimeout */, willResizeMenu());
+ false /* allowMenuTimeout */, willResizeMenu());
}
}
@@ -358,7 +358,7 @@
if (mShowPipMenuOnAnimationEnd) {
mMenuController.showMenu(MENU_STATE_CLOSE, mMotionHelper.getBounds(),
- mMovementBounds, true /* allowMenuTimeout */, false /* willResizeMenu */);
+ true /* allowMenuTimeout */, false /* willResizeMenu */);
mShowPipMenuOnAnimationEnd = false;
}
}
@@ -557,7 +557,7 @@
private void onAccessibilityShowMenu() {
mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
- mMovementBounds, true /* allowMenuTimeout */, willResizeMenu());
+ true /* allowMenuTimeout */, willResizeMenu());
}
private boolean handleTouchEvent(InputEvent inputEvent) {
@@ -628,8 +628,7 @@
// Let's not enable menu show/hide for a11y services.
if (!mAccessibilityManager.isTouchExplorationEnabled()) {
mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
- mMovementBounds, false /* allowMenuTimeout */,
- false /* willResizeMenu */);
+ false /* allowMenuTimeout */, false /* willResizeMenu */);
}
case MotionEvent.ACTION_HOVER_MOVE: {
if (!shouldDeliverToMenu && !mSendingHoverAccessibilityEvents) {
@@ -713,7 +712,7 @@
/**
* Sets the menu visibility.
*/
- private void setMenuState(int menuState, boolean resize) {
+ private void setMenuState(int menuState, boolean resize, Runnable callback) {
if (mMenuState == menuState && !resize) {
return;
}
@@ -727,7 +726,7 @@
mResizedBounds.set(mMotionHelper.getBounds());
Rect expandedBounds = new Rect(mExpandedBounds);
mSavedSnapFraction = mMotionHelper.animateToExpandedState(expandedBounds,
- mMovementBounds, mExpandedMovementBounds);
+ mMovementBounds, mExpandedMovementBounds, callback);
}
} else if (menuState == MENU_STATE_NONE && mMenuState == MENU_STATE_FULL) {
// Try and restore the PiP to the closest edge, using the saved snap fraction
@@ -893,7 +892,7 @@
// If the menu is still visible, then just poke the menu so that
// it will timeout after the user stops touching it
mMenuController.showMenu(mMenuState, mMotionHelper.getBounds(),
- mMovementBounds, true /* allowMenuTimeout */, willResizeMenu());
+ true /* allowMenuTimeout */, willResizeMenu());
} else {
// If the menu is not visible, then we can still be showing the activity for the
// dismiss overlay, so just finish it after the animation completes
@@ -917,7 +916,7 @@
// User has stalled long enough for this not to be a drag or a double tap, just
// expand the menu
mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
- mMovementBounds, true /* allowMenuTimeout */, willResizeMenu());
+ true /* allowMenuTimeout */, willResizeMenu());
} else {
// Next touch event _may_ be the second tap for the double-tap, schedule a
// fallback runnable to trigger the menu if no touch event occurs before the
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index 8feee10..f821b19 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -18,8 +18,10 @@
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.ColorStateList;
+import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
+import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.Drawable;
@@ -64,6 +66,8 @@
private boolean mTileState;
private boolean mCollapsedView;
private boolean mShowRippleEffect = true;
+ private float mStrokeWidthActive;
+ private float mStrokeWidthInactive;
private final ImageView mBg;
private final TextView mDetailText;
@@ -83,6 +87,10 @@
// Default to Quick Tile padding, and QSTileView will specify its own padding.
int padding = context.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_padding);
mIconFrame = new FrameLayout(context);
+ mStrokeWidthActive = context.getResources()
+ .getDimension(com.android.internal.R.dimen.config_qsTileStrokeWidthActive);
+ mStrokeWidthInactive = context.getResources()
+ .getDimension(com.android.internal.R.dimen.config_qsTileStrokeWidthInactive);
int size = context.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size);
addView(mIconFrame, new LayoutParams(size, size));
mBg = new ImageView(getContext());
@@ -206,7 +214,31 @@
mHandler.obtainMessage(H.STATE_CHANGED, state).sendToTarget();
}
+ private void updateStrokeShapeWidth(QSTile.State state) {
+ Resources resources = getContext().getResources();
+ if (!(mBg.getDrawable() instanceof ShapeDrawable)) {
+ return;
+ }
+ ShapeDrawable d = (ShapeDrawable) mBg.getDrawable();
+ d.getPaint().setStyle(Paint.Style.FILL);
+ switch (state.state) {
+ case Tile.STATE_INACTIVE:
+ if (mStrokeWidthInactive >= 0) {
+ d.getPaint().setStyle(Paint.Style.STROKE);
+ d.getPaint().setStrokeWidth(mStrokeWidthInactive);
+ }
+ break;
+ case Tile.STATE_ACTIVE:
+ if (mStrokeWidthActive >= 0) {
+ d.getPaint().setStyle(Paint.Style.STROKE);
+ d.getPaint().setStrokeWidth(mStrokeWidthActive);
+ }
+ break;
+ }
+ }
+
protected void handleStateChanged(QSTile.State state) {
+ updateStrokeShapeWidth(state);
int circleColor = getCircleColor(state.state);
boolean allowAnimations = animationsEnabled();
if (circleColor != mCircleColor) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
index ae0a1c4..b253635 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
@@ -137,7 +137,7 @@
* Check if the recording is ongoing
* @return
*/
- public boolean isRecording() {
+ public synchronized boolean isRecording() {
return mIsRecording;
}
@@ -157,7 +157,7 @@
* Update the current status
* @param isRecording
*/
- public void updateState(boolean isRecording) {
+ public synchronized void updateState(boolean isRecording) {
mIsRecording = isRecording;
for (RecordingStateChangeCallback cb : mListeners) {
if (isRecording) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index 390ac09..cf098d5 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -22,41 +22,27 @@
import android.app.PendingIntent;
import android.app.Service;
import android.content.ContentResolver;
-import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.Icon;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.VirtualDisplay;
import android.media.MediaRecorder;
-import android.media.projection.IMediaProjection;
-import android.media.projection.IMediaProjectionManager;
-import android.media.projection.MediaProjection;
-import android.media.projection.MediaProjectionManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.provider.MediaStore;
import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Size;
-import android.view.Surface;
-import android.view.WindowManager;
import android.widget.Toast;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.LongRunning;
-import java.io.File;
import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.file.Files;
-import java.text.SimpleDateFormat;
-import java.util.Date;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -66,13 +52,15 @@
public class RecordingService extends Service implements MediaRecorder.OnInfoListener {
public static final int REQUEST_CODE = 2;
- private static final int NOTIFICATION_ID = 1;
+ private static final int NOTIFICATION_RECORDING_ID = 4274;
+ private static final int NOTIFICATION_PROCESSING_ID = 4275;
+ private static final int NOTIFICATION_VIEW_ID = 4273;
private static final String TAG = "RecordingService";
private static final String CHANNEL_ID = "screen_record";
private static final String EXTRA_RESULT_CODE = "extra_resultCode";
private static final String EXTRA_DATA = "extra_data";
private static final String EXTRA_PATH = "extra_path";
- private static final String EXTRA_USE_AUDIO = "extra_useAudio";
+ private static final String EXTRA_AUDIO_SOURCE = "extra_useAudio";
private static final String EXTRA_SHOW_TAPS = "extra_showTaps";
private static final String ACTION_START = "com.android.systemui.screenrecord.START";
@@ -80,29 +68,19 @@
private static final String ACTION_SHARE = "com.android.systemui.screenrecord.SHARE";
private static final String ACTION_DELETE = "com.android.systemui.screenrecord.DELETE";
- private static final int TOTAL_NUM_TRACKS = 1;
- private static final int VIDEO_BIT_RATE = 10000000;
- private static final int VIDEO_FRAME_RATE = 30;
- private static final int AUDIO_BIT_RATE = 16;
- private static final int AUDIO_SAMPLE_RATE = 44100;
- private static final int MAX_DURATION_MS = 60 * 60 * 1000;
- private static final long MAX_FILESIZE_BYTES = 5000000000L;
-
private final RecordingController mController;
- private MediaProjection mMediaProjection;
- private Surface mInputSurface;
- private VirtualDisplay mVirtualDisplay;
- private MediaRecorder mMediaRecorder;
private Notification.Builder mRecordingNotificationBuilder;
- private boolean mUseAudio;
+ private ScreenRecordingAudioSource mAudioSource;
private boolean mShowTaps;
private boolean mOriginalShowTaps;
- private File mTempFile;
+ private ScreenMediaRecorder mRecorder;
+ private final Executor mLongExecutor;
@Inject
- public RecordingService(RecordingController controller) {
+ public RecordingService(RecordingController controller, @LongRunning Executor executor) {
mController = controller;
+ mLongExecutor = executor;
}
/**
@@ -113,16 +91,16 @@
* android.content.Intent)}
* @param data The data from {@link android.app.Activity#onActivityResult(int, int,
* android.content.Intent)}
- * @param useAudio True to enable microphone input while recording
+ * @param audioSource The ordinal value of the audio source
+ * {@link com.android.systemui.screenrecord.ScreenRecordingAudioSource}
* @param showTaps True to make touches visible while recording
*/
- public static Intent getStartIntent(Context context, int resultCode, Intent data,
- boolean useAudio, boolean showTaps) {
+ public static Intent getStartIntent(Context context, int resultCode,
+ int audioSource, boolean showTaps) {
return new Intent(context, RecordingService.class)
.setAction(ACTION_START)
.putExtra(EXTRA_RESULT_CODE, resultCode)
- .putExtra(EXTRA_DATA, data)
- .putExtra(EXTRA_USE_AUDIO, useAudio)
+ .putExtra(EXTRA_AUDIO_SOURCE, audioSource)
.putExtra(EXTRA_SHOW_TAPS, showTaps);
}
@@ -139,36 +117,31 @@
switch (action) {
case ACTION_START:
- mUseAudio = intent.getBooleanExtra(EXTRA_USE_AUDIO, false);
+ mAudioSource = ScreenRecordingAudioSource
+ .values()[intent.getIntExtra(EXTRA_AUDIO_SOURCE, 0)];
+ Log.d(TAG, "recording with audio source" + mAudioSource);
mShowTaps = intent.getBooleanExtra(EXTRA_SHOW_TAPS, false);
- try {
- IBinder b = ServiceManager.getService(MEDIA_PROJECTION_SERVICE);
- IMediaProjectionManager mediaService =
- IMediaProjectionManager.Stub.asInterface(b);
- IMediaProjection proj = mediaService.createProjection(getUserId(),
- getPackageName(),
- MediaProjectionManager.TYPE_SCREEN_CAPTURE, false);
- IBinder projection = proj.asBinder();
- if (projection == null) {
- Log.e(TAG, "Projection was null");
- Toast.makeText(this, R.string.screenrecord_start_error, Toast.LENGTH_LONG)
- .show();
- return Service.START_NOT_STICKY;
- }
- mMediaProjection = new MediaProjection(getApplicationContext(),
- IMediaProjection.Stub.asInterface(projection));
- startRecording();
- } catch (RemoteException e) {
- e.printStackTrace();
- Toast.makeText(this, R.string.screenrecord_start_error, Toast.LENGTH_LONG)
- .show();
- return Service.START_NOT_STICKY;
- }
+
+ mOriginalShowTaps = Settings.System.getInt(
+ getApplicationContext().getContentResolver(),
+ Settings.System.SHOW_TOUCHES, 0) != 0;
+
+ setTapsVisible(mShowTaps);
+
+ mRecorder = new ScreenMediaRecorder(
+ getApplicationContext(),
+ getUserId(),
+ mAudioSource,
+ this
+ );
+ startRecording();
break;
case ACTION_STOP:
stopRecording();
+ notificationManager.cancel(NOTIFICATION_RECORDING_ID);
saveRecording(notificationManager);
+ stopSelf();
break;
case ACTION_SHARE:
@@ -183,10 +156,10 @@
sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
// Remove notification
- notificationManager.cancel(NOTIFICATION_ID);
+ notificationManager.cancel(NOTIFICATION_RECORDING_ID);
startActivity(Intent.createChooser(shareIntent, shareLabel)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
break;
case ACTION_DELETE:
// Close quick shade
@@ -202,7 +175,7 @@
Toast.LENGTH_LONG).show();
// Remove notification
- notificationManager.cancel(NOTIFICATION_ID);
+ notificationManager.cancel(NOTIFICATION_RECORDING_ID);
Log.d(TAG, "Deleted recording " + uri);
break;
}
@@ -224,70 +197,15 @@
*/
private void startRecording() {
try {
- File cacheDir = getCacheDir();
- cacheDir.mkdirs();
- mTempFile = File.createTempFile("temp", ".mp4", cacheDir);
- Log.d(TAG, "Writing video output to: " + mTempFile.getAbsolutePath());
-
- mOriginalShowTaps = 1 == Settings.System.getInt(
- getApplicationContext().getContentResolver(),
- Settings.System.SHOW_TOUCHES, 0);
- setTapsVisible(mShowTaps);
-
- // Set up media recorder
- mMediaRecorder = new MediaRecorder();
- if (mUseAudio) {
- mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
- }
- mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
- mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
-
- // Set up video
- DisplayMetrics metrics = new DisplayMetrics();
- WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
- wm.getDefaultDisplay().getRealMetrics(metrics);
- int screenWidth = metrics.widthPixels;
- int screenHeight = metrics.heightPixels;
- mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
- mMediaRecorder.setVideoSize(screenWidth, screenHeight);
- mMediaRecorder.setVideoFrameRate(VIDEO_FRAME_RATE);
- mMediaRecorder.setVideoEncodingBitRate(VIDEO_BIT_RATE);
- mMediaRecorder.setMaxDuration(MAX_DURATION_MS);
- mMediaRecorder.setMaxFileSize(MAX_FILESIZE_BYTES);
-
- // Set up audio
- if (mUseAudio) {
- mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
- mMediaRecorder.setAudioChannels(TOTAL_NUM_TRACKS);
- mMediaRecorder.setAudioEncodingBitRate(AUDIO_BIT_RATE);
- mMediaRecorder.setAudioSamplingRate(AUDIO_SAMPLE_RATE);
- }
-
- mMediaRecorder.setOutputFile(mTempFile);
- mMediaRecorder.prepare();
-
- // Create surface
- mInputSurface = mMediaRecorder.getSurface();
- mVirtualDisplay = mMediaProjection.createVirtualDisplay(
- "Recording Display",
- screenWidth,
- screenHeight,
- metrics.densityDpi,
- DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
- mInputSurface,
- null,
- null);
-
- mMediaRecorder.setOnInfoListener(this);
- mMediaRecorder.start();
+ mRecorder.start();
mController.updateState(true);
- } catch (IOException e) {
- Log.e(TAG, "Error starting screen recording: " + e.getMessage());
+ createRecordingNotification();
+ } catch (IOException | RemoteException e) {
+ Toast.makeText(this,
+ R.string.screenrecord_start_error, Toast.LENGTH_LONG)
+ .show();
e.printStackTrace();
- throw new RuntimeException(e);
}
-
- createRecordingNotification();
}
private void createRecordingNotification() {
@@ -306,7 +224,7 @@
extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
res.getString(R.string.screenrecord_name));
- String notificationTitle = mUseAudio
+ String notificationTitle = mAudioSource == ScreenRecordingAudioSource.NONE
? res.getString(R.string.screenrecord_ongoing_screen_and_audio)
: res.getString(R.string.screenrecord_ongoing_screen_only);
@@ -323,9 +241,10 @@
this, REQUEST_CODE, getStopIntent(this),
PendingIntent.FLAG_UPDATE_CURRENT))
.addExtras(extras);
- notificationManager.notify(NOTIFICATION_ID, mRecordingNotificationBuilder.build());
+ notificationManager.notify(NOTIFICATION_RECORDING_ID,
+ mRecordingNotificationBuilder.build());
Notification notification = mRecordingNotificationBuilder.build();
- startForeground(NOTIFICATION_ID, notification);
+ startForeground(NOTIFICATION_RECORDING_ID, notification);
}
private Notification createSaveNotification(Uri uri) {
@@ -392,47 +311,38 @@
private void stopRecording() {
setTapsVisible(mOriginalShowTaps);
- mMediaRecorder.stop();
- mMediaRecorder.release();
- mMediaRecorder = null;
- mMediaProjection.stop();
- mMediaProjection = null;
- mInputSurface.release();
- mVirtualDisplay.release();
- stopSelf();
+ mRecorder.end();
mController.updateState(false);
}
private void saveRecording(NotificationManager notificationManager) {
- String fileName = new SimpleDateFormat("'screen-'yyyyMMdd-HHmmss'.mp4'")
- .format(new Date());
+ Resources res = getApplicationContext().getResources();
+ String notificationTitle = mAudioSource == ScreenRecordingAudioSource.NONE
+ ? res.getString(R.string.screenrecord_ongoing_screen_only)
+ : res.getString(R.string.screenrecord_ongoing_screen_and_audio);
+ Notification.Builder builder = new Notification.Builder(getApplicationContext(), CHANNEL_ID)
+ .setContentTitle(notificationTitle)
+ .setContentText(
+ getResources().getString(R.string.screenrecord_background_processing_label))
+ .setSmallIcon(R.drawable.ic_screenrecord);
+ notificationManager.notify(NOTIFICATION_PROCESSING_ID, builder.build());
- ContentValues values = new ContentValues();
- values.put(MediaStore.Video.Media.DISPLAY_NAME, fileName);
- values.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4");
- values.put(MediaStore.Video.Media.DATE_ADDED, System.currentTimeMillis());
- values.put(MediaStore.Video.Media.DATE_TAKEN, System.currentTimeMillis());
-
- ContentResolver resolver = getContentResolver();
- Uri collectionUri = MediaStore.Video.Media.getContentUri(
- MediaStore.VOLUME_EXTERNAL_PRIMARY);
- Uri itemUri = resolver.insert(collectionUri, values);
-
- try {
- // Add to the mediastore
- OutputStream os = resolver.openOutputStream(itemUri, "w");
- Files.copy(mTempFile.toPath(), os);
- os.close();
-
- Notification notification = createSaveNotification(itemUri);
- notificationManager.notify(NOTIFICATION_ID, notification);
-
- mTempFile.delete();
- } catch (IOException e) {
- Log.e(TAG, "Error saving screen recording: " + e.getMessage());
- Toast.makeText(this, R.string.screenrecord_delete_error, Toast.LENGTH_LONG)
- .show();
- }
+ mLongExecutor.execute(() -> {
+ try {
+ Log.d(TAG, "saving recording");
+ Notification notification = createSaveNotification(mRecorder.save());
+ if (!mController.isRecording()) {
+ Log.d(TAG, "showing saved notification");
+ notificationManager.notify(NOTIFICATION_VIEW_ID, notification);
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Error saving screen recording: " + e.getMessage());
+ Toast.makeText(this, R.string.screenrecord_delete_error, Toast.LENGTH_LONG)
+ .show();
+ } finally {
+ notificationManager.cancel(NOTIFICATION_PROCESSING_ID);
+ }
+ });
}
private void setTapsVisible(boolean turnOn) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java
new file mode 100644
index 0000000..752f4fd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2020 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.systemui.screenrecord;
+
+import android.content.Context;
+import android.media.AudioAttributes;
+import android.media.AudioFormat;
+import android.media.AudioPlaybackCaptureConfiguration;
+import android.media.AudioRecord;
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaFormat;
+import android.media.MediaMuxer;
+import android.media.MediaRecorder;
+import android.media.projection.MediaProjection;
+import android.util.Log;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * Recording internal audio
+ */
+public class ScreenInternalAudioRecorder {
+ private static String TAG = "ScreenAudioRecorder";
+ private static final int TIMEOUT = 500;
+ private final Context mContext;
+ private AudioRecord mAudioRecord;
+ private AudioRecord mAudioRecordMic;
+ private Config mConfig = new Config();
+ private Thread mThread;
+ private MediaProjection mMediaProjection;
+ private MediaCodec mCodec;
+ private long mPresentationTime;
+ private long mTotalBytes;
+ private MediaMuxer mMuxer;
+ private String mOutFile;
+ private boolean mMic;
+
+ private int mTrackId = -1;
+
+ public ScreenInternalAudioRecorder(String outFile, Context context,
+ MediaProjection mp, boolean includeMicInput) throws IOException {
+ mMic = includeMicInput;
+ mOutFile = outFile;
+ mMuxer = new MediaMuxer(outFile, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
+ mContext = context;
+ mMediaProjection = mp;
+ Log.d(TAG, "creating audio file " + outFile);
+ setupSimple();
+ }
+ /**
+ * Audio recoding configuration
+ */
+ public static class Config {
+ public int channelOutMask = AudioFormat.CHANNEL_OUT_MONO;
+ public int channelInMask = AudioFormat.CHANNEL_IN_MONO;
+ public int encoding = AudioFormat.ENCODING_PCM_16BIT;
+ public int sampleRate = 44100;
+ public int bitRate = 196000;
+ public int bufferSizeBytes = 1 << 17;
+ public boolean privileged = true;
+ public boolean legacy_app_looback = false;
+
+ @Override
+ public String toString() {
+ return "channelMask=" + channelOutMask
+ + "\n encoding=" + encoding
+ + "\n sampleRate=" + sampleRate
+ + "\n bufferSize=" + bufferSizeBytes
+ + "\n privileged=" + privileged
+ + "\n legacy app looback=" + legacy_app_looback;
+ }
+
+ }
+
+ private void setupSimple() throws IOException {
+ int size = AudioRecord.getMinBufferSize(
+ mConfig.sampleRate, mConfig.channelInMask,
+ mConfig.encoding) * 2;
+
+ Log.d(TAG, "audio buffer size: " + size);
+
+ AudioFormat format = new AudioFormat.Builder()
+ .setEncoding(mConfig.encoding)
+ .setSampleRate(mConfig.sampleRate)
+ .setChannelMask(mConfig.channelOutMask)
+ .build();
+
+ AudioPlaybackCaptureConfiguration playbackConfig =
+ new AudioPlaybackCaptureConfiguration.Builder(mMediaProjection)
+ .addMatchingUsage(AudioAttributes.USAGE_MEDIA)
+ .addMatchingUsage(AudioAttributes.USAGE_UNKNOWN)
+ .addMatchingUsage(AudioAttributes.USAGE_GAME)
+ .build();
+
+ mAudioRecord = new AudioRecord.Builder()
+ .setAudioFormat(format)
+ .setAudioPlaybackCaptureConfig(playbackConfig)
+ .build();
+
+ if (mMic) {
+ mAudioRecordMic = new AudioRecord(MediaRecorder.AudioSource.VOICE_COMMUNICATION,
+ mConfig.sampleRate, AudioFormat.CHANNEL_IN_MONO, mConfig.encoding, size);
+ }
+
+ mCodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC);
+ MediaFormat medFormat = MediaFormat.createAudioFormat(
+ MediaFormat.MIMETYPE_AUDIO_AAC, mConfig.sampleRate, 1);
+ medFormat.setInteger(MediaFormat.KEY_AAC_PROFILE,
+ MediaCodecInfo.CodecProfileLevel.AACObjectLC);
+ medFormat.setInteger(MediaFormat.KEY_BIT_RATE, mConfig.bitRate);
+ medFormat.setInteger(MediaFormat.KEY_PCM_ENCODING, mConfig.encoding);
+ mCodec.configure(medFormat,
+ null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
+
+ mThread = new Thread(() -> {
+ short[] bufferInternal = null;
+ short[] bufferMic = null;
+ byte[] buffer = null;
+
+ if (mMic) {
+ bufferInternal = new short[size / 2];
+ bufferMic = new short[size / 2];
+ } else {
+ buffer = new byte[size];
+ }
+
+ while (true) {
+ int readBytes = 0;
+ int readShortsInternal = 0;
+ int readShortsMic = 0;
+ if (mMic) {
+ readShortsInternal = mAudioRecord.read(bufferInternal, 0,
+ bufferInternal.length);
+ readShortsMic = mAudioRecordMic.read(bufferMic, 0, bufferMic.length);
+ readBytes = Math.min(readShortsInternal, readShortsMic) * 2;
+ buffer = addAndConvertBuffers(bufferInternal, readShortsInternal, bufferMic,
+ readShortsMic);
+ } else {
+ readBytes = mAudioRecord.read(buffer, 0, buffer.length);
+ }
+
+ //exit the loop when at end of stream
+ if (readBytes < 0) {
+ Log.e(TAG, "read error " + readBytes +
+ ", shorts internal: " + readShortsInternal +
+ ", shorts mic: " + readShortsMic);
+ break;
+ }
+ encode(buffer, readBytes);
+ }
+ endStream();
+ });
+ }
+
+ private byte[] addAndConvertBuffers(short[] a1, int a1Limit, short[] a2, int a2Limit) {
+ int size = Math.max(a1Limit, a2Limit);
+ if (size < 0) return new byte[0];
+ byte[] buff = new byte[size * 2];
+ for (int i = 0; i < size; i++) {
+ int sum;
+ if (i > a1Limit) {
+ sum = a2[i];
+ } else if (i > a2Limit) {
+ sum = a1[i];
+ } else {
+ sum = (int) a1[i] + (int) a2[i];
+ }
+
+ if (sum > Short.MAX_VALUE) sum = Short.MAX_VALUE;
+ if (sum < Short.MIN_VALUE) sum = Short.MIN_VALUE;
+ int byteIndex = i * 2;
+ buff[byteIndex] = (byte) (sum & 0xff);
+ buff[byteIndex + 1] = (byte) ((sum >> 8) & 0xff);
+ }
+ return buff;
+ }
+
+ private void encode(byte[] buffer, int readBytes) {
+ int offset = 0;
+ while (readBytes > 0) {
+ int totalBytesRead = 0;
+ int bufferIndex = mCodec.dequeueInputBuffer(TIMEOUT);
+ if (bufferIndex < 0) {
+ writeOutput();
+ return;
+ }
+ ByteBuffer buff = mCodec.getInputBuffer(bufferIndex);
+ buff.clear();
+ int bufferSize = buff.capacity();
+ int bytesToRead = readBytes > bufferSize ? bufferSize : readBytes;
+ totalBytesRead += bytesToRead;
+ readBytes -= bytesToRead;
+ buff.put(buffer, offset, bytesToRead);
+ offset += bytesToRead;
+ mCodec.queueInputBuffer(bufferIndex, 0, bytesToRead, mPresentationTime, 0);
+ mTotalBytes += totalBytesRead;
+ mPresentationTime = 1000000L * (mTotalBytes / 2) / mConfig.sampleRate;
+
+ writeOutput();
+ }
+ }
+
+ private void endStream() {
+ int bufferIndex = mCodec.dequeueInputBuffer(TIMEOUT);
+ mCodec.queueInputBuffer(bufferIndex, 0, 0, mPresentationTime,
+ MediaCodec.BUFFER_FLAG_END_OF_STREAM);
+ writeOutput();
+ }
+
+ private void writeOutput() {
+ while (true) {
+ MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
+ int bufferIndex = mCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT);
+ if (bufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+ mTrackId = mMuxer.addTrack(mCodec.getOutputFormat());
+ mMuxer.start();
+ continue;
+ }
+ if (bufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
+ break;
+ }
+ if (mTrackId < 0) return;
+ ByteBuffer buff = mCodec.getOutputBuffer(bufferIndex);
+
+ if (!((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0
+ && bufferInfo.size != 0)) {
+ mMuxer.writeSampleData(mTrackId, buff, bufferInfo);
+ }
+ mCodec.releaseOutputBuffer(bufferIndex, false);
+ }
+ }
+
+ /**
+ * start recording
+ */
+ public void start() {
+ if (mThread != null) {
+ Log.e(TAG, "a recording is being done in parallel or stop is not called");
+ }
+ mAudioRecord.startRecording();
+ if (mMic) mAudioRecordMic.startRecording();
+ Log.d(TAG, "channel count " + mAudioRecord.getChannelCount());
+ mCodec.start();
+ if (mAudioRecord.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) {
+ Log.e(TAG, "Error starting audio recording");
+ return;
+ }
+ mThread.start();
+ }
+
+ /**
+ * end recording
+ */
+ public void end() {
+ mAudioRecord.stop();
+ if (mMic) {
+ mAudioRecordMic.stop();
+ }
+ mAudioRecord.release();
+ if (mMic) {
+ mAudioRecordMic.release();
+ }
+ try {
+ mThread.join();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ mCodec.stop();
+ mCodec.release();
+ mMuxer.stop();
+ mMuxer.release();
+ mThread = null;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
new file mode 100644
index 0000000..c967648
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2020 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.systemui.screenrecord;
+
+import static android.content.Context.MEDIA_PROJECTION_SERVICE;
+
+import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.INTERNAL;
+import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.MIC;
+import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.MIC_AND_INTERNAL;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.media.MediaMuxer;
+import android.media.MediaRecorder;
+import android.media.projection.IMediaProjection;
+import android.media.projection.IMediaProjectionManager;
+import android.media.projection.MediaProjection;
+import android.media.projection.MediaProjectionManager;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.MediaStore;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.Surface;
+import android.view.WindowManager;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * Recording screen and mic/internal audio
+ */
+public class ScreenMediaRecorder {
+ private static final int TOTAL_NUM_TRACKS = 1;
+ private static final int VIDEO_BIT_RATE = 10000000;
+ private static final int VIDEO_FRAME_RATE = 30;
+ private static final int AUDIO_BIT_RATE = 16;
+ private static final int AUDIO_SAMPLE_RATE = 44100;
+ private static final int MAX_DURATION_MS = 60 * 60 * 1000;
+ private static final long MAX_FILESIZE_BYTES = 5000000000L;
+ private static final String TAG = "ScreenMediaRecorder";
+
+
+ private File mTempVideoFile;
+ private File mTempAudioFile;
+ private MediaProjection mMediaProjection;
+ private Surface mInputSurface;
+ private VirtualDisplay mVirtualDisplay;
+ private MediaRecorder mMediaRecorder;
+ private int mUser;
+ private ScreenRecordingMuxer mMuxer;
+ private ScreenInternalAudioRecorder mAudio;
+ private ScreenRecordingAudioSource mAudioSource;
+
+ private Context mContext;
+ MediaRecorder.OnInfoListener mListener;
+
+ public ScreenMediaRecorder(Context context,
+ int user, ScreenRecordingAudioSource audioSource,
+ MediaRecorder.OnInfoListener listener) {
+ mContext = context;
+ mUser = user;
+ mListener = listener;
+ mAudioSource = audioSource;
+ }
+
+ private void prepare() throws IOException, RemoteException {
+ //Setup media projection
+ IBinder b = ServiceManager.getService(MEDIA_PROJECTION_SERVICE);
+ IMediaProjectionManager mediaService =
+ IMediaProjectionManager.Stub.asInterface(b);
+ IMediaProjection proj = null;
+ proj = mediaService.createProjection(mUser, mContext.getPackageName(),
+ MediaProjectionManager.TYPE_SCREEN_CAPTURE, false);
+ IBinder projection = proj.asBinder();
+ mMediaProjection = new MediaProjection(mContext,
+ IMediaProjection.Stub.asInterface(projection));
+
+ File cacheDir = mContext.getCacheDir();
+ cacheDir.mkdirs();
+ mTempVideoFile = File.createTempFile("temp", ".mp4", cacheDir);
+
+ // Set up media recorder
+ mMediaRecorder = new MediaRecorder();
+
+ // Set up audio source
+ if (mAudioSource == MIC) {
+ mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
+ }
+ mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
+
+ mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
+
+
+ // Set up video
+ DisplayMetrics metrics = new DisplayMetrics();
+ WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+ wm.getDefaultDisplay().getRealMetrics(metrics);
+ int screenWidth = metrics.widthPixels;
+ int screenHeight = metrics.heightPixels;
+ mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
+ mMediaRecorder.setVideoSize(screenWidth, screenHeight);
+ mMediaRecorder.setVideoFrameRate(VIDEO_FRAME_RATE);
+ mMediaRecorder.setVideoEncodingBitRate(VIDEO_BIT_RATE);
+ mMediaRecorder.setMaxDuration(MAX_DURATION_MS);
+ mMediaRecorder.setMaxFileSize(MAX_FILESIZE_BYTES);
+
+ // Set up audio
+ if (mAudioSource == MIC) {
+ mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.HE_AAC);
+ mMediaRecorder.setAudioChannels(TOTAL_NUM_TRACKS);
+ mMediaRecorder.setAudioEncodingBitRate(AUDIO_BIT_RATE);
+ mMediaRecorder.setAudioSamplingRate(AUDIO_SAMPLE_RATE);
+ }
+
+ mMediaRecorder.setOutputFile(mTempVideoFile);
+ mMediaRecorder.prepare();
+ // Create surface
+ mInputSurface = mMediaRecorder.getSurface();
+ mVirtualDisplay = mMediaProjection.createVirtualDisplay(
+ "Recording Display",
+ screenWidth,
+ screenHeight,
+ metrics.densityDpi,
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
+ mInputSurface,
+ null,
+ null);
+
+ mMediaRecorder.setOnInfoListener(mListener);
+ if (mAudioSource == INTERNAL ||
+ mAudioSource == MIC_AND_INTERNAL) {
+ mTempAudioFile = File.createTempFile("temp", ".aac",
+ mContext.getCacheDir());
+ mAudio = new ScreenInternalAudioRecorder(mTempAudioFile.getAbsolutePath(), mContext,
+ mMediaProjection, mAudioSource == MIC_AND_INTERNAL);
+ }
+
+ }
+
+ /**
+ * Start screen recording
+ */
+ void start() throws IOException, RemoteException {
+ Log.d(TAG, "start recording");
+ prepare();
+ mMediaRecorder.start();
+ recordInternalAudio();
+ }
+
+ /**
+ * End screen recording
+ */
+ void end() {
+ mMediaRecorder.stop();
+ mMediaProjection.stop();
+ mMediaRecorder.release();
+ mMediaRecorder = null;
+ mMediaProjection = null;
+ mInputSurface.release();
+ mVirtualDisplay.release();
+ stopInternalAudioRecording();
+
+ Log.d(TAG, "end recording");
+ }
+
+ private void stopInternalAudioRecording() {
+ if (mAudioSource == INTERNAL || mAudioSource == MIC_AND_INTERNAL) {
+ mAudio.end();
+ mAudio = null;
+ }
+ }
+
+ private void recordInternalAudio() {
+ if (mAudioSource == INTERNAL || mAudioSource == MIC_AND_INTERNAL) {
+ mAudio.start();
+ }
+ }
+
+ /**
+ * Store recorded video
+ */
+ Uri save() throws IOException {
+ String fileName = new SimpleDateFormat("'screen-'yyyyMMdd-HHmmss'.mp4'")
+ .format(new Date());
+
+ ContentValues values = new ContentValues();
+ values.put(MediaStore.Video.Media.DISPLAY_NAME, fileName);
+ values.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4");
+ values.put(MediaStore.Video.Media.DATE_ADDED, System.currentTimeMillis());
+ values.put(MediaStore.Video.Media.DATE_TAKEN, System.currentTimeMillis());
+
+ ContentResolver resolver = mContext.getContentResolver();
+ Uri collectionUri = MediaStore.Video.Media.getContentUri(
+ MediaStore.VOLUME_EXTERNAL_PRIMARY);
+ Uri itemUri = resolver.insert(collectionUri, values);
+
+ Log.d(TAG, itemUri.toString());
+ if (mAudioSource == MIC_AND_INTERNAL || mAudioSource == INTERNAL) {
+ try {
+ Log.d(TAG, "muxing recording");
+ File file = File.createTempFile("temp", ".mp4",
+ mContext.getCacheDir());
+ mMuxer = new ScreenRecordingMuxer(MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4,
+ file.getAbsolutePath(),
+ mTempVideoFile.getAbsolutePath(),
+ mTempAudioFile.getAbsolutePath());
+ mMuxer.mux();
+ mTempVideoFile.delete();
+ mTempVideoFile = file;
+ } catch (IOException e) {
+ Log.e(TAG, "muxing recording " + e.getMessage());
+ e.printStackTrace();
+ }
+ }
+
+ // Add to the mediastore
+ OutputStream os = resolver.openOutputStream(itemUri, "w");
+ Files.copy(mTempVideoFile.toPath(), os);
+ os.close();
+ mTempVideoFile.delete();
+ if (mTempAudioFile != null) mTempAudioFile.delete();
+ return itemUri;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
index 26973d0..c2473280 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
@@ -16,17 +16,30 @@
package com.android.systemui.screenrecord;
+import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.INTERNAL;
+import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.MIC;
+import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.MIC_AND_INTERNAL;
+import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.NONE;
+
import android.app.Activity;
import android.app.PendingIntent;
import android.os.Bundle;
+import android.util.Log;
import android.view.Gravity;
+import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
import android.widget.Button;
+import android.widget.Spinner;
import android.widget.Switch;
import com.android.systemui.R;
+import java.util.ArrayList;
+import java.util.List;
+
import javax.inject.Inject;
/**
@@ -35,10 +48,15 @@
public class ScreenRecordDialog extends Activity {
private static final long DELAY_MS = 3000;
private static final long INTERVAL_MS = 1000;
+ private static final String TAG = "ScreenRecordDialog";
private final RecordingController mController;
- private Switch mAudioSwitch;
private Switch mTapsSwitch;
+ private Switch mAudioSwitch;
+ private Spinner mOptions;
+ private List<ScreenRecordingAudioSource> mModes;
+ private int mSelected;
+
@Inject
public ScreenRecordDialog(RecordingController controller) {
@@ -68,17 +86,32 @@
finish();
});
+ mModes = new ArrayList<>();
+ mModes.add(INTERNAL);
+ mModes.add(MIC);
+ mModes.add(MIC_AND_INTERNAL);
+
mAudioSwitch = findViewById(R.id.screenrecord_audio_switch);
mTapsSwitch = findViewById(R.id.screenrecord_taps_switch);
+ mOptions = findViewById(R.id.screen_recording_options);
+ ArrayAdapter a = new ScreenRecordingAdapter(getApplicationContext(),
+ android.R.layout.simple_spinner_dropdown_item,
+ mModes);
+ a.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ mOptions.setAdapter(a);
+
}
private void requestScreenCapture() {
- boolean useAudio = mAudioSwitch.isChecked();
boolean showTaps = mTapsSwitch.isChecked();
+ ScreenRecordingAudioSource audioMode = mAudioSwitch.isChecked()
+ ? (ScreenRecordingAudioSource) mOptions.getSelectedItem()
+ : NONE;
PendingIntent startIntent = PendingIntent.getForegroundService(this,
RecordingService.REQUEST_CODE,
RecordingService.getStartIntent(
- ScreenRecordDialog.this, RESULT_OK, null, useAudio, showTaps),
+ ScreenRecordDialog.this, RESULT_OK,
+ audioMode.ordinal(), showTaps),
PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent stopIntent = PendingIntent.getService(this,
RecordingService.REQUEST_CODE,
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingAdapter.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingAdapter.java
new file mode 100644
index 0000000..2e0e746
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingAdapter.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2020 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.systemui.screenrecord;
+
+import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.INTERNAL;
+import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.MIC;
+import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.MIC_AND_INTERNAL;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+
+import java.util.List;
+
+/**
+ * Screen recording view adapter
+ */
+public class ScreenRecordingAdapter extends ArrayAdapter<ScreenRecordingAudioSource> {
+ private LinearLayout mSelectedMic;
+ private LinearLayout mSelectedInternal;
+ private LinearLayout mSelectedMicAndInternal;
+ private LinearLayout mMicOption;
+ private LinearLayout mMicAndInternalOption;
+ private LinearLayout mInternalOption;
+
+ public ScreenRecordingAdapter(Context context, int resource,
+ List<ScreenRecordingAudioSource> objects) {
+ super(context, resource, objects);
+ initViews();
+ }
+
+ private void initViews() {
+ mSelectedInternal = getSelected(R.string.screenrecord_device_audio_label);
+ mSelectedMic = getSelected(R.string.screenrecord_mic_label);
+ mSelectedMicAndInternal = getSelected(R.string.screenrecord_device_audio_and_mic_label);
+
+ mMicOption = getOption(R.string.screenrecord_mic_label, Resources.ID_NULL);
+ mMicOption.removeViewAt(1);
+
+ mMicAndInternalOption = getOption(
+ R.string.screenrecord_device_audio_and_mic_label, Resources.ID_NULL);
+ mMicAndInternalOption.removeViewAt(1);
+
+ mInternalOption = getOption(R.string.screenrecord_device_audio_label,
+ R.string.screenrecord_device_audio_description);
+ }
+
+ private LinearLayout getOption(int label, int description) {
+ LayoutInflater inflater = (LayoutInflater) getContext()
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ LinearLayout layout = (LinearLayout) inflater
+ .inflate(R.layout.screen_record_dialog_audio_source, null, false);
+ ((TextView) layout.findViewById(R.id.screen_recording_dialog_source_text))
+ .setText(label);
+ if (description != Resources.ID_NULL)
+ ((TextView) layout.findViewById(R.id.screen_recording_dialog_source_description))
+ .setText(description);
+ return layout;
+ }
+
+ private LinearLayout getSelected(int label) {
+ LayoutInflater inflater = LayoutInflater.from(getContext());
+ LinearLayout layout = (LinearLayout) inflater
+ .inflate(R.layout.screen_record_dialog_audio_source_selected, null, false);
+ ((TextView) layout.findViewById(R.id.screen_recording_dialog_source_text))
+ .setText(label);
+ return layout;
+ }
+
+ private void setDescription(LinearLayout layout, int description) {
+ if (description != Resources.ID_NULL) {
+ ((TextView) layout.getChildAt(1)).setText(description);
+ }
+ }
+
+ @Override
+ public View getDropDownView(int position, View convertView, ViewGroup parent) {
+ switch (getItem(position)) {
+ case INTERNAL:
+ return mInternalOption;
+ case MIC_AND_INTERNAL:
+ return mMicAndInternalOption;
+ case MIC:
+ return mMicOption;
+ default:
+ return super.getDropDownView(position, convertView, parent);
+ }
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ switch (getItem(position)) {
+ case INTERNAL:
+ return mSelectedInternal;
+ case MIC_AND_INTERNAL:
+ return mSelectedMicAndInternal;
+ case MIC:
+ return mSelectedMic;
+ default:
+ return super.getView(position, convertView, parent);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingAudioSource.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingAudioSource.java
new file mode 100644
index 0000000..ee11865
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingAudioSource.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 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.systemui.screenrecord;
+
+/**
+ * Audio sources
+ */
+public enum ScreenRecordingAudioSource {
+ NONE,
+ INTERNAL,
+ MIC,
+ MIC_AND_INTERNAL;
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingMuxer.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingMuxer.java
new file mode 100644
index 0000000..7ffcfd4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingMuxer.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2020 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.systemui.screenrecord;
+
+import android.media.MediaCodec;
+import android.media.MediaExtractor;
+import android.media.MediaMuxer;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Pair;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+/**
+ * Mixing audio and video tracks
+ */
+public class ScreenRecordingMuxer {
+ // size of a memory page for cache coherency
+ private static final int BUFFER_SIZE = 1024 * 4096;
+ private String[] mFiles;
+ private String mOutFile;
+ private int mFormat;
+ private ArrayMap<Pair<MediaExtractor, Integer>, Integer> mExtractorIndexToMuxerIndex
+ = new ArrayMap<>();
+ private ArrayList<MediaExtractor> mExtractors = new ArrayList<>();
+
+ private static String TAG = "ScreenRecordingMuxer";
+ public ScreenRecordingMuxer(@MediaMuxer.Format int format, String outfileName,
+ String... inputFileNames) {
+ mFiles = inputFileNames;
+ mOutFile = outfileName;
+ mFormat = format;
+ Log.d(TAG, "out: " + mOutFile + " , in: " + mFiles[0]);
+ }
+
+ /**
+ * RUN IN THE BACKGROUND THREAD!
+ */
+ public void mux() throws IOException {
+ MediaMuxer muxer = null;
+ muxer = new MediaMuxer(mOutFile, mFormat);
+ // Add extractors
+ for (String file: mFiles) {
+ MediaExtractor extractor = new MediaExtractor();
+ try {
+ extractor.setDataSource(file);
+ } catch (IOException e) {
+ Log.e(TAG, "error creating extractor: " + file);
+ e.printStackTrace();
+ continue;
+ }
+ Log.d(TAG, file + " track count: " + extractor.getTrackCount());
+ mExtractors.add(extractor);
+ for (int i = 0; i < extractor.getTrackCount(); i++) {
+ int muxId = muxer.addTrack(extractor.getTrackFormat(i));
+ Log.d(TAG, "created extractor format" + extractor.getTrackFormat(i).toString());
+ mExtractorIndexToMuxerIndex.put(Pair.create(extractor, i), muxId);
+ }
+ }
+
+ muxer.start();
+ for (Pair<MediaExtractor, Integer> pair: mExtractorIndexToMuxerIndex.keySet()) {
+ MediaExtractor extractor = pair.first;
+ extractor.selectTrack(pair.second);
+ int muxId = mExtractorIndexToMuxerIndex.get(pair);
+ Log.d(TAG, "track format: " + extractor.getTrackFormat(pair.second));
+ extractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
+ ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
+ MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+ int offset;
+ while (true) {
+ offset = buffer.arrayOffset();
+ info.size = extractor.readSampleData(buffer, offset);
+ if (info.size < 0) break;
+ info.presentationTimeUs = extractor.getSampleTime();
+ info.flags = extractor.getSampleFlags();
+ muxer.writeSampleData(muxId, buffer, info);
+ extractor.advance();
+ }
+ }
+
+ for (MediaExtractor extractor: mExtractors) {
+ extractor.release();
+ }
+ muxer.stop();
+ muxer.release();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 2419515..96d6ecb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -263,7 +263,7 @@
default void showAuthenticationDialog(Bundle bundle,
IBiometricServiceReceiverInternal receiver, int biometricModality,
boolean requireConfirmation, int userId, String opPackageName,
- long operationId) { }
+ long operationId, int sysUiSessionId) { }
default void onBiometricAuthenticated() { }
default void onBiometricHelp(String message) { }
default void onBiometricError(int modality, int error, int vendorCode) { }
@@ -782,7 +782,7 @@
@Override
public void showAuthenticationDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
int biometricModality, boolean requireConfirmation, int userId, String opPackageName,
- long operationId) {
+ long operationId, int sysUiSessionId) {
synchronized (mLock) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = bundle;
@@ -792,6 +792,7 @@
args.argi2 = userId;
args.arg4 = opPackageName;
args.arg5 = operationId;
+ args.argi3 = sysUiSessionId;
mHandler.obtainMessage(MSG_BIOMETRIC_SHOW, args)
.sendToTarget();
}
@@ -1169,7 +1170,8 @@
(boolean) someArgs.arg3 /* requireConfirmation */,
someArgs.argi2 /* userId */,
(String) someArgs.arg4 /* opPackageName */,
- (long) someArgs.arg5 /* operationId */);
+ (long) someArgs.arg5 /* operationId */,
+ someArgs.argi3 /* sysUiSessionId */);
}
someArgs.recycle();
break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 8fcc67a..e7f2618 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -184,8 +184,11 @@
mLowPriorityInflationHelper.recheckLowPriorityViewAndInflate(ent, ent.getRow());
boolean isChildInGroup = mGroupManager.isChildInGroupWithSummary(ent.getSbn());
- boolean groupChangesAllowed = mVisualStabilityManager.areGroupChangesAllowed()
- || !ent.hasFinishedInitialization();
+ boolean groupChangesAllowed =
+ mVisualStabilityManager.areGroupChangesAllowed() // user isn't looking at notifs
+ || !ent.hasFinishedInitialization() // notif recently added
+ || !mListContainer.containsView(ent.getRow()); // notif recently unfiltered
+
NotificationEntry parent = mGroupManager.getGroupSummary(ent.getSbn());
if (!groupChangesAllowed) {
// We don't to change groups while the user is looking at them
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
index 88f148b..02c98ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
@@ -121,14 +121,14 @@
}
override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
- return maybeStartExpansion(event)
+ return canHandleMotionEvent() && startExpansion(event)
}
- private fun maybeStartExpansion(event: MotionEvent): Boolean {
- if (!wakeUpCoordinator.canShowPulsingHuns || qsExpanded ||
- bouncerShowing) {
- return false
- }
+ private fun canHandleMotionEvent(): Boolean {
+ return !wakeUpCoordinator.canShowPulsingHuns || qsExpanded || bouncerShowing
+ }
+
+ private fun startExpansion(event: MotionEvent): Boolean {
if (velocityTracker == null) {
velocityTracker = VelocityTracker.obtain()
}
@@ -177,9 +177,14 @@
}
override fun onTouchEvent(event: MotionEvent): Boolean {
- if (!isExpanding) {
- return maybeStartExpansion(event)
+ if (!canHandleMotionEvent()) {
+ return false
}
+
+ if (!isExpanding || event.actionMasked == MotionEvent.ACTION_DOWN) {
+ return startExpansion(event)
+ }
+
velocityTracker!!.addMovement(event)
val y = event.y
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
index 55a20fa..040dbe3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
@@ -288,7 +288,7 @@
mContext,
0,
new Intent(Intent.ACTION_VIEW).setData(Uri.parse(helpUrl)),
- 0,
+ PendingIntent.FLAG_IMMUTABLE,
null,
user)
: null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index f1cb783..d251777 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -440,6 +440,10 @@
mLogger.logLifetimeExtended(key, extender.getClass().getName(), "pending");
}
}
+ if (!lifetimeExtended) {
+ // At this point, we are guaranteed the notification will be removed
+ mAllNotifications.remove(pendingEntry);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
index 9738bcc..c78370e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
@@ -27,20 +27,17 @@
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
-import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_IMPORTANT_PERSON
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON
-import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_PERSON
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_HEADS_UP
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_PEOPLE
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT
-
+import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.PriorityBucket
import com.android.systemui.statusbar.phone.NotificationGroupManager
import com.android.systemui.statusbar.policy.HeadsUpManager
import dagger.Lazy
-import java.util.Objects;
+import java.util.Objects
import javax.inject.Inject
-import kotlin.Comparator
private const val TAG = "NotifRankingManager"
@@ -140,33 +137,36 @@
.filterNot(notifFilter::shouldFilterOut)
.sortedWith(rankingComparator)
.toList()
- for (entry in filtered) {
- assignBucketForEntry(entry)
- }
+ assignBuckets(filtered)
return filtered
}
- private fun assignBucketForEntry(entry: NotificationEntry) {
+ private fun assignBuckets(entries: List<NotificationEntry>) {
+ entries.forEach { it.bucket = getBucketForEntry(it) }
+ if (!usePeopleFiltering) {
+ // If we don't have a Conversation section, just assign buckets normally based on the
+ // content.
+ return
+ }
+ // If HUNs are not continuous with the top section, break out into a new Incoming section.
+ entries.asReversed().asSequence().zipWithNext().forEach { (next, entry) ->
+ if (entry.isRowHeadsUp && entry.bucket > next.bucket) {
+ entry.bucket = BUCKET_HEADS_UP
+ }
+ }
+ }
+
+ @PriorityBucket
+ private fun getBucketForEntry(entry: NotificationEntry): Int {
val isHeadsUp = entry.isRowHeadsUp
val isMedia = isImportantMedia(entry)
val isSystemMax = entry.isSystemMax()
- setBucket(entry, isHeadsUp, isMedia, isSystemMax)
- }
-
- private fun setBucket(
- entry: NotificationEntry,
- isHeadsUp: Boolean,
- isMedia: Boolean,
- isSystemMax: Boolean
- ) {
- if (usePeopleFiltering && isHeadsUp) {
- entry.bucket = BUCKET_HEADS_UP
- } else if (usePeopleFiltering && entry.getPeopleNotificationType() != TYPE_NON_PERSON) {
- entry.bucket = BUCKET_PEOPLE
- } else if (isHeadsUp || isMedia || isSystemMax || entry.isHighPriority()) {
- entry.bucket = BUCKET_ALERTING
- } else {
- entry.bucket = BUCKET_SILENT
+ return when {
+ usePeopleFiltering && entry.getPeopleNotificationType() != TYPE_NON_PERSON ->
+ BUCKET_PEOPLE
+ isHeadsUp || isMedia || isSystemMax || entry.isHighPriority() ->
+ BUCKET_ALERTING
+ else -> BUCKET_SILENT
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
index d02037c..6eec1ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
@@ -109,6 +109,7 @@
@Nullable private View.OnClickListener mOnClearGentleNotifsClickListener;
private SectionHeaderView mAlertingHeader;
+ private SectionHeaderView mIncomingHeader;
private PeopleHubView mPeopleHubView;
private boolean mPeopleHubVisible = false;
@@ -199,6 +200,11 @@
mPeopleHubSubscription = mPeopleHubViewAdapter.bindView(mPeopleHubViewBoundary);
}
+ mIncomingHeader = reinflateView(
+ mIncomingHeader, layoutInflater, R.layout.status_bar_notification_section_header);
+ mIncomingHeader.setHeaderText(R.string.notification_section_header_incoming);
+ mIncomingHeader.setOnHeaderClickListener(this::onGentleHeaderClick);
+
if (mMediaControlsView != null) {
mKeyguardMediaPlayer.unbindView();
}
@@ -218,6 +224,7 @@
|| view == mMediaControlsView
|| view == mPeopleHubView
|| view == mAlertingHeader
+ || view == mIncomingHeader
|| !Objects.equals(getBucket(view), getBucket(previous));
}
@@ -229,6 +236,8 @@
private Integer getBucket(View view) {
if (view == mGentleHeader) {
return BUCKET_SILENT;
+ } else if (view == mIncomingHeader) {
+ return BUCKET_HEADS_UP;
} else if (view == mMediaControlsView) {
return BUCKET_MEDIA_CONTROLS;
} else if (view == mPeopleHubView) {
@@ -267,6 +276,8 @@
// Currently, just putting media controls in the front and incrementing the position based
// on the number of heads-up notifs.
int mediaControlsTarget = isKeyguard && usingMediaControls ? 0 : -1;
+ int currentIncomingHeaderIdx = -1;
+ int incomingHeaderTarget = -1;
int currentPeopleHeaderIdx = -1;
int peopleHeaderTarget = -1;
int currentAlertingHeaderIdx = -1;
@@ -281,6 +292,10 @@
View child = mParent.getChildAt(i);
// Track the existing positions of the headers
+ if (child == mIncomingHeader) {
+ currentIncomingHeaderIdx = i;
+ continue;
+ }
if (child == mMediaControlsView) {
currentMediaControlsIdx = i;
continue;
@@ -306,6 +321,26 @@
// Once we enter a new section, calculate the target position for the header.
switch (row.getEntry().getBucket()) {
case BUCKET_HEADS_UP:
+ if (showHeaders && incomingHeaderTarget == -1) {
+ incomingHeaderTarget = i;
+ // Offset the target if there are other headers before this that will be
+ // moved.
+ if (currentIncomingHeaderIdx != -1) {
+ incomingHeaderTarget--;
+ }
+ if (currentMediaControlsIdx != -1) {
+ incomingHeaderTarget--;
+ }
+ if (currentPeopleHeaderIdx != -1) {
+ incomingHeaderTarget--;
+ }
+ if (currentAlertingHeaderIdx != -1) {
+ incomingHeaderTarget--;
+ }
+ if (currentGentleHeaderIdx != -1) {
+ incomingHeaderTarget--;
+ }
+ }
if (mediaControlsTarget != -1) {
mediaControlsTarget++;
}
@@ -378,8 +413,8 @@
alertingHeaderTarget, mAlertingHeader, currentAlertingHeaderIdx);
adjustHeaderVisibilityAndPosition(
peopleHeaderTarget, mPeopleHubView, currentPeopleHeaderIdx);
- adjustViewPosition(
- mediaControlsTarget, mMediaControlsView, currentMediaControlsIdx);
+ adjustViewPosition(mediaControlsTarget, mMediaControlsView, currentMediaControlsIdx);
+ adjustViewPosition(incomingHeaderTarget, mIncomingHeader, currentIncomingHeaderIdx);
// Update headers to reflect state of section contents
mGentleHeader.setAreThereDismissableGentleNotifs(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
index fc6a028..567ddb6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
@@ -321,7 +321,8 @@
|| state.mPanelVisible || state.mKeyguardFadingAway || state.mBouncerShowing
|| state.mHeadsUpShowing
|| state.mScrimsVisibility != ScrimController.TRANSPARENT)
- || state.mBackgroundBlurRadius > 0;
+ || state.mBackgroundBlurRadius > 0
+ || state.mLaunchingActivity;
}
private void applyFitsSystemWindows(State state) {
@@ -485,6 +486,11 @@
apply(mCurrentState);
}
+ void setLaunchingActivity(boolean launching) {
+ mCurrentState.mLaunchingActivity = launching;
+ apply(mCurrentState);
+ }
+
public void setScrimsVisibility(int scrimsVisibility) {
mCurrentState.mScrimsVisibility = scrimsVisibility;
apply(mCurrentState);
@@ -645,6 +651,7 @@
boolean mForceCollapsed;
boolean mForceDozeBrightness;
boolean mForceUserActivity;
+ boolean mLaunchingActivity;
boolean mBackdropShowing;
boolean mWallpaperSupportsAmbientMode;
boolean mNotTouchable;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
index 596a607..0d25898 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
@@ -93,6 +93,7 @@
private PhoneStatusBarView mStatusBarView;
private PhoneStatusBarTransitions mBarTransitions;
private StatusBar mService;
+ private NotificationShadeWindowController mNotificationShadeWindowController;
private DragDownHelper mDragDownHelper;
private boolean mDoubleTapEnabled;
private boolean mSingleTapEnabled;
@@ -430,10 +431,14 @@
public void setExpandAnimationPending(boolean pending) {
mExpandAnimationPending = pending;
+ mNotificationShadeWindowController
+ .setLaunchingActivity(mExpandAnimationPending | mExpandAnimationRunning);
}
public void setExpandAnimationRunning(boolean running) {
mExpandAnimationRunning = running;
+ mNotificationShadeWindowController
+ .setLaunchingActivity(mExpandAnimationPending | mExpandAnimationRunning);
}
public void cancelExpandHelper() {
@@ -456,8 +461,9 @@
}
}
- public void setService(StatusBar statusBar) {
+ public void setService(StatusBar statusBar, NotificationShadeWindowController controller) {
mService = statusBar;
+ mNotificationShadeWindowController = controller;
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index c590139..33997b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -440,24 +440,7 @@
if (!(relevantState && mExpansionAffectsAlpha)) {
return;
}
- applyExpansionToAlpha();
- if (mUpdatePending) {
- return;
- }
- setOrAdaptCurrentAnimation(mScrimBehind);
- setOrAdaptCurrentAnimation(mScrimInFront);
- setOrAdaptCurrentAnimation(mScrimForBubble);
- dispatchScrimState(mScrimBehind.getViewAlpha());
-
- // Reset wallpaper timeout if it's already timeout like expanding panel while PULSING
- // and docking.
- if (mWallpaperVisibilityTimedOut) {
- mWallpaperVisibilityTimedOut = false;
- DejankUtils.postAfterTraversal(() -> {
- mTimeTicker.schedule(mDozeParameters.getWallpaperAodDuration(),
- AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
- });
- }
+ applyAndDispatchExpansion();
}
}
@@ -513,6 +496,27 @@
}
}
+ private void applyAndDispatchExpansion() {
+ applyExpansionToAlpha();
+ if (mUpdatePending) {
+ return;
+ }
+ setOrAdaptCurrentAnimation(mScrimBehind);
+ setOrAdaptCurrentAnimation(mScrimInFront);
+ setOrAdaptCurrentAnimation(mScrimForBubble);
+ dispatchScrimState(mScrimBehind.getViewAlpha());
+
+ // Reset wallpaper timeout if it's already timeout like expanding panel while PULSING
+ // and docking.
+ if (mWallpaperVisibilityTimedOut) {
+ mWallpaperVisibilityTimedOut = false;
+ DejankUtils.postAfterTraversal(() -> {
+ mTimeTicker.schedule(mDozeParameters.getWallpaperAodDuration(),
+ AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
+ });
+ }
+ }
+
/**
* Sets the given drawable as the background of the scrim that shows up behind the
* notifications.
@@ -1006,6 +1010,9 @@
public void setExpansionAffectsAlpha(boolean expansionAffectsAlpha) {
mExpansionAffectsAlpha = expansionAffectsAlpha;
+ if (expansionAffectsAlpha) {
+ applyAndDispatchExpansion();
+ }
}
public void setKeyguardOccluded(boolean keyguardOccluded) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index bbf83bc..dd54a3d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1001,7 +1001,7 @@
updateTheme();
inflateStatusBarWindow();
- mNotificationShadeWindowViewController.setService(this);
+ mNotificationShadeWindowViewController.setService(this, mNotificationShadeWindowController);
mNotificationShadeWindowView.setOnTouchListener(getStatusBarWindowTouchListener());
// TODO: Deal with the ugliness that comes from having some of the statusbar broken out
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 1df617d..44ece35 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -285,18 +285,7 @@
mLogger.logHandleClickAfterKeyguardDismissed(sbn.getKey());
// TODO: Some of this code may be able to move to NotificationEntryManager.
- if (mHeadsUpManager != null && mHeadsUpManager.isAlerting(sbn.getKey())) {
- // Release the HUN notification to the shade.
-
- if (mPresenter.isPresenterFullyCollapsed()) {
- HeadsUpUtil.setIsClickedHeadsUpNotification(row, true);
- }
- //
- // In most cases, when FLAG_AUTO_CANCEL is set, the notification will
- // become canceled shortly by NoMan, but we can't assume that.
- mHeadsUpManager.removeNotification(sbn.getKey(),
- true /* releaseImmediately */);
- }
+ removeHUN(row);
NotificationEntry parentToCancel = null;
if (shouldAutoCancel(sbn) && mGroupManager.isOnlyChildInGroup(sbn)) {
NotificationEntry summarySbn = mGroupManager.getLogicalGroupSummary(sbn);
@@ -461,8 +450,12 @@
row, mStatusBar.isOccluded())),
new UserHandle(UserHandle.getUserId(appUid)));
mActivityLaunchAnimator.setLaunchResult(launchResult, true /* isActivityIntent */);
+
+ // Putting it back on the main thread, since we're touching views
+ mMainThreadHandler.post(() -> {
+ removeHUN(row);
+ });
if (shouldCollapse()) {
- // Putting it back on the main thread, since we're touching views
mMainThreadHandler.post(() -> mCommandQueue.animateCollapsePanels(
CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */));
}
@@ -494,6 +487,20 @@
}, null, false /* afterKeyguardGone */);
}
+ private void removeHUN(ExpandableNotificationRow row) {
+ String key = row.getEntry().getSbn().getKey();
+ if (mHeadsUpManager != null && mHeadsUpManager.isAlerting(key)) {
+ // Release the HUN notification to the shade.
+ if (mPresenter.isPresenterFullyCollapsed()) {
+ HeadsUpUtil.setIsClickedHeadsUpNotification(row, true);
+ }
+
+ // In most cases, when FLAG_AUTO_CANCEL is set, the notification will
+ // become canceled shortly by NoMan, but we can't assume that.
+ mHeadsUpManager.removeNotification(key, true /* releaseImmediately */);
+ }
+ }
+
private void handleFullScreenIntent(NotificationEntry entry) {
if (mNotificationInterruptStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) {
if (shouldSuppressFullScreenIntent(entry)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index a81189e..b9168e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -53,6 +53,11 @@
boolean isAodPowerSave();
/**
+ * Initializes the class.
+ */
+ default void init() { }
+
+ /**
* Returns {@code true} if reverse is supported.
*/
default boolean isReverseSupported() { return false; }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index 95b41a1..00419e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -80,7 +80,7 @@
@VisibleForTesting
@Inject
- protected BatteryControllerImpl(Context context, EnhancedEstimates enhancedEstimates,
+ public BatteryControllerImpl(Context context, EnhancedEstimates enhancedEstimates,
PowerManager powerManager, BroadcastDispatcher broadcastDispatcher,
@Main Handler mainHandler, @Background Handler bgHandler) {
mContext = context;
@@ -89,10 +89,6 @@
mPowerManager = powerManager;
mEstimates = enhancedEstimates;
mBroadcastDispatcher = broadcastDispatcher;
-
- registerReceiver();
- updatePowerSave();
- updateEstimate();
}
private void registerReceiver() {
@@ -104,6 +100,13 @@
}
@Override
+ public void init() {
+ registerReceiver();
+ updatePowerSave();
+ updateEstimate();
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("BatteryController state:");
pw.print(" mLevel="); pw.println(mLevel);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index a284335..f41a27c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -329,7 +329,8 @@
return mDataSaverController;
}
- private void registerListeners() {
+ @VisibleForTesting
+ void registerListeners() {
for (int i = 0; i < mMobileSignalControllers.size(); i++) {
MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
mobileSignalController.registerListener();
@@ -364,6 +365,18 @@
// Initial setup of WifiSignalController. Handled as if we had received a sticky broadcast
// of WifiManager.WIFI_STATE_CHANGED_ACTION or WifiManager.NETWORK_STATE_CHANGED_ACTION
mReceiverHandler.post(mWifiSignalController::fetchInitialState);
+
+ // Initial setup of mLastServiceState. Only run if there is no service state yet.
+ // Each MobileSignalController will also get their corresponding
+ mReceiverHandler.post(() -> {
+ if (mLastServiceState == null) {
+ mLastServiceState = mPhone.getServiceState();
+ if (mMobileSignalControllers.size() == 0) {
+ recalculateEmergency();
+ }
+ }
+ });
+
updateMobileControllers();
// Initial setup of emergency information. Handled as if we had received a sticky broadcast
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java
index 8acfbf2..7729965 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java
@@ -23,6 +23,7 @@
import android.os.Process;
import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.LongRunning;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
@@ -50,6 +51,17 @@
return thread.getLooper();
}
+ /** Long running tasks Looper */
+ @Provides
+ @Singleton
+ @LongRunning
+ public static Looper provideLongRunningLooper() {
+ HandlerThread thread = new HandlerThread("SysUiLng",
+ Process.THREAD_PRIORITY_BACKGROUND);
+ thread.start();
+ return thread.getLooper();
+ }
+
/** Main Looper */
@Provides
@Main
@@ -89,6 +101,16 @@
}
/**
+ * Provide a Long running Executor by default.
+ */
+ @Provides
+ @Singleton
+ @LongRunning
+ public static Executor provideLongRunningExecutor(@LongRunning Looper looper) {
+ return new ExecutorImpl(looper);
+ }
+
+ /**
* Provide a Background-Thread Executor.
*/
@Provides
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 6c00eca..c3106bb 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -70,6 +70,7 @@
import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer;
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.internal.telephony.TelephonyIntents;
import com.android.keyguard.KeyguardUpdateMonitor.BiometricAuthenticated;
import com.android.systemui.SysuiTestCase;
@@ -87,6 +88,7 @@
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
import java.util.ArrayList;
import java.util.List;
@@ -134,16 +136,17 @@
@Mock
private BroadcastDispatcher mBroadcastDispatcher;
@Mock
+ private TelephonyManager mTelephonyManager;
+ @Mock
private RingerModeTracker mRingerModeTracker;
@Mock
private LiveData<Integer> mRingerModeLiveData;
- @Mock
- private TelephonyManager mTelephonyManager;
// Direct executor
private Executor mBackgroundExecutor = Runnable::run;
private TestableLooper mTestableLooper;
private TestableKeyguardUpdateMonitor mKeyguardUpdateMonitor;
private TestableContext mSpiedContext;
+ private MockitoSession mMockitoSession;
@Before
public void setup() {
@@ -165,6 +168,9 @@
when(mStrongAuthTracker
.isUnlockingWithBiometricAllowed(anyBoolean() /* isStrongBiometric */))
.thenReturn(true);
+
+ when(mTelephonyManager.getServiceStateForSubscriber(anyInt()))
+ .thenReturn(new ServiceState());
mSpiedContext.addMockSystemService(TrustManager.class, mTrustManager);
mSpiedContext.addMockSystemService(FingerprintManager.class, mFingerprintManager);
mSpiedContext.addMockSystemService(BiometricManager.class, mBiometricManager);
@@ -176,6 +182,11 @@
when(mRingerModeTracker.getRingerMode()).thenReturn(mRingerModeLiveData);
+ mMockitoSession = ExtendedMockito.mockitoSession()
+ .spyStatic(SubscriptionManager.class).startMocking();
+ ExtendedMockito.doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
+ .when(SubscriptionManager::getDefaultSubscriptionId);
+
mTestableLooper = TestableLooper.get(this);
allowTestableLooperAsMainThread();
mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mSpiedContext);
@@ -183,6 +194,7 @@
@After
public void tearDown() {
+ mMockitoSession.finishMocking();
mKeyguardUpdateMonitor.destroy();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index fc1ddf7..821b850 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -470,7 +470,8 @@
true /* requireConfirmation */,
0 /* userId */,
"testPackage",
- 0 /* operationId */);
+ 0 /* operationId */,
+ 0 /* sysUiSessionId */);
}
private Bundle createTestDialogBundle(int authenticators) {
@@ -508,7 +509,7 @@
@Override
protected AuthDialog buildDialog(Bundle biometricPromptBundle,
boolean requireConfirmation, int userId, int type, String opPackageName,
- boolean skipIntro, long operationId) {
+ boolean skipIntro, long operationId, int sysUiSessionId) {
mLastBiometricPromptBundle = biometricPromptBundle;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index cffcabb..63e6e8c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -410,11 +410,12 @@
Bundle bundle = new Bundle();
String packageName = "test";
final long operationId = 1;
+ final int sysUiSessionId = 2;
mCommandQueue.showAuthenticationDialog(bundle, null /* receiver */, 1, true, 3,
- packageName, operationId);
+ packageName, operationId, sysUiSessionId);
waitForIdleSync();
verify(mCallbacks).showAuthenticationDialog(eq(bundle), eq(null), eq(1), eq(true), eq(3),
- eq(packageName), eq(operationId));
+ eq(packageName), eq(operationId), eq(sysUiSessionId));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 972357e..bb7f73a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -26,6 +26,7 @@
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -93,6 +94,7 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.List;
import java.util.Set;
@@ -261,6 +263,19 @@
}
@Test
+ public void testRemoveUninflatedNotification_removesNotificationFromAllNotifsList() {
+ // GIVEN an uninflated entry is added
+ mEntryManager.addNotification(mSbn, mRankingMap);
+ assertTrue(entriesContainKey(mEntryManager.getAllNotifs(), mSbn.getKey()));
+
+ // WHEN the uninflated entry is removed
+ mEntryManager.performRemoveNotification(mSbn, UNDEFINED_DISMISS_REASON);
+
+ // THEN the entry is still removed from the allNotifications list
+ assertFalse(entriesContainKey(mEntryManager.getAllNotifs(), mSbn.getKey()));
+ }
+
+ @Test
public void testRemoveNotification_onEntryRemoveNotFiredIfEntryDoesntExist() {
mEntryManager.removeNotification("not_a_real_key", mRankingMap, UNDEFINED_DISMISS_REASON);
@@ -545,6 +560,15 @@
/* End annex */
+ private boolean entriesContainKey(Collection<NotificationEntry> entries, String key) {
+ for (NotificationEntry entry : entries) {
+ if (entry.getSbn().getKey().equals(key)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private Notification.Action createAction() {
return new Notification.Action.Builder(
Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
index cc2d1c2..e04d25b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
@@ -83,6 +83,7 @@
@Mock private NotificationStackScrollLayout mNotificationStackScrollLayout;
@Mock private NotificationShadeDepthController mNotificationShadeDepthController;
@Mock private SuperStatusBarViewFactory mStatusBarViewFactory;
+ @Mock private NotificationShadeWindowController mNotificationShadeWindowController;
@Before
public void setUp() {
@@ -121,7 +122,7 @@
mNotificationPanelViewController,
mStatusBarViewFactory);
mController.setupExpandedStatusBar();
- mController.setService(mStatusBar);
+ mController.setService(mStatusBar, mNotificationShadeWindowController);
mController.setDragDownHelper(mDragDownHelper);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
index 05a4867..f83fbd4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
@@ -55,6 +55,7 @@
MockitoAnnotations.initMocks(this);
mBatteryController = new BatteryControllerImpl(getContext(), mock(EnhancedEstimates.class),
mPowerManager, mBroadcastDispatcher, new Handler(), new Handler());
+ mBatteryController.init();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
index 399b5c2..3b27437 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
@@ -25,6 +25,7 @@
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkCapabilities;
+import android.os.Handler;
import android.os.Looper;
import android.telephony.CellSignalStrength;
import android.telephony.ServiceState;
@@ -46,6 +47,7 @@
import org.mockito.Mockito;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
@SmallTest
@@ -68,6 +70,28 @@
}
@Test
+ public void testServiceStateInitialState() throws Exception {
+ // Verify that NetworkControllerImpl pulls the service state from Telephony upon
+ // initialization rather than relying on the sticky behavior of ACTION_SERVICE_STATE
+
+ when(mServiceState.isEmergencyOnly()).thenReturn(true);
+ when(mMockTm.getServiceState()).thenReturn(mServiceState);
+ when(mMockSm.getCompleteActiveSubscriptionInfoList()).thenReturn(Collections.emptyList());
+
+ mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm,
+ mMockNsm, mMockSm, mConfig, Looper.getMainLooper(), mCallbackHandler,
+ mock(AccessPointControllerImpl.class), mock(DataUsageController.class),
+ mMockSubDefaults, mock(DeviceProvisionedController.class), mMockBd);
+ mNetworkController.registerListeners();
+
+ // Wait for the main looper to execute the previous command
+ Handler mainThreadHandler = new Handler(Looper.getMainLooper());
+ waitForIdleSync(mainThreadHandler);
+
+ verifyEmergencyOnly(true);
+ }
+
+ @Test
public void testNoSimsIconPresent() {
// No Subscriptions.
mNetworkController.mMobileSignalControllers.clear();
diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp
index bfb6524..cbc5e14 100644
--- a/packages/Tethering/Android.bp
+++ b/packages/Tethering/Android.bp
@@ -27,7 +27,7 @@
"androidx.annotation_annotation",
"netd_aidl_interface-V3-java",
"netlink-client",
- "networkstack-aidl-interfaces-unstable-java",
+ "networkstack-aidl-interfaces-java",
"android.hardware.tetheroffload.config-V1.0-java",
"android.hardware.tetheroffload.control-V1.0-java",
"net-utils-framework-common",
@@ -109,6 +109,7 @@
manifest: "AndroidManifest_InProcess.xml",
// InProcessTethering is a replacement for Tethering
overrides: ["Tethering"],
+ apex_available: ["com.android.tethering"],
}
// Updatable tethering packaged as an application
diff --git a/packages/Tethering/apex/Android.bp b/packages/Tethering/apex/Android.bp
index 24df5f6..20ccd2a 100644
--- a/packages/Tethering/apex/Android.bp
+++ b/packages/Tethering/apex/Android.bp
@@ -36,3 +36,12 @@
name: "com.android.tethering.certificate",
certificate: "com.android.tethering",
}
+
+override_apex {
+ name: "com.android.tethering.inprocess",
+ base: "com.android.tethering",
+ package_name: "com.android.tethering.inprocess",
+ apps: [
+ "InProcessTethering",
+ ],
+}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java b/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
index 049a9f6..23b8be1 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
@@ -37,6 +37,7 @@
import android.content.IntentFilter;
import android.net.util.SharedLog;
import android.os.Bundle;
+import android.os.ConditionVariable;
import android.os.Handler;
import android.os.Parcel;
import android.os.PersistableBundle;
@@ -45,13 +46,12 @@
import android.os.SystemProperties;
import android.provider.Settings;
import android.telephony.CarrierConfigManager;
-import android.util.ArraySet;
import android.util.SparseIntArray;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.StateMachine;
import java.io.PrintWriter;
+import java.util.BitSet;
/**
* Re-check tethering provisioning for enabled downstream tether types.
@@ -73,39 +73,37 @@
private final ComponentName mSilentProvisioningService;
private static final int MS_PER_HOUR = 60 * 60 * 1000;
+ private static final int DUMP_TIMEOUT = 10_000;
- // The ArraySet contains enabled downstream types, ex:
+ // The BitSet is the bit map of each enabled downstream types, ex:
// {@link TetheringManager.TETHERING_WIFI}
// {@link TetheringManager.TETHERING_USB}
// {@link TetheringManager.TETHERING_BLUETOOTH}
- private final ArraySet<Integer> mCurrentTethers;
+ private final BitSet mCurrentDownstreams;
private final Context mContext;
- private final int mPermissionChangeMessageCode;
private final SharedLog mLog;
private final SparseIntArray mEntitlementCacheValue;
private final Handler mHandler;
- private final StateMachine mTetherMasterSM;
// Key: TetheringManager.TETHERING_*(downstream).
// Value: TetheringManager.TETHER_ERROR_{NO_ERROR or PROVISION_FAILED}(provisioning result).
- private final SparseIntArray mCellularPermitted;
+ private final SparseIntArray mCurrentEntitlementResults;
+ private final Runnable mPermissionChangeCallback;
private PendingIntent mProvisioningRecheckAlarm;
- private boolean mCellularUpstreamPermitted = true;
+ private boolean mLastCellularUpstreamPermitted = true;
private boolean mUsingCellularAsUpstream = false;
private boolean mNeedReRunProvisioningUi = false;
private OnUiEntitlementFailedListener mListener;
private TetheringConfigurationFetcher mFetcher;
- public EntitlementManager(Context ctx, StateMachine tetherMasterSM, SharedLog log,
- int permissionChangeMessageCode) {
-
+ public EntitlementManager(Context ctx, Handler h, SharedLog log,
+ Runnable callback) {
mContext = ctx;
mLog = log.forSubComponent(TAG);
- mCurrentTethers = new ArraySet<Integer>();
- mCellularPermitted = new SparseIntArray();
+ mCurrentDownstreams = new BitSet();
+ mCurrentEntitlementResults = new SparseIntArray();
mEntitlementCacheValue = new SparseIntArray();
- mTetherMasterSM = tetherMasterSM;
- mPermissionChangeMessageCode = permissionChangeMessageCode;
- mHandler = tetherMasterSM.getHandler();
+ mPermissionChangeCallback = callback;
+ mHandler = h;
mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_PROVISIONING_ALARM),
null, mHandler);
mSilentProvisioningService = ComponentName.unflattenFromString(
@@ -144,13 +142,19 @@
* Check if cellular upstream is permitted.
*/
public boolean isCellularUpstreamPermitted() {
- // If provisioning is required and EntitlementManager don't know any downstream,
- // cellular upstream should not be allowed.
final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
- if (mCurrentTethers.size() == 0 && isTetherProvisioningRequired(config)) {
- return false;
- }
- return mCellularUpstreamPermitted;
+
+ return isCellularUpstreamPermitted(config);
+ }
+
+ private boolean isCellularUpstreamPermitted(final TetheringConfiguration config) {
+ if (!isTetherProvisioningRequired(config)) return true;
+
+ // If provisioning is required and EntitlementManager doesn't know any downstreams,
+ // cellular upstream should not be allowed.
+ if (mCurrentDownstreams.isEmpty()) return false;
+
+ return mCurrentEntitlementResults.indexOfValue(TETHER_ERROR_NO_ERROR) > -1;
}
/**
@@ -164,29 +168,22 @@
public void startProvisioningIfNeeded(int downstreamType, boolean showProvisioningUi) {
if (!isValidDownstreamType(downstreamType)) return;
- if (!mCurrentTethers.contains(downstreamType)) mCurrentTethers.add(downstreamType);
+ mCurrentDownstreams.set(downstreamType, true);
final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
- if (isTetherProvisioningRequired(config)) {
- // If provisioning is required and the result is not available yet,
- // cellular upstream should not be allowed.
- if (mCellularPermitted.size() == 0) {
- mCellularUpstreamPermitted = false;
- }
- // If upstream is not cellular, provisioning app would not be launched
- // till upstream change to cellular.
- if (mUsingCellularAsUpstream) {
- if (showProvisioningUi) {
- runUiTetherProvisioning(downstreamType, config.activeDataSubId);
- } else {
- runSilentTetherProvisioning(downstreamType, config.activeDataSubId);
- }
- mNeedReRunProvisioningUi = false;
+ if (!isTetherProvisioningRequired(config)) return;
+
+ // If upstream is not cellular, provisioning app would not be launched
+ // till upstream change to cellular.
+ if (mUsingCellularAsUpstream) {
+ if (showProvisioningUi) {
+ runUiTetherProvisioning(downstreamType, config.activeDataSubId);
} else {
- mNeedReRunProvisioningUi |= showProvisioningUi;
+ runSilentTetherProvisioning(downstreamType, config.activeDataSubId);
}
+ mNeedReRunProvisioningUi = false;
} else {
- mCellularUpstreamPermitted = true;
+ mNeedReRunProvisioningUi |= showProvisioningUi;
}
}
@@ -195,14 +192,14 @@
*
* @param type tethering type from TetheringManager.TETHERING_{@code *}
*/
- public void stopProvisioningIfNeeded(int type) {
- if (!isValidDownstreamType(type)) return;
+ public void stopProvisioningIfNeeded(int downstreamType) {
+ if (!isValidDownstreamType(downstreamType)) return;
- mCurrentTethers.remove(type);
+ mCurrentDownstreams.set(downstreamType, false);
// There are lurking bugs where the notion of "provisioning required" or
// "tethering supported" may change without without tethering being notified properly.
// Remove the mapping all the time no matter provisioning is required or not.
- removeDownstreamMapping(type);
+ removeDownstreamMapping(downstreamType);
}
/**
@@ -213,7 +210,7 @@
public void notifyUpstream(boolean isCellular) {
if (DBG) {
mLog.i("notifyUpstream: " + isCellular
- + ", mCellularUpstreamPermitted: " + mCellularUpstreamPermitted
+ + ", mLastCellularUpstreamPermitted: " + mLastCellularUpstreamPermitted
+ ", mNeedReRunProvisioningUi: " + mNeedReRunProvisioningUi);
}
mUsingCellularAsUpstream = isCellular;
@@ -231,7 +228,7 @@
}
private void maybeRunProvisioning(final TetheringConfiguration config) {
- if (mCurrentTethers.size() == 0 || !isTetherProvisioningRequired(config)) {
+ if (mCurrentDownstreams.isEmpty() || !isTetherProvisioningRequired(config)) {
return;
}
@@ -239,8 +236,9 @@
// are allowed. Therefore even if the silent check here ends in a failure and the UI later
// yields success, then the downstream that got a failure will re-evaluate as a result of
// the change and get the new correct value.
- for (Integer downstream : mCurrentTethers) {
- if (mCellularPermitted.indexOfKey(downstream) < 0) {
+ for (int downstream = mCurrentDownstreams.nextSetBit(0); downstream >= 0;
+ downstream = mCurrentDownstreams.nextSetBit(downstream + 1)) {
+ if (mCurrentEntitlementResults.indexOfKey(downstream) < 0) {
if (mNeedReRunProvisioningUi) {
mNeedReRunProvisioningUi = false;
runUiTetherProvisioning(downstream, config.activeDataSubId);
@@ -286,7 +284,7 @@
mLog.log("reevaluateSimCardProvisioning() don't run in TetherMaster thread");
}
mEntitlementCacheValue.clear();
- mCellularPermitted.clear();
+ mCurrentEntitlementResults.clear();
// TODO: refine provisioning check to isTetherProvisioningRequired() ??
if (!config.hasMobileHotspotProvisionApp()
@@ -410,26 +408,25 @@
}
private void evaluateCellularPermission(final TetheringConfiguration config) {
- final boolean oldPermitted = mCellularUpstreamPermitted;
- mCellularUpstreamPermitted = (!isTetherProvisioningRequired(config)
- || mCellularPermitted.indexOfValue(TETHER_ERROR_NO_ERROR) > -1);
+ final boolean permitted = isCellularUpstreamPermitted(config);
if (DBG) {
- mLog.i("Cellular permission change from " + oldPermitted
- + " to " + mCellularUpstreamPermitted);
+ mLog.i("Cellular permission change from " + mLastCellularUpstreamPermitted
+ + " to " + permitted);
}
- if (mCellularUpstreamPermitted != oldPermitted) {
- mLog.log("Cellular permission change: " + mCellularUpstreamPermitted);
- mTetherMasterSM.sendMessage(mPermissionChangeMessageCode);
+ if (mLastCellularUpstreamPermitted != permitted) {
+ mLog.log("Cellular permission change: " + permitted);
+ mPermissionChangeCallback.run();
}
// Only schedule periodic re-check when tether is provisioned
// and the result is ok.
- if (mCellularUpstreamPermitted && mCellularPermitted.size() > 0) {
+ if (permitted && mCurrentEntitlementResults.size() > 0) {
scheduleProvisioningRechecks(config);
} else {
cancelTetherProvisioningRechecks();
}
+ mLastCellularUpstreamPermitted = permitted;
}
/**
@@ -441,10 +438,10 @@
*/
protected void addDownstreamMapping(int type, int resultCode) {
mLog.i("addDownstreamMapping: " + type + ", result: " + resultCode
- + " ,TetherTypeRequested: " + mCurrentTethers.contains(type));
- if (!mCurrentTethers.contains(type)) return;
+ + " ,TetherTypeRequested: " + mCurrentDownstreams.get(type));
+ if (!mCurrentDownstreams.get(type)) return;
- mCellularPermitted.put(type, resultCode);
+ mCurrentEntitlementResults.put(type, resultCode);
final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
evaluateCellularPermission(config);
}
@@ -455,7 +452,7 @@
*/
protected void removeDownstreamMapping(int type) {
mLog.i("removeDownstreamMapping: " + type);
- mCellularPermitted.delete(type);
+ mCurrentEntitlementResults.delete(type);
final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
evaluateCellularPermission(config);
}
@@ -488,17 +485,25 @@
* @param pw {@link PrintWriter} is used to print formatted
*/
public void dump(PrintWriter pw) {
- pw.print("mCellularUpstreamPermitted: ");
- pw.println(mCellularUpstreamPermitted);
- for (Integer type : mCurrentTethers) {
- pw.print("Type: ");
- pw.print(typeString(type));
- if (mCellularPermitted.indexOfKey(type) > -1) {
- pw.print(", Value: ");
- pw.println(errorString(mCellularPermitted.get(type)));
- } else {
- pw.println(", Value: empty");
+ final ConditionVariable mWaiting = new ConditionVariable();
+ mHandler.post(() -> {
+ pw.print("isCellularUpstreamPermitted: ");
+ pw.println(isCellularUpstreamPermitted());
+ for (int type = mCurrentDownstreams.nextSetBit(0); type >= 0;
+ type = mCurrentDownstreams.nextSetBit(type + 1)) {
+ pw.print("Type: ");
+ pw.print(typeString(type));
+ if (mCurrentEntitlementResults.indexOfKey(type) > -1) {
+ pw.print(", Value: ");
+ pw.println(errorString(mCurrentEntitlementResults.get(type)));
+ } else {
+ pw.println(", Value: empty");
+ }
}
+ mWaiting.open();
+ });
+ if (!mWaiting.block(DUMP_TIMEOUT)) {
+ pw.println("... dump timed out after " + DUMP_TIMEOUT + "ms");
}
}
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 952325c..753abc9 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -62,7 +62,6 @@
import static com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE;
-import android.app.usage.NetworkStatsManager;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothPan;
import android.bluetooth.BluetoothProfile;
@@ -268,12 +267,9 @@
mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper, deps);
mTetherMasterSM.start();
- final NetworkStatsManager statsManager =
- (NetworkStatsManager) mContext.getSystemService(Context.NETWORK_STATS_SERVICE);
mHandler = mTetherMasterSM.getHandler();
- mOffloadController = new OffloadController(mHandler,
- mDeps.getOffloadHardwareInterface(mHandler, mLog), mContext.getContentResolver(),
- statsManager, mLog, new OffloadController.Dependencies() {
+ mOffloadController = mDeps.getOffloadController(mHandler, mLog,
+ new OffloadController.Dependencies() {
@Override
public TetheringConfiguration getTetherConfig() {
@@ -288,8 +284,9 @@
filter.addAction(ACTION_CARRIER_CONFIG_CHANGED);
// EntitlementManager will send EVENT_UPSTREAM_PERMISSION_CHANGED when cellular upstream
// permission is changed according to entitlement check result.
- mEntitlementMgr = mDeps.getEntitlementManager(mContext, mTetherMasterSM, mLog,
- TetherMasterSM.EVENT_UPSTREAM_PERMISSION_CHANGED);
+ mEntitlementMgr = mDeps.getEntitlementManager(mContext, mHandler, mLog,
+ () -> mTetherMasterSM.sendMessage(
+ TetherMasterSM.EVENT_UPSTREAM_PERMISSION_CHANGED));
mEntitlementMgr.setOnUiEntitlementFailedListener((int downstream) -> {
mLog.log("OBSERVED UiEnitlementFailed");
stopTethering(downstream);
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
index 9b54b5f..ce546c7 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
@@ -16,6 +16,7 @@
package com.android.networkstack.tethering;
+import android.app.usage.NetworkStatsManager;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.net.INetd;
@@ -47,6 +48,19 @@
}
/**
+ * Get a reference to the offload controller to be used by tethering.
+ */
+ @NonNull
+ public OffloadController getOffloadController(@NonNull Handler h,
+ @NonNull SharedLog log, @NonNull OffloadController.Dependencies deps) {
+ final NetworkStatsManager statsManager =
+ (NetworkStatsManager) getContext().getSystemService(Context.NETWORK_STATS_SERVICE);
+ return new OffloadController(h, getOffloadHardwareInterface(h, log),
+ getContext().getContentResolver(), statsManager, log, deps);
+ }
+
+
+ /**
* Get a reference to the UpstreamNetworkMonitor to be used by tethering.
*/
public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx, StateMachine target,
@@ -82,9 +96,9 @@
/**
* Get a reference to the EntitlementManager to be used by tethering.
*/
- public EntitlementManager getEntitlementManager(Context ctx, StateMachine target,
- SharedLog log, int what) {
- return new EntitlementManager(ctx, target, log, what);
+ public EntitlementManager getEntitlementManager(Context ctx, Handler h, SharedLog log,
+ Runnable callback) {
+ return new EntitlementManager(ctx, h, log, callback);
}
/**
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
index 8bd0edc..a692935 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
@@ -37,6 +37,8 @@
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -45,7 +47,7 @@
import android.content.res.Resources;
import android.net.util.SharedLog;
import android.os.Bundle;
-import android.os.Message;
+import android.os.Handler;
import android.os.PersistableBundle;
import android.os.ResultReceiver;
import android.os.SystemProperties;
@@ -56,26 +58,22 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.util.State;
-import com.android.internal.util.StateMachine;
import com.android.internal.util.test.BroadcastInterceptingContext;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
-import java.util.ArrayList;
-
@RunWith(AndroidJUnit4.class)
@SmallTest
public final class EntitlementManagerTest {
- private static final int EVENT_EM_UPDATE = 1;
private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app";
@@ -90,8 +88,8 @@
private final PersistableBundle mCarrierConfig = new PersistableBundle();
private final TestLooper mLooper = new TestLooper();
private Context mMockContext;
+ private Runnable mPermissionChangeCallback;
- private TestStateMachine mSM;
private WrappedEntitlementManager mEnMgr;
private TetheringConfiguration mConfig;
private MockitoSession mMockingSession;
@@ -112,9 +110,9 @@
public int uiProvisionCount = 0;
public int silentProvisionCount = 0;
- public WrappedEntitlementManager(Context ctx, StateMachine target,
- SharedLog log, int what) {
- super(ctx, target, log, what);
+ public WrappedEntitlementManager(Context ctx, Handler h, SharedLog log,
+ Runnable callback) {
+ super(ctx, h, log, callback);
}
public void reset() {
@@ -169,8 +167,9 @@
when(mLog.forSubComponent(anyString())).thenReturn(mLog);
mMockContext = new MockContext(mContext);
- mSM = new TestStateMachine();
- mEnMgr = new WrappedEntitlementManager(mMockContext, mSM, mLog, EVENT_EM_UPDATE);
+ mPermissionChangeCallback = spy(() -> { });
+ mEnMgr = new WrappedEntitlementManager(mMockContext, new Handler(mLooper.getLooper()), mLog,
+ mPermissionChangeCallback);
mEnMgr.setOnUiEntitlementFailedListener(mEntitlementFailedListener);
mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
mEnMgr.setTetheringConfigurationFetcher(() -> {
@@ -180,10 +179,6 @@
@After
public void tearDown() throws Exception {
- if (mSM != null) {
- mSM.quit();
- mSM = null;
- }
mMockingSession.finishMocking();
}
@@ -350,68 +345,105 @@
mEnMgr.reset();
}
+ private void assertPermissionChangeCallback(InOrder inOrder) {
+ inOrder.verify(mPermissionChangeCallback, times(1)).run();
+ }
+
+ private void assertNoPermissionChange(InOrder inOrder) {
+ inOrder.verifyNoMoreInteractions();
+ }
+
@Test
public void verifyPermissionResult() {
+ final InOrder inOrder = inOrder(mPermissionChangeCallback);
setupForRequiredProvisioning();
mEnMgr.notifyUpstream(true);
mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
mLooper.dispatchAll();
+ // Permitted: true -> false
+ assertPermissionChangeCallback(inOrder);
assertFalse(mEnMgr.isCellularUpstreamPermitted());
+
mEnMgr.stopProvisioningIfNeeded(TETHERING_WIFI);
mLooper.dispatchAll();
+ // Permitted: false -> false
+ assertNoPermissionChange(inOrder);
+
mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
mLooper.dispatchAll();
+ // Permitted: false -> true
+ assertPermissionChangeCallback(inOrder);
assertTrue(mEnMgr.isCellularUpstreamPermitted());
}
@Test
public void verifyPermissionIfAllNotApproved() {
+ final InOrder inOrder = inOrder(mPermissionChangeCallback);
setupForRequiredProvisioning();
mEnMgr.notifyUpstream(true);
mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
mLooper.dispatchAll();
+ // Permitted: true -> false
+ assertPermissionChangeCallback(inOrder);
assertFalse(mEnMgr.isCellularUpstreamPermitted());
+
mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true);
mLooper.dispatchAll();
+ // Permitted: false -> false
+ assertNoPermissionChange(inOrder);
assertFalse(mEnMgr.isCellularUpstreamPermitted());
+
mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
mEnMgr.startProvisioningIfNeeded(TETHERING_BLUETOOTH, true);
mLooper.dispatchAll();
+ // Permitted: false -> false
+ assertNoPermissionChange(inOrder);
assertFalse(mEnMgr.isCellularUpstreamPermitted());
}
@Test
public void verifyPermissionIfAnyApproved() {
+ final InOrder inOrder = inOrder(mPermissionChangeCallback);
setupForRequiredProvisioning();
mEnMgr.notifyUpstream(true);
mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
mLooper.dispatchAll();
+ // Permitted: true -> true
+ assertNoPermissionChange(inOrder);
assertTrue(mEnMgr.isCellularUpstreamPermitted());
- mLooper.dispatchAll();
+
mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
mEnMgr.startProvisioningIfNeeded(TETHERING_USB, true);
mLooper.dispatchAll();
+ // Permitted: true -> true
+ assertNoPermissionChange(inOrder);
assertTrue(mEnMgr.isCellularUpstreamPermitted());
+
mEnMgr.stopProvisioningIfNeeded(TETHERING_WIFI);
mLooper.dispatchAll();
+ // Permitted: true -> false
+ assertPermissionChangeCallback(inOrder);
assertFalse(mEnMgr.isCellularUpstreamPermitted());
-
}
@Test
public void verifyPermissionWhenProvisioningNotStarted() {
+ final InOrder inOrder = inOrder(mPermissionChangeCallback);
assertTrue(mEnMgr.isCellularUpstreamPermitted());
+ assertNoPermissionChange(inOrder);
setupForRequiredProvisioning();
assertFalse(mEnMgr.isCellularUpstreamPermitted());
+ assertNoPermissionChange(inOrder);
}
@Test
public void testRunTetherProvisioning() {
+ final InOrder inOrder = inOrder(mPermissionChangeCallback);
setupForRequiredProvisioning();
// 1. start ui provisioning, upstream is mobile
mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
@@ -421,16 +453,22 @@
mLooper.dispatchAll();
assertEquals(1, mEnMgr.uiProvisionCount);
assertEquals(0, mEnMgr.silentProvisionCount);
+ // Permitted: true -> true
+ assertNoPermissionChange(inOrder);
assertTrue(mEnMgr.isCellularUpstreamPermitted());
mEnMgr.reset();
+
// 2. start no-ui provisioning
mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, false);
mLooper.dispatchAll();
assertEquals(0, mEnMgr.uiProvisionCount);
assertEquals(1, mEnMgr.silentProvisionCount);
+ // Permitted: true -> true
+ assertNoPermissionChange(inOrder);
assertTrue(mEnMgr.isCellularUpstreamPermitted());
mEnMgr.reset();
+
// 3. tear down mobile, then start ui provisioning
mEnMgr.notifyUpstream(false);
mLooper.dispatchAll();
@@ -438,44 +476,58 @@
mLooper.dispatchAll();
assertEquals(0, mEnMgr.uiProvisionCount);
assertEquals(0, mEnMgr.silentProvisionCount);
+ assertNoPermissionChange(inOrder);
mEnMgr.reset();
+
// 4. switch upstream back to mobile
mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
mEnMgr.notifyUpstream(true);
mLooper.dispatchAll();
assertEquals(1, mEnMgr.uiProvisionCount);
assertEquals(0, mEnMgr.silentProvisionCount);
+ // Permitted: true -> true
+ assertNoPermissionChange(inOrder);
assertTrue(mEnMgr.isCellularUpstreamPermitted());
mEnMgr.reset();
+
// 5. tear down mobile, then switch SIM
mEnMgr.notifyUpstream(false);
mLooper.dispatchAll();
mEnMgr.reevaluateSimCardProvisioning(mConfig);
assertEquals(0, mEnMgr.uiProvisionCount);
assertEquals(0, mEnMgr.silentProvisionCount);
+ assertNoPermissionChange(inOrder);
mEnMgr.reset();
+
// 6. switch upstream back to mobile again
mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISIONING_FAILED;
mEnMgr.notifyUpstream(true);
mLooper.dispatchAll();
assertEquals(0, mEnMgr.uiProvisionCount);
assertEquals(3, mEnMgr.silentProvisionCount);
+ // Permitted: true -> false
+ assertPermissionChangeCallback(inOrder);
assertFalse(mEnMgr.isCellularUpstreamPermitted());
mEnMgr.reset();
+
// 7. start ui provisioning, upstream is mobile, downstream is ethernet
mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
mEnMgr.startProvisioningIfNeeded(TETHERING_ETHERNET, true);
mLooper.dispatchAll();
assertEquals(1, mEnMgr.uiProvisionCount);
assertEquals(0, mEnMgr.silentProvisionCount);
+ // Permitted: false -> true
+ assertPermissionChangeCallback(inOrder);
assertTrue(mEnMgr.isCellularUpstreamPermitted());
mEnMgr.reset();
+
// 8. downstream is invalid
mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI_P2P, true);
mLooper.dispatchAll();
assertEquals(0, mEnMgr.uiProvisionCount);
assertEquals(0, mEnMgr.silentProvisionCount);
+ assertNoPermissionChange(inOrder);
mEnMgr.reset();
}
@@ -491,32 +543,4 @@
assertEquals(1, mEnMgr.uiProvisionCount);
verify(mEntitlementFailedListener, times(1)).onUiEntitlementFailed(TETHERING_WIFI);
}
-
- public class TestStateMachine extends StateMachine {
- public final ArrayList<Message> messages = new ArrayList<>();
- private final State
- mLoggingState = new EntitlementManagerTest.TestStateMachine.LoggingState();
-
- class LoggingState extends State {
- @Override public void enter() {
- messages.clear();
- }
-
- @Override public void exit() {
- messages.clear();
- }
-
- @Override public boolean processMessage(Message msg) {
- messages.add(msg);
- return false;
- }
- }
-
- public TestStateMachine() {
- super("EntitlementManagerTest.TestStateMachine", mLooper.getLooper());
- addState(mLoggingState);
- setInitialState(mLoggingState);
- super.start();
- }
- }
}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index 0363f5f..fa260a4 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -150,6 +150,8 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.util.ArrayList;
@@ -212,6 +214,9 @@
private Tethering mTethering;
private PhoneStateListener mPhoneStateListener;
private InterfaceConfigurationParcel mInterfaceConfiguration;
+ private TetheringConfiguration mConfig;
+ private EntitlementManager mEntitleMgr;
+ private OffloadController mOffloadCtrl;
private class TestContext extends BroadcastInterceptingContext {
TestContext(Context base) {
@@ -297,8 +302,9 @@
}
}
- private class MockTetheringConfiguration extends TetheringConfiguration {
- MockTetheringConfiguration(Context ctx, SharedLog log, int id) {
+ // MyTetheringConfiguration is used to override static method for testing.
+ private class MyTetheringConfiguration extends TetheringConfiguration {
+ MyTetheringConfiguration(Context ctx, SharedLog log, int id) {
super(ctx, log, id);
}
@@ -328,6 +334,15 @@
}
@Override
+ public OffloadController getOffloadController(Handler h, SharedLog log,
+ OffloadController.Dependencies deps) {
+ mOffloadCtrl = spy(super.getOffloadController(h, log, deps));
+ // Return real object here instead of mock because
+ // testReportFailCallbackIfOffloadNotSupported depend on real OffloadController object.
+ return mOffloadCtrl;
+ }
+
+ @Override
public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx,
StateMachine target, SharedLog log, int what) {
mUpstreamNetworkMonitorMasterSM = target;
@@ -352,6 +367,13 @@
}
@Override
+ public EntitlementManager getEntitlementManager(Context ctx, Handler h, SharedLog log,
+ Runnable callback) {
+ mEntitleMgr = spy(super.getEntitlementManager(ctx, h, log, callback));
+ return mEntitleMgr;
+ }
+
+ @Override
public boolean isTetheringSupported() {
return true;
}
@@ -359,7 +381,8 @@
@Override
public TetheringConfiguration generateTetheringConfiguration(Context ctx, SharedLog log,
int subId) {
- return new MockTetheringConfiguration(ctx, log, subId);
+ mConfig = spy(new MyTetheringConfiguration(ctx, log, subId));
+ return mConfig;
}
@Override
@@ -1726,6 +1749,19 @@
verify(mNotificationUpdater, never()).onUpstreamCapabilitiesChanged(any());
}
+ @Test
+ public void testDumpTetheringLog() throws Exception {
+ final FileDescriptor mockFd = mock(FileDescriptor.class);
+ final PrintWriter mockPw = mock(PrintWriter.class);
+ runUsbTethering(null);
+ mLooper.startAutoDispatch();
+ mTethering.dump(mockFd, mockPw, new String[0]);
+ verify(mConfig).dump(any());
+ verify(mEntitleMgr).dump(any());
+ verify(mOffloadCtrl).dump(any());
+ mLooper.stopAutoDispatch();
+ }
+
// TODO: Test that a request for hotspot mode doesn't interfere with an
// already operating tethering mode interface.
}
diff --git a/packages/overlays/IconShapeTaperedRectOverlay/res/values/config.xml b/packages/overlays/IconShapeTaperedRectOverlay/res/values/config.xml
index 8e80c9d..63ba20e 100644
--- a/packages/overlays/IconShapeTaperedRectOverlay/res/values/config.xml
+++ b/packages/overlays/IconShapeTaperedRectOverlay/res/values/config.xml
@@ -24,6 +24,5 @@
<!-- Corner radius for bottom sheet system dialogs -->
<dimen name="config_bottomDialogCornerRadius">0dp</dimen>
<!-- Tile stroke width -->
- <dimen name="config_qsTileStrokeWidthInactive">10dp</dimen>
-
+ <dimen name="config_qsTileStrokeWidthInactive">1dp</dimen>
</resources>
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index edb4445..0f98992 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -94,8 +94,10 @@
if (userState == null) return;
final long identity = Binder.clearCallingIdentity();
try {
- int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
- | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS;
+ int flags = Context.BIND_AUTO_CREATE
+ | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
+ | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS
+ | Context.BIND_INCLUDE_CAPABILITIES;
if (userState.getBindInstantServiceAllowedLocked()) {
flags |= Context.BIND_ALLOW_INSTANT;
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java
index 3612e09..3282870 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java
@@ -17,17 +17,17 @@
package com.android.server.autofill;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.ComponentName;
import android.os.Bundle;
import android.os.Handler;
import android.view.autofill.AutofillId;
import android.view.inputmethod.InlineSuggestionsRequest;
-import android.view.inputmethod.InlineSuggestionsResponse;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.autofill.ui.InlineFillUi;
import com.android.server.inputmethod.InputMethodManagerInternal;
-import java.util.Collections;
import java.util.Optional;
import java.util.function.Consumer;
@@ -46,8 +46,12 @@
@NonNull
private final Handler mHandler;
+ @Nullable
@GuardedBy("mLock")
private AutofillInlineSuggestionsRequestSession mSession;
+ @Nullable
+ @GuardedBy("mLock")
+ private InlineFillUi mInlineFillUi;
AutofillInlineSessionController(InputMethodManagerInternal inputMethodManagerInternal,
int userId, ComponentName componentName, Handler handler, Object lock) {
@@ -72,16 +76,16 @@
// TODO(b/151123764): rename the method to better reflect what it does.
if (mSession != null) {
// Send an empty response to IME and destroy the existing session.
- mSession.onInlineSuggestionsResponseLocked(mSession.getAutofillIdLocked(),
- new InlineSuggestionsResponse(Collections.EMPTY_LIST));
+ mSession.onInlineSuggestionsResponseLocked(
+ InlineFillUi.emptyUi(mSession.getAutofillIdLocked()));
mSession.destroySessionLocked();
+ mInlineFillUi = null;
}
// TODO(b/151123764): consider reusing the same AutofillInlineSession object for the
// same field.
mSession = new AutofillInlineSuggestionsRequestSession(mInputMethodManagerInternal, mUserId,
mComponentName, mHandler, mLock, autofillId, requestConsumer, uiExtras);
mSession.onCreateInlineSuggestionsRequestLocked();
-
}
/**
@@ -101,30 +105,63 @@
/**
* Requests the IME to hide the current suggestions, if any. Returns true if the message is sent
- * to the IME.
+ * to the IME. This only hides the UI temporarily. For example if user starts typing/deleting
+ * characters, new filterText will kick in and may revive the suggestion UI.
*/
@GuardedBy("mLock")
boolean hideInlineSuggestionsUiLocked(@NonNull AutofillId autofillId) {
if (mSession != null) {
- return mSession.onInlineSuggestionsResponseLocked(autofillId,
- new InlineSuggestionsResponse(Collections.EMPTY_LIST));
+ return mSession.onInlineSuggestionsResponseLocked(InlineFillUi.emptyUi(autofillId));
}
return false;
}
/**
- * Requests showing the inline suggestion in the IME when the IME becomes visible and is focused
- * on the {@code autofillId}.
- *
- * @return false if there is no session, or if the IME callback is not available in the session.
+ * Permanently delete the current inline fill UI. Notify the IME to hide the suggestions as
+ * well.
*/
@GuardedBy("mLock")
- boolean onInlineSuggestionsResponseLocked(@NonNull AutofillId autofillId,
- @NonNull InlineSuggestionsResponse inlineSuggestionsResponse) {
- // TODO(b/151123764): rename the method to better reflect what it does.
- if (mSession != null) {
- return mSession.onInlineSuggestionsResponseLocked(autofillId,
- inlineSuggestionsResponse);
+ boolean deleteInlineFillUiLocked(@NonNull AutofillId autofillId) {
+ mInlineFillUi = null;
+ return hideInlineSuggestionsUiLocked(autofillId);
+ }
+
+ /**
+ * Updates the inline fill UI with the filter text. It'll send updated inline suggestions to
+ * the IME.
+ */
+ @GuardedBy("mLock")
+ boolean filterInlineFillUiLocked(@NonNull AutofillId autofillId, @Nullable String filterText) {
+ if (mInlineFillUi != null && mInlineFillUi.getAutofillId().equals(autofillId)) {
+ mInlineFillUi.setFilterText(filterText);
+ return requestImeToShowInlineSuggestionsLocked();
+ }
+ return false;
+ }
+
+ /**
+ * Set the current inline fill UI. It'll request the IME to show the inline suggestions when
+ * the IME becomes visible and is focused on the {@code autofillId}.
+ *
+ * @return false if the suggestions are not sent to IME because there is no session, or if the
+ * IME callback is not available in the session.
+ */
+ @GuardedBy("mLock")
+ boolean setInlineFillUiLocked(@NonNull InlineFillUi inlineFillUi) {
+ mInlineFillUi = inlineFillUi;
+ return requestImeToShowInlineSuggestionsLocked();
+ }
+
+ /**
+ * Sends the suggestions from the current inline fill UI to the IME.
+ *
+ * @return false if the suggestions are not sent to IME because there is no session, or if the
+ * IME callback is not available in the session.
+ */
+ @GuardedBy("mLock")
+ private boolean requestImeToShowInlineSuggestionsLocked() {
+ if (mSession != null && mInlineFillUi != null) {
+ return mSession.onInlineSuggestionsResponseLocked(mInlineFillUi);
}
return false;
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
index ce11c76..0bf8993 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
@@ -27,7 +27,6 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
-import android.util.Log;
import android.util.Slog;
import android.view.autofill.AutofillId;
import android.view.inputmethod.InlineSuggestionsRequest;
@@ -37,6 +36,7 @@
import com.android.internal.view.IInlineSuggestionsRequestCallback;
import com.android.internal.view.IInlineSuggestionsResponseCallback;
import com.android.internal.view.InlineSuggestionsRequestInfo;
+import com.android.server.autofill.ui.InlineFillUi;
import com.android.server.inputmethod.InputMethodManagerInternal;
import java.lang.ref.WeakReference;
@@ -105,7 +105,7 @@
private boolean mImeInputViewStarted;
@GuardedBy("mLock")
@Nullable
- private InlineSuggestionsResponse mInlineSuggestionsResponse;
+ private InlineFillUi mInlineFillUi;
@GuardedBy("mLock")
private boolean mPreviousResponseIsNotEmpty;
@@ -155,18 +155,20 @@
* @return false if the IME callback is not available.
*/
@GuardedBy("mLock")
- boolean onInlineSuggestionsResponseLocked(@NonNull AutofillId autofillId,
- @NonNull InlineSuggestionsResponse inlineSuggestionsResponse) {
+ boolean onInlineSuggestionsResponseLocked(@NonNull InlineFillUi inlineFillUi) {
if (mDestroyed) {
return false;
}
- if (sDebug) Log.d(TAG, "onInlineSuggestionsResponseLocked called for:" + autofillId);
+ if (sDebug) {
+ Slog.d(TAG,
+ "onInlineSuggestionsResponseLocked called for:" + inlineFillUi.getAutofillId());
+ }
if (mImeRequest == null || mResponseCallback == null) {
return false;
}
// TODO(b/151123764): each session should only correspond to one field.
- mAutofillId = autofillId;
- mInlineSuggestionsResponse = inlineSuggestionsResponse;
+ mAutofillId = inlineFillUi.getAutofillId();
+ mInlineFillUi = inlineFillUi;
maybeUpdateResponseToImeLocked();
return true;
}
@@ -190,12 +192,12 @@
if (mDestroyed) {
return;
}
- if (sDebug) Log.d(TAG, "onCreateInlineSuggestionsRequestLocked called: " + mAutofillId);
+ if (sDebug) Slog.d(TAG, "onCreateInlineSuggestionsRequestLocked called: " + mAutofillId);
mInputMethodManagerInternal.onCreateInlineSuggestionsRequest(mUserId,
new InlineSuggestionsRequestInfo(mComponentName, mAutofillId, mUiExtras),
new InlineSuggestionsRequestCallbackImpl(this));
mTimeoutCallback = () -> {
- Log.w(TAG, "Timed out waiting for IME callback InlineSuggestionsRequest.");
+ Slog.w(TAG, "Timed out waiting for IME callback InlineSuggestionsRequest.");
handleOnReceiveImeRequest(null, null);
};
mHandler.postDelayed(mTimeoutCallback, CREATE_INLINE_SUGGESTIONS_REQUEST_TIMEOUT_MS);
@@ -206,7 +208,7 @@
*/
@GuardedBy("mLock")
private void maybeUpdateResponseToImeLocked() {
- if (sVerbose) Log.v(TAG, "maybeUpdateResponseToImeLocked called");
+ if (sVerbose) Slog.v(TAG, "maybeUpdateResponseToImeLocked called");
if (mDestroyed || mResponseCallback == null) {
return;
}
@@ -216,18 +218,19 @@
// Although the inline suggestions should disappear when IME hides which removes them
// from the view hierarchy, but we still send an empty response to be extra safe.
- if (sVerbose) Log.v(TAG, "Send empty inline response");
+ if (sVerbose) Slog.v(TAG, "Send empty inline response");
updateResponseToImeUncheckLocked(new InlineSuggestionsResponse(Collections.EMPTY_LIST));
mPreviousResponseIsNotEmpty = false;
- } else if (mImeInputViewStarted && mInlineSuggestionsResponse != null && match(mAutofillId,
+ } else if (mImeInputViewStarted && mInlineFillUi != null && match(mAutofillId,
mImeCurrentFieldId)) {
// 2. if IME is visible, and response is not null, send the response
- boolean isEmptyResponse = mInlineSuggestionsResponse.getInlineSuggestions().isEmpty();
+ InlineSuggestionsResponse response = mInlineFillUi.getInlineSuggestionsResponse();
+ boolean isEmptyResponse = response.getInlineSuggestions().isEmpty();
if (isEmptyResponse && !mPreviousResponseIsNotEmpty) {
// No-op if both the previous response and current response are empty.
return;
}
- updateResponseToImeUncheckLocked(mInlineSuggestionsResponse);
+ updateResponseToImeUncheckLocked(response);
mPreviousResponseIsNotEmpty = !isEmptyResponse;
}
}
@@ -240,7 +243,7 @@
if (mDestroyed) {
return;
}
- if (sDebug) Log.d(TAG, "Send inline response: " + response.getInlineSuggestions().size());
+ if (sDebug) Slog.d(TAG, "Send inline response: " + response.getInlineSuggestions().size());
try {
mResponseCallback.onInlineSuggestionsResponse(mAutofillId, response);
} catch (RemoteException e) {
@@ -262,7 +265,7 @@
mImeRequestReceived = true;
if (mTimeoutCallback != null) {
- if (sVerbose) Log.v(TAG, "removing timeout callback");
+ if (sVerbose) Slog.v(TAG, "removing timeout callback");
mHandler.removeCallbacks(mTimeoutCallback);
mTimeoutCallback = null;
}
@@ -333,7 +336,7 @@
@BinderThread
@Override
public void onInlineSuggestionsUnsupported() throws RemoteException {
- if (sDebug) Log.d(TAG, "onInlineSuggestionsUnsupported() called.");
+ if (sDebug) Slog.d(TAG, "onInlineSuggestionsUnsupported() called.");
final AutofillInlineSuggestionsRequestSession session = mSession.get();
if (session != null) {
session.mHandler.sendMessage(obtainMessage(
@@ -346,7 +349,7 @@
@Override
public void onInlineSuggestionsRequest(InlineSuggestionsRequest request,
IInlineSuggestionsResponseCallback callback) {
- if (sDebug) Log.d(TAG, "onInlineSuggestionsRequest() received: " + request);
+ if (sDebug) Slog.d(TAG, "onInlineSuggestionsRequest() received: " + request);
final AutofillInlineSuggestionsRequestSession session = mSession.get();
if (session != null) {
session.mHandler.sendMessage(obtainMessage(
@@ -357,7 +360,7 @@
@Override
public void onInputMethodStartInput(AutofillId imeFieldId) throws RemoteException {
- if (sVerbose) Log.v(TAG, "onInputMethodStartInput() received on " + imeFieldId);
+ if (sVerbose) Slog.v(TAG, "onInputMethodStartInput() received on " + imeFieldId);
final AutofillInlineSuggestionsRequestSession session = mSession.get();
if (session != null) {
session.mHandler.sendMessage(obtainMessage(
@@ -369,14 +372,14 @@
@Override
public void onInputMethodShowInputRequested(boolean requestResult) throws RemoteException {
if (sVerbose) {
- Log.v(TAG, "onInputMethodShowInputRequested() received: " + requestResult);
+ Slog.v(TAG, "onInputMethodShowInputRequested() received: " + requestResult);
}
}
@BinderThread
@Override
public void onInputMethodStartInputView() {
- if (sVerbose) Log.v(TAG, "onInputMethodStartInputView() received");
+ if (sVerbose) Slog.v(TAG, "onInputMethodStartInputView() received");
final AutofillInlineSuggestionsRequestSession session = mSession.get();
if (session != null) {
session.mHandler.sendMessage(obtainMessage(
@@ -388,7 +391,7 @@
@BinderThread
@Override
public void onInputMethodFinishInputView() {
- if (sVerbose) Log.v(TAG, "onInputMethodFinishInputView() received");
+ if (sVerbose) Slog.v(TAG, "onInputMethodFinishInputView() received");
final AutofillInlineSuggestionsRequestSession session = mSession.get();
if (session != null) {
session.mHandler.sendMessage(obtainMessage(
@@ -399,7 +402,7 @@
@Override
public void onInputMethodFinishInput() throws RemoteException {
- if (sVerbose) Log.v(TAG, "onInputMethodFinishInput() received");
+ if (sVerbose) Slog.v(TAG, "onInputMethodFinishInput() received");
final AutofillInlineSuggestionsRequestSession session = mSession.get();
if (session != null) {
session.mHandler.sendMessage(obtainMessage(
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 1bc026c..1aeb19b 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -260,7 +260,7 @@
if (isEnabledLocked()) return FLAG_ADD_CLIENT_ENABLED;
// Check if it's enabled for augmented autofill
- if (isAugmentedAutofillServiceAvailableLocked()
+ if (componentName != null && isAugmentedAutofillServiceAvailableLocked()
&& isWhitelistedForAugmentedAutofillLocked(componentName)) {
return FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY;
}
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index 6cec8d8..851e4cc 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -48,17 +48,15 @@
import android.view.autofill.AutofillValue;
import android.view.autofill.IAutoFillManagerClient;
import android.view.inputmethod.InlineSuggestionsRequest;
-import android.view.inputmethod.InlineSuggestionsResponse;
import com.android.internal.infra.AbstractRemoteService;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.infra.ServiceConnector;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.os.IResultReceiver;
-import com.android.server.autofill.ui.InlineSuggestionFactory;
+import com.android.server.autofill.ui.InlineFillUi;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.TimeUnit;
@@ -149,7 +147,7 @@
int taskId, @NonNull ComponentName activityComponent, @NonNull AutofillId focusedId,
@Nullable AutofillValue focusedValue,
@Nullable InlineSuggestionsRequest inlineSuggestionsRequest,
- @Nullable Function<InlineSuggestionsResponse, Boolean> inlineSuggestionsCallback,
+ @Nullable Function<InlineFillUi, Boolean> inlineSuggestionsCallback,
@NonNull Runnable onErrorCallback,
@Nullable RemoteInlineSuggestionRenderService remoteRenderService) {
long requestTime = SystemClock.elapsedRealtime();
@@ -173,7 +171,8 @@
mCallbacks.resetLastResponse();
maybeRequestShowInlineSuggestions(sessionId,
inlineSuggestionsRequest, inlineSuggestionsData,
- clientState, focusedId, inlineSuggestionsCallback,
+ clientState, focusedId, focusedValue,
+ inlineSuggestionsCallback,
client, onErrorCallback, remoteRenderService);
requestAutofill.complete(null);
}
@@ -239,8 +238,8 @@
private void maybeRequestShowInlineSuggestions(int sessionId,
@Nullable InlineSuggestionsRequest request,
@Nullable List<Dataset> inlineSuggestionsData, @Nullable Bundle clientState,
- @NonNull AutofillId focusedId,
- @Nullable Function<InlineSuggestionsResponse, Boolean> inlineSuggestionsCallback,
+ @NonNull AutofillId focusedId, @Nullable AutofillValue focusedValue,
+ @Nullable Function<InlineFillUi, Boolean> inlineSuggestionsCallback,
@NonNull IAutoFillManagerClient client, @NonNull Runnable onErrorCallback,
@Nullable RemoteInlineSuggestionRenderService remoteRenderService) {
if (inlineSuggestionsData == null || inlineSuggestionsData.isEmpty()
@@ -250,10 +249,14 @@
}
mCallbacks.setLastResponse(sessionId);
- final InlineSuggestionsResponse inlineSuggestionsResponse =
- InlineSuggestionFactory.createAugmentedInlineSuggestionsResponse(
- request, inlineSuggestionsData, focusedId,
- new InlineSuggestionFactory.InlineSuggestionUiCallback() {
+ final String filterText =
+ focusedValue != null && focusedValue.isText()
+ ? focusedValue.getTextValue().toString() : null;
+
+ final InlineFillUi inlineFillUi =
+ InlineFillUi.forAugmentedAutofill(
+ request, inlineSuggestionsData, focusedId, filterText,
+ new InlineFillUi.InlineSuggestionUiCallback() {
@Override
public void autofill(Dataset dataset) {
mCallbacks.logAugmentedAutofillSelected(sessionId,
@@ -265,8 +268,8 @@
&& fieldIds.get(0).equals(focusedId);
client.autofill(sessionId, fieldIds, dataset.getFieldValues(),
hideHighlight);
- inlineSuggestionsCallback.apply(new InlineSuggestionsResponse(
- Collections.EMPTY_LIST));
+ inlineSuggestionsCallback.apply(
+ InlineFillUi.emptyUi(focusedId));
} catch (RemoteException e) {
Slog.w(TAG, "Encounter exception autofilling the values");
}
@@ -283,11 +286,7 @@
}
}, onErrorCallback, remoteRenderService);
- if (inlineSuggestionsResponse == null) {
- Slog.w(TAG, "InlineSuggestionFactory created null response");
- return;
- }
- if (inlineSuggestionsCallback.apply(inlineSuggestionsResponse)) {
+ if (inlineSuggestionsCallback.apply(inlineFillUi)) {
mCallbacks.logAugmentedAutofillShown(sessionId, clientState);
}
}
diff --git a/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java b/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java
index 255adcd..617c111 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java
@@ -47,7 +47,7 @@
private static final String TAG = "RemoteInlineSuggestionRenderService";
- private final int mIdleUnbindTimeoutMs = 5000;
+ private final long mIdleUnbindTimeoutMs = PERMANENT_BOUND_TIMEOUT_MS;
RemoteInlineSuggestionRenderService(Context context, ComponentName componentName,
String serviceInterface, int userId, InlineSuggestionRenderCallbacks callback,
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 20d1b98..ff4e7ba 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -94,7 +94,6 @@
import android.view.autofill.IAutoFillManagerClient;
import android.view.autofill.IAutofillWindowPresenter;
import android.view.inputmethod.InlineSuggestionsRequest;
-import android.view.inputmethod.InlineSuggestionsResponse;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -102,7 +101,7 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.ArrayUtils;
import com.android.server.autofill.ui.AutoFillUI;
-import com.android.server.autofill.ui.InlineSuggestionFactory;
+import com.android.server.autofill.ui.InlineFillUi;
import com.android.server.autofill.ui.PendingUi;
import com.android.server.inputmethod.InputMethodManagerInternal;
@@ -2662,10 +2661,20 @@
}
} else if (viewState.id.equals(this.mCurrentViewId)
&& (viewState.getState() & ViewState.STATE_INLINE_SHOWN) != 0) {
- requestShowInlineSuggestionsLocked(viewState.getResponse(), filterText);
+ if ((viewState.getState() & ViewState.STATE_INLINE_DISABLED) != 0) {
+ final FillResponse response = viewState.getResponse();
+ if (response != null) {
+ response.getDatasets().clear();
+ }
+ mInlineSessionController.deleteInlineFillUiLocked(viewState.id);
+ } else {
+ mInlineSessionController.filterInlineFillUiLocked(mCurrentViewId, filterText);
+ }
} else if (viewState.id.equals(this.mCurrentViewId)
&& (viewState.getState() & ViewState.STATE_TRIGGERED_AUGMENTED_AUTOFILL) != 0) {
if (!TextUtils.isEmpty(filterText)) {
+ // TODO: we should be able to replace this with controller#filterInlineFillUiLocked
+ // to accomplish filtering for augmented autofill.
mInlineSessionController.hideInlineSuggestionsUiLocked(mCurrentViewId);
}
}
@@ -2816,26 +2825,15 @@
return false;
}
- final ViewState currentView = mViewStates.get(focusedId);
- if ((currentView.getState() & ViewState.STATE_INLINE_DISABLED) != 0) {
- response.getDatasets().clear();
- }
- InlineSuggestionsResponse inlineSuggestionsResponse =
- InlineSuggestionFactory.createInlineSuggestionsResponse(
- inlineSuggestionsRequest.get(), response, filterText, focusedId,
- this, () -> {
- synchronized (mLock) {
- mInlineSessionController.hideInlineSuggestionsUiLocked(
- focusedId);
- }
- }, remoteRenderService);
- if (inlineSuggestionsResponse == null) {
- Slog.w(TAG, "InlineSuggestionFactory created null response");
- return false;
- }
-
- return mInlineSessionController.onInlineSuggestionsResponseLocked(focusedId,
- inlineSuggestionsResponse);
+ InlineFillUi inlineFillUi = InlineFillUi.forAutofill(
+ inlineSuggestionsRequest.get(), response, focusedId, filterText,
+ /*uiCallback*/this, /*onErrorCallback*/ () -> {
+ synchronized (mLock) {
+ mInlineSessionController.hideInlineSuggestionsUiLocked(
+ focusedId);
+ }
+ }, remoteRenderService);
+ return mInlineSessionController.setInlineFillUiLocked(inlineFillUi);
}
boolean isDestroyed() {
@@ -3119,11 +3117,10 @@
final AutofillId focusedId = mCurrentViewId;
- final Function<InlineSuggestionsResponse, Boolean> inlineSuggestionsResponseCallback =
+ final Function<InlineFillUi, Boolean> inlineSuggestionsResponseCallback =
response -> {
synchronized (mLock) {
- return mInlineSessionController.onInlineSuggestionsResponseLocked(
- focusedId, response);
+ return mInlineSessionController.setInlineFillUiLocked(response);
}
};
final Consumer<InlineSuggestionsRequest> requestAugmentedAutofill =
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index 344b92f..8902087 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -130,9 +130,9 @@
}
FillUi(@NonNull Context context, @NonNull FillResponse response,
- @NonNull AutofillId focusedViewId, @NonNull @Nullable String filterText,
- @NonNull OverlayControl overlayControl, @NonNull CharSequence serviceLabel,
- @NonNull Drawable serviceIcon, boolean nightMode, @NonNull Callback callback) {
+ @NonNull AutofillId focusedViewId, @Nullable String filterText,
+ @NonNull OverlayControl overlayControl, @NonNull CharSequence serviceLabel,
+ @NonNull Drawable serviceIcon, boolean nightMode, @NonNull Callback callback) {
if (sVerbose) Slog.v(TAG, "nightMode: " + nightMode);
mThemeId = nightMode ? THEME_ID_DARK : THEME_ID_LIGHT;
mCallback = callback;
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineContentProviderImpl.java b/services/autofill/java/com/android/server/autofill/ui/InlineContentProviderImpl.java
new file mode 100644
index 0000000..7fbf4b9
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineContentProviderImpl.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2020 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.server.autofill.ui;
+
+import static com.android.server.autofill.Helper.sVerbose;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Handler;
+import android.util.Slog;
+
+import com.android.internal.view.inline.IInlineContentCallback;
+import com.android.internal.view.inline.IInlineContentProvider;
+import com.android.server.FgThread;
+
+/**
+ * We create one instance of this class for each {@link android.view.inputmethod.InlineSuggestion}
+ * instance. Each inline suggestion instance will only be sent to the remote IME process once. In
+ * case of filtering and resending the suggestion when keyboard state changes between hide and
+ * show, a new instance of this class will be created using {@link #copy()}, with the same backing
+ * {@link RemoteInlineSuggestionUi}. When the
+ * {@link #provideContent(int, int, IInlineContentCallback)} is called the first time (it's only
+ * allowed to be called at most once), the passed in width/height is used to determine whether
+ * the existing {@link RemoteInlineSuggestionUi} provided in the constructor can be reused, or a
+ * new one should be created to suit the new size requirement for the view. In normal cases,
+ * we should not expect the size requirement to change, although in theory the public API allows
+ * the IME to do that.
+ *
+ * <p>This design is to enable us to be able to reuse the backing remote view while still keeping
+ * the callbacks relatively well aligned. For example, if we allow multiple remote IME binder
+ * callbacks to call into one instance of this class, then binder A may call in with width/height
+ * X for which we create a view (i.e. {@link RemoteInlineSuggestionUi}) for it,
+ *
+ * See also {@link RemoteInlineSuggestionUi} for relevant information.
+ */
+final class InlineContentProviderImpl extends IInlineContentProvider.Stub {
+
+ // TODO(b/153615023): consider not holding strong reference to heavy objects in this stub, to
+ // avoid memory leak in case the client app is holding the remote reference for a longer
+ // time than expected. Essentially we need strong reference in the system process to
+ // the member variables, but weak reference to them in the IInlineContentProvider.Stub.
+
+ private static final String TAG = InlineContentProviderImpl.class.getSimpleName();
+
+ private final Handler mHandler = FgThread.getHandler();;
+
+ @NonNull
+ private final RemoteInlineSuggestionViewConnector mRemoteInlineSuggestionViewConnector;
+ @Nullable
+ private RemoteInlineSuggestionUi mRemoteInlineSuggestionUi;
+
+ private boolean mProvideContentCalled = false;
+
+ InlineContentProviderImpl(
+ @NonNull RemoteInlineSuggestionViewConnector remoteInlineSuggestionViewConnector,
+ @Nullable RemoteInlineSuggestionUi remoteInlineSuggestionUi) {
+ mRemoteInlineSuggestionViewConnector = remoteInlineSuggestionViewConnector;
+ mRemoteInlineSuggestionUi = remoteInlineSuggestionUi;
+ }
+
+ /**
+ * Returns a new instance of this class, with the same {@code mInlineSuggestionRenderer} and
+ * {@code mRemoteInlineSuggestionUi}. The latter may or may not be reusable depending on the
+ * size information provided when the client calls {@link #provideContent(int, int,
+ * IInlineContentCallback)}.
+ */
+ @NonNull
+ public InlineContentProviderImpl copy() {
+ return new InlineContentProviderImpl(mRemoteInlineSuggestionViewConnector,
+ mRemoteInlineSuggestionUi);
+ }
+
+ /**
+ * Provides a SurfacePackage associated with the inline suggestion view to the IME. If such
+ * view doesn't exit, then create a new one. This method should be called once per lifecycle
+ * of this object. Any further calls to the method will be ignored.
+ */
+ @Override
+ public void provideContent(int width, int height, IInlineContentCallback callback) {
+ mHandler.post(() -> handleProvideContent(width, height, callback));
+ }
+
+ @Override
+ public void requestSurfacePackage() {
+ mHandler.post(this::handleGetSurfacePackage);
+ }
+
+ @Override
+ public void onSurfacePackageReleased() {
+ mHandler.post(this::handleOnSurfacePackageReleased);
+ }
+
+ private void handleProvideContent(int width, int height, IInlineContentCallback callback) {
+ if (sVerbose) Slog.v(TAG, "handleProvideContent");
+ if (mProvideContentCalled) {
+ // This method should only be called once.
+ return;
+ }
+ mProvideContentCalled = true;
+ if (mRemoteInlineSuggestionUi == null || !mRemoteInlineSuggestionUi.match(width, height)) {
+ mRemoteInlineSuggestionUi = new RemoteInlineSuggestionUi(
+ mRemoteInlineSuggestionViewConnector,
+ width, height, mHandler);
+ }
+ mRemoteInlineSuggestionUi.setInlineContentCallback(callback);
+ mRemoteInlineSuggestionUi.requestSurfacePackage();
+ }
+
+ private void handleGetSurfacePackage() {
+ if (sVerbose) Slog.v(TAG, "handleGetSurfacePackage");
+ if (!mProvideContentCalled || mRemoteInlineSuggestionUi == null) {
+ // provideContent should be called first, and remote UI should not be null.
+ return;
+ }
+ mRemoteInlineSuggestionUi.requestSurfacePackage();
+ }
+
+ private void handleOnSurfacePackageReleased() {
+ if (sVerbose) Slog.v(TAG, "handleOnSurfacePackageReleased");
+ if (!mProvideContentCalled || mRemoteInlineSuggestionUi == null) {
+ // provideContent should be called first, and remote UI should not be null.
+ return;
+ }
+ mRemoteInlineSuggestionUi.surfacePackageReleased();
+ }
+}
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java b/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java
new file mode 100644
index 0000000..6522522
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2020 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.server.autofill.ui;
+
+import static com.android.server.autofill.Helper.sVerbose;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.service.autofill.Dataset;
+import android.service.autofill.FillResponse;
+import android.service.autofill.InlinePresentation;
+import android.text.TextUtils;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+import android.view.inputmethod.InlineSuggestion;
+import android.view.inputmethod.InlineSuggestionsRequest;
+import android.view.inputmethod.InlineSuggestionsResponse;
+
+import com.android.internal.view.inline.IInlineContentProvider;
+import com.android.server.autofill.RemoteInlineSuggestionRenderService;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Pattern;
+
+
+/**
+ * UI for a particular field (i.e. {@link AutofillId}) based on an inline autofill response from
+ * the autofill service or the augmented autofill service. It wraps multiple inline suggestions.
+ *
+ * <p> This class is responsible for filtering the suggestions based on the filtered text.
+ * It'll create {@link InlineSuggestion} instances by reusing the backing remote views (from the
+ * renderer service) if possible.
+ */
+public final class InlineFillUi {
+
+ private static final String TAG = "InlineFillUi";
+
+ /**
+ * The id of the field which the current Ui is for.
+ */
+ @NonNull
+ final AutofillId mAutofillId;
+
+ /**
+ * The list of inline suggestions, before applying any filtering
+ */
+ @NonNull
+ private final ArrayList<InlineSuggestion> mInlineSuggestions;
+
+ /**
+ * The corresponding data sets for the inline suggestions. The list may be null if the current
+ * Ui is the authentication UI for the response. If non-null, the size of data sets should equal
+ * that of inline suggestions.
+ */
+ @Nullable
+ private final ArrayList<Dataset> mDatasets;
+
+ /**
+ * The filter text which will be applied on the inline suggestion list before they are returned
+ * as a response.
+ */
+ @Nullable
+ private String mFilterText;
+
+ /**
+ * Returns an empty inline autofill UI.
+ */
+ @NonNull
+ public static InlineFillUi emptyUi(@NonNull AutofillId autofillId) {
+ return new InlineFillUi(autofillId, new SparseArray<>(), null);
+ }
+
+ /**
+ * Returns an inline autofill UI for a field based on an Autofilll response.
+ */
+ @NonNull
+ public static InlineFillUi forAutofill(@NonNull InlineSuggestionsRequest request,
+ @NonNull FillResponse response,
+ @NonNull AutofillId focusedViewId, @Nullable String filterText,
+ @NonNull AutoFillUI.AutoFillUiCallback uiCallback,
+ @NonNull Runnable onErrorCallback,
+ @Nullable RemoteInlineSuggestionRenderService remoteRenderService) {
+
+ if (InlineSuggestionFactory.responseNeedAuthentication(response)) {
+ InlineSuggestion inlineAuthentication =
+ InlineSuggestionFactory.createInlineAuthentication(request, response,
+ focusedViewId, uiCallback, onErrorCallback, remoteRenderService);
+ return new InlineFillUi(focusedViewId, inlineAuthentication, filterText);
+ } else if (response.getDatasets() != null) {
+ SparseArray<Pair<Dataset, InlineSuggestion>> inlineSuggestions =
+ InlineSuggestionFactory.createAutofillInlineSuggestions(request,
+ response.getRequestId(),
+ response.getDatasets(), focusedViewId, uiCallback, onErrorCallback,
+ remoteRenderService);
+ return new InlineFillUi(focusedViewId, inlineSuggestions, filterText);
+ }
+ return new InlineFillUi(focusedViewId, new SparseArray<>(), filterText);
+ }
+
+ /**
+ * Returns an inline autofill UI for a field based on an Autofilll response.
+ */
+ @NonNull
+ public static InlineFillUi forAugmentedAutofill(@NonNull InlineSuggestionsRequest request,
+ @NonNull List<Dataset> datasets,
+ @NonNull AutofillId focusedViewId, @Nullable String filterText,
+ @NonNull InlineSuggestionUiCallback uiCallback,
+ @NonNull Runnable onErrorCallback,
+ @Nullable RemoteInlineSuggestionRenderService remoteRenderService) {
+ SparseArray<Pair<Dataset, InlineSuggestion>> inlineSuggestions =
+ InlineSuggestionFactory.createAugmentedAutofillInlineSuggestions(request, datasets,
+ focusedViewId,
+ uiCallback, onErrorCallback, remoteRenderService);
+ return new InlineFillUi(focusedViewId, inlineSuggestions, filterText);
+ }
+
+ InlineFillUi(@NonNull AutofillId autofillId,
+ @NonNull SparseArray<Pair<Dataset, InlineSuggestion>> inlineSuggestions,
+ @Nullable String filterText) {
+ mAutofillId = autofillId;
+ int size = inlineSuggestions.size();
+ mDatasets = new ArrayList<>(size);
+ mInlineSuggestions = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ Pair<Dataset, InlineSuggestion> value = inlineSuggestions.valueAt(i);
+ mDatasets.add(value.first);
+ mInlineSuggestions.add(value.second);
+ }
+ mFilterText = filterText;
+ }
+
+ InlineFillUi(@NonNull AutofillId autofillId, InlineSuggestion inlineSuggestion,
+ @Nullable String filterText) {
+ mAutofillId = autofillId;
+ mDatasets = null;
+ mInlineSuggestions = new ArrayList<>();
+ mInlineSuggestions.add(inlineSuggestion);
+ mFilterText = filterText;
+ }
+
+ @NonNull
+ public AutofillId getAutofillId() {
+ return mAutofillId;
+ }
+
+ public void setFilterText(@Nullable String filterText) {
+ mFilterText = filterText;
+ }
+
+ /**
+ * Returns the list of filtered inline suggestions suitable for being sent to the IME.
+ */
+ @NonNull
+ public InlineSuggestionsResponse getInlineSuggestionsResponse() {
+ final int size = mInlineSuggestions.size();
+ if (size == 0) {
+ return new InlineSuggestionsResponse(Collections.emptyList());
+ }
+ final List<InlineSuggestion> inlineSuggestions = new ArrayList<>();
+ if (mDatasets == null || mDatasets.size() != size) {
+ // authentication case
+ for (int i = 0; i < size; i++) {
+ inlineSuggestions.add(copy(i, mInlineSuggestions.get(i)));
+ }
+ return new InlineSuggestionsResponse(inlineSuggestions);
+ }
+ for (int i = 0; i < size; i++) {
+ final Dataset dataset = mDatasets.get(i);
+ final int fieldIndex = dataset.getFieldIds().indexOf(mAutofillId);
+ if (fieldIndex < 0) {
+ Slog.w(TAG, "AutofillId=" + mAutofillId + " not found in dataset");
+ continue;
+ }
+ final InlinePresentation inlinePresentation = dataset.getFieldInlinePresentation(
+ fieldIndex);
+ if (inlinePresentation == null) {
+ Slog.w(TAG, "InlinePresentation not found in dataset");
+ continue;
+ }
+ if (!inlinePresentation.isPinned() // don't filter pinned suggestions
+ && !includeDataset(dataset, fieldIndex, mFilterText)) {
+ continue;
+ }
+ inlineSuggestions.add(copy(i, mInlineSuggestions.get(i)));
+ }
+ return new InlineSuggestionsResponse(inlineSuggestions);
+ }
+
+ /**
+ * Returns a copy of the suggestion, that internally copies the {@link IInlineContentProvider}
+ * so that it's not reused by the remote IME process across different inline suggestions.
+ * See {@link InlineContentProviderImpl} for why this is needed.
+ *
+ * <p>Note that although it copies the {@link IInlineContentProvider}, the underlying remote
+ * view (in the renderer service) is still reused.
+ */
+ @NonNull
+ private InlineSuggestion copy(int index, @NonNull InlineSuggestion inlineSuggestion) {
+ final IInlineContentProvider contentProvider = inlineSuggestion.getContentProvider();
+ if (contentProvider instanceof InlineContentProviderImpl) {
+ // We have to create a new inline suggestion instance to ensure we don't reuse the
+ // same {@link IInlineContentProvider}, but the underlying views are reused when
+ // calling {@link InlineContentProviderImpl#copy()}.
+ InlineSuggestion newInlineSuggestion = new InlineSuggestion(inlineSuggestion
+ .getInfo(), ((InlineContentProviderImpl) contentProvider).copy());
+ // The remote view is only set when the content provider is called to inflate the view,
+ // which happens after it's sent to the IME (i.e. not now), so we keep the latest
+ // content provider (through newInlineSuggestion) to make sure the next time we copy it,
+ // we get to reuse the view.
+ mInlineSuggestions.set(index, newInlineSuggestion);
+ return newInlineSuggestion;
+ }
+ return inlineSuggestion;
+ }
+
+ // TODO: Extract the shared filtering logic here and in FillUi to a common method.
+ private static boolean includeDataset(Dataset dataset, int fieldIndex,
+ @Nullable String filterText) {
+ // Show everything when the user input is empty.
+ if (TextUtils.isEmpty(filterText)) {
+ return true;
+ }
+
+ final String constraintLowerCase = filterText.toString().toLowerCase();
+
+ // Use the filter provided by the service, if available.
+ final Dataset.DatasetFieldFilter filter = dataset.getFilter(fieldIndex);
+ if (filter != null) {
+ Pattern filterPattern = filter.pattern;
+ if (filterPattern == null) {
+ if (sVerbose) {
+ Slog.v(TAG, "Explicitly disabling filter for dataset id" + dataset.getId());
+ }
+ return true;
+ }
+ return filterPattern.matcher(constraintLowerCase).matches();
+ }
+
+ final AutofillValue value = dataset.getFieldValues().get(fieldIndex);
+ if (value == null || !value.isText()) {
+ return dataset.getAuthentication() != null;
+ }
+ final String valueText = value.getTextValue().toString().toLowerCase();
+ return valueText.toLowerCase().startsWith(constraintLowerCase);
+ }
+
+ /**
+ * Callback from the inline suggestion Ui.
+ */
+ public interface InlineSuggestionUiCallback {
+ /**
+ * Callback to autofill a dataset to the client app.
+ */
+ void autofill(@NonNull Dataset dataset);
+
+ /**
+ * Callback to start Intent in client app.
+ */
+ void startIntentSender(@NonNull IntentSender intentSender, @NonNull Intent intent);
+ }
+}
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
index 79c9efa..089eeb2 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
@@ -17,59 +17,57 @@
package com.android.server.autofill.ui;
import static com.android.server.autofill.Helper.sDebug;
-import static com.android.server.autofill.Helper.sVerbose;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Intent;
import android.content.IntentSender;
import android.os.IBinder;
-import android.os.RemoteException;
import android.service.autofill.Dataset;
import android.service.autofill.FillResponse;
-import android.service.autofill.IInlineSuggestionUiCallback;
import android.service.autofill.InlinePresentation;
-import android.text.TextUtils;
+import android.util.Pair;
import android.util.Slog;
-import android.view.SurfaceControlViewHost;
+import android.util.SparseArray;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
-import android.view.autofill.AutofillValue;
import android.view.inputmethod.InlineSuggestion;
import android.view.inputmethod.InlineSuggestionInfo;
import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.inputmethod.InlineSuggestionsResponse;
import android.widget.inline.InlinePresentationSpec;
-import com.android.internal.view.inline.IInlineContentCallback;
import com.android.internal.view.inline.IInlineContentProvider;
-import com.android.server.LocalServices;
-import com.android.server.UiThread;
import com.android.server.autofill.RemoteInlineSuggestionRenderService;
-import com.android.server.inputmethod.InputMethodManagerInternal;
-import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
-import java.util.regex.Pattern;
-public final class InlineSuggestionFactory {
+final class InlineSuggestionFactory {
private static final String TAG = "InlineSuggestionFactory";
- /**
- * Callback from the inline suggestion Ui.
- */
- public interface InlineSuggestionUiCallback {
- /**
- * Callback to autofill a dataset to the client app.
- */
- void autofill(@NonNull Dataset dataset);
+ public static boolean responseNeedAuthentication(@NonNull FillResponse response) {
+ return response.getAuthentication() != null && response.getInlinePresentation() != null;
+ }
- /**
- * Callback to start Intent in client app.
- */
- void startIntentSender(@NonNull IntentSender intentSender, @NonNull Intent intent);
+ public static InlineSuggestion createInlineAuthentication(
+ @NonNull InlineSuggestionsRequest request, @NonNull FillResponse response,
+ @NonNull AutofillId autofillId,
+ @NonNull AutoFillUI.AutoFillUiCallback client, @NonNull Runnable onErrorCallback,
+ @Nullable RemoteInlineSuggestionRenderService remoteRenderService) {
+ final BiConsumer<Dataset, Integer> onClickFactory = (dataset, datasetIndex) -> {
+ client.requestHideFillUi(autofillId);
+ client.authenticate(response.getRequestId(),
+ datasetIndex, response.getAuthentication(), response.getClientState(),
+ /* authenticateInline= */ true);
+ };
+ final Consumer<IntentSender> intentSenderConsumer = (intentSender) ->
+ client.startIntentSender(intentSender, new Intent());
+ InlinePresentation inlineAuthentication = response.getInlinePresentation();
+ return createInlineAuthSuggestion(inlineAuthentication,
+ remoteRenderService, onClickFactory, onErrorCallback, intentSenderConsumer,
+ request.getHostInputToken(), request.getHostDisplayId());
}
/**
@@ -77,33 +75,23 @@
* autofill service, potentially filtering the datasets.
*/
@Nullable
- public static InlineSuggestionsResponse createInlineSuggestionsResponse(
- @NonNull InlineSuggestionsRequest request, @NonNull FillResponse response,
- @Nullable String filterText, @NonNull AutofillId autofillId,
+ public static SparseArray<Pair<Dataset, InlineSuggestion>> createAutofillInlineSuggestions(
+ @NonNull InlineSuggestionsRequest request, int requestId,
+ @NonNull List<Dataset> datasets,
+ @NonNull AutofillId autofillId,
@NonNull AutoFillUI.AutoFillUiCallback client, @NonNull Runnable onErrorCallback,
@Nullable RemoteInlineSuggestionRenderService remoteRenderService) {
if (sDebug) Slog.d(TAG, "createInlineSuggestionsResponse called");
- final BiConsumer<Dataset, Integer> onClickFactory;
- if (response.getAuthentication() != null) {
- onClickFactory = (dataset, datasetIndex) -> {
- client.requestHideFillUi(autofillId);
- client.authenticate(response.getRequestId(),
- datasetIndex, response.getAuthentication(), response.getClientState(),
- /* authenticateInline= */ true);
- };
- } else {
- onClickFactory = (dataset, datasetIndex) -> {
- client.requestHideFillUi(autofillId);
- client.fill(response.getRequestId(), datasetIndex, dataset);
- };
- }
+ final Consumer<IntentSender> intentSenderConsumer = (intentSender) ->
+ client.startIntentSender(intentSender, new Intent());
+ final BiConsumer<Dataset, Integer> onClickFactory = (dataset, datasetIndex) -> {
+ client.requestHideFillUi(autofillId);
+ client.fill(requestId, datasetIndex, dataset);
+ };
- final InlinePresentation inlineAuthentication =
- response.getAuthentication() == null ? null : response.getInlinePresentation();
- return createInlineSuggestionsResponseInternal(/* isAugmented= */ false, request,
- response.getDatasets(), filterText, inlineAuthentication, autofillId,
- onErrorCallback, onClickFactory, (intentSender) ->
- client.startIntentSender(intentSender, new Intent()), remoteRenderService);
+ return createInlineSuggestionsInternal(/* isAugmented= */ false, request,
+ datasets, autofillId,
+ onErrorCallback, onClickFactory, intentSenderConsumer, remoteRenderService);
}
/**
@@ -111,16 +99,16 @@
* autofill service.
*/
@Nullable
- public static InlineSuggestionsResponse createAugmentedInlineSuggestionsResponse(
+ public static SparseArray<Pair<Dataset, InlineSuggestion>>
+ createAugmentedAutofillInlineSuggestions(
@NonNull InlineSuggestionsRequest request, @NonNull List<Dataset> datasets,
@NonNull AutofillId autofillId,
- @NonNull InlineSuggestionUiCallback inlineSuggestionUiCallback,
+ @NonNull InlineFillUi.InlineSuggestionUiCallback inlineSuggestionUiCallback,
@NonNull Runnable onErrorCallback,
@Nullable RemoteInlineSuggestionRenderService remoteRenderService) {
if (sDebug) Slog.d(TAG, "createAugmentedInlineSuggestionsResponse called");
- return createInlineSuggestionsResponseInternal(/* isAugmented= */ true, request,
- datasets, /* filterText= */ null, /* inlineAuthentication= */ null,
- autofillId, onErrorCallback,
+ return createInlineSuggestionsInternal(/* isAugmented= */ true, request,
+ datasets, autofillId, onErrorCallback,
(dataset, datasetIndex) ->
inlineSuggestionUiCallback.autofill(dataset),
(intentSender) ->
@@ -129,29 +117,13 @@
}
@Nullable
- private static InlineSuggestionsResponse createInlineSuggestionsResponseInternal(
+ private static SparseArray<Pair<Dataset, InlineSuggestion>> createInlineSuggestionsInternal(
boolean isAugmented, @NonNull InlineSuggestionsRequest request,
- @Nullable List<Dataset> datasets, @Nullable String filterText,
- @Nullable InlinePresentation inlineAuthentication, @NonNull AutofillId autofillId,
+ @NonNull List<Dataset> datasets, @NonNull AutofillId autofillId,
@NonNull Runnable onErrorCallback, @NonNull BiConsumer<Dataset, Integer> onClickFactory,
@NonNull Consumer<IntentSender> intentSenderConsumer,
@Nullable RemoteInlineSuggestionRenderService remoteRenderService) {
-
- final ArrayList<InlineSuggestion> inlineSuggestions = new ArrayList<>();
- if (inlineAuthentication != null) {
- InlineSuggestion inlineAuthSuggestion = createInlineAuthSuggestion(inlineAuthentication,
- remoteRenderService, onClickFactory, onErrorCallback, intentSenderConsumer,
- request.getHostInputToken(), request.getHostDisplayId());
- inlineSuggestions.add(inlineAuthSuggestion);
-
- return new InlineSuggestionsResponse(inlineSuggestions);
- }
-
- if (datasets == null) {
- Slog.w(TAG, "Datasets should not be null here");
- return null;
- }
-
+ SparseArray<Pair<Dataset, InlineSuggestion>> response = new SparseArray<>(datasets.size());
for (int datasetIndex = 0; datasetIndex < datasets.size(); datasetIndex++) {
final Dataset dataset = datasets.get(datasetIndex);
final int fieldIndex = dataset.getFieldIds().indexOf(autofillId);
@@ -165,50 +137,14 @@
Slog.w(TAG, "InlinePresentation not found in dataset");
continue;
}
- if (!inlinePresentation.isPinned() // don't filter pinned suggestions
- && !includeDataset(dataset, fieldIndex, filterText)) {
- continue;
- }
InlineSuggestion inlineSuggestion = createInlineSuggestion(isAugmented, dataset,
datasetIndex,
mergedInlinePresentation(request, datasetIndex, inlinePresentation),
onClickFactory, remoteRenderService, onErrorCallback, intentSenderConsumer,
request.getHostInputToken(), request.getHostDisplayId());
-
- inlineSuggestions.add(inlineSuggestion);
+ response.append(datasetIndex, Pair.create(dataset, inlineSuggestion));
}
- return new InlineSuggestionsResponse(inlineSuggestions);
- }
-
- // TODO: Extract the shared filtering logic here and in FillUi to a common method.
- private static boolean includeDataset(Dataset dataset, int fieldIndex,
- @Nullable String filterText) {
- // Show everything when the user input is empty.
- if (TextUtils.isEmpty(filterText)) {
- return true;
- }
-
- final String constraintLowerCase = filterText.toString().toLowerCase();
-
- // Use the filter provided by the service, if available.
- final Dataset.DatasetFieldFilter filter = dataset.getFilter(fieldIndex);
- if (filter != null) {
- Pattern filterPattern = filter.pattern;
- if (filterPattern == null) {
- if (sVerbose) {
- Slog.v(TAG, "Explicitly disabling filter for dataset id" + dataset.getId());
- }
- return true;
- }
- return filterPattern.matcher(constraintLowerCase).matches();
- }
-
- final AutofillValue value = dataset.getFieldValues().get(fieldIndex);
- if (value == null || !value.isText()) {
- return dataset.getAuthentication() == null;
- }
- final String valueText = value.getTextValue().toString().toLowerCase();
- return valueText.toLowerCase().startsWith(constraintLowerCase);
+ return response;
}
private static InlineSuggestion createInlineSuggestion(boolean isAugmented,
@@ -276,78 +212,20 @@
inlinePresentation.isPinned());
}
- private static IInlineContentProvider.Stub createInlineContentProvider(
+ private static IInlineContentProvider createInlineContentProvider(
@NonNull InlinePresentation inlinePresentation, @Nullable Runnable onClickAction,
@NonNull Runnable onErrorCallback,
@NonNull Consumer<IntentSender> intentSenderConsumer,
@Nullable RemoteInlineSuggestionRenderService remoteRenderService,
@Nullable IBinder hostInputToken,
int displayId) {
- return new IInlineContentProvider.Stub() {
- @Override
- public void provideContent(int width, int height, IInlineContentCallback callback) {
- UiThread.getHandler().post(() -> {
- final IInlineSuggestionUiCallback uiCallback = createInlineSuggestionUiCallback(
- callback, onClickAction, onErrorCallback, intentSenderConsumer);
-
- if (remoteRenderService == null) {
- Slog.e(TAG, "RemoteInlineSuggestionRenderService is null");
- return;
- }
-
- remoteRenderService.renderSuggestion(uiCallback, inlinePresentation,
- width, height, hostInputToken, displayId);
- });
- }
- };
- }
-
- private static IInlineSuggestionUiCallback.Stub createInlineSuggestionUiCallback(
- @NonNull IInlineContentCallback callback, @NonNull Runnable onAutofillCallback,
- @NonNull Runnable onErrorCallback,
- @NonNull Consumer<IntentSender> intentSenderConsumer) {
- return new IInlineSuggestionUiCallback.Stub() {
- @Override
- public void onClick() throws RemoteException {
- onAutofillCallback.run();
- callback.onClick();
- }
-
- @Override
- public void onLongClick() throws RemoteException {
- callback.onLongClick();
- }
-
- @Override
- public void onContent(SurfaceControlViewHost.SurfacePackage surface, int width,
- int height)
- throws RemoteException {
- callback.onContent(surface, width, height);
- surface.release();
- }
-
- @Override
- public void onError() throws RemoteException {
- onErrorCallback.run();
- }
-
- @Override
- public void onTransferTouchFocusToImeWindow(IBinder sourceInputToken, int displayId)
- throws RemoteException {
- final InputMethodManagerInternal inputMethodManagerInternal =
- LocalServices.getService(InputMethodManagerInternal.class);
- if (!inputMethodManagerInternal.transferTouchFocusToImeWindow(sourceInputToken,
- displayId)) {
- Slog.e(TAG, "Cannot transfer touch focus from suggestion to IME");
- onErrorCallback.run();
- }
- }
-
- @Override
- public void onStartIntentSender(IntentSender intentSender) {
- intentSenderConsumer.accept(intentSender);
- }
- };
+ RemoteInlineSuggestionViewConnector
+ remoteInlineSuggestionViewConnector = new RemoteInlineSuggestionViewConnector(
+ remoteRenderService, inlinePresentation, hostInputToken, displayId, onClickAction,
+ onErrorCallback, intentSenderConsumer);
+ InlineContentProviderImpl inlineContentProvider = new InlineContentProviderImpl(
+ remoteInlineSuggestionViewConnector, null);
+ return inlineContentProvider;
}
private InlineSuggestionFactory() {
diff --git a/services/autofill/java/com/android/server/autofill/ui/RemoteInlineSuggestionUi.java b/services/autofill/java/com/android/server/autofill/ui/RemoteInlineSuggestionUi.java
new file mode 100644
index 0000000..368f717
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/ui/RemoteInlineSuggestionUi.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2020 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.server.autofill.ui;
+
+import static com.android.server.autofill.Helper.sDebug;
+import static com.android.server.autofill.Helper.sVerbose;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.IntentSender;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.service.autofill.IInlineSuggestionUi;
+import android.service.autofill.IInlineSuggestionUiCallback;
+import android.service.autofill.ISurfacePackageResultCallback;
+import android.util.Slog;
+import android.view.SurfaceControlViewHost;
+
+import com.android.internal.view.inline.IInlineContentCallback;
+
+/**
+ * The instance of this class lives in the system server, orchestrating the communication between
+ * the remote process owning embedded view (i.e. ExtServices) and the remote process hosting the
+ * embedded view (i.e. IME). It's also responsible for releasing the embedded view from the owning
+ * process when it's not longer needed in the hosting process.
+ *
+ * <p>An instance of this class may be reused to associate with multiple instances of
+ * {@link InlineContentProviderImpl}s, each of which wraps a callback from the IME. But at any
+ * given time, there is only one active IME callback which this class will callback into.
+ *
+ * <p>This class is thread safe, because all the outside calls are piped into a single handler
+ * thread to be processed.
+ */
+final class RemoteInlineSuggestionUi {
+
+ private static final String TAG = RemoteInlineSuggestionUi.class.getSimpleName();
+
+ // The delay time to release the remote inline suggestion view (in the renderer
+ // process) after receiving a signal about the surface package being released due to being
+ // detached from the window in the host app (in the IME process). The release will be
+ // canceled if the host app reattaches the view to a window within this delay time.
+ // TODO(b/154683107): try out using the Chroreographer to schedule the release right at the
+ // next frame. Basically if the view is not re-attached to the window immediately in the next
+ // frame after it was detached, then it will be released.
+ private static final long RELEASE_REMOTE_VIEW_HOST_DELAY_MS = 200;
+
+ @NonNull
+ private final Handler mHandler;
+ @NonNull
+ private final RemoteInlineSuggestionViewConnector mRemoteInlineSuggestionViewConnector;
+ private final int mWidth;
+ private final int mHeight;
+ @NonNull
+ private final InlineSuggestionUiCallbackImpl mInlineSuggestionUiCallback;
+
+ @Nullable
+ private IInlineContentCallback mInlineContentCallback; // from IME
+
+ /**
+ * Remote inline suggestion view, backed by an instance of {@link SurfaceControlViewHost} in
+ * the render service process. We takes care of releasing it when there is no remote
+ * reference to it (from IME), and we will create a new instance of the view when it's needed
+ * by IME again.
+ */
+ @Nullable
+ private IInlineSuggestionUi mInlineSuggestionUi;
+ private int mRefCount = 0;
+ private boolean mWaitingForUiCreation = false;
+ private int mActualWidth;
+ private int mActualHeight;
+
+ @Nullable
+ private Runnable mDelayedReleaseViewRunnable;
+
+ RemoteInlineSuggestionUi(
+ @NonNull RemoteInlineSuggestionViewConnector remoteInlineSuggestionViewConnector,
+ int width, int height, Handler handler) {
+ mHandler = handler;
+ mRemoteInlineSuggestionViewConnector = remoteInlineSuggestionViewConnector;
+ mWidth = width;
+ mHeight = height;
+ mInlineSuggestionUiCallback = new InlineSuggestionUiCallbackImpl();
+ }
+
+ /**
+ * Updates the callback from the IME process. It'll swap out the previous IME callback, and
+ * all the subsequent callback events (onClick, onLongClick, touch event transfer, etc) will
+ * be directed to the new callback.
+ */
+ void setInlineContentCallback(@NonNull IInlineContentCallback inlineContentCallback) {
+ mHandler.post(() -> {
+ mInlineContentCallback = inlineContentCallback;
+ });
+ }
+
+ /**
+ * Handles the request from the IME process to get a new surface package. May create a new
+ * view in the renderer process if the existing view is already released.
+ */
+ void requestSurfacePackage() {
+ mHandler.post(this::handleRequestSurfacePackage);
+ }
+
+ /**
+ * Handles the signal from the IME process that the previously sent surface package has been
+ * released.
+ */
+ void surfacePackageReleased() {
+ mHandler.post(() -> handleUpdateRefCount(-1));
+ }
+
+ /**
+ * Returns true if the provided size matches the remote view's size.
+ */
+ boolean match(int width, int height) {
+ return mWidth == width && mHeight == height;
+ }
+
+ private void handleRequestSurfacePackage() {
+ cancelPendingReleaseViewRequest();
+
+ if (mInlineSuggestionUi == null) {
+ if (mWaitingForUiCreation) {
+ // This could happen in the following case: the remote embedded view was released
+ // when previously detached from window. An event after that to re-attached to
+ // the window will cause us calling the renderSuggestion again. Now, before the
+ // render call returns a new surface package, if the view is detached and
+ // re-attached to the window, causing this method to be called again, we will get
+ // to this state. This request will be ignored and the surface package will still
+ // be sent back once the view is rendered.
+ if (sDebug) Slog.d(TAG, "Inline suggestion ui is not ready");
+ } else {
+ mRemoteInlineSuggestionViewConnector.renderSuggestion(mWidth, mHeight,
+ mInlineSuggestionUiCallback);
+ mWaitingForUiCreation = true;
+ }
+ } else {
+ try {
+ mInlineSuggestionUi.getSurfacePackage(new ISurfacePackageResultCallback.Stub() {
+ @Override
+ public void onResult(SurfaceControlViewHost.SurfacePackage result) {
+ mHandler.post(() -> {
+ if (sVerbose) Slog.v(TAG, "Sending refreshed SurfacePackage to IME");
+ try {
+ mInlineContentCallback.onContent(result, mActualWidth,
+ mActualHeight);
+ handleUpdateRefCount(1);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException calling onContent");
+ }
+ });
+ }
+ });
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException calling getSurfacePackage.");
+ }
+ }
+ }
+
+ private void handleUpdateRefCount(int delta) {
+ cancelPendingReleaseViewRequest();
+ mRefCount += delta;
+ if (mRefCount <= 0) {
+ mDelayedReleaseViewRunnable = () -> {
+ if (mInlineSuggestionUi != null) {
+ try {
+ if (sVerbose) Slog.v(TAG, "releasing the host");
+ mInlineSuggestionUi.releaseSurfaceControlViewHost();
+ mInlineSuggestionUi = null;
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException calling releaseSurfaceControlViewHost");
+ }
+ }
+ mDelayedReleaseViewRunnable = null;
+ };
+ mHandler.postDelayed(mDelayedReleaseViewRunnable, RELEASE_REMOTE_VIEW_HOST_DELAY_MS);
+ }
+ }
+
+ private void cancelPendingReleaseViewRequest() {
+ if (mDelayedReleaseViewRunnable != null) {
+ mHandler.removeCallbacks(mDelayedReleaseViewRunnable);
+ mDelayedReleaseViewRunnable = null;
+ }
+ }
+
+ /**
+ * This is called when a new inline suggestion UI is inflated from the ext services.
+ */
+ private void handleInlineSuggestionUiReady(IInlineSuggestionUi content,
+ SurfaceControlViewHost.SurfacePackage surfacePackage, int width, int height) {
+ mInlineSuggestionUi = content;
+ mRefCount = 0;
+ mWaitingForUiCreation = false;
+ mActualWidth = width;
+ mActualHeight = height;
+ if (mInlineContentCallback != null) {
+ try {
+ if (sVerbose) Slog.v(TAG, "Sending new UI content to IME");
+ handleUpdateRefCount(1);
+ mInlineContentCallback.onContent(surfacePackage, mActualWidth, mActualHeight);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException calling onContent");
+ }
+ }
+ if (surfacePackage != null) {
+ surfacePackage.release();
+ }
+ }
+
+ private void handleOnClick() {
+ // Autofill the value
+ mRemoteInlineSuggestionViewConnector.onClick();
+
+ // Notify the remote process (IME) that hosts the embedded UI that it's clicked
+ if (mInlineContentCallback != null) {
+ try {
+ mInlineContentCallback.onClick();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException calling onClick");
+ }
+ }
+ }
+
+ private void handleOnLongClick() {
+ // Notify the remote process (IME) that hosts the embedded UI that it's long clicked
+ if (mInlineContentCallback != null) {
+ try {
+ mInlineContentCallback.onLongClick();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException calling onLongClick");
+ }
+ }
+ }
+
+ private void handleOnError() {
+ mRemoteInlineSuggestionViewConnector.onError();
+ }
+
+ private void handleOnTransferTouchFocusToImeWindow(IBinder sourceInputToken, int displayId) {
+ mRemoteInlineSuggestionViewConnector.onTransferTouchFocusToImeWindow(sourceInputToken,
+ displayId);
+ }
+
+ private void handleOnStartIntentSender(IntentSender intentSender) {
+ mRemoteInlineSuggestionViewConnector.onStartIntentSender(intentSender);
+ }
+
+ /**
+ * Responsible for communicating with the inline suggestion view owning process.
+ */
+ private class InlineSuggestionUiCallbackImpl extends IInlineSuggestionUiCallback.Stub {
+
+ @Override
+ public void onClick() {
+ mHandler.post(RemoteInlineSuggestionUi.this::handleOnClick);
+ }
+
+ @Override
+ public void onLongClick() {
+ mHandler.post(RemoteInlineSuggestionUi.this::handleOnLongClick);
+ }
+
+ @Override
+ public void onContent(IInlineSuggestionUi content,
+ SurfaceControlViewHost.SurfacePackage surface, int width, int height) {
+ mHandler.post(() -> handleInlineSuggestionUiReady(content, surface, width, height));
+ }
+
+ @Override
+ public void onError() {
+ mHandler.post(RemoteInlineSuggestionUi.this::handleOnError);
+ }
+
+ @Override
+ public void onTransferTouchFocusToImeWindow(IBinder sourceInputToken, int displayId) {
+ mHandler.post(() -> handleOnTransferTouchFocusToImeWindow(sourceInputToken, displayId));
+ }
+
+ @Override
+ public void onStartIntentSender(IntentSender intentSender) {
+ mHandler.post(() -> handleOnStartIntentSender(intentSender));
+ }
+ }
+}
diff --git a/services/autofill/java/com/android/server/autofill/ui/RemoteInlineSuggestionViewConnector.java b/services/autofill/java/com/android/server/autofill/ui/RemoteInlineSuggestionViewConnector.java
new file mode 100644
index 0000000..9d23c17
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/ui/RemoteInlineSuggestionViewConnector.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2020 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.server.autofill.ui;
+
+import static com.android.server.autofill.Helper.sDebug;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.IntentSender;
+import android.os.IBinder;
+import android.service.autofill.IInlineSuggestionUiCallback;
+import android.service.autofill.InlinePresentation;
+import android.util.Slog;
+
+import com.android.server.LocalServices;
+import com.android.server.autofill.RemoteInlineSuggestionRenderService;
+import com.android.server.inputmethod.InputMethodManagerInternal;
+
+import java.util.function.Consumer;
+
+/**
+ * Wraps the parameters needed to create a new inline suggestion view in the remote renderer
+ * service, and handles the callback from the events on the created remote view.
+ */
+final class RemoteInlineSuggestionViewConnector {
+ private static final String TAG = RemoteInlineSuggestionViewConnector.class.getSimpleName();
+
+ @Nullable
+ private final RemoteInlineSuggestionRenderService mRemoteRenderService;
+ @NonNull
+ private final InlinePresentation mInlinePresentation;
+ @Nullable
+ private final IBinder mHostInputToken;
+ private final int mDisplayId;
+
+ @NonNull
+ private final Runnable mOnAutofillCallback;
+ @NonNull
+ private final Runnable mOnErrorCallback;
+ @NonNull
+ private final Consumer<IntentSender> mStartIntentSenderFromClientApp;
+
+ RemoteInlineSuggestionViewConnector(
+ @Nullable RemoteInlineSuggestionRenderService remoteRenderService,
+ @NonNull InlinePresentation inlinePresentation,
+ @Nullable IBinder hostInputToken,
+ int displayId,
+ @NonNull Runnable onAutofillCallback,
+ @NonNull Runnable onErrorCallback,
+ @NonNull Consumer<IntentSender> startIntentSenderFromClientApp) {
+ mRemoteRenderService = remoteRenderService;
+ mInlinePresentation = inlinePresentation;
+ mHostInputToken = hostInputToken;
+ mDisplayId = displayId;
+
+ mOnAutofillCallback = onAutofillCallback;
+ mOnErrorCallback = onErrorCallback;
+ mStartIntentSenderFromClientApp = startIntentSenderFromClientApp;
+ }
+
+ /**
+ * Calls the remote renderer service to create a new inline suggestion view.
+ *
+ * @return true if the call is made to the remote renderer service, false otherwise.
+ */
+ public boolean renderSuggestion(int width, int height,
+ @NonNull IInlineSuggestionUiCallback callback) {
+ if (mRemoteRenderService != null) {
+ if (sDebug) Slog.d(TAG, "Request to recreate the UI");
+ mRemoteRenderService.renderSuggestion(callback, mInlinePresentation, width, height,
+ mHostInputToken, mDisplayId);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Handles the callback for the event of remote view being clicked.
+ */
+ public void onClick() {
+ mOnAutofillCallback.run();
+ }
+
+ /**
+ * Handles the callback for the remote error when creating or interacting with the view.
+ */
+ public void onError() {
+ mOnErrorCallback.run();
+ }
+
+ /**
+ * Handles the callback for transferring the touch event on the remote view to the IME
+ * process.
+ */
+ public void onTransferTouchFocusToImeWindow(IBinder sourceInputToken, int displayId) {
+ final InputMethodManagerInternal inputMethodManagerInternal =
+ LocalServices.getService(InputMethodManagerInternal.class);
+ if (!inputMethodManagerInternal.transferTouchFocusToImeWindow(sourceInputToken,
+ displayId)) {
+ Slog.e(TAG, "Cannot transfer touch focus from suggestion to IME");
+ mOnErrorCallback.run();
+ }
+ }
+
+ /**
+ * Handles starting an intent sender from the client app's process.
+ */
+ public void onStartIntentSender(IntentSender intentSender) {
+ mStartIntentSenderFromClientApp.accept(intentSender);
+ }
+}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 21ac3cd..cf85b1d 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -130,6 +130,7 @@
"dnsresolver_aidl_interface-java",
"netd_aidl_interfaces-platform-java",
"overlayable_policy_aidl-java",
+ "SurfaceFlingerProperties",
],
}
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index bfcde97..829fca6 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -253,10 +253,7 @@
logCriticalInfo(Log.DEBUG,
"Finished rescue level " + levelToString(level));
} catch (Throwable t) {
- final String msg = ExceptionUtils.getCompleteMessage(t);
- EventLogTags.writeRescueFailure(level, msg);
- logCriticalInfo(Log.ERROR,
- "Failed rescue level " + levelToString(level) + ": " + msg);
+ logRescueException(level, t);
}
}
@@ -274,11 +271,31 @@
resetAllSettings(context, Settings.RESET_MODE_TRUSTED_DEFAULTS, failedPackage);
break;
case LEVEL_FACTORY_RESET:
- RecoverySystem.rebootPromptAndWipeUserData(context, TAG);
+ // Request the reboot from a separate thread to avoid deadlock on PackageWatchdog
+ // when device shutting down.
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ RecoverySystem.rebootPromptAndWipeUserData(context, TAG);
+ } catch (Throwable t) {
+ logRescueException(level, t);
+ }
+ }
+ };
+ Thread thread = new Thread(runnable);
+ thread.start();
break;
}
}
+ private static void logRescueException(int level, Throwable t) {
+ final String msg = ExceptionUtils.getCompleteMessage(t);
+ EventLogTags.writeRescueFailure(level, msg);
+ logCriticalInfo(Log.ERROR,
+ "Failed rescue level " + levelToString(level) + ": " + msg);
+ }
+
private static int mapRescueLevelToUserImpact(int rescueLevel) {
switch(rescueLevel) {
case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS:
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 97a5cfe..1d40e2e 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -23,6 +23,7 @@
import static java.util.Arrays.copyOf;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -2459,7 +2460,7 @@
intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId);
intent.putExtra(PHONE_CONSTANTS_SLOT_KEY, phoneId);
intent.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, phoneId);
- mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL, Manifest.permission.READ_PHONE_STATE);
}
private void broadcastSignalStrengthChanged(SignalStrength signalStrength, int phoneId,
@@ -2584,7 +2585,7 @@
intent.putExtra(PHONE_CONSTANTS_DATA_APN_TYPE_KEY,
ApnSetting.getApnTypesStringFromBitmask(apnType));
intent.putExtra(PHONE_CONSTANTS_SUBSCRIPTION_KEY, subId);
- mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL, Manifest.permission.READ_PHONE_STATE);
}
private void enforceNotifyPermissionOrCarrierPrivilege(String method) {
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 70b6398..dd3de35 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -122,6 +122,7 @@
private boolean mVrHeadset;
private boolean mComputedNightMode;
private int mCarModeEnableFlags;
+ private boolean mSetupWizardComplete;
// flag set by resource, whether to enable Car dock launch when starting car mode.
private boolean mEnableCarDockLaunch = true;
@@ -163,6 +164,12 @@
mConfiguration.setToDefaults();
}
+ @VisibleForTesting
+ protected UiModeManagerService(Context context, boolean setupWizardComplete) {
+ this(context);
+ mSetupWizardComplete = setupWizardComplete;
+ }
+
private static Intent buildHomeIntent(String category) {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(category);
@@ -276,6 +283,25 @@
}
};
+ private final ContentObserver mSetupWizardObserver = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ synchronized (mLock) {
+ // setup wizard is done now so we can unblock
+ if (setupWizardCompleteForCurrentUser() && !selfChange) {
+ mSetupWizardComplete = true;
+ getContext().getContentResolver()
+ .unregisterContentObserver(mSetupWizardObserver);
+ // update night mode
+ Context context = getContext();
+ updateNightModeFromSettingsLocked(context, context.getResources(),
+ UserHandle.getCallingUserId());
+ updateLocked(0, 0);
+ }
+ }
+ }
+ };
+
private final ContentObserver mDarkThemeObserver = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange, Uri uri) {
@@ -293,6 +319,13 @@
}
@Override
+ public void onSwitchUser(int userHandle) {
+ super.onSwitchUser(userHandle);
+ getContext().getContentResolver().unregisterContentObserver(mSetupWizardObserver);
+ verifySetupWizardCompleted();
+ }
+
+ @Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
synchronized (mLock) {
@@ -330,8 +363,13 @@
@Override
public void onStart() {
final Context context = getContext();
+ // If setup isn't complete for this user listen for completion so we can unblock
+ // being able to send a night mode configuration change event
+ verifySetupWizardCompleted();
final Resources res = context.getResources();
+ mNightMode = res.getInteger(
+ com.android.internal.R.integer.config_defaultNightMode);
mDefaultUiModeType = res.getInteger(
com.android.internal.R.integer.config_defaultUiModeType);
mCarModeKeepsScreenOn = (res.getInteger(
@@ -404,6 +442,20 @@
return mConfiguration;
}
+ // Records whether setup wizard has happened or not and adds an observer for this user if not.
+ private void verifySetupWizardCompleted() {
+ final Context context = getContext();
+ final int userId = UserHandle.getCallingUserId();
+ if (!setupWizardCompleteForCurrentUser()) {
+ mSetupWizardComplete = false;
+ context.getContentResolver().registerContentObserver(
+ Secure.getUriFor(
+ Secure.USER_SETUP_COMPLETE), false, mSetupWizardObserver, userId);
+ } else {
+ mSetupWizardComplete = true;
+ }
+ }
+
private boolean setupWizardCompleteForCurrentUser() {
return Secure.getIntForUser(getContext().getContentResolver(),
Secure.USER_SETUP_COMPLETE, 0, UserHandle.getCallingUserId()) == 1;
@@ -429,27 +481,35 @@
* @return True if the new value is different from the old value. False otherwise.
*/
private boolean updateNightModeFromSettingsLocked(Context context, Resources res, int userId) {
- final int defaultNightMode = res.getInteger(
- com.android.internal.R.integer.config_defaultNightMode);
int oldNightMode = mNightMode;
- mNightMode = Secure.getIntForUser(context.getContentResolver(),
- Secure.UI_NIGHT_MODE, defaultNightMode, userId);
- mOverrideNightModeOn = Secure.getIntForUser(context.getContentResolver(),
- Secure.UI_NIGHT_MODE_OVERRIDE_ON, 0, userId) != 0;
- mOverrideNightModeOff = Secure.getIntForUser(context.getContentResolver(),
- Secure.UI_NIGHT_MODE_OVERRIDE_OFF, 0, userId) != 0;
- mCustomAutoNightModeStartMilliseconds = LocalTime.ofNanoOfDay(
- Secure.getLongForUser(context.getContentResolver(),
- Secure.DARK_THEME_CUSTOM_START_TIME,
- DEFAULT_CUSTOM_NIGHT_START_TIME.toNanoOfDay() / 1000L, userId) * 1000);
- mCustomAutoNightModeEndMilliseconds = LocalTime.ofNanoOfDay(
- Secure.getLongForUser(context.getContentResolver(),
- Secure.DARK_THEME_CUSTOM_END_TIME,
- DEFAULT_CUSTOM_NIGHT_END_TIME.toNanoOfDay() / 1000L, userId) * 1000);
+ if (mSetupWizardComplete) {
+ mNightMode = Secure.getIntForUser(context.getContentResolver(),
+ Secure.UI_NIGHT_MODE, mNightMode, userId);
+ mOverrideNightModeOn = Secure.getIntForUser(context.getContentResolver(),
+ Secure.UI_NIGHT_MODE_OVERRIDE_ON, 0, userId) != 0;
+ mOverrideNightModeOff = Secure.getIntForUser(context.getContentResolver(),
+ Secure.UI_NIGHT_MODE_OVERRIDE_OFF, 0, userId) != 0;
+ mCustomAutoNightModeStartMilliseconds = LocalTime.ofNanoOfDay(
+ Secure.getLongForUser(context.getContentResolver(),
+ Secure.DARK_THEME_CUSTOM_START_TIME,
+ DEFAULT_CUSTOM_NIGHT_START_TIME.toNanoOfDay() / 1000L, userId) * 1000);
+ mCustomAutoNightModeEndMilliseconds = LocalTime.ofNanoOfDay(
+ Secure.getLongForUser(context.getContentResolver(),
+ Secure.DARK_THEME_CUSTOM_END_TIME,
+ DEFAULT_CUSTOM_NIGHT_END_TIME.toNanoOfDay() / 1000L, userId) * 1000);
+ }
return oldNightMode != mNightMode;
}
+ private static long toMilliSeconds(LocalTime t) {
+ return t.toNanoOfDay() / 1000;
+ }
+
+ private static LocalTime fromMilliseconds(long t) {
+ return LocalTime.ofNanoOfDay(t * 1000);
+ }
+
private void registerScreenOffEventLocked() {
if (mPowerSave) return;
mWaitForScreenOff = true;
@@ -1385,8 +1445,11 @@
pw.println("UiModeManager service (uimode) commands:");
pw.println(" help");
pw.println(" Print this help text.");
- pw.println(" night [yes|no|auto]");
+ pw.println(" night [yes|no|auto|custom]");
pw.println(" Set or read night mode.");
+ pw.println(" time [start|end] <ISO time>");
+ pw.println(" Set custom start/end schedule time"
+ + " (night mode must be set to custom to apply).");
}
@Override
@@ -1399,6 +1462,8 @@
switch (cmd) {
case "night":
return handleNightMode();
+ case "time":
+ return handleCustomTime();
default:
return handleDefaultCommands(cmd);
}
@@ -1409,6 +1474,34 @@
return -1;
}
+ private int handleCustomTime() throws RemoteException {
+ final String modeStr = getNextArg();
+ if (modeStr == null) {
+ printCustomTime();
+ return 0;
+ }
+ switch (modeStr) {
+ case "start":
+ final String start = getNextArg();
+ mInterface.setCustomNightModeStart(toMilliSeconds(LocalTime.parse(start)));
+ return 0;
+ case "end":
+ final String end = getNextArg();
+ mInterface.setCustomNightModeEnd(toMilliSeconds(LocalTime.parse(end)));
+ return 0;
+ default:
+ getErrPrintWriter().println("command must be in [start|end]");
+ return -1;
+ }
+ }
+
+ private void printCustomTime() throws RemoteException {
+ getOutPrintWriter().println("start " + fromMilliseconds(
+ mInterface.getCustomNightModeStart()).toString());
+ getOutPrintWriter().println("end " + fromMilliseconds(
+ mInterface.getCustomNightModeEnd()).toString());
+ }
+
private int handleNightMode() throws RemoteException {
final PrintWriter err = getErrPrintWriter();
final String modeStr = getNextArg();
@@ -1424,7 +1517,8 @@
return 0;
} else {
err.println("Error: mode must be '" + NIGHT_MODE_STR_YES + "', '"
- + NIGHT_MODE_STR_NO + "', or '" + NIGHT_MODE_STR_AUTO + "'");
+ + NIGHT_MODE_STR_NO + "', or '" + NIGHT_MODE_STR_AUTO
+ + "', or '" + NIGHT_MODE_STR_CUSTOM + "'");
return -1;
}
}
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 70fbca5..7e28e94 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -170,6 +170,8 @@
final String mOpPackageName;
// Info to be shown on BiometricDialog when all cookies are returned.
final Bundle mBundle;
+ // Random id associated to this AuthSession
+ final int mSysUiSessionId;
final int mCallingUid;
final int mCallingPid;
final int mCallingUserId;
@@ -203,11 +205,13 @@
mClientReceiver = receiver;
mOpPackageName = opPackageName;
mBundle = bundle;
+ mSysUiSessionId = mRandom.nextInt();
mCallingUid = callingUid;
mCallingPid = callingPid;
mCallingUserId = callingUserId;
mModality = modality;
mRequireConfirmation = requireConfirmation;
+ Slog.d(TAG, "New AuthSession, mSysUiSessionId: " + mSysUiSessionId);
}
boolean isCrypto() {
@@ -1457,7 +1461,8 @@
false /* requireConfirmation */,
mCurrentAuthSession.mUserId,
mCurrentAuthSession.mOpPackageName,
- mCurrentAuthSession.mSessionId);
+ mCurrentAuthSession.mSessionId,
+ mCurrentAuthSession.mSysUiSessionId);
} else {
mPendingAuthSession.mClientReceiver.onError(modality, error, vendorCode);
mPendingAuthSession = null;
@@ -1680,7 +1685,8 @@
mStatusBarService.showAuthenticationDialog(mCurrentAuthSession.mBundle,
mInternalReceiver, modality, requireConfirmation, userId,
mCurrentAuthSession.mOpPackageName,
- mCurrentAuthSession.mSessionId);
+ mCurrentAuthSession.mSessionId,
+ mCurrentAuthSession.mSysUiSessionId);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
}
@@ -1766,7 +1772,8 @@
false /* requireConfirmation */,
mCurrentAuthSession.mUserId,
mCurrentAuthSession.mOpPackageName,
- sessionId);
+ sessionId,
+ mCurrentAuthSession.mSysUiSessionId);
} else {
mPendingAuthSession.mState = STATE_AUTH_CALLED;
for (AuthenticatorWrapper authenticator : mAuthenticators) {
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index b2bf1fc..ccbe96f 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -2667,8 +2667,7 @@
mRequestStatistics.statistics);
for (Map.Entry<PackageProviderKey, PackageStatistics> entry
: sorted.entrySet()) {
- PackageProviderKey key = entry.getKey();
- ipw.println(key.mPackageName + ": " + key.mProviderName + ": " + entry.getValue());
+ ipw.println(entry.getKey() + ": " + entry.getValue());
}
ipw.decreaseIndent();
diff --git a/services/core/java/com/android/server/location/LocationRequestStatistics.java b/services/core/java/com/android/server/location/LocationRequestStatistics.java
index dcdf48b..e629b42 100644
--- a/services/core/java/com/android/server/location/LocationRequestStatistics.java
+++ b/services/core/java/com/android/server/location/LocationRequestStatistics.java
@@ -16,6 +16,7 @@
package com.android.server.location;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.SystemClock;
import android.util.Log;
@@ -25,6 +26,7 @@
import com.android.internal.util.IndentingPrintWriter;
import java.util.ArrayList;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.Objects;
@@ -121,14 +123,30 @@
this.mProviderName = providerName;
}
+ @NonNull
+ @Override
+ public String toString() {
+ return mProviderName + ": " + mPackageName
+ + (mFeatureId == null ? "" : ": " + mFeatureId);
+ }
+
+ /**
+ * Sort by provider, then package, then feature
+ */
@Override
public int compareTo(PackageProviderKey other) {
final int providerCompare = mProviderName.compareTo(other.mProviderName);
if (providerCompare != 0) {
return providerCompare;
- } else {
- return mProviderName.compareTo(other.mProviderName);
}
+
+ final int packageCompare = mPackageName.compareTo(other.mPackageName);
+ if (packageCompare != 0) {
+ return packageCompare;
+ }
+
+ return Objects.compare(mFeatureId, other.mFeatureId, Comparator
+ .nullsFirst(String::compareTo));
}
@Override
diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
index 3cf22c8..54958d3 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
@@ -29,6 +29,7 @@
import android.content.IntentFilter;
import android.media.AudioManager;
import android.media.MediaRoute2Info;
+import android.text.TextUtils;
import android.util.Slog;
import android.util.SparseBooleanArray;
@@ -210,7 +211,11 @@
newBtRoute.btDevice = device;
// Current / Max volume will be set when connected.
// TODO: Is there any BT device which has fixed volume?
- newBtRoute.route = new MediaRoute2Info.Builder(device.getAddress(), device.getName())
+ String deviceName = device.getName();
+ if (TextUtils.isEmpty(deviceName)) {
+ deviceName = mContext.getResources().getText(R.string.unknownName).toString();
+ }
+ newBtRoute.route = new MediaRoute2Info.Builder(device.getAddress(), deviceName)
.addFeature(MediaRoute2Info.FEATURE_LIVE_AUDIO)
.setConnectionState(MediaRoute2Info.CONNECTION_STATE_DISCONNECTED)
.setDescription(mContext.getResources().getText(
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
index a5de90c..a435f1e 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
@@ -315,9 +315,7 @@
return;
}
- sessionInfo = new RoutingSessionInfo.Builder(sessionInfo)
- .setProviderId(getUniqueId())
- .build();
+ sessionInfo = updateSessionInfo(sessionInfo);
boolean duplicateSessionAlreadyExists = false;
synchronized (mLock) {
@@ -348,9 +346,7 @@
return;
}
- sessionInfo = new RoutingSessionInfo.Builder(sessionInfo)
- .setProviderId(getUniqueId())
- .build();
+ sessionInfo = updateSessionInfo(sessionInfo);
boolean found = false;
synchronized (mLock) {
@@ -380,9 +376,7 @@
return;
}
- sessionInfo = new RoutingSessionInfo.Builder(sessionInfo)
- .setProviderId(getUniqueId())
- .build();
+ sessionInfo = updateSessionInfo(sessionInfo);
boolean found = false;
synchronized (mLock) {
@@ -403,6 +397,13 @@
mCallback.onSessionReleased(this, sessionInfo);
}
+ private RoutingSessionInfo updateSessionInfo(RoutingSessionInfo sessionInfo) {
+ return new RoutingSessionInfo.Builder(sessionInfo)
+ .setOwnerPackageName(mComponentName.getPackageName())
+ .setProviderId(getUniqueId())
+ .build();
+ }
+
private void onRequestFailed(Connection connection, long requestId, int reason) {
if (mActiveConnection != connection) {
return;
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index ec941c8..3283fd9b 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -799,7 +799,6 @@
writePolicyAL();
}
- enableFirewallChainUL(FIREWALL_CHAIN_STANDBY, true);
setRestrictBackgroundUL(mLoadedRestrictBackground, "init_service");
updateRulesForGlobalChangeAL(false);
updateNotificationsNL();
@@ -871,6 +870,9 @@
new NetworkRequest.Builder().build(), mNetworkCallback);
mAppStandby.addListener(new NetPolicyAppIdleStateChangeListener());
+ synchronized (mUidRulesFirstLock) {
+ updateRulesForAppIdleParoleUL();
+ }
// Listen for subscriber changes
mContext.getSystemService(SubscriptionManager.class).addOnSubscriptionsChangedListener(
@@ -3893,6 +3895,39 @@
}
/**
+ * Toggle the firewall standby chain and inform listeners if the uid rules have effectively
+ * changed.
+ */
+ @GuardedBy("mUidRulesFirstLock")
+ private void updateRulesForAppIdleParoleUL() {
+ final boolean paroled = mAppStandby.isInParole();
+ final boolean enableChain = !paroled;
+ enableFirewallChainUL(FIREWALL_CHAIN_STANDBY, enableChain);
+
+ int ruleCount = mUidFirewallStandbyRules.size();
+ for (int i = 0; i < ruleCount; i++) {
+ final int uid = mUidFirewallStandbyRules.keyAt(i);
+ int oldRules = mUidRules.get(uid);
+ if (enableChain) {
+ // Chain wasn't enabled before and the other power-related
+ // chains are whitelists, so we can clear the
+ // MASK_ALL_NETWORKS part of the rules and re-inform listeners if
+ // the effective rules result in blocking network access.
+ oldRules &= MASK_METERED_NETWORKS;
+ } else {
+ // Skip if it had no restrictions to begin with
+ if ((oldRules & MASK_ALL_NETWORKS) == 0) continue;
+ }
+ final int newUidRules = updateRulesForPowerRestrictionsUL(uid, oldRules, paroled);
+ if (newUidRules == RULE_NONE) {
+ mUidRules.delete(uid);
+ } else {
+ mUidRules.put(uid, newUidRules);
+ }
+ }
+ }
+
+ /**
* Update rules that might be changed by {@link #mRestrictBackground},
* {@link #mRestrictPower}, or {@link #mDeviceIdleMode} value.
*/
@@ -4347,7 +4382,7 @@
private void updateRulesForPowerRestrictionsUL(int uid) {
final int oldUidRules = mUidRules.get(uid, RULE_NONE);
- final int newUidRules = updateRulesForPowerRestrictionsUL(uid, oldUidRules);
+ final int newUidRules = updateRulesForPowerRestrictionsUL(uid, oldUidRules, false);
if (newUidRules == RULE_NONE) {
mUidRules.delete(uid);
@@ -4361,28 +4396,30 @@
*
* @param uid the uid of the app to update rules for
* @param oldUidRules the current rules for the uid, in order to determine if there's a change
+ * @param paroled whether to ignore idle state of apps and only look at other restrictions
*
* @return the new computed rules for the uid
*/
- private int updateRulesForPowerRestrictionsUL(int uid, int oldUidRules) {
+ private int updateRulesForPowerRestrictionsUL(int uid, int oldUidRules, boolean paroled) {
if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK,
- "updateRulesForPowerRestrictionsUL: " + uid + "/" + oldUidRules);
+ "updateRulesForPowerRestrictionsUL: " + uid + "/" + oldUidRules + "/"
+ + (paroled ? "P" : "-"));
}
try {
- return updateRulesForPowerRestrictionsULInner(uid, oldUidRules);
+ return updateRulesForPowerRestrictionsULInner(uid, oldUidRules, paroled);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
}
}
- private int updateRulesForPowerRestrictionsULInner(int uid, int oldUidRules) {
+ private int updateRulesForPowerRestrictionsULInner(int uid, int oldUidRules, boolean paroled) {
if (!isUidValidForBlacklistRules(uid)) {
if (LOGD) Slog.d(TAG, "no need to update restrict power rules for uid " + uid);
return RULE_NONE;
}
- final boolean isIdle = isUidIdle(uid);
+ final boolean isIdle = !paroled && isUidIdle(uid);
final boolean restrictMode = isIdle || mRestrictPower || mDeviceIdleMode;
final boolean isForeground = isUidForegroundOnRestrictPowerUL(uid);
@@ -4452,6 +4489,14 @@
} catch (NameNotFoundException nnfe) {
}
}
+
+ @Override
+ public void onParoleStateChanged(boolean isParoleOn) {
+ synchronized (mUidRulesFirstLock) {
+ mLogger.paroleStateChanged(isParoleOn);
+ updateRulesForAppIdleParoleUL();
+ }
+ }
}
private void dispatchUidRulesChanged(INetworkPolicyListener listener, int uid, int uidRules) {
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 1951fc0..adf0176 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -178,6 +178,9 @@
// Perform polling, persist network, and register the global alert again.
private static final int MSG_PERFORM_POLL_REGISTER_ALERT = 2;
private static final int MSG_UPDATE_IFACES = 3;
+ // A message for broadcasting ACTION_NETWORK_STATS_UPDATED in handler thread to prevent
+ // deadlock.
+ private static final int MSG_BROADCAST_NETWORK_STATS_UPDATED = 4;
/** Flags to control detail level of poll event. */
private static final int FLAG_PERSIST_NETWORK = 0x1;
@@ -386,6 +389,13 @@
registerGlobalAlert();
break;
}
+ case MSG_BROADCAST_NETWORK_STATS_UPDATED: {
+ final Intent updatedIntent = new Intent(ACTION_NETWORK_STATS_UPDATED);
+ updatedIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ mContext.sendBroadcastAsUser(updatedIntent, UserHandle.ALL,
+ READ_NETWORK_USAGE_HISTORY);
+ break;
+ }
}
}
}
@@ -1513,10 +1523,7 @@
}
// finally, dispatch updated event to any listeners
- final Intent updatedIntent = new Intent(ACTION_NETWORK_STATS_UPDATED);
- updatedIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- mContext.sendBroadcastAsUser(updatedIntent, UserHandle.ALL,
- READ_NETWORK_USAGE_HISTORY);
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_BROADCAST_NETWORK_STATS_UPDATED));
Trace.traceEnd(TRACE_TAG_NETWORK);
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 8e3de15..a9fa2b1 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -1382,13 +1382,21 @@
*/
public boolean isConversation() {
Notification notification = getNotification();
- if (mChannel.isDemoted()
- || !Notification.MessagingStyle.class.equals(notification.getNotificationStyle())) {
+ if (!Notification.MessagingStyle.class.equals(notification.getNotificationStyle())) {
+ // very common; don't bother logging
+ return false;
+ }
+ if (mChannel.isDemoted()) {
return false;
}
if (mIsNotConversationOverride) {
return false;
}
+ if (mTargetSdkVersion >= Build.VERSION_CODES.R
+ && Notification.MessagingStyle.class.equals(notification.getNotificationStyle())
+ && mShortcutInfo == null) {
+ return false;
+ }
return true;
}
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 92c14527..aafd261 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -30,9 +30,9 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.parsing.PackageInfoWithoutStateUtils;
-import android.content.pm.parsing.ParsingPackageUtils;
import android.os.Binder;
import android.os.Environment;
import android.os.RemoteException;
@@ -50,7 +50,6 @@
import com.android.internal.util.Preconditions;
import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.parsing.pkg.AndroidPackage;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.utils.TimingsTraceAndSlog;
import com.google.android.collect.Lists;
@@ -491,7 +490,7 @@
for (ApexInfo ai : allPkgs) {
File apexFile = new File(ai.modulePath);
- parallelPackageParser.submit(apexFile, 0);
+ parallelPackageParser.submit(apexFile, PackageParser.PARSE_COLLECT_CERTIFICATES);
parsingApexInfo.put(apexFile, ai);
}
@@ -504,18 +503,8 @@
ApexInfo ai = parsingApexInfo.get(parseResult.scanFile);
if (throwable == null) {
- // Unfortunately, ParallelPackageParser won't collect certificates for us. We
- // need to manually collect them here.
- ParsedPackage pp = parseResult.parsedPackage;
- try {
- pp.setSigningDetails(
- ParsingPackageUtils.getSigningDetails(pp, false));
- } catch (PackageParserException e) {
- throw new IllegalStateException(
- "Unable to collect certificates for " + ai.modulePath, e);
- }
final PackageInfo packageInfo = PackageInfoWithoutStateUtils.generate(
- pp, ai, flags);
+ parseResult.parsedPackage, ai, flags);
if (packageInfo == null) {
throw new IllegalStateException("Unable to generate package info: "
+ ai.modulePath);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 3367cd5..5b5f334 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -676,7 +676,7 @@
session = new PackageInstallerSession(mInternalCallback, mContext, mPm, this,
mInstallThread.getLooper(), mStagingManager, sessionId, userId, callingUid,
installSource, params, createdMillis,
- stageDir, stageCid, null, false, false, false, null, SessionInfo.INVALID_ID,
+ stageDir, stageCid, null, false, false, false, false, null, SessionInfo.INVALID_ID,
false, false, false, SessionInfo.STAGED_SESSION_NO_ERROR, "");
synchronized (mSessions) {
@@ -830,7 +830,7 @@
synchronized (mSessions) {
final PackageInstallerSession session = mSessions.get(sessionId);
- return session != null
+ return (session != null && !(session.isStaged() && session.isDestroyed()))
? session.generateInfoForCaller(true /*withIcon*/, Binder.getCallingUid())
: null;
}
@@ -851,7 +851,8 @@
synchronized (mSessions) {
for (int i = 0; i < mSessions.size(); i++) {
final PackageInstallerSession session = mSessions.valueAt(i);
- if (session.userId == userId && !session.hasParentSessionId()) {
+ if (session.userId == userId && !session.hasParentSessionId()
+ && !(session.isStaged() && session.isDestroyed())) {
result.add(session.generateInfoForCaller(false, callingUid));
}
}
@@ -1269,7 +1270,7 @@
public void onStagedSessionChanged(PackageInstallerSession session) {
session.markUpdated();
writeSessionsAsync();
- if (mOkToSendBroadcasts) {
+ if (mOkToSendBroadcasts && !session.isDestroyed()) {
// we don't scrub the data here as this is sent only to the installer several
// privileged system packages
mPm.sendSessionUpdatedBroadcast(
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index d3f377e..e0d057a 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -190,6 +190,7 @@
private static final String ATTR_SESSION_STAGE_CID = "sessionStageCid";
private static final String ATTR_PREPARED = "prepared";
private static final String ATTR_COMMITTED = "committed";
+ private static final String ATTR_DESTROYED = "destroyed";
private static final String ATTR_SEALED = "sealed";
private static final String ATTR_MULTI_PACKAGE = "multiPackage";
private static final String ATTR_PARENT_SESSION_ID = "parentSessionId";
@@ -533,7 +534,7 @@
int sessionId, int userId, int installerUid, @NonNull InstallSource installSource,
SessionParams params, long createdMillis,
File stageDir, String stageCid, InstallationFile[] files, boolean prepared,
- boolean committed, boolean sealed,
+ boolean committed, boolean destroyed, boolean sealed,
@Nullable int[] childSessionIds, int parentSessionId, boolean isReady,
boolean isFailed, boolean isApplied, int stagedSessionErrorCode,
String stagedSessionErrorMessage) {
@@ -579,6 +580,7 @@
mPrepared = prepared;
mCommitted = committed;
+ mDestroyed = destroyed;
mStagedSessionReady = isReady;
mStagedSessionFailed = isFailed;
mStagedSessionApplied = isApplied;
@@ -713,6 +715,13 @@
}
}
+ /** {@hide} */
+ boolean isDestroyed() {
+ synchronized (mLock) {
+ return mDestroyed;
+ }
+ }
+
/** Returns true if a staged session has reached a final state and can be forgotten about */
public boolean isStagedAndInTerminalState() {
synchronized (mLock) {
@@ -1068,6 +1077,19 @@
}
/**
+ * Check if the caller is the owner of this session. Otherwise throw a
+ * {@link SecurityException}.
+ */
+ @GuardedBy("mLock")
+ private void assertCallerIsOwnerOrRootOrSystemLocked() {
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid != Process.ROOT_UID && callingUid != mInstallerUid
+ && callingUid != Process.SYSTEM_UID) {
+ throw new SecurityException("Session does not belong to uid " + callingUid);
+ }
+ }
+
+ /**
* If anybody is reading or writing data of the session, throw an {@link SecurityException}.
*/
@GuardedBy("mLock")
@@ -2552,7 +2574,13 @@
+ mParentSessionId + " and may not be abandoned directly.");
}
synchronized (mLock) {
- assertCallerIsOwnerOrRootLocked();
+ if (params.isStaged && mDestroyed) {
+ // If a user abandons staged session in an unsafe state, then system will try to
+ // abandon the destroyed staged session when it is safe on behalf of the user.
+ assertCallerIsOwnerOrRootOrSystemLocked();
+ } else {
+ assertCallerIsOwnerOrRootLocked();
+ }
if (isStagedAndInTerminalState()) {
// We keep the session in the database if it's in a finalized state. It will be
@@ -2562,11 +2590,12 @@
return;
}
if (mCommitted && params.isStaged) {
- synchronized (mLock) {
- mDestroyed = true;
+ mDestroyed = true;
+ if (!mStagingManager.abortCommittedSessionLocked(this)) {
+ // Do not clean up the staged session from system. It is not safe yet.
+ mCallback.onStagedSessionChanged(this);
+ return;
}
- mStagingManager.abortCommittedSession(this);
-
cleanStageDir();
}
@@ -2926,6 +2955,7 @@
/** {@hide} */
void setStagedSessionReady() {
synchronized (mLock) {
+ if (mDestroyed) return; // Do not allow destroyed staged session to change state
mStagedSessionReady = true;
mStagedSessionApplied = false;
mStagedSessionFailed = false;
@@ -2939,6 +2969,7 @@
void setStagedSessionFailed(@StagedSessionErrorCode int errorCode,
String errorMessage) {
synchronized (mLock) {
+ if (mDestroyed) return; // Do not allow destroyed staged session to change state
mStagedSessionReady = false;
mStagedSessionApplied = false;
mStagedSessionFailed = true;
@@ -2953,6 +2984,7 @@
/** {@hide} */
void setStagedSessionApplied() {
synchronized (mLock) {
+ if (mDestroyed) return; // Do not allow destroyed staged session to change state
mStagedSessionReady = false;
mStagedSessionApplied = true;
mStagedSessionFailed = false;
@@ -3197,7 +3229,7 @@
*/
void write(@NonNull XmlSerializer out, @NonNull File sessionsDir) throws IOException {
synchronized (mLock) {
- if (mDestroyed) {
+ if (mDestroyed && !params.isStaged) {
return;
}
@@ -3223,6 +3255,7 @@
}
writeBooleanAttribute(out, ATTR_PREPARED, isPrepared());
writeBooleanAttribute(out, ATTR_COMMITTED, isCommitted());
+ writeBooleanAttribute(out, ATTR_DESTROYED, isDestroyed());
writeBooleanAttribute(out, ATTR_SEALED, isSealed());
writeBooleanAttribute(out, ATTR_MULTI_PACKAGE, params.isMultiPackage);
@@ -3352,6 +3385,7 @@
final String stageCid = readStringAttribute(in, ATTR_SESSION_STAGE_CID);
final boolean prepared = readBooleanAttribute(in, ATTR_PREPARED, true);
final boolean committed = readBooleanAttribute(in, ATTR_COMMITTED);
+ final boolean destroyed = readBooleanAttribute(in, ATTR_DESTROYED);
final boolean sealed = readBooleanAttribute(in, ATTR_SEALED);
final int parentSessionId = readIntAttribute(in, ATTR_PARENT_SESSION_ID,
SessionInfo.INVALID_ID);
@@ -3473,7 +3507,7 @@
return new PackageInstallerSession(callback, context, pm, sessionProvider,
installerThread, stagingManager, sessionId, userId, installerUid,
installSource, params, createdMillis, stageDir, stageCid, fileArray,
- prepared, committed, sealed, childSessionIdsArray, parentSessionId,
+ prepared, committed, destroyed, sealed, childSessionIdsArray, parentSessionId,
isReady, isFailed, isApplied, stagedSessionErrorCode, stagedSessionErrorMessage);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f31dbbf..bde9d57 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -5966,25 +5966,7 @@
|| shouldFilterApplicationLocked(ps2, callingUid, callingUserId)) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
- SigningDetails p1SigningDetails = p1.getSigningDetails();
- SigningDetails p2SigningDetails = p2.getSigningDetails();
- int result = compareSignatures(p1SigningDetails.signatures,
- p2SigningDetails.signatures);
- // To support backwards compatibility with clients of this API expecting pre-key
- // rotation results if either of the packages has a signing lineage the oldest signer
- // in the lineage is used for signature verification.
- if (result != PackageManager.SIGNATURE_MATCH && (
- p1SigningDetails.hasPastSigningCertificates()
- || p2SigningDetails.hasPastSigningCertificates())) {
- Signature[] p1Signatures = p1SigningDetails.hasPastSigningCertificates()
- ? new Signature[]{p1SigningDetails.pastSigningCertificates[0]}
- : p1SigningDetails.signatures;
- Signature[] p2Signatures = p2SigningDetails.hasPastSigningCertificates()
- ? new Signature[]{p2SigningDetails.pastSigningCertificates[0]}
- : p2SigningDetails.signatures;
- result = compareSignatures(p1Signatures, p2Signatures);
- }
- return result;
+ return checkSignaturesInternal(p1.getSigningDetails(), p2.getSigningDetails());
}
}
@@ -5998,21 +5980,21 @@
final int appId2 = UserHandle.getAppId(uid2);
// reader
synchronized (mLock) {
- Signature[] s1;
- Signature[] s2;
+ SigningDetails p1SigningDetails;
+ SigningDetails p2SigningDetails;
Object obj = mSettings.getSettingLPr(appId1);
if (obj != null) {
if (obj instanceof SharedUserSetting) {
if (isCallerInstantApp) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
- s1 = ((SharedUserSetting)obj).signatures.mSigningDetails.signatures;
+ p1SigningDetails = ((SharedUserSetting) obj).signatures.mSigningDetails;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
- s1 = ps.signatures.mSigningDetails.signatures;
+ p1SigningDetails = ps.signatures.mSigningDetails;
} else {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
@@ -6025,23 +6007,53 @@
if (isCallerInstantApp) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
- s2 = ((SharedUserSetting)obj).signatures.mSigningDetails.signatures;
+ p2SigningDetails = ((SharedUserSetting) obj).signatures.mSigningDetails;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
- s2 = ps.signatures.mSigningDetails.signatures;
+ p2SigningDetails = ps.signatures.mSigningDetails;
} else {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
} else {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
- return compareSignatures(s1, s2);
+ return checkSignaturesInternal(p1SigningDetails, p2SigningDetails);
}
}
+ private int checkSignaturesInternal(SigningDetails p1SigningDetails,
+ SigningDetails p2SigningDetails) {
+ if (p1SigningDetails == null) {
+ return p2SigningDetails == null
+ ? PackageManager.SIGNATURE_NEITHER_SIGNED
+ : PackageManager.SIGNATURE_FIRST_NOT_SIGNED;
+ }
+ if (p2SigningDetails == null) {
+ return PackageManager.SIGNATURE_SECOND_NOT_SIGNED;
+ }
+ int result = compareSignatures(p1SigningDetails.signatures, p2SigningDetails.signatures);
+ if (result == PackageManager.SIGNATURE_MATCH) {
+ return result;
+ }
+ // To support backwards compatibility with clients of this API expecting pre-key
+ // rotation results if either of the packages has a signing lineage the oldest signer
+ // in the lineage is used for signature verification.
+ if (p1SigningDetails.hasPastSigningCertificates()
+ || p2SigningDetails.hasPastSigningCertificates()) {
+ Signature[] p1Signatures = p1SigningDetails.hasPastSigningCertificates()
+ ? new Signature[]{p1SigningDetails.pastSigningCertificates[0]}
+ : p1SigningDetails.signatures;
+ Signature[] p2Signatures = p2SigningDetails.hasPastSigningCertificates()
+ ? new Signature[]{p2SigningDetails.pastSigningCertificates[0]}
+ : p2SigningDetails.signatures;
+ result = compareSignatures(p1Signatures, p2Signatures);
+ }
+ return result;
+ }
+
@Override
public boolean hasSigningCertificate(
String packageName, byte[] certificate, @PackageManager.CertificateInputType int type) {
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 9a297d6..a83fa32 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -61,6 +61,7 @@
import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.apk.ApkSignatureVerifier;
@@ -137,6 +138,9 @@
synchronized (mStagedSessions) {
for (int i = 0; i < mStagedSessions.size(); i++) {
final PackageInstallerSession stagedSession = mStagedSessions.valueAt(i);
+ if (stagedSession.isDestroyed()) {
+ continue;
+ }
result.add(stagedSession.generateInfoForCaller(false /*icon*/, callingUid));
}
}
@@ -202,7 +206,7 @@
final IntArray childSessionIds = new IntArray();
if (session.isMultiPackage()) {
for (int id : session.getChildSessionIds()) {
- if (isApexSession(mStagedSessions.get(id))) {
+ if (isApexSession(getStagedSession(id))) {
childSessionIds.add(id);
}
}
@@ -797,6 +801,8 @@
+ session.sessionId + " [" + errorMessage + "]");
session.setStagedSessionFailed(
SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, errorMessage);
+ mPreRebootVerificationHandler.onPreRebootVerificationComplete(
+ session.sessionId);
return;
}
mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete(
@@ -880,7 +886,8 @@
synchronized (mStagedSessions) {
for (int i = 0; i < mStagedSessions.size(); i++) {
final PackageInstallerSession stagedSession = mStagedSessions.valueAt(i);
- if (!stagedSession.isCommitted() || stagedSession.isStagedAndInTerminalState()) {
+ if (!stagedSession.isCommitted() || stagedSession.isStagedAndInTerminalState()
+ || stagedSession.isDestroyed()) {
continue;
}
if (stagedSession.isMultiPackage()) {
@@ -943,27 +950,68 @@
}
}
- void abortCommittedSession(@NonNull PackageInstallerSession session) {
+ /**
+ * <p>Abort committed staged session
+ *
+ * <p>This method must be called while holding {@link PackageInstallerSession.mLock}.
+ *
+ * <p>The method returns {@code false} to indicate it is not safe to clean up the session from
+ * system yet. When it is safe, the method returns {@code true}.
+ *
+ * <p> When it is safe to clean up, {@link StagingManager} will call
+ * {@link PackageInstallerSession#abandon()} on the session again.
+ *
+ * @return {@code true} if it is safe to cleanup the session resources, otherwise {@code false}.
+ */
+ boolean abortCommittedSessionLocked(@NonNull PackageInstallerSession session) {
+ int sessionId = session.sessionId;
if (session.isStagedSessionApplied()) {
- Slog.w(TAG, "Cannot abort applied session : " + session.sessionId);
- return;
+ Slog.w(TAG, "Cannot abort applied session : " + sessionId);
+ return false;
}
- abortSession(session);
+ if (!session.isDestroyed()) {
+ throw new IllegalStateException("Committed session must be destroyed before aborting it"
+ + " from StagingManager");
+ }
+ if (getStagedSession(sessionId) == null) {
+ Slog.w(TAG, "Session " + sessionId + " has been abandoned already");
+ return false;
+ }
- boolean hasApex = sessionContainsApex(session);
- if (hasApex) {
- ApexSessionInfo apexSession = mApexManager.getStagedSessionInfo(session.sessionId);
- if (apexSession == null || isApexSessionFinalized(apexSession)) {
- Slog.w(TAG,
- "Cannot abort session " + session.sessionId
- + " because it is not active or APEXD is not reachable");
- return;
- }
- try {
- mApexManager.abortStagedSession(session.sessionId);
- } catch (Exception ignore) {
+ // If pre-reboot verification is running, then return false. StagingManager will call
+ // abandon again when pre-reboot verification ends.
+ if (mPreRebootVerificationHandler.isVerificationRunning(sessionId)) {
+ Slog.w(TAG, "Session " + sessionId + " aborted before pre-reboot "
+ + "verification completed.");
+ return false;
+ }
+
+ // A session could be marked ready once its pre-reboot verification ends
+ if (session.isStagedSessionReady()) {
+ if (sessionContainsApex(session)) {
+ try {
+ ApexSessionInfo apexSession =
+ mApexManager.getStagedSessionInfo(session.sessionId);
+ if (apexSession == null || isApexSessionFinalized(apexSession)) {
+ Slog.w(TAG,
+ "Cannot abort session " + session.sessionId
+ + " because it is not active.");
+ } else {
+ mApexManager.abortStagedSession(session.sessionId);
+ }
+ } catch (Exception e) {
+ // Failed to contact apexd service. The apex might still be staged. We can still
+ // safely cleanup the staged session since pre-reboot verification is complete.
+ // Also, cleaning up the stageDir prevents the apex from being activated.
+ Slog.w(TAG, "Could not contact apexd to abort staged session " + sessionId);
+ }
}
}
+
+ // Session was successfully aborted from apexd (if required) and pre-reboot verification
+ // is also complete. It is now safe to clean up the session from system.
+ abortSession(session);
+ return true;
}
private boolean isApexSessionFinalized(ApexSessionInfo session) {
@@ -1042,6 +1090,11 @@
// Final states, nothing to do.
return;
}
+ if (session.isDestroyed()) {
+ // Device rebooted before abandoned session was cleaned up.
+ session.abandon();
+ return;
+ }
if (!session.isStagedSessionReady()) {
// The framework got restarted before the pre-reboot verification could complete,
// restart the verification.
@@ -1124,10 +1177,20 @@
}
}
+ private PackageInstallerSession getStagedSession(int sessionId) {
+ PackageInstallerSession session;
+ synchronized (mStagedSessions) {
+ session = mStagedSessions.get(sessionId);
+ }
+ return session;
+ }
+
private final class PreRebootVerificationHandler extends Handler {
// Hold session ids before handler gets ready to do the verification.
private IntArray mPendingSessionIds;
private boolean mIsReady;
+ @GuardedBy("mVerificationRunning")
+ private final SparseBooleanArray mVerificationRunning = new SparseBooleanArray();
PreRebootVerificationHandler(Looper looper) {
super(looper);
@@ -1155,13 +1218,15 @@
@Override
public void handleMessage(Message msg) {
final int sessionId = msg.arg1;
- final PackageInstallerSession session;
- synchronized (mStagedSessions) {
- session = mStagedSessions.get(sessionId);
- }
- // Maybe session was aborted before pre-reboot verification was complete
+ final PackageInstallerSession session = getStagedSession(sessionId);
if (session == null) {
- Slog.d(TAG, "Stopping pre-reboot verification for sessionId: " + sessionId);
+ Slog.wtf(TAG, "Session disappeared in the middle of pre-reboot verification: "
+ + sessionId);
+ return;
+ }
+ if (session.isDestroyed()) {
+ // No point in running verification on a destroyed session
+ onPreRebootVerificationComplete(sessionId);
return;
}
switch (msg.what) {
@@ -1200,9 +1265,40 @@
mPendingSessionIds.add(sessionId);
return;
}
+
+ PackageInstallerSession session = getStagedSession(sessionId);
+ synchronized (mVerificationRunning) {
+ // Do not start verification on a session that has been abandoned
+ if (session == null || session.isDestroyed()) {
+ return;
+ }
+ Slog.d(TAG, "Starting preRebootVerification for session " + sessionId);
+ mVerificationRunning.put(sessionId, true);
+ }
obtainMessage(MSG_PRE_REBOOT_VERIFICATION_START, sessionId, 0).sendToTarget();
}
+ // Things to do when pre-reboot verification completes for a particular sessionId
+ private void onPreRebootVerificationComplete(int sessionId) {
+ // Remove it from mVerificationRunning so that verification is considered complete
+ synchronized (mVerificationRunning) {
+ Slog.d(TAG, "Stopping preRebootVerification for session " + sessionId);
+ mVerificationRunning.delete(sessionId);
+ }
+ // Check if the session was destroyed while pre-reboot verification was running. If so,
+ // abandon it again.
+ PackageInstallerSession session = getStagedSession(sessionId);
+ if (session != null && session.isDestroyed()) {
+ session.abandon();
+ }
+ }
+
+ private boolean isVerificationRunning(int sessionId) {
+ synchronized (mVerificationRunning) {
+ return mVerificationRunning.get(sessionId);
+ }
+ }
+
private void notifyPreRebootVerification_Start_Complete(int sessionId) {
obtainMessage(MSG_PRE_REBOOT_VERIFICATION_APEX, sessionId, 0).sendToTarget();
}
@@ -1221,8 +1317,6 @@
* See {@link PreRebootVerificationHandler} to see all nodes of pre reboot verification
*/
private void handlePreRebootVerification_Start(@NonNull PackageInstallerSession session) {
- Slog.d(TAG, "Starting preRebootVerification for session " + session.sessionId);
-
if ((session.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
// If rollback is enabled for this session, we call through to the RollbackManager
// with the list of sessions it must enable rollback for. Note that
@@ -1269,6 +1363,7 @@
}
} catch (PackageManagerException e) {
session.setStagedSessionFailed(e.error, e.getMessage());
+ onPreRebootVerificationComplete(session.sessionId);
return;
}
@@ -1301,6 +1396,7 @@
// TODO(b/118865310): abort the session on apexd.
} catch (PackageManagerException e) {
session.setStagedSessionFailed(e.error, e.getMessage());
+ onPreRebootVerificationComplete(session.sessionId);
}
}
@@ -1323,9 +1419,18 @@
Slog.e(TAG, "Failed to get hold of StorageManager", e);
session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN,
"Failed to get hold of StorageManager");
+ onPreRebootVerificationComplete(session.sessionId);
return;
}
+ // Stop pre-reboot verification before marking session ready. From this point on, if we
+ // abandon the session then it will be cleaned up immediately. If session is abandoned
+ // after this point, then even if for some reason system tries to install the session
+ // or activate its apex, there won't be any files to work with as they will be cleaned
+ // up by the system as part of abandonment. If session is abandoned before this point,
+ // then the session is already destroyed and cannot be marked ready anymore.
+ onPreRebootVerificationComplete(session.sessionId);
+
// Proactively mark session as ready before calling apexd. Although this call order
// looks counter-intuitive, this is the easiest way to ensure that session won't end up
// in the inconsistent state:
@@ -1337,15 +1442,16 @@
// only apex part of the train will be applied, leaving device in an inconsistent state.
Slog.d(TAG, "Marking session " + session.sessionId + " as ready");
session.setStagedSessionReady();
- final boolean hasApex = sessionContainsApex(session);
- if (!hasApex) {
- // Session doesn't contain apex, nothing to do.
- return;
- }
- try {
- mApexManager.markStagedSessionReady(session.sessionId);
- } catch (PackageManagerException e) {
- session.setStagedSessionFailed(e.error, e.getMessage());
+ if (session.isStagedSessionReady()) {
+ final boolean hasApex = sessionContainsApex(session);
+ if (hasApex) {
+ try {
+ mApexManager.markStagedSessionReady(session.sessionId);
+ } catch (PackageManagerException e) {
+ session.setStagedSessionFailed(e.error, e.getMessage());
+ return;
+ }
+ }
}
}
}
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index eb79b6e..d3cd1a9 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -37,6 +37,9 @@
},
{
"include-filter": "android.content.pm.cts.PackageManagerShellCommandIncrementalTest"
+ },
+ {
+ "include-filter": "android.content.pm.cts.PackageManagerTest"
}
]
},
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index e6af86e..16d96d9 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -4516,8 +4516,8 @@
switch(cmd) {
case "list":
return runList(pw, shell);
- case "list-missing-system-packages":
- return runListMissingSystemPackages(pw, shell);
+ case "report-system-user-package-whitelist-problems":
+ return runReportPackageWhitelistProblems(pw, shell);
default:
return shell.handleDefaultCommands(cmd);
}
@@ -4584,17 +4584,22 @@
}
}
- private int runListMissingSystemPackages(PrintWriter pw, Shell shell) {
+ private int runReportPackageWhitelistProblems(PrintWriter pw, Shell shell) {
boolean verbose = false;
- boolean force = false;
+ boolean criticalOnly = false;
+ int mode = UserSystemPackageInstaller.USER_TYPE_PACKAGE_WHITELIST_MODE_NONE;
String opt;
while ((opt = shell.getNextOption()) != null) {
switch (opt) {
case "-v":
+ case "--verbose":
verbose = true;
break;
- case "--force":
- force = true;
+ case "--critical-only":
+ criticalOnly = true;
+ break;
+ case "--mode":
+ mode = Integer.parseInt(shell.getNextArgRequired());
break;
default:
pw.println("Invalid option: " + opt);
@@ -4602,8 +4607,12 @@
}
}
+ Slog.d(LOG_TAG, "runReportPackageWhitelistProblems(): verbose=" + verbose
+ + ", criticalOnly=" + criticalOnly
+ + ", mode=" + UserSystemPackageInstaller.modeToString(mode));
+
try (IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ")) {
- mSystemPackageInstaller.dumpMissingSystemPackages(ipw, force, verbose);
+ mSystemPackageInstaller.dumpPackageWhitelistProblems(ipw, mode, verbose, criticalOnly);
}
return 0;
}
@@ -5176,13 +5185,18 @@
final PrintWriter pw = getOutPrintWriter();
pw.println("User manager (user) commands:");
pw.println(" help");
- pw.println(" Print this help text.");
+ pw.println(" Prints this help text.");
pw.println("");
pw.println(" list [-v] [-all]");
pw.println(" Prints all users on the system.");
- pw.println(" list-missing-system-packages [-v] [--force]");
- pw.println(" Prints all system packages that were not explicitly configured to be "
- + "installed.");
+ pw.println(" report-system-user-package-whitelist-problems [-v | --verbose] "
+ + "[--critical-only] [--mode MODE]");
+ pw.println(" Reports all issues on user-type package whitelist XML files. Options:");
+ pw.println(" -v | --verbose : shows extra info, like number of issues");
+ pw.println(" --critical-only: show only critical issues, excluding warnings");
+ pw.println(" --mode MODE: shows what errors would be if device used mode MODE (where"
+ + " MODE is the whitelist mode integer as defined by "
+ + "config_userTypePackageWhitelistMode)");
}
}
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 0b6024a..1fec8aa 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -208,7 +208,6 @@
Sets.newArraySet(
UserManager.DISALLOW_CONFIG_DATE_TIME,
UserManager.DISALLOW_CAMERA,
- UserManager.DISALLOW_ADD_USER,
UserManager.DISALLOW_BLUETOOTH,
UserManager.DISALLOW_BLUETOOTH_SHARING,
UserManager.DISALLOW_CONFIG_CELL_BROADCASTS,
diff --git a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
index cd1087f5..9ec03e5 100644
--- a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
+++ b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
@@ -27,7 +27,7 @@
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.Pair;
+import android.util.DebugUtils;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
@@ -41,6 +41,7 @@
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -111,14 +112,20 @@
* frameworks/base/core/res/res/values/config.xml
*/
static final String PACKAGE_WHITELIST_MODE_PROP = "persist.debug.user.package_whitelist_mode";
- static final int USER_TYPE_PACKAGE_WHITELIST_MODE_DISABLE = 0x00;
- static final int USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE = 0x01;
- static final int USER_TYPE_PACKAGE_WHITELIST_MODE_LOG = 0x02;
- static final int USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST = 0x04;
- static final int USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST_SYSTEM = 0x08;
- static final int USER_TYPE_PACKAGE_WHITELIST_MODE_IGNORE_OTA = 0x10;
+
+ // NOTE: flags below are public so they can used by DebugUtils.flagsToString. And this class
+ // itself is package-protected, so it doesn't matter...
+ public static final int USER_TYPE_PACKAGE_WHITELIST_MODE_DISABLE = 0x00;
+ public static final int USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE = 0x01;
+ public static final int USER_TYPE_PACKAGE_WHITELIST_MODE_LOG = 0x02;
+ public static final int USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST = 0x04;
+ public static final int USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST_SYSTEM = 0x08;
+ public static final int USER_TYPE_PACKAGE_WHITELIST_MODE_IGNORE_OTA = 0x10;
static final int USER_TYPE_PACKAGE_WHITELIST_MODE_DEVICE_DEFAULT = -1;
+ // Used by Shell command only
+ static final int USER_TYPE_PACKAGE_WHITELIST_MODE_NONE = -1000;
+
@IntDef(flag = true, prefix = "USER_TYPE_PACKAGE_WHITELIST_MODE_", value = {
USER_TYPE_PACKAGE_WHITELIST_MODE_DISABLE,
USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE,
@@ -266,58 +273,56 @@
if (!isLogMode(mode) && !isEnforceMode(mode)) {
return;
}
- final List<Pair<Boolean, String>> warnings = checkSystemPackagesWhitelistWarnings(mode);
- final int size = warnings.size();
- if (size == 0) {
- Slog.v(TAG, "checkWhitelistedSystemPackages(mode=" + mode + "): no warnings");
- return;
- }
+ Slog.v(TAG, "Checking that all system packages are whitelisted.");
- if (isImplicitWhitelistMode(mode) && !isLogMode(mode)) {
- // Only shows whether all whitelisted packages are indeed on the system.
- for (int i = 0; i < size; i++) {
- final Pair<Boolean, String> pair = warnings.get(i);
- final boolean isSevere = pair.first;
- if (!isSevere) {
- final String msg = pair.second;
- Slog.w(TAG, msg);
- }
+ // Check whether all whitelisted packages are indeed on the system.
+ final List<String> warnings = getPackagesWhitelistWarnings();
+ final int numberWarnings = warnings.size();
+ if (numberWarnings == 0) {
+ Slog.v(TAG, "checkWhitelistedSystemPackages(mode=" + modeToString(mode)
+ + ") has no warnings");
+ } else {
+ Slog.w(TAG, "checkWhitelistedSystemPackages(mode=" + modeToString(mode)
+ + ") has " + numberWarnings + " warnings:");
+ for (int i = 0; i < numberWarnings; i++) {
+ Slog.w(TAG, warnings.get(i));
}
+ }
+
+ // Check whether all system packages are indeed whitelisted.
+ if (isImplicitWhitelistMode(mode) && !isLogMode(mode)) {
return;
}
- Slog.v(TAG, "checkWhitelistedSystemPackages(mode=" + mode + "): " + size + " warnings");
+ final List<String> errors = getPackagesWhitelistErrors(mode);
+ final int numberErrors = errors.size();
+
+ if (numberErrors == 0) {
+ Slog.v(TAG, "checkWhitelistedSystemPackages(mode=" + modeToString(mode)
+ + ") has no errors");
+ return;
+ }
+ Slog.e(TAG, "checkWhitelistedSystemPackages(mode=" + modeToString(mode) + ") has "
+ + numberErrors + " errors:");
+
boolean doWtf = !isImplicitWhitelistMode(mode);
- for (int i = 0; i < size; i++) {
- final Pair<Boolean, String> pair = warnings.get(i);
- final boolean isSevere = pair.first;
- final String msg = pair.second;
- if (isSevere) {
- if (doWtf) {
- Slog.wtf(TAG, msg);
- } else {
- Slog.e(TAG, msg);
- }
+ for (int i = 0; i < numberWarnings; i++) {
+ final String msg = errors.get(i);
+ if (doWtf) {
+ Slog.wtf(TAG, msg);
} else {
- Slog.w(TAG, msg);
+ Slog.e(TAG, msg);
}
}
}
- // TODO: method below was created to refactor the one-time logging logic so it can be used on
- // dump / cmd as well. It could to be further refactored (for example, creating a new
- // structure for the warnings so it doesn't need a Pair).
/**
- * Gets warnings for system user whitelisting.
- *
- * @return list of warnings, where {@code Pair.first} is the severity ({@code true} for WTF,
- * {@code false} for WARN) and {@code Pair.second} the message.
+ * Gets packages that are listed in the whitelist XML but are not present on the system image.
*/
@NonNull
- private List<Pair<Boolean, String>> checkSystemPackagesWhitelistWarnings(
- @PackageWhitelistMode int mode) {
+ private List<String> getPackagesWhitelistWarnings() {
final Set<String> allWhitelistedPackages = getWhitelistedSystemPackages();
- final List<Pair<Boolean, String>> warnings = new ArrayList<>();
+ final List<String> warnings = new ArrayList<>();
final PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class);
// Check whether all whitelisted packages are indeed on the system.
@@ -326,25 +331,39 @@
for (String pkgName : allWhitelistedPackages) {
final AndroidPackage pkg = pmInt.getPackage(pkgName);
if (pkg == null) {
- warnings.add(new Pair<>(false, String.format(notPresentFmt, pkgName)));
+ warnings.add(String.format(notPresentFmt, pkgName));
} else if (!pkg.isSystem()) {
- warnings.add(new Pair<>(false, String.format(notSystemFmt, pkgName)));
+ warnings.add(String.format(notSystemFmt, pkgName));
}
}
+ return warnings;
+ }
+
+ /**
+ * Gets packages that are not listed in the whitelist XMLs when they should be.
+ */
+ @NonNull
+ private List<String> getPackagesWhitelistErrors(@PackageWhitelistMode int mode) {
+ if ((!isEnforceMode(mode) || isImplicitWhitelistMode(mode)) && !isLogMode(mode)) {
+ return Collections.emptyList();
+ }
+
+ final List<String> errors = new ArrayList<>();
+ final Set<String> allWhitelistedPackages = getWhitelistedSystemPackages();
+ final PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class);
// Check whether all system packages are indeed whitelisted.
final String logMessageFmt = "System package %s is not whitelisted using "
+ "'install-in-user-type' in SystemConfig for any user types!";
- final boolean isSevere = isEnforceMode(mode);
pmInt.forEachPackage(pkg -> {
if (!pkg.isSystem()) return;
final String pkgName = pkg.getManifestPackageName();
if (!allWhitelistedPackages.contains(pkgName)) {
- warnings.add(new Pair<>(isSevere, String.format(logMessageFmt, pkgName)));
+ errors.add(String.format(logMessageFmt, pkgName));
}
});
- return warnings;
+ return errors;
}
/** Whether to only install system packages in new users for which they are whitelisted. */
@@ -420,10 +439,28 @@
if (runtimeMode != USER_TYPE_PACKAGE_WHITELIST_MODE_DEVICE_DEFAULT) {
return runtimeMode;
}
+ return getDeviceDefaultWhitelistMode();
+ }
+
+ /** Gets the PackageWhitelistMode as defined by {@code config_userTypePackageWhitelistMode}. */
+ private @PackageWhitelistMode int getDeviceDefaultWhitelistMode() {
return Resources.getSystem()
.getInteger(com.android.internal.R.integer.config_userTypePackageWhitelistMode);
}
+ static @NonNull String modeToString(@PackageWhitelistMode int mode) {
+ // Must handle some types separately because they're not bitwise flags
+ switch (mode) {
+ case USER_TYPE_PACKAGE_WHITELIST_MODE_DEVICE_DEFAULT:
+ return "DEVICE_DEFAULT";
+ case USER_TYPE_PACKAGE_WHITELIST_MODE_NONE:
+ return "NONE";
+ default:
+ return DebugUtils.flagsToString(UserSystemPackageInstaller.class,
+ "USER_TYPE_PACKAGE_WHITELIST_MODE_", mode);
+ }
+ }
+
/**
* Gets the system packages names that should be installed on the given user.
* See {@link #getInstallablePackagesForUserType(String)}.
@@ -703,34 +740,44 @@
pw.decreaseIndent(); pw.decreaseIndent();
pw.increaseIndent();
- dumpMissingSystemPackages(pw, /* force= */ true, /* verbose= */ true);
+ dumpPackageWhitelistProblems(pw, mode, /* verbose= */ true, /* criticalOnly= */ false);
pw.decreaseIndent();
}
- void dumpMissingSystemPackages(IndentingPrintWriter pw, boolean force, boolean verbose) {
- final int mode = getWhitelistMode();
- final boolean show = force || (isEnforceMode(mode) && !isImplicitWhitelistMode(mode));
- if (!show) return;
+ void dumpPackageWhitelistProblems(IndentingPrintWriter pw, @PackageWhitelistMode int mode,
+ boolean verbose, boolean criticalOnly) {
+ // Handle special cases first
+ if (mode == USER_TYPE_PACKAGE_WHITELIST_MODE_NONE) {
+ mode = getWhitelistMode();
+ } else if (mode == USER_TYPE_PACKAGE_WHITELIST_MODE_DEVICE_DEFAULT) {
+ mode = getDeviceDefaultWhitelistMode();
+ }
+ Slog.v(TAG, "dumpPackageWhitelistProblems(): using mode " + modeToString(mode));
- final List<Pair<Boolean, String>> warnings = checkSystemPackagesWhitelistWarnings(mode);
- final int size = warnings.size();
+ final List<String> errors = getPackagesWhitelistErrors(mode);
+ showIssues(pw, verbose, errors, "errors");
+ if (criticalOnly) return;
+
+ final List<String> warnings = getPackagesWhitelistWarnings();
+ showIssues(pw, verbose, warnings, "warnings");
+ }
+
+ private static void showIssues(IndentingPrintWriter pw, boolean verbose, List<String> issues,
+ String issueType) {
+ final int size = issues.size();
if (size == 0) {
if (verbose) {
- pw.println("All system packages are accounted for");
+ pw.print("No "); pw.println(issueType);
}
return;
}
-
if (verbose) {
- pw.print(size); pw.println(" warnings for system user:");
+ pw.print(size); pw.print(' '); pw.println(issueType);
pw.increaseIndent();
}
for (int i = 0; i < size; i++) {
- final Pair<Boolean, String> pair = warnings.get(i);
- final String lvl = pair.first ? "WTF" : "WARN";
- final String msg = pair.second;
- pw.print(lvl); pw.print(": "); pw.println(msg);
+ pw.println(issues.get(i));
}
if (verbose) {
pw.decreaseIndent();
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 9051d85..8f3bf39 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -408,6 +408,25 @@
}
grantRuntimePermissionsForSystemPackage(pm, userId, pkg);
}
+
+ // Grant READ_PHONE_STATE to all system apps that have READ_PRIVILEGED_PHONE_STATE
+ for (PackageInfo pkg : packages) {
+ if (pkg == null
+ || !doesPackageSupportRuntimePermissions(pkg)
+ || ArrayUtils.isEmpty(pkg.requestedPermissions)
+ || !pkg.applicationInfo.isPrivilegedApp()) {
+ continue;
+ }
+ for (String permission : pkg.requestedPermissions) {
+ if (Manifest.permission.READ_PRIVILEGED_PHONE_STATE.equals(permission)) {
+ grantRuntimePermissions(pm, pkg,
+ Collections.singleton(Manifest.permission.READ_PHONE_STATE),
+ true, // systemFixed
+ userId);
+ }
+ }
+ }
+
}
@SafeVarargs
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index f178903..a1cc44a 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -149,6 +149,8 @@
import libcore.util.EmptyArray;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -417,6 +419,11 @@
LocalServices.addService(PermissionManagerInternal.class, localService);
}
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ mContext.getSystemService(PermissionControllerManager.class).dump(fd, pw, args);
+ }
+
/**
* Creates and returns an initialized, internal service for use by other components.
* <p>
@@ -1282,6 +1289,7 @@
final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName);
final int callingUid = Binder.getCallingUid();
+ final int packageUid = UserHandle.getUid(userId, pkg.getUid());
if (!checkAutoRevokeAccess(pkg, callingUid)) {
return false;
@@ -1289,7 +1297,7 @@
if (mAppOpsManager
.checkOpNoThrow(AppOpsManager.OP_AUTO_REVOKE_MANAGED_BY_INSTALLER,
- callingUid, packageName)
+ packageUid, packageName)
!= MODE_ALLOWED) {
// Whitelist user set - don't override
return false;
@@ -1298,7 +1306,7 @@
final long identity = Binder.clearCallingIdentity();
try {
mAppOpsManager.setMode(AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
- callingUid, packageName,
+ packageUid, packageName,
whitelisted ? MODE_IGNORED : MODE_ALLOWED);
} finally {
Binder.restoreCallingIdentity(identity);
@@ -1331,6 +1339,7 @@
final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName);
final int callingUid = Binder.getCallingUid();
+ final int packageUid = UserHandle.getUid(userId, pkg.getUid());
if (!checkAutoRevokeAccess(pkg, callingUid)) {
return false;
@@ -1339,7 +1348,7 @@
final long identity = Binder.clearCallingIdentity();
try {
return mAppOpsManager.checkOpNoThrow(
- AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, callingUid, packageName)
+ AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, packageUid, packageName)
== MODE_IGNORED;
} finally {
Binder.restoreCallingIdentity(identity);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 2f84a99..3bc151a 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1222,10 +1222,12 @@
case LONG_PRESS_POWER_NOTHING:
break;
case LONG_PRESS_POWER_GLOBAL_ACTIONS:
- mPowerKeyHandled = true;
- performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
- "Power - Long Press - Global Actions");
- showGlobalActionsInternal();
+ if (!mPowerKeyHandled) {
+ mPowerKeyHandled = true;
+ performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
+ "Power - Long Press - Global Actions");
+ showGlobalActionsInternal();
+ }
break;
case LONG_PRESS_POWER_SHUT_OFF:
case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index b0c702f..3b4c423 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -30,6 +30,7 @@
import static android.util.MathUtils.constrain;
import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_IS_UID;
+import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_TRUNCATE_TIMESTAMP;
import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
import static com.android.server.stats.pull.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs;
import static com.android.server.stats.pull.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs;
@@ -755,6 +756,13 @@
stats.getValues(j, entry);
StatsEvent.Builder e = StatsEvent.newBuilder();
e.setAtomId(atomTag);
+ switch (atomTag) {
+ case FrameworkStatsLog.MOBILE_BYTES_TRANSFER:
+ case FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG:
+ e.addBooleanAnnotation(ANNOTATION_ID_TRUNCATE_TIMESTAMP, true);
+ break;
+ default:
+ }
e.writeInt(entry.uid);
e.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
if (withFgbg) {
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 289bf66..4ba58bd 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -665,12 +665,12 @@
@Override
public void showAuthenticationDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
int biometricModality, boolean requireConfirmation, int userId, String opPackageName,
- long operationId) {
+ long operationId, int sysUiSessionId) {
enforceBiometricDialog();
if (mBar != null) {
try {
mBar.showAuthenticationDialog(bundle, receiver, biometricModality,
- requireConfirmation, userId, opPackageName, operationId);
+ requireConfirmation, userId, opPackageName, operationId, sysUiSessionId);
} catch (RemoteException ex) {
}
}
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index 6734ce7..72cdf4a 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -21,9 +21,8 @@
import static android.Manifest.permission.GET_APP_GRANTED_URI_PERMISSIONS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
+import static android.content.Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION;
import static android.content.Intent.FLAG_GRANT_PREFIX_URI_PERMISSION;
-import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
-import static android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
@@ -1138,8 +1137,8 @@
targetHoldsPermission = false;
}
- final boolean basicGrant = (modeFlags & ~(FLAG_GRANT_READ_URI_PERMISSION
- | FLAG_GRANT_WRITE_URI_PERMISSION)) == 0;
+ final boolean basicGrant = (modeFlags
+ & (FLAG_GRANT_PERSISTABLE_URI_PERMISSION | FLAG_GRANT_PREFIX_URI_PERMISSION)) == 0;
if (basicGrant && targetHoldsPermission) {
// When caller holds permission, and this is a simple permission
// grant, we can skip generating any bookkeeping; when any advanced
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index f05217c..850b657 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -570,7 +570,7 @@
private final WindowState.UpdateReportedVisibilityResults mReportedVisibilityResults =
new WindowState.UpdateReportedVisibilityResults();
- boolean mUseTransferredAnimation;
+ private boolean mUseTransferredAnimation;
/**
* @see #currentLaunchCanTurnScreenOn()
@@ -1160,8 +1160,14 @@
} else {
mLastReportedMultiWindowMode = inMultiWindowMode;
computeConfigurationAfterMultiWindowModeChange();
- ensureActivityConfiguration(0 /* globalChanges */, PRESERVE_WINDOWS,
- true /* ignoreVisibility */);
+ // If the activity is in stopping or stopped state, for instance, it's in the
+ // split screen task and not the top one, the last configuration it should keep
+ // is the one before multi-window mode change.
+ final ActivityState state = getState();
+ if (state != STOPPED && state != STOPPING) {
+ ensureActivityConfiguration(0 /* globalChanges */, PRESERVE_WINDOWS,
+ true /* ignoreVisibility */);
+ }
}
}
}
@@ -3366,12 +3372,14 @@
}
setClientVisible(fromActivity.mClientVisible);
- transferAnimation(fromActivity);
+ if (fromActivity.isAnimating()) {
+ transferAnimation(fromActivity);
- // When transferring an animation, we no longer need to apply an animation to the
- // the token we transfer the animation over. Thus, set this flag to indicate we've
- // transferred the animation.
- mUseTransferredAnimation = true;
+ // When transferring an animation, we no longer need to apply an animation to
+ // the token we transfer the animation over. Thus, set this flag to indicate
+ // we've transferred the animation.
+ mUseTransferredAnimation = true;
+ }
mWmService.updateFocusedWindowLocked(
UPDATE_FOCUS_WILL_PLACE_SURFACES, true /*updateInputWindows*/);
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index db5e972..9df3f85 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -242,12 +242,6 @@
*/
boolean mInResumeTopActivity = false;
- private boolean mUpdateBoundsDeferred;
- private boolean mUpdateBoundsDeferredCalled;
- private boolean mUpdateDisplayedBoundsDeferredCalled;
- private final Rect mDeferredBounds = new Rect();
- private final Rect mDeferredDisplayedBounds = new Rect();
-
int mCurrentUser;
/** For comparison with DisplayContent bounds. */
@@ -846,58 +840,6 @@
return getDisplayContent();
}
- /**
- * Defers updating the bounds of the stack. If the stack was resized/repositioned while
- * deferring, the bounds will update in {@link #continueUpdateBounds()}.
- */
- void deferUpdateBounds() {
- if (!mUpdateBoundsDeferred) {
- mUpdateBoundsDeferred = true;
- mUpdateBoundsDeferredCalled = false;
- }
- }
-
- /**
- * Continues updating bounds after updates have been deferred. If there was a resize attempt
- * between {@link #deferUpdateBounds()} and {@link #continueUpdateBounds()}, the stack will
- * be resized to that bounds.
- */
- void continueUpdateBounds() {
- if (mUpdateBoundsDeferred) {
- mUpdateBoundsDeferred = false;
- if (mUpdateBoundsDeferredCalled) {
- setTaskBounds(mDeferredBounds);
- setBounds(mDeferredBounds);
- }
- }
- }
-
- private boolean updateBoundsAllowed(Rect bounds) {
- if (!mUpdateBoundsDeferred) {
- return true;
- }
- if (bounds != null) {
- mDeferredBounds.set(bounds);
- } else {
- mDeferredBounds.setEmpty();
- }
- mUpdateBoundsDeferredCalled = true;
- return false;
- }
-
- private boolean updateDisplayedBoundsAllowed(Rect bounds) {
- if (!mUpdateBoundsDeferred) {
- return true;
- }
- if (bounds != null) {
- mDeferredDisplayedBounds.set(bounds);
- } else {
- mDeferredDisplayedBounds.setEmpty();
- }
- mUpdateDisplayedBoundsDeferredCalled = true;
- return false;
- }
-
/** @return true if the stack can only contain one task */
boolean isSingleTaskInstance() {
final DisplayContent display = getDisplay();
@@ -2687,10 +2629,6 @@
// TODO: Can only be called from special methods in ActivityStackSupervisor.
// Need to consolidate those calls points into this resize method so anyone can call directly.
void resize(Rect displayedBounds, boolean preserveWindows, boolean deferResume) {
- if (!updateBoundsAllowed(displayedBounds)) {
- return;
- }
-
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "stack.resize_" + getRootTaskId());
mAtmService.deferWindowLayout();
try {
@@ -2730,10 +2668,6 @@
* basically resizes both stack and task bounds to the same bounds.
*/
private void setTaskBounds(Rect bounds) {
- if (!updateBoundsAllowed(bounds)) {
- return;
- }
-
final PooledConsumer c = PooledLambda.obtainConsumer(ActivityStack::setTaskBounds,
PooledLambda.__(Task.class), bounds);
forAllLeafTasks(c, true /* traverseTopToBottom */);
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index ed21539..fb7ba62 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -69,7 +69,6 @@
import static com.android.server.wm.ActivityTaskManagerService.ANIMATE;
import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_SUPERVISOR_STACK_MSG;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
-import static com.android.server.wm.RootWindowContainer.MATCH_TASK_IN_STACKS_ONLY;
import static com.android.server.wm.RootWindowContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
import static com.android.server.wm.RootWindowContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
import static com.android.server.wm.RootWindowContainer.TAG_STATES;
@@ -125,7 +124,6 @@
import android.os.WorkSource;
import android.provider.MediaStore;
import android.util.ArrayMap;
-import android.util.ArraySet;
import android.util.MergedConfiguration;
import android.util.Slog;
import android.util.SparseArray;
@@ -364,11 +362,6 @@
*/
boolean mAppVisibilitiesChangedSinceLastPause;
- /**
- * Set of tasks that are in resizing mode during an app transition to fill the "void".
- */
- private final ArraySet<Integer> mResizingTasksDuringAnimation = new ArraySet<>();
-
private KeyguardController mKeyguardController;
private PowerManager mPowerManager;
@@ -1415,29 +1408,6 @@
return mLaunchParamsController;
}
- private void deferUpdateRecentsHomeStackBounds() {
- mRootWindowContainer.deferUpdateBounds(ACTIVITY_TYPE_RECENTS);
- mRootWindowContainer.deferUpdateBounds(ACTIVITY_TYPE_HOME);
- }
-
- private void continueUpdateRecentsHomeStackBounds() {
- mRootWindowContainer.continueUpdateBounds(ACTIVITY_TYPE_RECENTS);
- mRootWindowContainer.continueUpdateBounds(ACTIVITY_TYPE_HOME);
- }
-
- void notifyAppTransitionDone() {
- continueUpdateRecentsHomeStackBounds();
- for (int i = mResizingTasksDuringAnimation.size() - 1; i >= 0; i--) {
- final int taskId = mResizingTasksDuringAnimation.valueAt(i);
- final Task task =
- mRootWindowContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_ONLY);
- if (task != null) {
- task.setTaskDockedResizing(false);
- }
- }
- mResizingTasksDuringAnimation.clear();
- }
-
void setSplitScreenResizing(boolean resizing) {
if (resizing == mDockedStackResizing) {
return;
@@ -2471,16 +2441,6 @@
}
}
- /**
- * Puts a task into resizing mode during the next app transition.
- *
- * @param task The task to put into resizing mode
- */
- void setResizingDuringAnimation(Task task) {
- mResizingTasksDuringAnimation.add(task.mTaskId);
- task.setTaskDockedResizing(true);
- }
-
int startActivityFromRecents(int callingPid, int callingUid, int taskId,
SafeActivityOptions options) {
Task task = null;
@@ -2508,27 +2468,12 @@
mService.deferWindowLayout();
try {
- if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- // Defer updating the stack in which recents is until the app transition is done, to
- // not run into issues where we still need to draw the task in recents but the
- // docked stack is already created.
- deferUpdateRecentsHomeStackBounds();
- // TODO(task-hierarchy): Remove when tiles are in hierarchy.
- // Unset launching windowing mode to prevent creating split-screen-primary stack
- // in RWC#anyTaskForId() below.
- activityOptions.setLaunchWindowingMode(WINDOWING_MODE_UNDEFINED);
- }
-
task = mRootWindowContainer.anyTaskForId(taskId,
MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, activityOptions, ON_TOP);
if (task == null) {
- continueUpdateRecentsHomeStackBounds();
mWindowManager.executeAppTransition();
throw new IllegalArgumentException(
"startActivityFromRecents: Task " + taskId + " not found.");
- } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- && task.getWindowingMode() != windowingMode) {
- mService.moveTaskToSplitScreenPrimaryTask(task, true /* toTop */);
}
if (windowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
@@ -2577,12 +2522,6 @@
false /* validateIncomingUser */, null /* originatingPendingIntent */,
false /* allowBackgroundActivityStart */);
} finally {
- if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && task != null) {
- // If we are launching the task in the docked stack, put it into resizing mode so
- // the window renders full-screen with the background filling the void. Also only
- // call this at the end to make sure that tasks exists on the window manager side.
- setResizingDuringAnimation(task);
- }
mService.continueWindowLayout();
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 4181f4b..d5df906 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -152,17 +152,6 @@
IVoiceInteractor mInteractor);
/**
- * Callback for window manager to let activity manager know that the app transition was
- * cancelled.
- */
- public abstract void notifyAppTransitionCancelled();
-
- /**
- * Callback for window manager to let activity manager know that the app transition is finished.
- */
- public abstract void notifyAppTransitionFinished();
-
- /**
* Returns the top activity from each of the currently visible stacks. The first entry will be
* the focused activity.
*/
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 6a8d5d9..36caeec 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -6079,20 +6079,6 @@
}
@Override
- public void notifyAppTransitionFinished() {
- synchronized (mGlobalLock) {
- mStackSupervisor.notifyAppTransitionDone();
- }
- }
-
- @Override
- public void notifyAppTransitionCancelled() {
- synchronized (mGlobalLock) {
- mStackSupervisor.notifyAppTransitionDone();
- }
- }
-
- @Override
public List<IBinder> getTopVisibleActivities() {
synchronized (mGlobalLock) {
return mRootWindowContainer.getTopVisibleActivities();
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 1f10c46..5dd8e28 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1620,9 +1620,7 @@
}
}
- // Announce rotation only if we will not animate as we already have the
- // windows in final state. Otherwise, we make this call at the rotation end.
- if (screenRotationAnimation == null && mWmService.mAccessibilityController != null) {
+ if (mWmService.mAccessibilityController != null) {
mWmService.mAccessibilityController.onRotationChangedLocked(this);
}
}
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 56986c2..dfd44f5 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -166,11 +166,18 @@
return;
}
- mTmpRect.set(mWin.getFrameLw());
- if (mFrameProvider != null) {
- mFrameProvider.accept(mWin.getDisplayContent().mDisplayFrames, mWin, mTmpRect);
+ // Make sure we set the valid source frame only when server visible is true, because the
+ // frame may not yet determined that server side doesn't think the window is ready to
+ // visible. (i.e. No surface, pending insets that were given during layout, etc..)
+ if (mServerVisible) {
+ mTmpRect.set(mWin.getFrameLw());
+ if (mFrameProvider != null) {
+ mFrameProvider.accept(mWin.getDisplayContent().mDisplayFrames, mWin, mTmpRect);
+ } else {
+ mTmpRect.inset(mWin.mGivenContentInsets);
+ }
} else {
- mTmpRect.inset(mWin.mGivenContentInsets);
+ mTmpRect.setEmpty();
}
mSource.setFrame(mTmpRect);
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 8b9e9fe..9a30f1c 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2504,20 +2504,6 @@
return list;
}
- void deferUpdateBounds(int activityType) {
- final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
- if (stack != null) {
- stack.deferUpdateBounds();
- }
- }
-
- void continueUpdateBounds(int activityType) {
- final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
- if (stack != null) {
- stack.continueUpdateBounds();
- }
- }
-
@Override
public void onDisplayAdded(int displayId) {
if (DEBUG_STACK) Slog.v(TAG, "Display added displayId=" + displayId);
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index 90936ef..14ab2e3 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -732,13 +732,6 @@
mService.mAnimator.mBulkUpdateParams |= WindowSurfacePlacer.SET_UPDATE_ROTATION;
kill();
mService.updateRotation(false, false);
- AccessibilityController accessibilityController = mService.mAccessibilityController;
-
- if (accessibilityController != null) {
- // We just finished rotation animation which means we did not
- // announce the rotation and waited for it to end, announce now.
- accessibilityController.onRotationChangedLocked(mDisplayContent);
- }
}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 44a8daa..caa0ddb 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -82,7 +82,6 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskManagerService.TAG_STACK;
-import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
import static com.android.server.wm.IdentifierProto.HASH_CODE;
import static com.android.server.wm.IdentifierProto.TITLE;
import static com.android.server.wm.IdentifierProto.USER_ID;
@@ -2013,7 +2012,7 @@
}
void updateSurfaceSize(SurfaceControl.Transaction transaction) {
- if (mSurfaceControl == null || mCreatedByOrganizer) {
+ if (mSurfaceControl == null || isOrganized()) {
return;
}
@@ -3059,15 +3058,6 @@
return mDragResizeMode;
}
- /**
- * Puts this task into docked drag resizing mode. See {@link DragResizeMode}.
- *
- * @param resizing Whether to put the task into drag resize mode.
- */
- public void setTaskDockedResizing(boolean resizing) {
- setDragResizing(resizing, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
- }
-
void adjustBoundsForDisplayChangeIfNeeded(final DisplayContent displayContent) {
if (displayContent == null) {
return;
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index 5f3c633..132d00a 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -123,19 +123,20 @@
}
final WallpaperController wallpaperController = mDisplayContent.mWallpaperController;
+ final WindowState wallpaperTarget = wallpaperController.getWallpaperTarget();
- if (visible) {
- final WindowState wallpaperTarget = wallpaperController.getWallpaperTarget();
+ if (visible && wallpaperTarget != null) {
final RecentsAnimationController recentsAnimationController =
mWmService.getRecentsAnimationController();
- if (wallpaperTarget != null
- && recentsAnimationController != null
+ if (recentsAnimationController != null
&& recentsAnimationController.isAnimatingTask(wallpaperTarget.getTask())) {
// If the Recents animation is running, and the wallpaper target is the animating
// task we want the wallpaper to be rotated in the same orientation as the
// RecentsAnimation's target (e.g the launcher)
recentsAnimationController.linkFixedRotationTransformIfNeeded(this);
- } else if (wallpaperTarget != null
+ } else if ((wallpaperTarget.mActivityRecord == null
+ // Ignore invisible activity because it may be moving to background.
+ || wallpaperTarget.mActivityRecord.mVisibleRequested)
&& wallpaperTarget.mToken.hasFixedRotationTransform()) {
// If the wallpaper target has a fixed rotation, we want the wallpaper to follow its
// rotation
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 73126c8..814fa72 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -196,6 +196,7 @@
import android.provider.Settings;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
+import android.sysprop.SurfaceFlingerProperties;
import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -305,7 +306,9 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.NoSuchElementException;
import java.util.Objects;
+import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;
@@ -1064,12 +1067,10 @@
@Override
public void onAppTransitionCancelledLocked(int transit) {
- mAtmInternal.notifyAppTransitionCancelled();
}
@Override
public void onAppTransitionFinishedLocked(IBinder token) {
- mAtmInternal.notifyAppTransitionFinished();
final ActivityRecord atoken = mRoot.getActivityRecord(token);
if (atoken == null) {
return;
@@ -4694,6 +4695,11 @@
}
private static boolean queryWideColorGamutSupport() {
+ boolean defaultValue = false;
+ Optional<Boolean> hasWideColorProp = SurfaceFlingerProperties.has_wide_color_display();
+ if (hasWideColorProp.isPresent()) {
+ return hasWideColorProp.get();
+ }
try {
ISurfaceFlingerConfigs surfaceFlinger = ISurfaceFlingerConfigs.getService();
OptionalBool hasWideColor = surfaceFlinger.hasWideColorDisplay();
@@ -4702,11 +4708,18 @@
}
} catch (RemoteException e) {
// Ignore, we're in big trouble if we can't talk to SurfaceFlinger's config store
+ } catch (NoSuchElementException e) {
+ return defaultValue;
}
return false;
}
private static boolean queryHdrSupport() {
+ boolean defaultValue = false;
+ Optional<Boolean> hasHdrProp = SurfaceFlingerProperties.has_HDR_display();
+ if (hasHdrProp.isPresent()) {
+ return hasHdrProp.get();
+ }
try {
ISurfaceFlingerConfigs surfaceFlinger = ISurfaceFlingerConfigs.getService();
OptionalBool hasHdr = surfaceFlinger.hasHDRDisplay();
@@ -4715,6 +4728,8 @@
}
} catch (RemoteException e) {
// Ignore, we're in big trouble if we can't talk to SurfaceFlinger's config store
+ } catch (NoSuchElementException e) {
+ return defaultValue;
}
return false;
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 0e83bee..c570cf1 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -846,6 +846,23 @@
}
}
+ private boolean shouldConsumeMainWindowSizeTransaction() {
+ // We only consume the transaction when the client is calling relayout
+ // because this is the only time we know the frameNumber will be valid
+ // due to the client renderer being paused. Put otherwise, only when
+ // mInRelayout is true can we guarantee the next frame will contain
+ // the most recent configuration.
+ if (!mWin.mInRelayout) return false;
+ // Since we can only do this for one window, we focus on the main application window
+ if (mAttrType != TYPE_BASE_APPLICATION) return false;
+ final Task task = mWin.getTask();
+ if (task == null) return false;
+ if (task.getMainWindowSizeChangeTransaction() == null) return false;
+ // Likewise we only focus on the task root, since we can only use one window
+ if (!mWin.mActivityRecord.isRootOfTask()) return false;
+ return true;
+ }
+
void setSurfaceBoundariesLocked(final boolean recoveringMemory) {
if (mSurfaceController == null) {
return;
@@ -886,8 +903,9 @@
clipRect = mTmpClipRect;
}
- if (w.mInRelayout && (mAttrType == TYPE_BASE_APPLICATION) && (task != null)
- && (task.getMainWindowSizeChangeTransaction() != null)) {
+ if (shouldConsumeMainWindowSizeTransaction()) {
+ task.getSurfaceControl().deferTransactionUntil(mWin.getClientViewRootSurface(),
+ mWin.getFrameNumber());
mSurfaceController.deferTransactionUntil(mWin.getClientViewRootSurface(),
mWin.getFrameNumber());
SurfaceControl.mergeToGlobalTransaction(task.getMainWindowSizeChangeTransaction());
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 9bc5d34..2013945 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -237,27 +237,28 @@
/* --- InputDispatcherPolicyInterface implementation --- */
virtual void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
- uint32_t policyFlags);
+ uint32_t policyFlags) override;
virtual void notifyConfigurationChanged(nsecs_t when);
- virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
- const sp<IBinder>& token,
- const std::string& reason);
+ virtual nsecs_t notifyAnr(const sp<InputApplicationHandle>& inputApplicationHandle,
+ const sp<IBinder>& token, const std::string& reason) override;
virtual void notifyInputChannelBroken(const sp<IBinder>& token);
- virtual void notifyFocusChanged(const sp<IBinder>& oldToken, const sp<IBinder>& newToken);
- virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags);
- virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig);
- virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags);
+ virtual void notifyFocusChanged(const sp<IBinder>& oldToken,
+ const sp<IBinder>& newToken) override;
+ virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override;
+ virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override;
+ virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent,
+ uint32_t& policyFlags) override;
virtual void interceptMotionBeforeQueueing(const int32_t displayId, nsecs_t when,
- uint32_t& policyFlags);
- virtual nsecs_t interceptKeyBeforeDispatching(
- const sp<IBinder>& token,
- const KeyEvent* keyEvent, uint32_t policyFlags);
- virtual bool dispatchUnhandledKey(const sp<IBinder>& token,
- const KeyEvent* keyEvent, uint32_t policyFlags, KeyEvent* outFallbackKeyEvent);
- virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType);
- virtual bool checkInjectEventsPermissionNonReentrant(
- int32_t injectorPid, int32_t injectorUid);
- virtual void onPointerDownOutsideFocus(const sp<IBinder>& touchedToken);
+ uint32_t& policyFlags) override;
+ virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>& token,
+ const KeyEvent* keyEvent,
+ uint32_t policyFlags) override;
+ virtual bool dispatchUnhandledKey(const sp<IBinder>& token, const KeyEvent* keyEvent,
+ uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) override;
+ virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType) override;
+ virtual bool checkInjectEventsPermissionNonReentrant(int32_t injectorPid,
+ int32_t injectorUid) override;
+ virtual void onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) override;
/* --- PointerControllerPolicyInterface implementation --- */
@@ -692,9 +693,8 @@
return handle->getInputApplicationHandleObjLocalRef(env);
}
-
-nsecs_t NativeInputManager::notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
- const sp<IBinder>& token, const std::string& reason) {
+nsecs_t NativeInputManager::notifyAnr(const sp<InputApplicationHandle>& inputApplicationHandle,
+ const sp<IBinder>& token, const std::string& reason) {
#if DEBUG_INPUT_DISPATCHER_POLICY
ALOGD("notifyANR");
#endif
@@ -1453,9 +1453,13 @@
return INPUT_EVENT_INJECTION_FAILED;
}
- return (jint) im->getInputManager()->getDispatcher()->injectInputEvent(
- & keyEvent, injectorPid, injectorUid, syncMode, timeoutMillis,
- uint32_t(policyFlags));
+ const int32_t result =
+ im->getInputManager()->getDispatcher()->injectInputEvent(&keyEvent, injectorPid,
+ injectorUid, syncMode,
+ std::chrono::milliseconds(
+ timeoutMillis),
+ uint32_t(policyFlags));
+ return static_cast<jint>(result);
} else if (env->IsInstanceOf(inputEventObj, gMotionEventClassInfo.clazz)) {
const MotionEvent* motionEvent = android_view_MotionEvent_getNativePtr(env, inputEventObj);
if (!motionEvent) {
@@ -1463,9 +1467,13 @@
return INPUT_EVENT_INJECTION_FAILED;
}
- return (jint) im->getInputManager()->getDispatcher()->injectInputEvent(
- motionEvent, injectorPid, injectorUid, syncMode, timeoutMillis,
- uint32_t(policyFlags));
+ const int32_t result =
+ (jint)im->getInputManager()
+ ->getDispatcher()
+ ->injectInputEvent(motionEvent, injectorPid, injectorUid, syncMode,
+ std::chrono::milliseconds(timeoutMillis),
+ uint32_t(policyFlags));
+ return static_cast<jint>(result);
} else {
jniThrowRuntimeException(env, "Invalid input event type.");
return INPUT_EVENT_INJECTION_FAILED;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 3323fa4..966694a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -4567,9 +4567,11 @@
}
if (isProfileOwner(adminReceiver, userHandle)) {
if (isProfileOwnerOfOrganizationOwnedDevice(userHandle)) {
+ UserHandle parentUserHandle = UserHandle.of(getProfileParentId(userHandle));
mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
- false,
- UserHandle.of(getProfileParentId(userHandle)));
+ false, parentUserHandle);
+ mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER,
+ false, parentUserHandle);
}
final ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver,
userHandle, /* parent */ false);
@@ -7213,6 +7215,8 @@
mUserManager.setUserRestriction(
UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, false,
UserHandle.SYSTEM);
+ mUserManager.setUserRestriction(
+ UserManager.DISALLOW_ADD_USER, false, UserHandle.SYSTEM);
// Device-wide policies set by the profile owner need to be cleaned up here.
mLockPatternUtils.setDeviceOwnerInfo(null);
@@ -13825,6 +13829,8 @@
mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, true,
parentUser);
+ mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER, true,
+ parentUser);
});
// markProfileOwnerOfOrganizationOwnedDevice will trigger writing of the profile owner
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index c3c2157..d412a19 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -266,6 +266,7 @@
mIncFs(sm.getIncFs()),
mAppOpsManager(sm.getAppOpsManager()),
mJni(sm.getJni()),
+ mLooper(sm.getLooper()),
mIncrementalDir(rootDir) {
if (!mVold) {
LOG(FATAL) << "Vold service is unavailable";
@@ -276,12 +277,22 @@
if (!mAppOpsManager) {
LOG(FATAL) << "AppOpsManager is unavailable";
}
+ if (!mJni) {
+ LOG(FATAL) << "JNI is unavailable";
+ }
+ if (!mLooper) {
+ LOG(FATAL) << "Looper is unavailable";
+ }
mJobQueue.reserve(16);
mJobProcessor = std::thread([this]() {
mJni->initializeForCurrentThread();
runJobProcessing();
});
+ mCmdLooperThread = std::thread([this]() {
+ mJni->initializeForCurrentThread();
+ runCmdLooper();
+ });
const auto mountedRootNames = adoptMountedInstances();
mountExistingImages(mountedRootNames);
@@ -294,6 +305,7 @@
}
mJobCondition.notify_all();
mJobProcessor.join();
+ mCmdLooperThread.join();
}
static const char* toString(IncrementalService::BindKind kind) {
@@ -1315,6 +1327,13 @@
return true;
}
+void IncrementalService::runCmdLooper() {
+ constexpr auto kTimeoutMsecs = 1000;
+ while (mRunning.load(std::memory_order_relaxed)) {
+ mLooper->pollAll(kTimeoutMsecs);
+ }
+}
+
IncrementalService::DataLoaderStubPtr IncrementalService::prepareDataLoader(
IncFsMount& ifs, DataLoaderParamsParcel&& params,
const DataLoaderStatusListener* externalListener) {
@@ -1337,8 +1356,16 @@
fsControlParcel.incremental->log.reset(dup(ifs.control.logs()));
fsControlParcel.service = new IncrementalServiceConnector(*this, ifs.mountId);
- ifs.dataLoaderStub = new DataLoaderStub(*this, ifs.mountId, std::move(params),
- std::move(fsControlParcel), externalListener);
+ incfs::UniqueControl healthControl = mIncFs->openMount(ifs.root.c_str());
+ if (healthControl.pendingReads() < 0) {
+ LOG(ERROR) << "Failed to open health control for: " << ifs.root << "("
+ << healthControl.cmd() << ":" << healthControl.pendingReads() << ":"
+ << healthControl.logs() << ")";
+ }
+
+ ifs.dataLoaderStub =
+ new DataLoaderStub(*this, ifs.mountId, std::move(params), std::move(fsControlParcel),
+ std::move(healthControl), externalListener);
}
template <class Duration>
@@ -1658,24 +1685,34 @@
IncrementalService::DataLoaderStub::DataLoaderStub(IncrementalService& service, MountId id,
DataLoaderParamsParcel&& params,
FileSystemControlParcel&& control,
+ incfs::UniqueControl&& healthControl,
const DataLoaderStatusListener* externalListener)
: mService(service),
mId(id),
mParams(std::move(params)),
mControl(std::move(control)),
+ mHealthControl(std::move(healthControl)),
mListener(externalListener ? *externalListener : DataLoaderStatusListener()) {
+ addToCmdLooperLocked();
}
-IncrementalService::DataLoaderStub::~DataLoaderStub() = default;
+IncrementalService::DataLoaderStub::~DataLoaderStub() {
+ if (mId != kInvalidStorageId) {
+ cleanupResources();
+ }
+}
void IncrementalService::DataLoaderStub::cleanupResources() {
requestDestroy();
auto now = Clock::now();
-
std::unique_lock lock(mMutex);
+
+ removeFromCmdLooperLocked();
+
mParams = {};
mControl = {};
+ mHealthControl = {};
mStatusCondition.wait_until(lock, now + 60s, [this] {
return mCurrentStatus == IDataLoaderStatusListener::DATA_LOADER_DESTROYED;
});
@@ -1710,21 +1747,19 @@
}
bool IncrementalService::DataLoaderStub::setTargetStatus(int newStatus) {
- int oldStatus, curStatus;
{
std::unique_lock lock(mMutex);
- oldStatus = mTargetStatus;
- curStatus = mCurrentStatus;
setTargetStatusLocked(newStatus);
}
- LOG(DEBUG) << "Target status update for DataLoader " << mId << ": " << oldStatus << " -> "
- << newStatus << " (current " << curStatus << ")";
return fsmStep();
}
void IncrementalService::DataLoaderStub::setTargetStatusLocked(int status) {
+ auto oldStatus = mTargetStatus;
mTargetStatus = status;
mTargetStatusTs = Clock::now();
+ LOG(DEBUG) << "Target status update for DataLoader " << mId << ": " << oldStatus << " -> "
+ << status << " (current " << mCurrentStatus << ")";
}
bool IncrementalService::DataLoaderStub::bind() {
@@ -1860,12 +1895,75 @@
return binder::Status::ok();
}
+void IncrementalService::DataLoaderStub::addToCmdLooperLocked() {
+ const auto pendingReadsFd = mHealthControl.pendingReads();
+ if (pendingReadsFd < 0) {
+ return;
+ }
+
+ mService.mLooper->addFd(
+ pendingReadsFd, android::Looper::POLL_CALLBACK, android::Looper::EVENT_INPUT,
+ [](int, int, void* data) -> int {
+ auto&& self = (DataLoaderStub*)data;
+ return self->onCmdLooperEvent();
+ },
+ this);
+ mService.mLooper->wake();
+}
+
+void IncrementalService::DataLoaderStub::removeFromCmdLooperLocked() {
+ const auto pendingReadsFd = mHealthControl.pendingReads();
+ if (pendingReadsFd < 0) {
+ return;
+ }
+
+ mService.mLooper->removeFd(pendingReadsFd);
+ mService.mLooper->wake();
+}
+
+int IncrementalService::DataLoaderStub::onCmdLooperEvent() {
+ if (!mService.mRunning.load(std::memory_order_relaxed)) {
+ return 0;
+ }
+
+ bool pendingReadsOccur = false;
+
+ {
+ std::unique_lock lock(mMutex);
+ const auto now = Clock::now();
+ if (now < mEarliestMissingPageTs) {
+ // Transition: duration::max -> now.
+ mEarliestMissingPageTs = now;
+ pendingReadsOccur = true;
+ }
+ }
+
+ if (pendingReadsOccur) {
+ LOG(INFO) << "Pending reads occur for, requesting start for: " << mId;
+ requestStart();
+ }
+
+ {
+ // Drop pending reads.
+ static constexpr auto kMaxDropIterations = 3;
+ std::unique_lock lock(mMutex);
+ for (int i = 0; i < kMaxDropIterations; ++i) {
+ if (IncFs_DropPendingReads(mHealthControl) <= 0) {
+ break;
+ }
+ }
+ }
+ return 1;
+}
+
void IncrementalService::DataLoaderStub::onDump(int fd) {
dprintf(fd, " dataLoader: {\n");
dprintf(fd, " currentStatus: %d\n", mCurrentStatus);
dprintf(fd, " targetStatus: %d\n", mTargetStatus);
dprintf(fd, " targetStatusTs: %lldmcs\n",
(long long)(elapsedMcs(mTargetStatusTs, Clock::now())));
+ dprintf(fd, " earliestMissingPageTs: %lldmcs\n",
+ (long long)(elapsedMcs(mEarliestMissingPageTs, Clock::now())));
const auto& params = mParams;
dprintf(fd, " dataLoaderParams: {\n");
dprintf(fd, " type: %s\n", toString(params.type).c_str());
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index cf310b1..640ca53 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -18,6 +18,7 @@
#include <android/content/pm/BnDataLoaderStatusListener.h>
#include <android/content/pm/DataLoaderParamsParcel.h>
+#include <android/content/pm/FileSystemControlParcel.h>
#include <android/content/pm/IDataLoaderStatusListener.h>
#include <android/os/incremental/BnIncrementalServiceConnector.h>
#include <binder/IAppOpsCallback.h>
@@ -160,6 +161,7 @@
DataLoaderStub(IncrementalService& service, MountId id,
content::pm::DataLoaderParamsParcel&& params,
content::pm::FileSystemControlParcel&& control,
+ incfs::UniqueControl&& healthControl,
const DataLoaderStatusListener* externalListener);
~DataLoaderStub();
// Cleans up the internal state and invalidates DataLoaderStub. Any subsequent calls will
@@ -178,6 +180,10 @@
private:
binder::Status onStatusChanged(MountId mount, int newStatus) final;
+ void addToCmdLooperLocked();
+ void removeFromCmdLooperLocked();
+ int onCmdLooperEvent();
+
bool isValid() const { return mId != kInvalidStorageId; }
sp<content::pm::IDataLoader> getDataLoader();
@@ -197,12 +203,15 @@
MountId mId = kInvalidStorageId;
content::pm::DataLoaderParamsParcel mParams;
content::pm::FileSystemControlParcel mControl;
+ incfs::UniqueControl mHealthControl;
DataLoaderStatusListener mListener;
std::condition_variable mStatusCondition;
int mCurrentStatus = content::pm::IDataLoaderStatusListener::DATA_LOADER_DESTROYED;
int mTargetStatus = content::pm::IDataLoaderStatusListener::DATA_LOADER_DESTROYED;
TimePoint mTargetStatusTs = {};
+
+ TimePoint mEarliestMissingPageTs{Clock::duration::max()};
};
using DataLoaderStubPtr = sp<DataLoaderStub>;
@@ -300,12 +309,15 @@
const incfs::FileId& libFileId, std::string_view targetLibPath,
Clock::time_point scheduledTs);
+ void runCmdLooper();
+
private:
const std::unique_ptr<VoldServiceWrapper> mVold;
const std::unique_ptr<DataLoaderManagerWrapper> mDataLoaderManager;
const std::unique_ptr<IncFsWrapper> mIncFs;
const std::unique_ptr<AppOpsManagerWrapper> mAppOpsManager;
const std::unique_ptr<JniWrapper> mJni;
+ const std::unique_ptr<LooperWrapper> mLooper;
const std::string mIncrementalDir;
mutable std::mutex mLock;
@@ -319,13 +331,16 @@
std::atomic_bool mSystemReady = false;
StorageId mNextId = 0;
+ std::atomic_bool mRunning{true};
+
using Job = std::function<void()>;
std::unordered_map<MountId, std::vector<Job>> mJobQueue;
MountId mPendingJobsMount = kInvalidStorageId;
std::condition_variable mJobCondition;
std::mutex mJobMutex;
std::thread mJobProcessor;
- bool mRunning = true;
+
+ std::thread mCmdLooperThread;
};
} // namespace android::incremental
diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp
index 85f7441..08fb486 100644
--- a/services/incremental/ServiceWrappers.cpp
+++ b/services/incremental/ServiceWrappers.cpp
@@ -113,6 +113,23 @@
JavaVM* const mJvm;
};
+class RealLooperWrapper final : public LooperWrapper {
+public:
+ int addFd(int fd, int ident, int events, android::Looper_callbackFunc callback,
+ void* data) final {
+ return mLooper.addFd(fd, ident, events, callback, data);
+ }
+ int removeFd(int fd) final { return mLooper.removeFd(fd); }
+ void wake() final { return mLooper.wake(); }
+ int pollAll(int timeoutMillis) final { return mLooper.pollAll(timeoutMillis); }
+
+private:
+ struct Looper : public android::Looper {
+ Looper() : android::Looper(/*allowNonCallbacks=*/false) {}
+ ~Looper() {}
+ } mLooper;
+};
+
class RealIncFs : public IncFsWrapper {
public:
RealIncFs() = default;
@@ -203,6 +220,10 @@
return std::make_unique<RealJniWrapper>(mJvm);
}
+std::unique_ptr<LooperWrapper> RealServiceManager::getLooper() {
+ return std::make_unique<RealLooperWrapper>();
+}
+
static JavaVM* getJavaVm(JNIEnv* env) {
CHECK(env);
JavaVM* jvm = nullptr;
diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h
index 3792830..abbf2f4 100644
--- a/services/incremental/ServiceWrappers.h
+++ b/services/incremental/ServiceWrappers.h
@@ -26,6 +26,7 @@
#include <binder/Status.h>
#include <incfs.h>
#include <jni.h>
+#include <utils/Looper.h>
#include <memory>
#include <span>
@@ -106,6 +107,16 @@
virtual void initializeForCurrentThread() const = 0;
};
+class LooperWrapper {
+public:
+ virtual ~LooperWrapper() = default;
+ virtual int addFd(int fd, int ident, int events, android::Looper_callbackFunc callback,
+ void* data) = 0;
+ virtual int removeFd(int fd) = 0;
+ virtual void wake() = 0;
+ virtual int pollAll(int timeoutMillis) = 0;
+};
+
class ServiceManagerWrapper {
public:
virtual ~ServiceManagerWrapper() = default;
@@ -114,6 +125,7 @@
virtual std::unique_ptr<IncFsWrapper> getIncFs() = 0;
virtual std::unique_ptr<AppOpsManagerWrapper> getAppOpsManager() = 0;
virtual std::unique_ptr<JniWrapper> getJni() = 0;
+ virtual std::unique_ptr<LooperWrapper> getLooper() = 0;
};
// --- Real stuff ---
@@ -127,6 +139,7 @@
std::unique_ptr<IncFsWrapper> getIncFs() final;
std::unique_ptr<AppOpsManagerWrapper> getAppOpsManager() final;
std::unique_ptr<JniWrapper> getJni() final;
+ std::unique_ptr<LooperWrapper> getLooper() final;
private:
template <class INTERFACE>
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index 2205bfe..325218d 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -242,6 +242,9 @@
void setDataLoaderStatusDestroyed() {
mListener->onStatusChanged(mId, IDataLoaderStatusListener::DATA_LOADER_DESTROYED);
}
+ void setDataLoaderStatusUnavailable() {
+ mListener->onStatusChanged(mId, IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE);
+ }
binder::Status unbindFromDataLoaderOk(int32_t id) {
if (mDataLoader) {
if (auto status = mDataLoader->destroy(id); !status.isOk()) {
@@ -286,6 +289,14 @@
void makeFileFails() { ON_CALL(*this, makeFile(_, _, _, _, _)).WillByDefault(Return(-1)); }
void makeFileSuccess() { ON_CALL(*this, makeFile(_, _, _, _, _)).WillByDefault(Return(0)); }
+ void openMountSuccess() {
+ ON_CALL(*this, openMount(_)).WillByDefault(Invoke(this, &MockIncFs::openMountForHealth));
+ }
+
+ static constexpr auto kPendingReadsFd = 42;
+ Control openMountForHealth(std::string_view) {
+ return UniqueControl(IncFs_CreateControl(-1, kPendingReadsFd, -1));
+ }
RawMetadata getMountInfoMetadata(const Control& control, std::string_view path) {
metadata::Mount m;
@@ -346,7 +357,42 @@
public:
MOCK_CONST_METHOD0(initializeForCurrentThread, void());
- MockJniWrapper() { EXPECT_CALL(*this, initializeForCurrentThread()).Times(1); }
+ MockJniWrapper() { EXPECT_CALL(*this, initializeForCurrentThread()).Times(2); }
+};
+
+class MockLooperWrapper : public LooperWrapper {
+public:
+ MOCK_METHOD5(addFd, int(int, int, int, android::Looper_callbackFunc, void*));
+ MOCK_METHOD1(removeFd, int(int));
+ MOCK_METHOD0(wake, void());
+ MOCK_METHOD1(pollAll, int(int));
+
+ MockLooperWrapper() {
+ ON_CALL(*this, addFd(_, _, _, _, _))
+ .WillByDefault(Invoke(this, &MockLooperWrapper::storeCallback));
+ ON_CALL(*this, removeFd(_)).WillByDefault(Invoke(this, &MockLooperWrapper::clearCallback));
+ ON_CALL(*this, pollAll(_)).WillByDefault(Invoke(this, &MockLooperWrapper::sleepFor));
+ }
+
+ int storeCallback(int, int, int, android::Looper_callbackFunc callback, void* data) {
+ mCallback = callback;
+ mCallbackData = data;
+ return 0;
+ }
+
+ int clearCallback(int) {
+ mCallback = nullptr;
+ mCallbackData = nullptr;
+ return 0;
+ }
+
+ int sleepFor(int timeoutMillis) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(timeoutMillis));
+ return 0;
+ }
+
+ android::Looper_callbackFunc mCallback = nullptr;
+ void* mCallbackData = nullptr;
};
class MockServiceManager : public ServiceManagerWrapper {
@@ -355,12 +401,14 @@
std::unique_ptr<MockDataLoaderManager> dataLoaderManager,
std::unique_ptr<MockIncFs> incfs,
std::unique_ptr<MockAppOpsManager> appOpsManager,
- std::unique_ptr<MockJniWrapper> jni)
+ std::unique_ptr<MockJniWrapper> jni,
+ std::unique_ptr<MockLooperWrapper> looper)
: mVold(std::move(vold)),
mDataLoaderManager(std::move(dataLoaderManager)),
mIncFs(std::move(incfs)),
mAppOpsManager(std::move(appOpsManager)),
- mJni(std::move(jni)) {}
+ mJni(std::move(jni)),
+ mLooper(std::move(looper)) {}
std::unique_ptr<VoldServiceWrapper> getVoldService() final { return std::move(mVold); }
std::unique_ptr<DataLoaderManagerWrapper> getDataLoaderManager() final {
return std::move(mDataLoaderManager);
@@ -368,6 +416,7 @@
std::unique_ptr<IncFsWrapper> getIncFs() final { return std::move(mIncFs); }
std::unique_ptr<AppOpsManagerWrapper> getAppOpsManager() final { return std::move(mAppOpsManager); }
std::unique_ptr<JniWrapper> getJni() final { return std::move(mJni); }
+ std::unique_ptr<LooperWrapper> getLooper() final { return std::move(mLooper); }
private:
std::unique_ptr<MockVoldService> mVold;
@@ -375,6 +424,7 @@
std::unique_ptr<MockIncFs> mIncFs;
std::unique_ptr<MockAppOpsManager> mAppOpsManager;
std::unique_ptr<MockJniWrapper> mJni;
+ std::unique_ptr<MockLooperWrapper> mLooper;
};
// --- IncrementalServiceTest ---
@@ -394,13 +444,16 @@
mAppOpsManager = appOps.get();
auto jni = std::make_unique<NiceMock<MockJniWrapper>>();
mJni = jni.get();
+ auto looper = std::make_unique<NiceMock<MockLooperWrapper>>();
+ mLooper = looper.get();
mIncrementalService =
std::make_unique<IncrementalService>(MockServiceManager(std::move(vold),
std::move(
dataloaderManager),
std::move(incFs),
std::move(appOps),
- std::move(jni)),
+ std::move(jni),
+ std::move(looper)),
mRootDir.path);
mDataLoaderParcel.packageName = "com.test";
mDataLoaderParcel.arguments = "uri";
@@ -430,12 +483,13 @@
}
protected:
- NiceMock<MockVoldService>* mVold;
- NiceMock<MockIncFs>* mIncFs;
- NiceMock<MockDataLoaderManager>* mDataLoaderManager;
- NiceMock<MockAppOpsManager>* mAppOpsManager;
- NiceMock<MockJniWrapper>* mJni;
- NiceMock<MockDataLoader>* mDataLoader;
+ NiceMock<MockVoldService>* mVold = nullptr;
+ NiceMock<MockIncFs>* mIncFs = nullptr;
+ NiceMock<MockDataLoaderManager>* mDataLoaderManager = nullptr;
+ NiceMock<MockAppOpsManager>* mAppOpsManager = nullptr;
+ NiceMock<MockJniWrapper>* mJni = nullptr;
+ NiceMock<MockLooperWrapper>* mLooper = nullptr;
+ NiceMock<MockDataLoader>* mDataLoader = nullptr;
std::unique_ptr<IncrementalService> mIncrementalService;
TemporaryDir mRootDir;
DataLoaderParamsParcel mDataLoaderParcel;
@@ -593,6 +647,54 @@
mDataLoaderManager->setDataLoaderStatusCreated();
}
+TEST_F(IncrementalServiceTest, testStartDataLoaderCreateUnavailable) {
+ mVold->mountIncFsSuccess();
+ mIncFs->makeFileSuccess();
+ mVold->bindMountSuccess();
+ mDataLoader->initializeCreateOkNoStatus();
+ mDataLoaderManager->bindToDataLoaderSuccess();
+ mDataLoaderManager->getDataLoaderSuccess();
+ EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(1);
+ EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
+ EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(1);
+ EXPECT_CALL(*mDataLoader, start(_)).Times(0);
+ EXPECT_CALL(*mDataLoader, destroy(_)).Times(1);
+ EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
+ TemporaryDir tempDir;
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
+ IncrementalService::CreateOptions::CreateNew);
+ ASSERT_GE(storageId, 0);
+ mDataLoaderManager->setDataLoaderStatusUnavailable();
+}
+
+TEST_F(IncrementalServiceTest, testStartDataLoaderRecreateOnPendingReads) {
+ mVold->mountIncFsSuccess();
+ mIncFs->makeFileSuccess();
+ mIncFs->openMountSuccess();
+ mVold->bindMountSuccess();
+ mDataLoader->initializeCreateOkNoStatus();
+ mDataLoaderManager->bindToDataLoaderSuccess();
+ mDataLoaderManager->getDataLoaderSuccess();
+ EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(2);
+ EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
+ EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(2);
+ EXPECT_CALL(*mDataLoader, start(_)).Times(0);
+ EXPECT_CALL(*mDataLoader, destroy(_)).Times(1);
+ EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
+ EXPECT_CALL(*mLooper, addFd(MockIncFs::kPendingReadsFd, _, _, _, _)).Times(1);
+ EXPECT_CALL(*mLooper, removeFd(MockIncFs::kPendingReadsFd)).Times(1);
+ TemporaryDir tempDir;
+ int storageId =
+ mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
+ IncrementalService::CreateOptions::CreateNew);
+ ASSERT_GE(storageId, 0);
+ mDataLoaderManager->setDataLoaderStatusUnavailable();
+ ASSERT_NE(nullptr, mLooper->mCallback);
+ ASSERT_NE(nullptr, mLooper->mCallbackData);
+ mLooper->mCallback(-1, -1, mLooper->mCallbackData);
+}
+
TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccess) {
mVold->mountIncFsSuccess();
mIncFs->makeFileSuccess();
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 0fc333f..fa3f330 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1891,6 +1891,10 @@
|| mPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
t.traceBegin("StartTvInputManager");
mSystemServiceManager.startService(TvInputManagerService.class);
+ t.traceEnd();
+ }
+
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_TUNER)) {
t.traceBegin("StartTunerResourceManager");
mSystemServiceManager.startService(TunerResourceManagerService.class);
t.traceEnd();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index 2ce70b6f..b6cf278 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -129,8 +129,11 @@
public void bind_requestsContextToBindService() {
mConnection.bindLocked();
verify(mMockContext).bindServiceAsUser(any(Intent.class), eq(mConnection),
- eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
- | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS), any(UserHandle.class));
+ eq(Context.BIND_AUTO_CREATE
+ | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
+ | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS
+ | Context.BIND_INCLUDE_CAPABILITIES),
+ any(UserHandle.class));
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index d292526..285caf3 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -186,7 +186,8 @@
anyBoolean() /* requireConfirmation */,
anyInt() /* userId */,
eq(TEST_PACKAGE_NAME),
- anyLong() /* sessionId */);
+ anyLong() /* sessionId */,
+ anyInt() /* sysUiSessionId */);
}
@Test
@@ -269,7 +270,8 @@
eq(false) /* requireConfirmation */,
anyInt() /* userId */,
eq(TEST_PACKAGE_NAME),
- anyLong() /* sessionId */);
+ anyLong() /* sessionId */,
+ anyInt() /* sysUiSessionId */);
}
@Test
@@ -407,7 +409,8 @@
anyBoolean() /* requireConfirmation */,
anyInt() /* userId */,
eq(TEST_PACKAGE_NAME),
- anyLong() /* sessionId */);
+ anyLong() /* sessionId */,
+ anyInt() /* sysUiSessionId */);
// Hardware authenticated
final byte[] HAT = generateRandomHAT();
@@ -462,7 +465,8 @@
anyBoolean() /* requireConfirmation */,
anyInt() /* userId */,
eq(TEST_PACKAGE_NAME),
- anyLong() /* sessionId */);
+ anyLong() /* sessionId */,
+ anyInt() /* sysUiSessionId */);
}
@Test
@@ -610,7 +614,8 @@
anyBoolean() /* requireConfirmation */,
anyInt() /* userId */,
anyString(),
- anyLong() /* sessionId */);
+ anyLong() /* sessionId */,
+ anyInt() /* sysUiSessionId */);
}
@Test
@@ -711,7 +716,8 @@
anyBoolean() /* requireConfirmation */,
anyInt() /* userId */,
eq(TEST_PACKAGE_NAME),
- anyLong() /* sessionId */);
+ anyLong() /* sessionId */,
+ anyInt() /* sysUiSessionId */);
}
@Test
@@ -1207,7 +1213,8 @@
anyBoolean() /* requireConfirmation */,
anyInt() /* userId */,
eq(TEST_PACKAGE_NAME),
- anyLong() /* sessionId */);
+ anyLong() /* sessionId */,
+ anyInt() /* sysUiSessionId */);
// Requesting strong and credential, when credential is setup
resetReceiver();
@@ -1227,7 +1234,8 @@
anyBoolean() /* requireConfirmation */,
anyInt() /* userId */,
eq(TEST_PACKAGE_NAME),
- anyLong() /* sessionId */);
+ anyLong() /* sessionId */,
+ anyInt() /* sysUiSessionId */);
// Un-downgrading the authenticator allows successful strong auth
for (BiometricService.AuthenticatorWrapper wrapper : mBiometricService.mAuthenticators) {
@@ -1250,7 +1258,8 @@
anyBoolean() /* requireConfirmation */,
anyInt() /* userId */,
eq(TEST_PACKAGE_NAME),
- anyLong() /* sessionId */);
+ anyLong() /* sessionId */,
+ anyInt() /* sysUiSessionId */);
}
@Test(expected = IllegalStateException.class)
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 6b36bc5..ed40fe7 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -1998,7 +1998,6 @@
private static final Set<String> PROFILE_OWNER_ORGANIZATION_OWNED_GLOBAL_RESTRICTIONS =
Sets.newSet(
UserManager.DISALLOW_CONFIG_DATE_TIME,
- UserManager.DISALLOW_ADD_USER,
UserManager.DISALLOW_BLUETOOTH_SHARING,
UserManager.DISALLOW_CONFIG_CELL_BROADCASTS,
UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS,
@@ -4005,6 +4004,12 @@
// Any caller should be able to call this method.
assertFalse(dpm.isOrganizationOwnedDeviceWithManagedProfile());
configureProfileOwnerOfOrgOwnedDevice(admin1, CALLER_USER_HANDLE);
+
+ verify(getServices().userManager).setUserRestriction(
+ eq(UserManager.DISALLOW_ADD_USER),
+ eq(true),
+ eq(UserHandle.of(UserHandle.USER_SYSTEM)));
+
assertTrue(dpm.isOrganizationOwnedDeviceWithManagedProfile());
// A random caller from another user should also be able to get the right result.
@@ -4012,6 +4017,35 @@
assertTrue(dpm.isOrganizationOwnedDeviceWithManagedProfile());
}
+ public void testMarkOrganizationOwnedDevice_baseRestrictionsAdded() throws Exception {
+ addManagedProfile(admin1, DpmMockContext.CALLER_UID, admin1);
+
+ configureProfileOwnerOfOrgOwnedDevice(admin1, CALLER_USER_HANDLE);
+
+ // Base restriction DISALLOW_REMOVE_MANAGED_PROFILE added
+ verify(getServices().userManager).setUserRestriction(
+ eq(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE),
+ eq(true),
+ eq(UserHandle.of(UserHandle.USER_SYSTEM)));
+
+ // Base restriction DISALLOW_ADD_USER added
+ verify(getServices().userManager).setUserRestriction(
+ eq(UserManager.DISALLOW_ADD_USER),
+ eq(true),
+ eq(UserHandle.of(UserHandle.USER_SYSTEM)));
+
+ // Assert base restrictions cannot be added or removed by admin
+ assertExpectException(SecurityException.class, null, () ->
+ parentDpm.addUserRestriction(admin1, UserManager.DISALLOW_REMOVE_MANAGED_PROFILE));
+ assertExpectException(SecurityException.class, null, () ->
+ parentDpm.clearUserRestriction(admin1,
+ UserManager.DISALLOW_REMOVE_MANAGED_PROFILE));
+ assertExpectException(SecurityException.class, null, () ->
+ parentDpm.addUserRestriction(admin1, UserManager.DISALLOW_ADD_USER));
+ assertExpectException(SecurityException.class, null, () ->
+ parentDpm.clearUserRestriction(admin1, UserManager.DISALLOW_ADD_USER));
+ }
+
public void testSetTime() throws Exception {
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setupDeviceOwner();
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
index d4edab4..63d797e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
@@ -178,6 +178,7 @@
/* files */ null,
/* prepared */ true,
/* committed */ true,
+ /* destroyed */ staged ? true : false,
/* sealed */ false, // Setting to true would trigger some PM logic.
/* childSessionIds */ childSessionIds != null ? childSessionIds : new int[0],
/* parentSessionId */ parentSessionId,
diff --git a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
index 0e48e7e..e86399e 100644
--- a/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/uri/UriGrantsManagerServiceTest.java
@@ -171,7 +171,7 @@
final Uri uri = Uri.parse("content://" + PKG_COMPLEX + "/");
{
final Intent intent = new Intent(Intent.ACTION_VIEW, uri)
- .addFlags(FLAG_READ);
+ .addFlags(FLAG_READ | Intent.FLAG_ACTIVITY_CLEAR_TASK);
assertNull(mService.checkGrantUriPermissionFromIntent(UID_PRIMARY_COMPLEX, PKG_SOCIAL,
intent, intent.getFlags(), null, USER_PRIMARY));
}
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 39062f0..48e22f6 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -165,6 +165,7 @@
long mElapsedRealtime;
boolean mIsAppIdleEnabled = true;
boolean mIsCharging;
+ boolean mIsRestrictedBucketEnabled = true;
List<String> mNonIdleWhitelistApps = new ArrayList<>();
boolean mDisplayOn;
DisplayManager.DisplayListener mDisplayListener;
@@ -212,6 +213,11 @@
}
@Override
+ boolean isRestrictedBucketEnabled() {
+ return mIsRestrictedBucketEnabled;
+ }
+
+ @Override
File getDataSystemDirectory() {
return new File(getContext().getFilesDir(), Long.toString(Math.randomLongInternal()));
}
@@ -366,29 +372,87 @@
mInjector.mElapsedRealtime, false));
}
+ private static class TestParoleListener extends AppIdleStateChangeListener {
+ private boolean mIsParoleOn = false;
+ private CountDownLatch mLatch;
+ private boolean mIsExpecting = false;
+ private boolean mExpectedParoleState;
+
+ boolean getParoleState() {
+ synchronized (this) {
+ return mIsParoleOn;
+ }
+ }
+
+ void rearmLatch(boolean expectedParoleState) {
+ synchronized (this) {
+ mLatch = new CountDownLatch(1);
+ mIsExpecting = true;
+ mExpectedParoleState = expectedParoleState;
+ }
+ }
+
+ void awaitOnLatch(long time) throws Exception {
+ mLatch.await(time, TimeUnit.MILLISECONDS);
+ }
+
+ @Override
+ public void onAppIdleStateChanged(String packageName, int userId, boolean idle,
+ int bucket, int reason) {
+ }
+
+ @Override
+ public void onParoleStateChanged(boolean isParoleOn) {
+ synchronized (this) {
+ // Only record information if it is being looked for
+ if (mLatch != null && mLatch.getCount() > 0) {
+ mIsParoleOn = isParoleOn;
+ if (mIsExpecting && isParoleOn == mExpectedParoleState) {
+ mLatch.countDown();
+ }
+ }
+ }
+ }
+ }
+
@Test
public void testIsAppIdle_Charging() throws Exception {
+ TestParoleListener paroleListener = new TestParoleListener();
+ mController.addListener(paroleListener);
+
setChargingState(mController, false);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
REASON_MAIN_FORCED_BY_SYSTEM);
assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false));
+ assertFalse(mController.isInParole());
+ paroleListener.rearmLatch(true);
setChargingState(mController, true);
+ paroleListener.awaitOnLatch(2000);
+ assertTrue(paroleListener.getParoleState());
assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
assertFalse(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
assertFalse(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false));
+ assertTrue(mController.isInParole());
+ paroleListener.rearmLatch(false);
setChargingState(mController, false);
+ paroleListener.awaitOnLatch(2000);
+ assertFalse(paroleListener.getParoleState());
assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false));
+ assertFalse(mController.isInParole());
}
@Test
public void testIsAppIdle_Enabled() throws Exception {
setChargingState(mController, false);
+ TestParoleListener paroleListener = new TestParoleListener();
+ mController.addListener(paroleListener);
+
setAppIdleEnabled(mController, true);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
REASON_MAIN_FORCED_BY_SYSTEM);
@@ -396,11 +460,17 @@
assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false));
+ paroleListener.rearmLatch(false);
setAppIdleEnabled(mController, false);
+ paroleListener.awaitOnLatch(2000);
+ assertTrue(paroleListener.mIsParoleOn);
assertFalse(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
assertFalse(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false));
+ paroleListener.rearmLatch(true);
setAppIdleEnabled(mController, true);
+ paroleListener.awaitOnLatch(2000);
+ assertFalse(paroleListener.getParoleState());
assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false));
@@ -447,6 +517,10 @@
assertEquals(bucket, getStandbyBucket(mController, PACKAGE_1));
}
+ private void assertNotBucket(int bucket) {
+ assertNotEquals(bucket, getStandbyBucket(mController, PACKAGE_1));
+ }
+
@Test
public void testBuckets() throws Exception {
assertTimeout(mController, 0, STANDBY_BUCKET_NEVER);
@@ -882,6 +956,48 @@
}
@Test
+ public void testRestrictedBucketDisabled() {
+ mInjector.mIsRestrictedBucketEnabled = false;
+ // Get the controller to read the new value. Capturing the ContentObserver isn't possible
+ // at the moment.
+ mController.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
+
+ reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
+ mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD;
+
+ // Nothing should be able to put it into the RESTRICTED bucket.
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+ REASON_MAIN_TIMEOUT);
+ assertNotBucket(STANDBY_BUCKET_RESTRICTED);
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+ REASON_MAIN_PREDICTED);
+ assertNotBucket(STANDBY_BUCKET_RESTRICTED);
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+ REASON_MAIN_FORCED_BY_SYSTEM);
+ assertNotBucket(STANDBY_BUCKET_RESTRICTED);
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+ REASON_MAIN_FORCED_BY_USER);
+ assertNotBucket(STANDBY_BUCKET_RESTRICTED);
+ }
+
+ @Test
+ public void testRestrictedBucket_EnabledToDisabled() {
+ reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
+ mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD;
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+ REASON_MAIN_FORCED_BY_SYSTEM);
+ assertBucket(STANDBY_BUCKET_RESTRICTED);
+
+ mInjector.mIsRestrictedBucketEnabled = false;
+ // Get the controller to read the new value. Capturing the ContentObserver isn't possible
+ // at the moment.
+ mController.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
+
+ mController.checkIdleStates(USER_ID);
+ assertNotBucket(STANDBY_BUCKET_RESTRICTED);
+ }
+
+ @Test
public void testPredictionRaiseFromRestrictedTimeout_highBucket() {
reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
index df92b6e..69ef499 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
@@ -31,6 +31,7 @@
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
import android.os.RemoteException;
+import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import com.android.server.twilight.TwilightManager;
@@ -54,6 +55,7 @@
import static junit.framework.TestCase.assertTrue;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
@@ -144,7 +146,7 @@
addLocalService(PowerManagerInternal.class, mLocalPowerManager);
addLocalService(TwilightManager.class, mTwilightManager);
- mUiManagerService = new UiModeManagerService(mContext);
+ mUiManagerService = new UiModeManagerService(mContext, true);
try {
mUiManagerService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
} catch (SecurityException e) {/* ignore for permission denial */}
diff --git a/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java b/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
index 3c2d550..182bf94 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
@@ -41,6 +41,7 @@
protected static final String PKG_N_MR1 = "com.example.n_mr1";
protected static final String PKG_O = "com.example.o";
protected static final String PKG_P = "com.example.p";
+ protected static final String PKG_R = "com.example.r";
@Rule
public final TestableContext mContext =
@@ -69,6 +70,8 @@
return Build.VERSION_CODES.O;
case PKG_P:
return Build.VERSION_CODES.P;
+ case PKG_R:
+ return Build.VERSION_CODES.R;
default:
return Build.VERSION_CODES.CUR_DEVELOPMENT;
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index babe80e..289933e 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -250,6 +250,8 @@
private static final int NOTIFICATION_LOCATION_UNKNOWN = 0;
+ private static final String VALID_CONVO_SHORTCUT_ID = "shortcut";
+
@Mock
private NotificationListeners mListeners;
@Mock private NotificationAssistants mAssistants;
@@ -471,6 +473,19 @@
mShortcutHelper.setLauncherApps(mLauncherApps);
mShortcutHelper.setShortcutServiceInternal(mShortcutServiceInternal);
+ // Pretend the shortcut exists
+ List<ShortcutInfo> shortcutInfos = new ArrayList<>();
+ ShortcutInfo info = mock(ShortcutInfo.class);
+ when(info.getPackage()).thenReturn(PKG);
+ when(info.getId()).thenReturn(VALID_CONVO_SHORTCUT_ID);
+ when(info.getUserId()).thenReturn(USER_SYSTEM);
+ when(info.isLongLived()).thenReturn(true);
+ when(info.isEnabled()).thenReturn(true);
+ shortcutInfos.add(info);
+ when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcutInfos);
+ when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
+ anyString(), anyInt(), any())).thenReturn(true);
+
// Set the testable bubble extractor
RankingHelper rankingHelper = mService.getRankingHelper();
BubbleExtractor extractor = rankingHelper.findExtractor(BubbleExtractor.class);
@@ -704,6 +719,7 @@
)
.setActions(replyAction)
.setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setShortcutId(VALID_CONVO_SHORTCUT_ID)
.setGroupSummary(isSummary);
if (groupKey != null) {
nb.setGroup(groupKey);
@@ -6100,7 +6116,6 @@
@Test
public void testNotificationBubbles_flagRemoved_whenShortcutRemoved()
throws RemoteException {
- final String shortcutId = "someshortcutId";
setUpPrefsForBubbles(PKG, mUid,
true /* global */,
BUBBLE_PREFERENCE_ALL /* app */,
@@ -6111,27 +6126,16 @@
// Messaging notification with shortcut info
Notification.BubbleMetadata metadata =
- new Notification.BubbleMetadata.Builder(shortcutId).build();
+ new Notification.BubbleMetadata.Builder(VALID_CONVO_SHORTCUT_ID).build();
Notification.Builder nb = getMessageStyleNotifBuilder(false /* addDefaultMetadata */,
null /* groupKey */, false /* isSummary */);
- nb.setShortcutId(shortcutId);
+ nb.setShortcutId(VALID_CONVO_SHORTCUT_ID);
nb.setBubbleMetadata(metadata);
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
"tag", mUid, 0, nb.build(), new UserHandle(mUid), null, 0);
NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
- // Pretend the shortcut exists
- List<ShortcutInfo> shortcutInfos = new ArrayList<>();
- ShortcutInfo info = mock(ShortcutInfo.class);
- when(info.getPackage()).thenReturn(PKG);
- when(info.getId()).thenReturn(shortcutId);
- when(info.getUserId()).thenReturn(USER_SYSTEM);
- when(info.isLongLived()).thenReturn(true);
- when(info.isEnabled()).thenReturn(true);
- shortcutInfos.add(info);
- when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcutInfos);
- when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
- anyString(), anyInt(), any())).thenReturn(true);
+
// Test: Send the bubble notification
mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
@@ -6149,7 +6153,7 @@
// Make sure the shortcut is cached.
verify(mShortcutServiceInternal).cacheShortcuts(
- anyInt(), any(), eq(PKG), eq(Collections.singletonList(shortcutId)),
+ anyInt(), any(), eq(PKG), eq(Collections.singletonList(VALID_CONVO_SHORTCUT_ID)),
eq(USER_SYSTEM));
// Test: Remove the shortcut
@@ -6613,6 +6617,7 @@
convo2.setNotificationChannel(channel2);
convos.add(convo2);
when(mPreferencesHelper.getConversations(anyString(), anyInt())).thenReturn(convos);
+ when(mLauncherApps.getShortcuts(any(), any())).thenReturn(null);
List<ConversationChannelWrapper> conversations =
mBinderService.getConversationsForPackage(PKG_P, mUid).getList();
@@ -6640,6 +6645,7 @@
NotificationRecord nr =
generateMessageBubbleNotifRecord(mTestNotificationChannel,
"testRecordMessages_invalidMsg");
+ when(mLauncherApps.getShortcuts(any(), any())).thenReturn(null);
mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
@@ -6660,17 +6666,6 @@
"testRecordMessages_validMsg", mUid, 0, nb.build(), new UserHandle(mUid), null, 0);
NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
- // Pretend the shortcut exists
- List<ShortcutInfo> shortcutInfos = new ArrayList<>();
- ShortcutInfo info = mock(ShortcutInfo.class);
- when(info.getPackage()).thenReturn(PKG);
- when(info.getId()).thenReturn("id");
- when(info.getUserId()).thenReturn(USER_SYSTEM);
- when(info.isLongLived()).thenReturn(true);
- when(info.isEnabled()).thenReturn(true);
- shortcutInfos.add(info);
- when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcutInfos);
-
mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index 9f593ce..b03596a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -39,7 +39,6 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.IActivityManager;
import android.app.Notification;
@@ -90,7 +89,7 @@
@Mock private PackageManager mPm;
@Mock private ContentResolver mContentResolver;
- private final String pkg = PKG_N_MR1;
+ private final String mPkg = PKG_O;
private final int uid = 9583;
private final int id1 = 1;
private final String tag1 = "tag1";
@@ -198,10 +197,14 @@
}
Notification n = builder.build();
- return new StatusBarNotification(pkg, pkg, id1, tag1, uid, uid, n, mUser, null, uid);
+ return new StatusBarNotification(mPkg, mPkg, id1, tag1, uid, uid, n, mUser, null, uid);
}
private StatusBarNotification getMessagingStyleNotification() {
+ return getMessagingStyleNotification(mPkg);
+ }
+
+ private StatusBarNotification getMessagingStyleNotification(String pkg) {
final Builder builder = new Builder(mMockContext)
.setContentTitle("foo")
.setSmallIcon(android.R.drawable.sym_def_app_icon);
@@ -658,7 +661,7 @@
Bundle signals = new Bundle();
signals.putInt(Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEGATIVE);
- record.addAdjustment(new Adjustment(pkg, record.getKey(), signals, null, sbn.getUserId()));
+ record.addAdjustment(new Adjustment(mPkg, record.getKey(), signals, null, sbn.getUserId()));
record.applyAdjustments();
@@ -687,7 +690,7 @@
Bundle signals = new Bundle();
signals.putInt(Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEGATIVE);
- record.addAdjustment(new Adjustment(pkg, record.getKey(), signals, null, sbn.getUserId()));
+ record.addAdjustment(new Adjustment(mPkg, record.getKey(), signals, null, sbn.getUserId()));
record.applyAdjustments();
assertEquals(USER_SENTIMENT_POSITIVE, record.getUserSentiment());
@@ -705,7 +708,7 @@
Bundle signals = new Bundle();
signals.putInt(Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEGATIVE);
- record.addAdjustment(new Adjustment(pkg, record.getKey(), signals, null, sbn.getUserId()));
+ record.addAdjustment(new Adjustment(mPkg, record.getKey(), signals, null, sbn.getUserId()));
record.applyAdjustments();
@@ -1134,6 +1137,15 @@
}
@Test
+ public void testIsConversation_noShortcut_targetsR() {
+ StatusBarNotification sbn = getMessagingStyleNotification(PKG_R);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+ record.setShortcutInfo(null);
+
+ assertFalse(record.isConversation());
+ }
+
+ @Test
public void testIsConversation_channelDemoted() {
StatusBarNotification sbn = getMessagingStyleNotification();
channel.setDemoted(true);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index cf3cfec..5c21853 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -31,10 +31,12 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
@@ -59,6 +61,8 @@
import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
+import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -365,6 +369,30 @@
assertHasStartingWindow(activity2);
}
+ @Test
+ public void testTransferStartingWindowCanAnimate() {
+ final ActivityRecord activity1 = createIsolatedTestActivityRecord();
+ final ActivityRecord activity2 = createIsolatedTestActivityRecord();
+ activity1.addStartingWindow(mPackageName,
+ android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
+ false, false);
+ waitUntilHandlersIdle();
+ activity2.addStartingWindow(mPackageName,
+ android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity1.appToken.asBinder(),
+ true, true, false, true, false, false);
+ waitUntilHandlersIdle();
+ assertNoStartingWindow(activity1);
+ assertHasStartingWindow(activity2);
+
+ // Assert that bottom activity is allowed to do animation.
+ doReturn(true).when(activity2).okToAnimate();
+ doReturn(true).when(activity2).isAnimating();
+ final OnAnimationFinishedCallback onAnimationFinishedCallback =
+ mock(OnAnimationFinishedCallback.class);
+ assertTrue(activity2.applyAnimation(null, TRANSIT_ACTIVITY_OPEN, true, false,
+ onAnimationFinishedCallback));
+ }
+
private ActivityRecord createIsolatedTestActivityRecord() {
final ActivityStack taskStack = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(taskStack, 0 /* userId */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 28ae36a..d605ab2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -38,13 +38,11 @@
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
@@ -346,82 +344,6 @@
assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
}
- // TODO(b/118118435): remove after migration
- @Test
- public void layoutWindowLw_appDrawsBarsLegacy() {
- assumeTrue(ViewRootImpl.sNewInsetsMode != ViewRootImpl.NEW_INSETS_MODE_FULL);
-
- mWindow.mAttrs.flags =
- FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
- addWindow(mWindow);
-
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
- assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
- assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
- assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0);
- }
-
- // TODO(b/118118435): remove after migration
- @Test
- public void layoutWindowLw_appWontDrawBars() {
- assumeTrue(ViewRootImpl.sNewInsetsMode != ViewRootImpl.NEW_INSETS_MODE_FULL);
-
- mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
- addWindow(mWindow);
-
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
- assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDecorFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT);
- }
-
- // TODO(b/118118435): remove after migration
- @Test
- public void layoutWindowLw_appWontDrawBars_forceStatusAndNav() throws Exception {
- assumeTrue(ViewRootImpl.sNewInsetsMode != ViewRootImpl.NEW_INSETS_MODE_FULL);
-
- mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
- mWindow.mAttrs.privateFlags = PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
- addWindow(mWindow);
-
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
- assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
- assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
- assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0);
- }
-
- // TODO(b/118118435): remove after migration (keyguard dialog is not special with the new logic)
- @Test
- public void layoutWindowLw_keyguardDialog_hideNav() {
- mWindow.mAttrs.type = TYPE_KEYGUARD_DIALOG;
- mWindow.mAttrs.flags =
- FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
- mWindow.mAttrs.systemUiVisibility = SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
- mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
- addWindow(mWindow);
-
- mDisplayPolicy.beginLayoutLw(mFrames, 0 /* uiMode */);
- mDisplayPolicy.layoutWindowLw(mWindow, null /* attached */, mFrames);
-
- assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
- assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
- assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
- assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0);
- }
-
@Test
public void layoutWindowLw_withDisplayCutout() {
addDisplayCutout();
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
index ca01676..9bf9ffe 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
@@ -16,8 +16,10 @@
package com.android.server.wm;
+import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -44,12 +46,17 @@
private InsetsSource mSource = new InsetsSource(ITYPE_STATUS_BAR);
private InsetsSourceProvider mProvider;
+ private InsetsSource mImeSource = new InsetsSource(ITYPE_IME);
+ private InsetsSourceProvider mImeProvider;
@Before
public void setUp() throws Exception {
mSource.setVisible(true);
mProvider = new InsetsSourceProvider(mSource,
mDisplayContent.getInsetsStateController(), mDisplayContent);
+ mProvider.setServerVisible(true);
+ mImeProvider = new InsetsSourceProvider(mImeSource,
+ mDisplayContent.getInsetsStateController(), mDisplayContent);
}
@Test
@@ -166,6 +173,30 @@
}
@Test
+ public void testUpdateSourceFrameForIme() {
+ final WindowState inputMethod = createWindow(null, TYPE_INPUT_METHOD, "inputMethod");
+
+ inputMethod.getFrameLw().set(new Rect(0, 400, 500, 500));
+
+ mImeProvider.setWindow(inputMethod, null, null);
+ mImeProvider.setServerVisible(false);
+ mImeSource.setVisible(true);
+ mImeProvider.updateSourceFrame();
+ assertEquals(new Rect(0, 0, 0, 0), mImeSource.getFrame());
+ Insets insets = mImeSource.calculateInsets(new Rect(0, 0, 500, 500),
+ false /* ignoreVisibility */);
+ assertEquals(Insets.of(0, 0, 0, 0), insets);
+
+ mImeProvider.setServerVisible(true);
+ mImeSource.setVisible(true);
+ mImeProvider.updateSourceFrame();
+ assertEquals(inputMethod.getFrameLw(), mImeSource.getFrame());
+ insets = mImeSource.calculateInsets(new Rect(0, 0, 500, 500),
+ false /* ignoreVisibility */);
+ assertEquals(Insets.of(0, 0, 0, 100), insets);
+ }
+
+ @Test
public void testInsetsModified() {
final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
index 520ac19..373f363 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -271,6 +271,29 @@
assertEquals(WINDOWING_MODE_FULLSCREEN, token.getWindowingMode());
}
+ @Test
+ public void testFixedRotationRecentsAnimatingTask() {
+ final RecentsAnimationController recentsController = mock(RecentsAnimationController.class);
+ doReturn(true).when(recentsController).isWallpaperVisible(eq(mAppWindow));
+ mWm.setRecentsAnimationController(recentsController);
+
+ mAppWindow.mActivityRecord.applyFixedRotationTransform(mDisplayContent.getDisplayInfo(),
+ mDisplayContent.mDisplayFrames, mDisplayContent.getConfiguration());
+ mAppWindow.mActivityRecord.mVisibleRequested = true;
+ mDisplayContent.mWallpaperController.adjustWallpaperWindows();
+
+ assertEquals(mAppWindow, mDisplayContent.mWallpaperController.getWallpaperTarget());
+ // Wallpaper should link the transform of its target.
+ assertTrue(mAppWindow.mActivityRecord.hasFixedRotationTransform());
+
+ mAppWindow.mActivityRecord.finishFixedRotationTransform();
+ // Invisible requested activity should not share its rotation transform.
+ mAppWindow.mActivityRecord.mVisibleRequested = false;
+ mDisplayContent.mWallpaperController.adjustWallpaperWindows();
+
+ assertFalse(mAppWindow.mActivityRecord.hasFixedRotationTransform());
+ }
+
private WindowState createWallpaperTargetWindow(DisplayContent dc) {
final ActivityRecord homeActivity = new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
.setStack(dc.getDefaultTaskDisplayArea().getRootHomeTask())
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index b7779fd..4e75b73 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -1166,6 +1166,7 @@
byte[] getBackupPayload(String key){
checkAndGetTimeLocked();
+ persistActiveStats();
return mDatabase.getBackupPayload(key);
}
diff --git a/telephony/common/com/android/internal/telephony/CellBroadcastUtils.java b/telephony/common/com/android/internal/telephony/CellBroadcastUtils.java
new file mode 100644
index 0000000..6c63755
--- /dev/null
+++ b/telephony/common/com/android/internal/telephony/CellBroadcastUtils.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2020 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.internal.telephony;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.provider.Telephony;
+import android.text.TextUtils;
+import android.util.Log;
+
+/**
+ * This class provides utility functions related to CellBroadcast.
+ */
+public class CellBroadcastUtils {
+ private static final String TAG = "CellBroadcastUtils";
+ private static final boolean VDBG = false;
+
+ /**
+ * Utility method to query the default CBR's package name.
+ */
+ public static String getDefaultCellBroadcastReceiverPackageName(Context context) {
+ PackageManager packageManager = context.getPackageManager();
+ ResolveInfo resolveInfo = packageManager.resolveActivity(
+ new Intent(Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION),
+ PackageManager.MATCH_SYSTEM_ONLY);
+ String packageName;
+
+ if (resolveInfo == null) {
+ Log.e(TAG, "getDefaultCellBroadcastReceiverPackageName: no package found");
+ return null;
+ }
+
+ packageName = resolveInfo.activityInfo.applicationInfo.packageName;
+
+ if (VDBG) {
+ Log.d(TAG, "getDefaultCellBroadcastReceiverPackageName: found package: " + packageName);
+ }
+
+ if (TextUtils.isEmpty(packageName) || packageManager.checkPermission(
+ android.Manifest.permission.READ_CELL_BROADCASTS, packageName)
+ == PackageManager.PERMISSION_DENIED) {
+ Log.e(TAG, "getDefaultCellBroadcastReceiverPackageName: returning null; "
+ + "permission check failed for : " + packageName);
+ return null;
+ }
+
+ return packageName;
+ }
+}
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index bf886c2..3a3fb28 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -1766,16 +1766,23 @@
return 1;
}
- // Determine the package name under which to merge resources.
- if (options_.rename_resources_package) {
- context_->SetCompilationPackage(options_.rename_resources_package.value());
- } else if (Maybe<AppInfo> maybe_app_info =
+ // First extract the Package name without modifying it (via --rename-manifest-package).
+ if (Maybe<AppInfo> maybe_app_info =
ExtractAppInfoFromManifest(manifest_xml.get(), context_->GetDiagnostics())) {
- // Extract the package name from the manifest ignoring the value of --rename-manifest-package.
const AppInfo& app_info = maybe_app_info.value();
context_->SetCompilationPackage(app_info.package);
}
+ // Determine the package name under which to merge resources.
+ if (options_.rename_resources_package) {
+ if (!options_.custom_java_package) {
+ // Generate the R.java under the original package name instead of the package name specified
+ // through --rename-resources-package.
+ options_.custom_java_package = context_->GetCompilationPackage();
+ }
+ context_->SetCompilationPackage(options_.rename_resources_package.value());
+ }
+
// Now that the compilation package is set, load the dependencies. This will also extract
// the Android framework's versionCode and versionName, if they exist.
if (!LoadSymbolsFromIncludePaths()) {
diff --git a/tools/stats_log_api_gen/java_writer.cpp b/tools/stats_log_api_gen/java_writer.cpp
index 556cf5e..f4c937c 100644
--- a/tools/stats_log_api_gen/java_writer.cpp
+++ b/tools/stats_log_api_gen/java_writer.cpp
@@ -124,13 +124,7 @@
// Print method body.
string indent("");
if (supportQ) {
- // TODO(b/146235828): Use just SDK_INT check once it is incremented from
- // Q.
- fprintf(out, " if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q\n");
- fprintf(out,
- " || (Build.VERSION.SDK_INT == "
- "Build.VERSION_CODES.Q\n");
- fprintf(out, " && Build.VERSION.PREVIEW_SDK_INT > 0)) {\n");
+ fprintf(out, " if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) {\n");
indent = " ";
}