diff options
490 files changed, 9822 insertions, 3406 deletions
diff --git a/apct-tests/perftests/textclassifier/Android.bp b/apct-tests/perftests/textclassifier/Android.bp index 9f795a7ed54a..c40e0252cb7e 100644 --- a/apct-tests/perftests/textclassifier/Android.bp +++ b/apct-tests/perftests/textclassifier/Android.bp @@ -19,7 +19,7 @@ android_test { "androidx.test.rules", "androidx.annotation_annotation", "apct-perftests-utils", - "collector-device-lib-platform", + "collector-device-lib", ], data: [":perfetto_artifacts"], platform_apis: true, diff --git a/api/current.txt b/api/current.txt index 09de005c008c..b70103b931d4 100644 --- a/api/current.txt +++ b/api/current.txt @@ -11900,6 +11900,7 @@ package android.content.pm { field public static final int INVALID_ID = -1; // 0xffffffff field public static final int STAGED_SESSION_ACTIVATION_FAILED = 2; // 0x2 field public static final int STAGED_SESSION_NO_ERROR = 0; // 0x0 + field public static final int STAGED_SESSION_OTHER_ERROR = 4; // 0x4 field public static final int STAGED_SESSION_UNKNOWN = 3; // 0x3 field public static final int STAGED_SESSION_VERIFICATION_FAILED = 1; // 0x1 } diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt index 3f3b8eaffdab..f53ac8c895ce 100644 --- a/api/module-lib-current.txt +++ b/api/module-lib-current.txt @@ -39,6 +39,14 @@ package android.media { } +package android.media.session { + + public final class MediaSession { + field public static final int FLAG_EXCLUSIVE_GLOBAL_PRIORITY = 65536; // 0x10000 + } + +} + package android.net { public final class TetheringConstants { diff --git a/api/system-current.txt b/api/system-current.txt index 56059dd9d864..88fe40a93010 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -12732,6 +12732,9 @@ package android.webkit { public interface PacProcessor { method @Nullable public String findProxyForUrl(@NonNull String); method @NonNull public static android.webkit.PacProcessor getInstance(); + method @NonNull public static android.webkit.PacProcessor getInstanceForNetwork(long); + method public default long getNetworkHandle(); + method public default void releasePacProcessor(); method public boolean setProxyScript(@NonNull String); } @@ -12871,6 +12874,7 @@ package android.webkit { method public android.webkit.CookieManager getCookieManager(); method public android.webkit.GeolocationPermissions getGeolocationPermissions(); method @NonNull public default android.webkit.PacProcessor getPacProcessor(); + method @NonNull public default android.webkit.PacProcessor getPacProcessorForNetwork(long); method public android.webkit.ServiceWorkerController getServiceWorkerController(); method public android.webkit.WebViewFactoryProvider.Statics getStatics(); method @Deprecated public android.webkit.TokenBindingService getTokenBindingService(); diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java index ed717c491467..4b7eda096e54 100644 --- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java +++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java @@ -19,6 +19,7 @@ package com.android.commands.bmgr; import android.annotation.IntDef; import android.annotation.UserIdInt; import android.app.backup.BackupManager; +import android.app.backup.BackupManager.OperationType; import android.app.backup.BackupManagerMonitor; import android.app.backup.BackupProgress; import android.app.backup.BackupTransport; @@ -666,7 +667,7 @@ public class Bmgr { // The rest of the 'list' options work with a restore session on the current transport try { - mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null); + mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null, OperationType.BACKUP); if (mRestore == null) { System.err.println(BMGR_ERR_NO_RESTORESESSION_FOR_USER + userId); return; @@ -821,7 +822,7 @@ public class Bmgr { try { boolean didRestore = false; - mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null); + mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null, OperationType.BACKUP); if (mRestore == null) { System.err.println(BMGR_ERR_NO_RESTORESESSION_FOR_USER + userId); return; diff --git a/config/hiddenapi-force-blacklist.txt b/config/hiddenapi-force-blocked.txt index b328f2ac1955..b328f2ac1955 100644 --- a/config/hiddenapi-force-blacklist.txt +++ b/config/hiddenapi-force-blocked.txt diff --git a/config/hiddenapi-greylist-max-o.txt b/config/hiddenapi-max-target-o.txt index 023bf3876228..023bf3876228 100644 --- a/config/hiddenapi-greylist-max-o.txt +++ b/config/hiddenapi-max-target-o.txt diff --git a/config/hiddenapi-greylist-max-p.txt b/config/hiddenapi-max-target-p.txt index 351e71dd9538..351e71dd9538 100644 --- a/config/hiddenapi-greylist-max-p.txt +++ b/config/hiddenapi-max-target-p.txt diff --git a/config/hiddenapi-greylist-max-q.txt b/config/hiddenapi-max-target-q.txt index 4832dd184ec5..4832dd184ec5 100644 --- a/config/hiddenapi-greylist-max-q.txt +++ b/config/hiddenapi-max-target-q.txt diff --git a/config/hiddenapi-greylist-packages.txt b/config/hiddenapi-unsupported-packages.txt index 986d2591a007..986d2591a007 100644 --- a/config/hiddenapi-greylist-packages.txt +++ b/config/hiddenapi-unsupported-packages.txt diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-unsupported.txt index a3543dc7f4ee..a3543dc7f4ee 100644 --- a/config/hiddenapi-greylist.txt +++ b/config/hiddenapi-unsupported.txt diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java index ca22bf4a62dc..d334de60713a 100644 --- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java +++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java @@ -364,6 +364,18 @@ public class AccessibilityServiceInfo implements Parcelable { */ public static final int FLAG_REQUEST_MULTI_FINGER_GESTURES = 0x0001000; + /** + * This flag requests that when when {@link #FLAG_REQUEST_MULTI_FINGER_GESTURES} is enabled, + * two-finger passthrough gestures are re-enabled. Two-finger swipe gestures are not detected, + * but instead passed through as one-finger gestures. In addition, three-finger swipes from the + * bottom of the screen are not detected, and instead are passed through unchanged. If {@link + * #FLAG_REQUEST_MULTI_FINGER_GESTURES} is disabled this flag has no effect. + * + * @see #FLAG_REQUEST_TOUCH_EXPLORATION_MODE + * @hide + */ + public static final int FLAG_REQUEST_2_FINGER_PASSTHROUGH = 0x0002000; + /** {@hide} */ public static final int FLAG_FORCE_DIRECT_BOOT_AWARE = 0x00010000; @@ -624,6 +636,7 @@ public class AccessibilityServiceInfo implements Parcelable { 0); flags = asAttributes.getInt( com.android.internal.R.styleable.AccessibilityService_accessibilityFlags, 0); + flags |= FLAG_REQUEST_2_FINGER_PASSTHROUGH; mSettingsActivityName = asAttributes.getString( com.android.internal.R.styleable.AccessibilityService_settingsActivity); if (asAttributes.getBoolean(com.android.internal.R.styleable @@ -1261,6 +1274,8 @@ public class AccessibilityServiceInfo implements Parcelable { return "FLAG_SERVICE_HANDLES_DOUBLE_TAP"; case FLAG_REQUEST_MULTI_FINGER_GESTURES: return "FLAG_REQUEST_MULTI_FINGER_GESTURES"; + case FLAG_REQUEST_2_FINGER_PASSTHROUGH: + return "FLAG_REQUEST_2_FINGER_PASSTHROUGH"; case FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY: return "FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY"; case FLAG_REPORT_VIEW_IDS: diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 9b13d256aea6..7cec717f96e0 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -37,6 +37,7 @@ import android.annotation.Nullable; import android.app.assist.AssistContent; import android.app.assist.AssistStructure; import android.app.backup.BackupAgent; +import android.app.backup.BackupManager; import android.app.servertransaction.ActivityLifecycleItem; import android.app.servertransaction.ActivityLifecycleItem.LifecycleState; import android.app.servertransaction.ActivityRelaunchItem; @@ -289,6 +290,8 @@ public final class ActivityThread extends ClientTransactionHandler { private final Object mNetworkPolicyLock = new Object(); + private static final String DEFAULT_FULL_BACKUP_AGENT = "android.app.backup.FullBackupAgent"; + /** * Denotes the sequence number of the process state change for which the main thread needs * to block until the network rules are updated for it. @@ -737,6 +740,7 @@ public final class ActivityThread extends ClientTransactionHandler { CompatibilityInfo compatInfo; int backupMode; int userId; + int operationType; public String toString() { return "CreateBackupAgentData{appInfo=" + appInfo + " backupAgent=" + appInfo.backupAgentName @@ -957,12 +961,13 @@ public final class ActivityThread extends ClientTransactionHandler { } public final void scheduleCreateBackupAgent(ApplicationInfo app, - CompatibilityInfo compatInfo, int backupMode, int userId) { + CompatibilityInfo compatInfo, int backupMode, int userId, int operationType) { CreateBackupAgentData d = new CreateBackupAgentData(); d.appInfo = app; d.compatInfo = compatInfo; d.backupMode = backupMode; d.userId = userId; + d.operationType = operationType; sendMessage(H.CREATE_BACKUP_AGENT, d); } @@ -4075,12 +4080,7 @@ public final class ActivityThread extends ClientTransactionHandler { return; } - String classname = data.appInfo.backupAgentName; - // full backup operation but no app-supplied agent? use the default implementation - if (classname == null && (data.backupMode == ApplicationThreadConstants.BACKUP_MODE_FULL - || data.backupMode == ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL)) { - classname = "android.app.backup.FullBackupAgent"; - } + String classname = getBackupAgentName(data); try { IBinder binder = null; @@ -4104,7 +4104,7 @@ public final class ActivityThread extends ClientTransactionHandler { context.setOuterContext(agent); agent.attach(context); - agent.onCreate(UserHandle.of(data.userId)); + agent.onCreate(UserHandle.of(data.userId), data.operationType); binder = agent.onBind(); backupAgents.put(packageName, agent); } catch (Exception e) { @@ -4132,6 +4132,23 @@ public final class ActivityThread extends ClientTransactionHandler { } } + private String getBackupAgentName(CreateBackupAgentData data) { + String agentName = data.appInfo.backupAgentName; + if (!UserHandle.isCore(data.appInfo.uid) + && data.operationType == BackupManager.OperationType.MIGRATION) { + // If this is a migration, use the default backup agent regardless of the app's + // preferences. + agentName = DEFAULT_FULL_BACKUP_AGENT; + } else { + // full backup operation but no app-supplied agent? use the default implementation + if (agentName == null && (data.backupMode == ApplicationThreadConstants.BACKUP_MODE_FULL + || data.backupMode == ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL)) { + agentName = DEFAULT_FULL_BACKUP_AGENT; + } + } + return agentName; + } + // Tear down a BackupAgent private void handleDestroyBackupAgent(CreateBackupAgentData data) { if (DEBUG_BACKUP) Slog.v(TAG, "handleDestroyBackupAgent: " + data); @@ -5111,6 +5128,7 @@ public final class ActivityThread extends ClientTransactionHandler { } } r.setState(ON_DESTROY); + mLastReportedWindowingMode.remove(r.activity.getActivityToken()); } schedulePurgeIdler(); // updatePendingActivityConfiguration() reads from mActivities to update @@ -5353,16 +5371,8 @@ public final class ActivityThread extends ClientTransactionHandler { throw e.rethrowFromSystemServer(); } - // Save the current windowing mode to be restored and compared to the new configuration's - // windowing mode (needed because we update the last reported windowing mode when launching - // an activity and we can't tell inside performLaunchActivity whether we are relaunching) - final int oldWindowingMode = mLastReportedWindowingMode.getOrDefault( - r.activity.getActivityToken(), WINDOWING_MODE_UNDEFINED); handleRelaunchActivityInner(r, configChanges, tmp.pendingResults, tmp.pendingIntents, pendingActions, tmp.startsNotResumed, tmp.overrideConfig, "handleRelaunchActivity"); - mLastReportedWindowingMode.put(r.activity.getActivityToken(), oldWindowingMode); - handleWindowingModeChangeIfNeeded(r.activity, r.activity.mCurrentConfig); - if (pendingActions != null) { // Only report a successful relaunch to WindowManager. pendingActions.setReportRelaunchToWindowManager(true); @@ -5628,10 +5638,6 @@ public final class ActivityThread extends ClientTransactionHandler { throw new IllegalArgumentException("Activity token not set. Is the activity attached?"); } - // multi-window / pip mode changes, if any, should be sent before the configuration change - // callback, see also PinnedStackTests#testConfigurationChangeOrderDuringTransition - handleWindowingModeChangeIfNeeded(activity, newConfig); - final boolean movedToDifferentDisplay = isDifferentDisplay(activity, displayId); boolean shouldReportChange = false; if (activity.mCurrentConfig == null) { @@ -5685,6 +5691,11 @@ public final class ActivityThread extends ClientTransactionHandler { } if (shouldReportChange) { + // multi-window / pip mode changes, if any, should be sent before the configuration + // change callback, see also + // PinnedStackTests#testConfigurationChangeOrderDuringTransition + handleWindowingModeChangeIfNeeded(activity, newConfig); + activity.mCalled = false; activity.onConfigurationChanged(configToReport); if (!activity.mCalled) { diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 3b6a7b8f7592..99640118ccdf 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -288,7 +288,8 @@ interface IActivityManager { void stopAppSwitches(); @UnsupportedAppUsage void resumeAppSwitches(); - boolean bindBackupAgent(in String packageName, int backupRestoreMode, int targetUserId); + boolean bindBackupAgent(in String packageName, int backupRestoreMode, int targetUserId, + int operationType); void backupAgentCreated(in String packageName, in IBinder agent, int userId); void unbindBackupAgent(in ApplicationInfo appInfo); int getUidForIntentSender(in IIntentSender sender); diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl index 6e9157e2a8c3..24da50481df3 100644 --- a/core/java/android/app/IApplicationThread.aidl +++ b/core/java/android/app/IApplicationThread.aidl @@ -95,7 +95,7 @@ oneway interface IApplicationThread { void profilerControl(boolean start, in ProfilerInfo profilerInfo, int profileType); void setSchedulingGroup(int group); void scheduleCreateBackupAgent(in ApplicationInfo app, in CompatibilityInfo compatInfo, - int backupMode, int userId); + int backupMode, int userId, int operationType); void scheduleDestroyBackupAgent(in ApplicationInfo app, in CompatibilityInfo compatInfo, int userId); void scheduleOnNewActivityOptions(IBinder token, in Bundle options); diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java index bca6f39e1ded..54f3f1026050 100644 --- a/core/java/android/app/PropertyInvalidatedCache.java +++ b/core/java/android/app/PropertyInvalidatedCache.java @@ -215,7 +215,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> { private long mMisses = 0; @GuardedBy("mLock") - private long mMissDisabled[] = new long[]{ 0, 0, 0 }; + private long mSkips[] = new long[]{ 0, 0, 0 }; @GuardedBy("mLock") private long mMissOverflow = 0; @@ -223,6 +223,9 @@ public abstract class PropertyInvalidatedCache<Query, Result> { @GuardedBy("mLock") private long mHighWaterMark = 0; + @GuardedBy("mLock") + private long mClears = 0; + // Most invalidation is done in a static context, so the counters need to be accessible. @GuardedBy("sCorkLock") private static final HashMap<String, Long> sInvalidates = new HashMap<>(); @@ -273,6 +276,13 @@ public abstract class PropertyInvalidatedCache<Query, Result> { */ private volatile SystemProperties.Handle mPropertyHandle; + /** + * The name by which this cache is known. This should normally be the + * binder call that is being cached, but the constructors default it to + * the property name. + */ + private final String mCacheName; + @GuardedBy("mLock") private final LinkedHashMap<Query, Result> mCache; @@ -297,9 +307,23 @@ public abstract class PropertyInvalidatedCache<Query, Result> { * * @param maxEntries Maximum number of entries to cache; LRU discard * @param propertyName Name of the system property holding the cache invalidation nonce + * Defaults the cache name to the property name. */ public PropertyInvalidatedCache(int maxEntries, @NonNull String propertyName) { + this(maxEntries, propertyName, propertyName); + } + + /** + * Make a new property invalidated cache. + * + * @param maxEntries Maximum number of entries to cache; LRU discard + * @param propertyName Name of the system property holding the cache invalidation nonce + * @param cacheName Name of this cache in debug and dumpsys + */ + public PropertyInvalidatedCache(int maxEntries, @NonNull String propertyName, + @NonNull String cacheName) { mPropertyName = propertyName; + mCacheName = cacheName; mMaxEntries = maxEntries; mCache = new LinkedHashMap<Query, Result>( 2 /* start small */, @@ -320,7 +344,6 @@ public abstract class PropertyInvalidatedCache<Query, Result> { }; synchronized (sCorkLock) { sCaches.put(this, null); - sInvalidates.put(propertyName, (long) 0); } } @@ -333,6 +356,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> { Log.d(TAG, "clearing cache for " + mPropertyName); } mCache.clear(); + mClears++; } } @@ -413,7 +437,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> { // Do not bother collecting statistics if the cache is // locally disabled. synchronized (mLock) { - mMissDisabled[(int) currentNonce]++; + mSkips[(int) currentNonce]++; } } @@ -742,12 +766,16 @@ public abstract class PropertyInvalidatedCache<Query, Result> { boolean alreadyQueued = mUncorkDeadlineMs >= 0; if (DEBUG) { Log.w(TAG, String.format( - "autoCork mUncorkDeadlineMs=%s", mUncorkDeadlineMs)); + "autoCork %s mUncorkDeadlineMs=%s", mPropertyName, + mUncorkDeadlineMs)); } mUncorkDeadlineMs = SystemClock.uptimeMillis() + mAutoCorkDelayMs; if (!alreadyQueued) { getHandlerLocked().sendEmptyMessageAtTime(0, mUncorkDeadlineMs); PropertyInvalidatedCache.corkInvalidations(mPropertyName); + } else { + final long count = sCorkedInvalidates.getOrDefault(mPropertyName, (long) 0); + sCorkedInvalidates.put(mPropertyName, count + 1); } } } @@ -756,7 +784,8 @@ public abstract class PropertyInvalidatedCache<Query, Result> { synchronized (mLock) { if (DEBUG) { Log.w(TAG, String.format( - "handleMsesage mUncorkDeadlineMs=%s", mUncorkDeadlineMs)); + "handleMsesage %s mUncorkDeadlineMs=%s", + mPropertyName, mUncorkDeadlineMs)); } if (mUncorkDeadlineMs < 0) { @@ -816,7 +845,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> { * method is public so clients can use it. */ public String cacheName() { - return mPropertyName; + return mCacheName; } /** @@ -864,16 +893,20 @@ public abstract class PropertyInvalidatedCache<Query, Result> { } synchronized (mLock) { - pw.println(String.format(" Cache Property Name: %s", cacheName())); - pw.println(String.format(" Hits: %d, Misses: %d, Invalidates: %d, Overflows: %d", - mHits, mMisses, invalidateCount, mMissOverflow)); - pw.println(String.format(" Miss-corked: %d, Miss-unset: %d, Miss-other: %d," + - " CorkedInvalidates: %d", - mMissDisabled[NONCE_CORKED], mMissDisabled[NONCE_UNSET], - mMissDisabled[NONCE_DISABLED], corkedInvalidates)); - pw.println(String.format(" Last Observed Nonce: %d", mLastSeenNonce)); - pw.println(String.format(" Current Size: %d, Max Size: %d, HW Mark: %d", - mCache.size(), mMaxEntries, mHighWaterMark)); + pw.println(String.format(" Cache Name: %s", cacheName())); + pw.println(String.format(" Property: %s", mPropertyName)); + final long skips = mSkips[NONCE_CORKED] + mSkips[NONCE_UNSET] + mSkips[NONCE_DISABLED]; + pw.println(String.format(" Hits: %d, Misses: %d, Skips: %d, Clears: %d", + mHits, mMisses, skips, mClears)); + pw.println(String.format(" Skip-corked: %d, Skip-unset: %d, Skip-other: %d", + mSkips[NONCE_CORKED], mSkips[NONCE_UNSET], + mSkips[NONCE_DISABLED])); + pw.println(String.format( + " Nonce: 0x%016x, Invalidates: %d, CorkedInvalidates: %d", + mLastSeenNonce, invalidateCount, corkedInvalidates)); + pw.println(String.format( + " Current Size: %d, Max Size: %d, HW Mark: %d, Overflows: %d", + mCache.size(), mMaxEntries, mHighWaterMark, mMissOverflow)); pw.println(String.format(" Enabled: %s", mDisabled ? "false" : "true")); Set<Map.Entry<Query, Result>> cacheEntries = mCache.entrySet(); diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java index 7c6eff143724..06d1b74abc86 100644 --- a/core/java/android/app/UiModeManager.java +++ b/core/java/android/app/UiModeManager.java @@ -510,6 +510,9 @@ public class UiModeManager { } /** + * Activating night mode for the current user + * + * @return {@code true} if the change is successful * @hide */ public boolean setNightModeActivated(boolean active) { diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java index a789169ade39..16ddcd1d0ea3 100644 --- a/core/java/android/app/backup/BackupAgent.java +++ b/core/java/android/app/backup/BackupAgent.java @@ -19,6 +19,7 @@ package android.app.backup; import android.annotation.Nullable; import android.app.IBackupAgent; import android.app.QueuedWork; +import android.app.backup.BackupManager.OperationType; import android.app.backup.FullBackup.BackupScheme.PathWithRequiredFlags; import android.content.Context; import android.content.ContextWrapper; @@ -38,6 +39,8 @@ import android.system.StructStat; import android.util.ArraySet; import android.util.Log; +import com.android.internal.annotations.VisibleForTesting; + import libcore.io.IoUtils; import org.xmlpull.v1.XmlPullParserException; @@ -50,6 +53,7 @@ import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.CountDownLatch; @@ -129,6 +133,7 @@ import java.util.concurrent.CountDownLatch; public abstract class BackupAgent extends ContextWrapper { private static final String TAG = "BackupAgent"; private static final boolean DEBUG = false; + private static final int DEFAULT_OPERATION_TYPE = OperationType.BACKUP; /** @hide */ public static final int RESULT_SUCCESS = 0; @@ -186,6 +191,9 @@ public abstract class BackupAgent extends ContextWrapper { Handler mHandler = null; @Nullable private UserHandle mUser; + // This field is written from the main thread (in onCreate), and read in a Binder thread (in + // onFullBackup that is called from system_server via Binder). + @OperationType private volatile int mOperationType = DEFAULT_OPERATION_TYPE; Handler getHandler() { if (mHandler == null) { @@ -229,6 +237,13 @@ public abstract class BackupAgent extends ContextWrapper { } /** + * @hide + */ + public void onCreate(UserHandle user) { + onCreate(user, DEFAULT_OPERATION_TYPE); + } + + /** * Provided as a convenience for agent implementations that need an opportunity * to do one-time initialization before the actual backup or restore operation * is begun with information about the calling user. @@ -236,10 +251,11 @@ public abstract class BackupAgent extends ContextWrapper { * * @hide */ - public void onCreate(UserHandle user) { + public void onCreate(UserHandle user, @OperationType int operationType) { onCreate(); mUser = user; + mOperationType = operationType; } /** @@ -390,12 +406,9 @@ public abstract class BackupAgent extends ContextWrapper { return; } - Map<String, Set<PathWithRequiredFlags>> manifestIncludeMap; - ArraySet<PathWithRequiredFlags> manifestExcludeSet; + IncludeExcludeRules includeExcludeRules; try { - manifestIncludeMap = - backupScheme.maybeParseAndGetCanonicalIncludePaths(); - manifestExcludeSet = backupScheme.maybeParseAndGetCanonicalExcludePaths(); + includeExcludeRules = getIncludeExcludeRules(backupScheme); } catch (IOException | XmlPullParserException e) { if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) { Log.v(FullBackup.TAG_XML_PARSER, @@ -404,6 +417,10 @@ public abstract class BackupAgent extends ContextWrapper { } return; } + Map<String, Set<PathWithRequiredFlags>> manifestIncludeMap + = includeExcludeRules.getIncludeMap(); + Set<PathWithRequiredFlags> manifestExcludeSet + = includeExcludeRules.getExcludeSet(); final String packageName = getPackageName(); final ApplicationInfo appInfo = getApplicationInfo(); @@ -528,6 +545,24 @@ public abstract class BackupAgent extends ContextWrapper { } } + /** @hide */ + @VisibleForTesting + public IncludeExcludeRules getIncludeExcludeRules(FullBackup.BackupScheme backupScheme) + throws IOException, XmlPullParserException { + if (mOperationType == OperationType.MIGRATION) { + return IncludeExcludeRules.emptyRules(); + } + + Map<String, Set<PathWithRequiredFlags>> manifestIncludeMap; + ArraySet<PathWithRequiredFlags> manifestExcludeSet; + + manifestIncludeMap = + backupScheme.maybeParseAndGetCanonicalIncludePaths(); + manifestExcludeSet = backupScheme.maybeParseAndGetCanonicalExcludePaths(); + + return new IncludeExcludeRules(manifestIncludeMap, manifestExcludeSet); + } + /** * Notification that the application's current backup operation causes it to exceed * the maximum size permitted by the transport. The ongoing backup operation is @@ -570,7 +605,7 @@ public abstract class BackupAgent extends ContextWrapper { */ private void applyXmlFiltersAndDoFullBackupForDomain(String packageName, String domainToken, Map<String, Set<PathWithRequiredFlags>> includeMap, - ArraySet<PathWithRequiredFlags> filterSet, ArraySet<String> traversalExcludeSet, + Set<PathWithRequiredFlags> filterSet, ArraySet<String> traversalExcludeSet, FullBackupDataOutput data) throws IOException { if (includeMap == null || includeMap.size() == 0) { // Do entire sub-tree for the provided token. @@ -742,7 +777,7 @@ public abstract class BackupAgent extends ContextWrapper { * @hide */ protected final void fullBackupFileTree(String packageName, String domain, String startingPath, - ArraySet<PathWithRequiredFlags> manifestExcludes, + Set<PathWithRequiredFlags> manifestExcludes, ArraySet<String> systemExcludes, FullBackupDataOutput output) { // Pull out the domain and set it aside to use when making the tarball. @@ -811,7 +846,7 @@ public abstract class BackupAgent extends ContextWrapper { } private boolean manifestExcludesContainFilePath( - ArraySet<PathWithRequiredFlags> manifestExcludes, String filePath) { + Set<PathWithRequiredFlags> manifestExcludes, String filePath) { for (PathWithRequiredFlags exclude : manifestExcludes) { String excludePath = exclude.getPath(); if (excludePath != null && excludePath.equals(filePath)) { @@ -1265,4 +1300,53 @@ public abstract class BackupAgent extends ContextWrapper { throw new IllegalStateException(mMessage); } } + + /** @hide */ + @VisibleForTesting + public static class IncludeExcludeRules { + private final Map<String, Set<PathWithRequiredFlags>> mManifestIncludeMap; + private final Set<PathWithRequiredFlags> mManifestExcludeSet; + + /** @hide */ + public IncludeExcludeRules( + Map<String, Set<PathWithRequiredFlags>> manifestIncludeMap, + Set<PathWithRequiredFlags> manifestExcludeSet) { + mManifestIncludeMap = manifestIncludeMap; + mManifestExcludeSet = manifestExcludeSet; + } + + /** @hide */ + @VisibleForTesting + public static IncludeExcludeRules emptyRules() { + return new IncludeExcludeRules(Collections.emptyMap(), new ArraySet<>()); + } + + private Map<String, Set<PathWithRequiredFlags>> getIncludeMap() { + return mManifestIncludeMap; + } + + private Set<PathWithRequiredFlags> getExcludeSet() { + return mManifestExcludeSet; + } + + /** @hide */ + @Override + public int hashCode() { + return Objects.hash(mManifestIncludeMap, mManifestExcludeSet); + } + + /** @hide */ + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object == null || getClass() != object.getClass()) { + return false; + } + IncludeExcludeRules that = (IncludeExcludeRules) object; + return Objects.equals(mManifestIncludeMap, that.mManifestIncludeMap) && + Objects.equals(mManifestExcludeSet, that.mManifestExcludeSet); + } + } } diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java index b1a62bf42cc4..9b67587c4dcd 100644 --- a/core/java/android/app/backup/BackupManager.java +++ b/core/java/android/app/backup/BackupManager.java @@ -355,7 +355,36 @@ public class BackupManager { try { // All packages, current transport IRestoreSession binder = - sService.beginRestoreSessionForUser(mContext.getUserId(), null, null); + sService.beginRestoreSessionForUser(mContext.getUserId(), null, null, + OperationType.BACKUP); + if (binder != null) { + session = new RestoreSession(mContext, binder); + } + } catch (RemoteException e) { + Log.e(TAG, "beginRestoreSession() couldn't connect"); + } + } + return session; + } + + /** + * Begin the process of restoring data from backup. See the + * {@link android.app.backup.RestoreSession} class for documentation on that process. + * + * @param operationType Type of the operation, see {@link OperationType} + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.BACKUP) + public RestoreSession beginRestoreSession(@OperationType int operationType) { + RestoreSession session = null; + checkServiceBinder(); + if (sService != null) { + try { + // All packages, current transport + IRestoreSession binder = + sService.beginRestoreSessionForUser(mContext.getUserId(), null, null, + operationType); if (binder != null) { session = new RestoreSession(mContext, binder); } diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl index 96b5dd593bbe..e177a74915ee 100644 --- a/core/java/android/app/backup/IBackupManager.aidl +++ b/core/java/android/app/backup/IBackupManager.aidl @@ -547,9 +547,11 @@ interface IBackupManager { * set can be restored. * @param transportID The name of the transport to use for the restore operation. * May be null, in which case the current active transport is used. + * @param operationType Type of the operation, see {@link BackupManager#OperationType} * @return An interface to the restore session, or null on error. */ - IRestoreSession beginRestoreSessionForUser(int userId, String packageName, String transportID); + IRestoreSession beginRestoreSessionForUser(int userId, String packageName, String transportID, + int operationType); /** * Notify the backup manager that a BackupAgent has completed the operation diff --git a/core/java/android/app/people/ConversationChannel.java b/core/java/android/app/people/ConversationChannel.java new file mode 100644 index 000000000000..39c5c85e456c --- /dev/null +++ b/core/java/android/app/people/ConversationChannel.java @@ -0,0 +1,97 @@ +/* + * 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.app.people; + +import android.app.NotificationChannel; +import android.content.pm.ShortcutInfo; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * The non-customized notification channel of a conversation. It contains the information to render + * the conversation and allows the user to open and customize the conversation setting. + * + * @hide + */ +public final class ConversationChannel implements Parcelable { + + private ShortcutInfo mShortcutInfo; + private NotificationChannel mParentNotificationChannel; + private long mLastEventTimestamp; + private boolean mHasActiveNotifications; + + public static final Creator<ConversationChannel> CREATOR = new Creator<ConversationChannel>() { + @Override + public ConversationChannel createFromParcel(Parcel in) { + return new ConversationChannel(in); + } + + @Override + public ConversationChannel[] newArray(int size) { + return new ConversationChannel[size]; + } + }; + + public ConversationChannel(ShortcutInfo shortcutInfo, + NotificationChannel parentNotificationChannel, long lastEventTimestamp, + boolean hasActiveNotifications) { + mShortcutInfo = shortcutInfo; + mParentNotificationChannel = parentNotificationChannel; + mLastEventTimestamp = lastEventTimestamp; + mHasActiveNotifications = hasActiveNotifications; + } + + public ConversationChannel(Parcel in) { + mShortcutInfo = in.readParcelable(ShortcutInfo.class.getClassLoader()); + mParentNotificationChannel = in.readParcelable(NotificationChannel.class.getClassLoader()); + mLastEventTimestamp = in.readLong(); + mHasActiveNotifications = in.readBoolean(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(mShortcutInfo, flags); + dest.writeParcelable(mParentNotificationChannel, flags); + dest.writeLong(mLastEventTimestamp); + dest.writeBoolean(mHasActiveNotifications); + } + + public ShortcutInfo getShortcutInfo() { + return mShortcutInfo; + } + + public NotificationChannel getParentNotificationChannel() { + return mParentNotificationChannel; + } + + public long getLastEventTimestamp() { + return mLastEventTimestamp; + } + + /** + * Whether this conversation has any active notifications. If it's true, the shortcut for this + * conversation can't be uncached until all its active notifications are dismissed. + */ + public boolean hasActiveNotifications() { + return mHasActiveNotifications; + } +} diff --git a/core/java/android/app/people/IPeopleManager.aidl b/core/java/android/app/people/IPeopleManager.aidl new file mode 100644 index 000000000000..61dac0d64422 --- /dev/null +++ b/core/java/android/app/people/IPeopleManager.aidl @@ -0,0 +1,42 @@ +/* + * 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.app.people; + +import android.content.pm.ParceledListSlice; +import android.net.Uri; +import android.os.IBinder; + +/** + * System private API for talking with the people service. + * {@hide} + */ +interface IPeopleManager { + /** + * Returns the recent conversations. The conversations that have customized notification + * settings are excluded from the returned list. + */ + ParceledListSlice getRecentConversations(); + + /** + * Removes the specified conversation from the recent conversations list and uncaches the + * shortcut associated with the conversation. + */ + void removeRecentConversation(in String packageName, int userId, in String shortcutId); + + /** Removes all the recent conversations and uncaches their cached shortcuts. */ + void removeAllRecentConversations(); +} diff --git a/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java b/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java index 6bd365fad6f6..0770aff4e9bb 100644 --- a/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java +++ b/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java @@ -117,7 +117,7 @@ public final class TimeZoneDetectorImpl implements TimeZoneDetector { } private void notifyConfigurationListeners(@NonNull TimeZoneConfiguration configuration) { - ArraySet<TimeZoneConfigurationListener> configurationListeners; + final ArraySet<TimeZoneConfigurationListener> configurationListeners; synchronized (this) { configurationListeners = new ArraySet<>(mConfigurationListeners); } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 16cdf2334ad8..52b04675b7a5 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3496,6 +3496,7 @@ public abstract class Context { //@hide: TIME_ZONE_DETECTOR_SERVICE, PERMISSION_SERVICE, LIGHTS_SERVICE, + //@hide: PEOPLE_SERVICE, }) @Retention(RetentionPolicy.SOURCE) public @interface ServiceName {} @@ -5189,6 +5190,14 @@ public abstract class Context { public static final String SMS_SERVICE = "sms"; /** + * Use with {@link #getSystemService(String)} to access people service. + * + * @see #getSystemService(String) + * @hide + */ + public static final String PEOPLE_SERVICE = "people"; + + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. * diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index df9db278e095..bed7b26034e5 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -2075,7 +2075,8 @@ public class PackageInstaller { STAGED_SESSION_NO_ERROR, STAGED_SESSION_VERIFICATION_FAILED, STAGED_SESSION_ACTIVATION_FAILED, - STAGED_SESSION_UNKNOWN}) + STAGED_SESSION_UNKNOWN, + STAGED_SESSION_OTHER_ERROR}) @Retention(RetentionPolicy.SOURCE) public @interface StagedSessionErrorCode{} /** @@ -2101,6 +2102,12 @@ public class PackageInstaller { */ public static final int STAGED_SESSION_UNKNOWN = 3; + /** + * Constant indicating that a known error occurred while processing this staged session, but + * the error could not be matched to other categories. + */ + public static final int STAGED_SESSION_OTHER_ERROR = 4; + /** {@hide} */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public int sessionId; diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 42a610700051..e08af5534afd 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -8089,7 +8089,8 @@ public abstract class PackageManager { private static final PropertyInvalidatedCache<ApplicationInfoQuery, ApplicationInfo> sApplicationInfoCache = new PropertyInvalidatedCache<ApplicationInfoQuery, ApplicationInfo>( - 16, PermissionManager.CACHE_KEY_PACKAGE_INFO) { + 16, PermissionManager.CACHE_KEY_PACKAGE_INFO, + "getApplicationInfo") { @Override protected ApplicationInfo recompute(ApplicationInfoQuery query) { return getApplicationInfoAsUserUncached( @@ -8190,7 +8191,8 @@ public abstract class PackageManager { private static final PropertyInvalidatedCache<PackageInfoQuery, PackageInfo> sPackageInfoCache = new PropertyInvalidatedCache<PackageInfoQuery, PackageInfo>( - 32, PermissionManager.CACHE_KEY_PACKAGE_INFO) { + 32, PermissionManager.CACHE_KEY_PACKAGE_INFO, + "getPackageInfo") { @Override protected PackageInfo recompute(PackageInfoQuery query) { return getPackageInfoAsUserUncached( diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java index f69bbe508492..f0a83f0b00b0 100644 --- a/core/java/android/hardware/camera2/CameraCaptureSession.java +++ b/core/java/android/hardware/camera2/CameraCaptureSession.java @@ -999,7 +999,7 @@ public abstract class CameraCaptureSession implements AutoCloseable { * <p>If the camera device configuration fails, then {@link #onConfigureFailed} will * be invoked instead of this callback.</p> * - * @param session the session returned by {@link CameraDevice#createCaptureSession} + * @param session the successfully configured session instance */ public abstract void onConfigured(@NonNull CameraCaptureSession session); @@ -1014,7 +1014,7 @@ public abstract class CameraCaptureSession implements AutoCloseable { * to the session prior to this callback will be discarded and will not produce any * callbacks on their listeners.</p> * - * @param session the session returned by {@link CameraDevice#createCaptureSession} + * @param session the session instance that failed during configuration */ public abstract void onConfigureFailed(@NonNull CameraCaptureSession session); @@ -1028,7 +1028,7 @@ public abstract class CameraCaptureSession implements AutoCloseable { * <p>Otherwise, this callback will be invoked any time the session finishes processing * all of its active capture requests, and no repeating request or burst is set up.</p> * - * @param session the session returned by {@link CameraDevice#createCaptureSession} + * @param session the session returned by {@link #onConfigured} * */ public void onReady(@NonNull CameraCaptureSession session) { @@ -1045,7 +1045,7 @@ public abstract class CameraCaptureSession implements AutoCloseable { * <p>If the session runs out of capture requests to process and calls {@link #onReady}, * then this callback will be invoked again once new requests are submitted for capture.</p> * - * @param session the session returned by {@link CameraDevice#createCaptureSession} + * @param session the session returned by {@link #onConfigured} */ public void onActive(@NonNull CameraCaptureSession session) { // default empty implementation @@ -1075,7 +1075,7 @@ public abstract class CameraCaptureSession implements AutoCloseable { * {@link #onReady}, which is fired when all requests in both queues have been processed.</p> * * @param session - * The session returned by {@link CameraDevice#createCaptureSession} + * The session returned by {@link #onConfigured} */ public void onCaptureQueueEmpty(@NonNull CameraCaptureSession session) { // default empty implementation @@ -1093,7 +1093,7 @@ public abstract class CameraCaptureSession implements AutoCloseable { * However, any in-progress capture requests submitted to the session will be completed * as normal.</p> * - * @param session the session returned by {@link CameraDevice#createCaptureSession} + * @param session the session returned by {@link #onConfigured} */ public void onClosed(@NonNull CameraCaptureSession session) { // default empty implementation @@ -1111,7 +1111,7 @@ public abstract class CameraCaptureSession implements AutoCloseable { * this callback is still invoked after the error is encountered, though some buffers may * not have been successfully pre-allocated.</p> * - * @param session the session returned by {@link CameraDevice#createCaptureSession} + * @param session the session returned by {@link #onConfigured} * @param surface the Surface that was used with the {@link #prepare} call. */ public void onSurfacePrepared(@NonNull CameraCaptureSession session, diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index 7ac8d052ea86..c7f89151624d 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -260,6 +260,13 @@ public abstract class DisplayManagerInternal { int displayId, long maxFrames, long timestamp); /** + * Temporarily ignore proximity-sensor-based display behavior until there is a change + * to the proximity sensor state. This allows the display to turn back on even if something + * is obstructing the proximity sensor. + */ + public abstract void ignoreProximitySensorUntilChanged(); + + /** * Describes the requested power state of the display. * * This object is intended to describe the general characteristics of the diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl index ba0636fa80ff..dc6f5799156d 100644 --- a/core/java/android/hardware/input/IInputManager.aidl +++ b/core/java/android/hardware/input/IInputManager.aidl @@ -23,6 +23,7 @@ import android.hardware.input.IInputDevicesChangedListener; import android.hardware.input.ITabletModeChangedListener; import android.hardware.input.TouchCalibration; import android.os.IBinder; +import android.os.VibrationEffect; import android.view.InputDevice; import android.view.InputEvent; import android.view.InputMonitor; @@ -83,7 +84,7 @@ interface IInputManager { int isMicMuted(); // Input device vibrator control. - void vibrate(int deviceId, in long[] pattern, in int[] amplitudes, int repeat, IBinder token); + void vibrate(int deviceId, in VibrationEffect effect, IBinder token); void cancelVibrate(int deviceId, IBinder token); void setPointerIconType(int typeId); diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index f0faeb078386..dd820fae3f2d 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -1297,27 +1297,8 @@ public final class InputManager { @Override public void vibrate(int uid, String opPkg, VibrationEffect effect, String reason, AudioAttributes attributes) { - long[] pattern; - int[] amplitudes; - int repeat; - if (effect instanceof VibrationEffect.OneShot) { - VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) effect; - pattern = new long[] { 0, oneShot.getDuration() }; - amplitudes = new int[] { 0, oneShot.getAmplitude() }; - repeat = -1; - } else if (effect instanceof VibrationEffect.Waveform) { - VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) effect; - pattern = waveform.getTimings(); - amplitudes = waveform.getAmplitudes(); - repeat = waveform.getRepeatIndex(); - } else { - // TODO: Add support for prebaked effects - Log.w(TAG, "Pre-baked effects aren't supported on input devices"); - return; - } - try { - mIm.vibrate(mDeviceId, pattern, amplitudes, repeat, mToken); + mIm.vibrate(mDeviceId, effect, mToken); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index fbe6a5052f3d..b0d449769be4 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -743,6 +743,12 @@ public abstract class BatteryStats implements Parcelable { @UnsupportedAppUsage public abstract ArrayMap<String, ? extends Pkg> getPackageStats(); + /** + * Returns the proportion of power consumed by the System Service + * calls made by this UID. + */ + public abstract double getProportionalSystemServiceUsage(); + public abstract ControllerActivityCounter getWifiControllerActivity(); public abstract ControllerActivityCounter getBluetoothControllerActivity(); public abstract ControllerActivityCounter getModemControllerActivity(); @@ -2882,6 +2888,17 @@ public abstract class BatteryStats implements Parcelable { public abstract int getDischargeAmountScreenDozeSinceCharge(); /** + * Returns the approximate CPU time (in microseconds) spent by the system server handling + * incoming service calls from apps. + * + * @param cluster the index of the CPU cluster. + * @param step the index of the CPU speed. This is not the actual speed of the CPU. + * @see com.android.internal.os.PowerProfile#getNumCpuClusters() + * @see com.android.internal.os.PowerProfile#getNumSpeedStepsInCpuCluster(int) + */ + public abstract long getSystemServiceTimeAtCpuSpeed(int cluster, int step); + + /** * Returns the total, last, or current battery uptime in microseconds. * * @param curTime the elapsed realtime in microseconds. diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java index e30a40964992..eb18b96e255b 100644 --- a/core/java/android/os/PowerManagerInternal.java +++ b/core/java/android/os/PowerManagerInternal.java @@ -17,6 +17,7 @@ package android.os; import android.view.Display; +import android.view.KeyEvent; import java.util.function.Consumer; @@ -313,4 +314,7 @@ public abstract class PowerManagerInternal { /** Returns information about the last wakeup event. */ public abstract PowerManager.WakeData getLastWakeup(); + + /** Allows power button to intercept a power key button press. */ + public abstract boolean interceptPowerKeyDown(KeyEvent event); } diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java index bf3d46fa4a44..0c190719af57 100644 --- a/core/java/android/permission/PermissionManager.java +++ b/core/java/android/permission/PermissionManager.java @@ -608,7 +608,7 @@ public final class PermissionManager { /** @hide */ private static final PropertyInvalidatedCache<PermissionQuery, Integer> sPermissionCache = new PropertyInvalidatedCache<PermissionQuery, Integer>( - 16, CACHE_KEY_PACKAGE_INFO) { + 16, CACHE_KEY_PACKAGE_INFO, "checkPermission") { @Override protected Integer recompute(PermissionQuery query) { return checkPermissionUncached(query.permission, query.pid, query.uid); @@ -689,7 +689,7 @@ public final class PermissionManager { private static PropertyInvalidatedCache<PackageNamePermissionQuery, Integer> sPackageNamePermissionCache = new PropertyInvalidatedCache<PackageNamePermissionQuery, Integer>( - 16, CACHE_KEY_PACKAGE_INFO) { + 16, CACHE_KEY_PACKAGE_INFO, "checkPackageNamePermission") { @Override protected Integer recompute(PackageNamePermissionQuery query) { return checkPackageNamePermissionUncached( diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 5acc11a868bf..660455e59a4a 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -7912,6 +7912,13 @@ public final class Settings { public static final String TAPS_APP_TO_EXIT = "taps_app_to_exit"; /** + * Internal use, one handed mode tutorial showed times. + * @hide + */ + public static final String ONE_HANDED_TUTORIAL_SHOW_COUNT = + "one_handed_tutorial_show_count"; + + /** * The current night mode that has been selected by the user. Owned * and controlled by UiModeManagerService. Constants are as per * UiModeManager. diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index c383bc7a4d70..7f45c044408a 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -618,16 +618,20 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation return false; } if (DEBUG) Log.d(TAG, "onStateChanged: " + state); - updateState(state); - - boolean localStateChanged = !mState.equals(mLastDispatchedState, - true /* excludingCaptionInsets */, true /* excludeInvisibleIme */); mLastDispatchedState.set(state, true /* copySources */); + final InsetsState lastState = new InsetsState(mState, true /* copySources */); + updateState(state); applyLocalVisibilityOverride(); - if (localStateChanged) { - if (DEBUG) Log.d(TAG, "onStateChanged, notifyInsetsChanged, send state to WM: " + mState); + + if (!mState.equals(lastState, true /* excludingCaptionInsets */, + true /* excludeInvisibleIme */)) { + if (DEBUG) Log.d(TAG, "onStateChanged, notifyInsetsChanged"); mHost.notifyInsetsChanged(); + } + if (!mState.equals(state, true /* excludingCaptionInsets */, + true /* excludeInvisibleIme */)) { + if (DEBUG) Log.d(TAG, "onStateChanged, send state to WM: " + mState); updateRequestedState(); } return true; diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 50ed00cd0aa7..2ce993dfedca 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -2390,6 +2390,13 @@ public final class SurfaceControl implements Parcelable { } /** + * @hide + */ + public static ScreenshotHardwareBuffer captureLayers(LayerCaptureArgs captureArgs) { + return nativeCaptureLayers(captureArgs); + } + + /** * Like {@link #captureLayers(SurfaceControl, Rect, float, int)} but with an array of layer * handles to exclude. * @hide diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 64ddb2f4d9d9..3f02d701f71f 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1814,19 +1814,13 @@ public final class ViewRootImpl implements ViewParent, /** * Called after window layout to update the bounds surface. If the surface insets have changed * or the surface has resized, update the bounds surface. - * - * @param shouldReparent Whether it should reparent the bounds layer to the main SurfaceControl. */ - private void updateBoundsLayer(boolean shouldReparent) { + private void updateBoundsLayer() { if (mBoundsLayer != null) { setBoundsLayerCrop(); - mTransaction.deferTransactionUntil(mBoundsLayer, getRenderSurfaceControl(), - mSurface.getNextFrameNumber()); - - if (shouldReparent) { - mTransaction.reparent(mBoundsLayer, getRenderSurfaceControl()); - } - mTransaction.apply(); + mTransaction.deferTransactionUntil(mBoundsLayer, + getRenderSurfaceControl(), mSurface.getNextFrameNumber()) + .apply(); } } @@ -2905,16 +2899,7 @@ public final class ViewRootImpl implements ViewParent, } if (surfaceSizeChanged || surfaceReplaced || surfaceCreated || windowAttributesChanged) { - // If the surface has been replaced, there's a chance the bounds layer is not parented - // to the new layer. When updating bounds layer, also reparent to the main VRI - // SurfaceControl to ensure it's correctly placed in the hierarchy. - // - // This needs to be done on the client side since WMS won't reparent the children to the - // new surface if it thinks the app is closing. WMS gets the signal that the app is - // stopping, but on the client side it doesn't get stopped since it's restarted quick - // enough. WMS doesn't want to keep around old children since they will leak when the - // client creates new children. - updateBoundsLayer(surfaceReplaced); + updateBoundsLayer(); } final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw); diff --git a/core/java/android/webkit/PacProcessor.java b/core/java/android/webkit/PacProcessor.java index 5ef450fa65dd..7e7b987f72f3 100644 --- a/core/java/android/webkit/PacProcessor.java +++ b/core/java/android/webkit/PacProcessor.java @@ -19,7 +19,7 @@ package android.webkit; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; - +import android.net.Network; /** * Class to evaluate PAC scripts. @@ -40,6 +40,20 @@ public interface PacProcessor { } /** + * Returns PacProcessor instance associated with the {@link Network}. + * The host resolution is done on this {@link Network}. + * + * @param networkHandle a handle representing {@link Network} handle. + * @return PacProcessor instance for the specified network. + * @see Network#getNetworkHandle + * @see Network#fromNetworkHandle + */ + @NonNull + static PacProcessor getInstanceForNetwork(long networkHandle) { + return WebViewFactory.getProvider().getPacProcessorForNetwork(networkHandle); + } + + /** * Set PAC script to use. * * @param script PAC script. @@ -55,4 +69,23 @@ public interface PacProcessor { */ @Nullable String findProxyForUrl(@NonNull String url); + + /** + * Stops support for this {@link PacProcessor} and release its resources. + * No methods of this class must be called after calling this method. + */ + default void releasePacProcessor() { + throw new UnsupportedOperationException("Not implemented"); + } + + /** + * Returns a network handle associated with this {@link PacProcessor}. + * + * @return a network handle or 0 if a network is unspecified. + * @see Network#getNetworkHandle + * @see Network#fromNetworkHandle + */ + default long getNetworkHandle() { + throw new UnsupportedOperationException("Not implemented"); + } } diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java index f7c3ec09dd67..f1863e319689 100644 --- a/core/java/android/webkit/WebViewFactoryProvider.java +++ b/core/java/android/webkit/WebViewFactoryProvider.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.SystemApi; import android.content.Context; import android.content.Intent; +import android.net.Network; import android.net.Uri; import java.util.List; @@ -175,7 +176,7 @@ public interface WebViewFactoryProvider { WebViewDatabase getWebViewDatabase(Context context); /** - * Gets the singleton PacProcessor instance. + * Gets the default PacProcessor instance. * @return the PacProcessor instance */ @NonNull @@ -184,6 +185,20 @@ public interface WebViewFactoryProvider { } /** + * Returns PacProcessor instance associated with the {@link Network}. + * The host resolution is done on this {@link Network}. + * + * @param networkHandle a network handle representing the {@link Network}. + * @return the {@link PacProcessor} instance associated with {@link Network}. + * @see Network#getNetworkHandle + * @see Network#fromNetworkHandle + */ + @NonNull + default PacProcessor getPacProcessorForNetwork(long networkHandle) { + throw new UnsupportedOperationException("Not implemented"); + } + + /** * Gets the classloader used to load internal WebView implementation classes. This interface * should only be used by the WebView Support Library. */ diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index c4eb39626d8b..7683067958d8 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -385,6 +385,7 @@ public class Editor { private final SuggestionHelper mSuggestionHelper = new SuggestionHelper(); private boolean mFlagCursorDragFromAnywhereEnabled; + private float mCursorDragDirectionMinXYRatio; private boolean mFlagInsertionHandleGesturesEnabled; // Specifies whether the new magnifier (with fish-eye effect) is enabled. @@ -425,6 +426,11 @@ public class Editor { mFlagCursorDragFromAnywhereEnabled = AppGlobals.getIntCoreSetting( WidgetFlags.KEY_ENABLE_CURSOR_DRAG_FROM_ANYWHERE, WidgetFlags.ENABLE_CURSOR_DRAG_FROM_ANYWHERE_DEFAULT ? 1 : 0) != 0; + final int cursorDragMinAngleFromVertical = AppGlobals.getIntCoreSetting( + WidgetFlags.KEY_CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL, + WidgetFlags.CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL_DEFAULT); + mCursorDragDirectionMinXYRatio = EditorTouchState.getXYRatio( + cursorDragMinAngleFromVertical); mFlagInsertionHandleGesturesEnabled = AppGlobals.getIntCoreSetting( WidgetFlags.KEY_ENABLE_INSERTION_HANDLE_GESTURES, WidgetFlags.ENABLE_INSERTION_HANDLE_GESTURES_DEFAULT ? 1 : 0) != 0; @@ -437,6 +443,8 @@ public class Editor { if (TextView.DEBUG_CURSOR) { logCursor("Editor", "Cursor drag from anywhere is %s.", mFlagCursorDragFromAnywhereEnabled ? "enabled" : "disabled"); + logCursor("Editor", "Cursor drag min angle from vertical is %d (= %f x/y ratio)", + cursorDragMinAngleFromVertical, mCursorDragDirectionMinXYRatio); logCursor("Editor", "Insertion handle gestures is %s.", mFlagInsertionHandleGesturesEnabled ? "enabled" : "disabled"); logCursor("Editor", "New magnifier is %s.", @@ -463,6 +471,11 @@ public class Editor { } @VisibleForTesting + public void setCursorDragMinAngleFromVertical(int degreesFromVertical) { + mCursorDragDirectionMinXYRatio = EditorTouchState.getXYRatio(degreesFromVertical); + } + + @VisibleForTesting public boolean getFlagInsertionHandleGesturesEnabled() { return mFlagInsertionHandleGesturesEnabled; } @@ -6127,10 +6140,11 @@ public class Editor { if (mIsDraggingCursor) { performCursorDrag(event); } else if (mFlagCursorDragFromAnywhereEnabled - && mTextView.getLayout() != null - && mTextView.isFocused() - && mTouchState.isMovedEnoughForDrag() - && !mTouchState.isDragCloseToVertical()) { + && mTextView.getLayout() != null + && mTextView.isFocused() + && mTouchState.isMovedEnoughForDrag() + && (mTouchState.getInitialDragDirectionXYRatio() + > mCursorDragDirectionMinXYRatio || mTouchState.isOnHandle())) { startCursorDrag(event); } break; diff --git a/core/java/android/widget/EditorTouchState.java b/core/java/android/widget/EditorTouchState.java index 9eb63087a66e..751436865ff5 100644 --- a/core/java/android/widget/EditorTouchState.java +++ b/core/java/android/widget/EditorTouchState.java @@ -59,7 +59,7 @@ public class EditorTouchState { private boolean mMultiTapInSameArea; private boolean mMovedEnoughForDrag; - private boolean mIsDragCloseToVertical; + private float mInitialDragDirectionXYRatio; public float getLastDownX() { return mLastDownX; @@ -98,8 +98,23 @@ public class EditorTouchState { return mMovedEnoughForDrag; } - public boolean isDragCloseToVertical() { - return mIsDragCloseToVertical && !mIsOnHandle; + /** + * When {@link #isMovedEnoughForDrag()} is {@code true}, this function returns the x/y ratio for + * the initial drag direction. Smaller values indicate that the direction is closer to vertical, + * while larger values indicate that the direction is closer to horizontal. For example: + * <ul> + * <li>if the drag direction is exactly vertical, this returns 0 + * <li>if the drag direction is exactly horizontal, this returns {@link Float#MAX_VALUE} + * <li>if the drag direction is 45 deg from vertical, this returns 1 + * <li>if the drag direction is 30 deg from vertical, this returns 0.58 (x delta is smaller + * than y delta) + * <li>if the drag direction is 60 deg from vertical, this returns 1.73 (x delta is bigger + * than y delta) + * </ul> + * This function never returns negative values, regardless of the direction of the drag. + */ + public float getInitialDragDirectionXYRatio() { + return mInitialDragDirectionXYRatio; } public void setIsOnHandle(boolean onHandle) { @@ -155,7 +170,7 @@ public class EditorTouchState { mLastDownY = event.getY(); mLastDownMillis = event.getEventTime(); mMovedEnoughForDrag = false; - mIsDragCloseToVertical = false; + mInitialDragDirectionXYRatio = 0.0f; } else if (action == MotionEvent.ACTION_UP) { if (TextView.DEBUG_CURSOR) { logCursor("EditorTouchState", "ACTION_UP"); @@ -164,7 +179,7 @@ public class EditorTouchState { mLastUpY = event.getY(); mLastUpMillis = event.getEventTime(); mMovedEnoughForDrag = false; - mIsDragCloseToVertical = false; + mInitialDragDirectionXYRatio = 0.0f; } else if (action == MotionEvent.ACTION_MOVE) { if (!mMovedEnoughForDrag) { float deltaX = event.getX() - mLastDownX; @@ -174,9 +189,8 @@ public class EditorTouchState { int touchSlop = config.getScaledTouchSlop(); mMovedEnoughForDrag = distanceSquared > touchSlop * touchSlop; if (mMovedEnoughForDrag) { - // If the direction of the swipe motion is within 45 degrees of vertical, it is - // considered a vertical drag. - mIsDragCloseToVertical = Math.abs(deltaX) <= Math.abs(deltaY); + mInitialDragDirectionXYRatio = (deltaY == 0) ? Float.MAX_VALUE : + Math.abs(deltaX / deltaY); } } } else if (action == MotionEvent.ACTION_CANCEL) { @@ -185,7 +199,7 @@ public class EditorTouchState { mMultiTapStatus = MultiTapStatus.NONE; mMultiTapInSameArea = false; mMovedEnoughForDrag = false; - mIsDragCloseToVertical = false; + mInitialDragDirectionXYRatio = 0.0f; } } @@ -201,4 +215,27 @@ public class EditorTouchState { float distanceSquared = (deltaX * deltaX) + (deltaY * deltaY); return distanceSquared <= maxDistance * maxDistance; } + + /** + * Returns the x/y ratio corresponding to the given angle relative to vertical. Smaller angle + * values (ie, closer to vertical) will result in a smaller x/y ratio. For example: + * <ul> + * <li>if the angle is 45 deg, the ratio is 1 + * <li>if the angle is 30 deg, the ratio is 0.58 (x delta is smaller than y delta) + * <li>if the angle is 60 deg, the ratio is 1.73 (x delta is bigger than y delta) + * </ul> + * If the passed-in value is <= 0, this function returns 0. If the passed-in value is >= 90, + * this function returns {@link Float#MAX_VALUE}. + * + * @see #getInitialDragDirectionXYRatio() + */ + public static float getXYRatio(int angleFromVerticalInDegrees) { + if (angleFromVerticalInDegrees <= 0) { + return 0.0f; + } + if (angleFromVerticalInDegrees >= 90) { + return Float.MAX_VALUE; + } + return (float) Math.tan(Math.toRadians(angleFromVerticalInDegrees)); + } } diff --git a/core/java/android/widget/WidgetFlags.java b/core/java/android/widget/WidgetFlags.java index 832dd5190d37..1a493653d811 100644 --- a/core/java/android/widget/WidgetFlags.java +++ b/core/java/android/widget/WidgetFlags.java @@ -41,6 +41,28 @@ public final class WidgetFlags { public static final boolean ENABLE_CURSOR_DRAG_FROM_ANYWHERE_DEFAULT = true; /** + * Threshold for the direction of a swipe gesture in order for it to be handled as a cursor drag + * rather than a scroll. The direction angle of the swipe gesture must exceed this value in + * order to trigger cursor drag; otherwise, the swipe will be assumed to be a scroll gesture. + * The value units for this flag is degrees and the valid range is [0,90] inclusive. If a value + * < 0 is set, 0 will be used instead; if a value > 90 is set, 90 will be used instead. + */ + public static final String CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL = + "CursorControlFeature__min_angle_from_vertical_to_start_cursor_drag"; + + /** + * The key used in app core settings for the flag + * {@link #CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL}. + */ + public static final String KEY_CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL = + "widget__min_angle_from_vertical_to_start_cursor_drag"; + + /** + * Default value for the flag {@link #CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL}. + */ + public static final int CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL_DEFAULT = 45; + + /** * The flag of finger-to-cursor distance in DP for cursor dragging. * The value unit is DP and the range is {0..100}. If the value is out of range, the legacy * value, which is based on handle size, will be used. diff --git a/core/java/com/android/internal/BrightnessSynchronizer.java b/core/java/com/android/internal/BrightnessSynchronizer.java index 42724bede481..f08d0ef8c052 100644 --- a/core/java/com/android/internal/BrightnessSynchronizer.java +++ b/core/java/com/android/internal/BrightnessSynchronizer.java @@ -83,63 +83,25 @@ public class BrightnessSynchronizer{ /** * Converts between the int brightness system and the float brightness system. */ - public static float brightnessIntToFloat(Context context, int brightnessInt) { - final PowerManager pm = context.getSystemService(PowerManager.class); - final float pmMinBrightness = pm.getBrightnessConstraint( - PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM); - final float pmMaxBrightness = pm.getBrightnessConstraint( - PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM); - final int minBrightnessInt = Math.round(brightnessFloatToIntRange(pmMinBrightness, - PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, - PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON)); - final int maxBrightnessInt = Math.round(brightnessFloatToIntRange(pmMaxBrightness, - PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, - PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON)); - - return brightnessIntToFloat(brightnessInt, minBrightnessInt, maxBrightnessInt, - pmMinBrightness, pmMaxBrightness); - } - - /** - * Converts between the int brightness system and the float brightness system. - */ - public static float brightnessIntToFloat(int brightnessInt, int minInt, int maxInt, - float minFloat, float maxFloat) { + public static float brightnessIntToFloat(int brightnessInt) { if (brightnessInt == PowerManager.BRIGHTNESS_OFF) { return PowerManager.BRIGHTNESS_OFF_FLOAT; } else if (brightnessInt == PowerManager.BRIGHTNESS_INVALID) { return PowerManager.BRIGHTNESS_INVALID_FLOAT; } else { - return MathUtils.constrainedMap(minFloat, maxFloat, (float) minInt, (float) maxInt, - brightnessInt); + final float minFloat = PowerManager.BRIGHTNESS_MIN; + final float maxFloat = PowerManager.BRIGHTNESS_MAX; + final float minInt = PowerManager.BRIGHTNESS_OFF + 1; + final float maxInt = PowerManager.BRIGHTNESS_ON; + return MathUtils.constrainedMap(minFloat, maxFloat, minInt, maxInt, brightnessInt); } } /** * Converts between the float brightness system and the int brightness system. */ - public static int brightnessFloatToInt(Context context, float brightnessFloat) { - return Math.round(brightnessFloatToIntRange(context, brightnessFloat)); - } - - /** - * Converts between the float brightness system and the int brightness system, but returns - * the converted value as a float within the int-system's range. This method helps with - * conversions from one system to the other without losing the floating-point precision. - */ - public static float brightnessFloatToIntRange(Context context, float brightnessFloat) { - final PowerManager pm = context.getSystemService(PowerManager.class); - final float minFloat = pm.getBrightnessConstraint( - PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM); - final float maxFloat = pm.getBrightnessConstraint( - PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM); - final float minInt = brightnessFloatToIntRange(minFloat, - PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, - PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON); - final float maxInt = brightnessFloatToIntRange(maxFloat, - PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, - PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON); - return brightnessFloatToIntRange(brightnessFloat, minFloat, maxFloat, minInt, maxInt); + public static int brightnessFloatToInt(float brightnessFloat) { + return Math.round(brightnessFloatToIntRange(brightnessFloat)); } /** @@ -148,20 +110,24 @@ public class BrightnessSynchronizer{ * Value returned as a float privimite (to preserve precision), but is a value within the * int-system range. */ - private static float brightnessFloatToIntRange(float brightnessFloat, float minFloat, - float maxFloat, float minInt, float maxInt) { + public static float brightnessFloatToIntRange(float brightnessFloat) { if (floatEquals(brightnessFloat, PowerManager.BRIGHTNESS_OFF_FLOAT)) { return PowerManager.BRIGHTNESS_OFF; } else if (Float.isNaN(brightnessFloat)) { return PowerManager.BRIGHTNESS_INVALID; } else { + final float minFloat = PowerManager.BRIGHTNESS_MIN; + final float maxFloat = PowerManager.BRIGHTNESS_MAX; + final float minInt = PowerManager.BRIGHTNESS_OFF + 1; + final float maxInt = PowerManager.BRIGHTNESS_ON; return MathUtils.constrainedMap(minInt, maxInt, minFloat, maxFloat, brightnessFloat); } } private static float getScreenBrightnessFloat(Context context) { return Settings.System.getFloatForUser(context.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS_FLOAT, Float.NaN, UserHandle.USER_CURRENT); + Settings.System.SCREEN_BRIGHTNESS_FLOAT, PowerManager.BRIGHTNESS_INVALID_FLOAT, + UserHandle.USER_CURRENT); } private static int getScreenBrightnessInt(Context context) { @@ -185,10 +151,10 @@ public class BrightnessSynchronizer{ if (topOfQueue != null && topOfQueue.equals(value)) { mWriteHistory.poll(); } else { - if (brightnessFloatToInt(mContext, mPreferredSettingValue) == value) { + if (brightnessFloatToInt(mPreferredSettingValue) == value) { return; } - float newBrightnessFloat = brightnessIntToFloat(mContext, value); + float newBrightnessFloat = brightnessIntToFloat(value); mWriteHistory.offer(newBrightnessFloat); mPreferredSettingValue = newBrightnessFloat; Settings.System.putFloatForUser(mContext.getContentResolver(), @@ -207,7 +173,7 @@ public class BrightnessSynchronizer{ * @param value Brightness setting as float to store in int setting. */ private void updateBrightnessIntFromFloat(float value) { - int newBrightnessInt = brightnessFloatToInt(mContext, value); + int newBrightnessInt = brightnessFloatToInt(value); Object topOfQueue = mWriteHistory.peek(); if (topOfQueue != null && topOfQueue.equals(value)) { mWriteHistory.poll(); diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 5b2835376741..fd90b56426aa 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -785,8 +785,8 @@ public class ChooserActivity extends ResolverActivity implements FrameworkStatsLog.SHARESHEET_STARTED, getReferrerPackageName(), target.getType(), - initialIntents == null ? 0 : initialIntents.length, mCallerChooserTargets == null ? 0 : mCallerChooserTargets.length, + initialIntents == null ? 0 : initialIntents.length, isWorkProfile(), findPreferredContentPreview(getTargetIntent(), getContentResolver()), target.getAction() @@ -3146,7 +3146,9 @@ public class ChooserActivity extends ResolverActivity implements // ends up disabled. That's because at some point the old tab's vertical scrolling is // disabled and the new tab's is enabled. For context, see b/159997845 setVerticalScrollEnabled(true); - mResolverDrawerLayout.scrollNestedScrollableChildBackToTop(); + if (mResolverDrawerLayout != null) { + mResolverDrawerLayout.scrollNestedScrollableChildBackToTop(); + } } @Override diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java index ea3d2de13ce6..eb59f0f59be1 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java @@ -123,10 +123,15 @@ public final class SystemUiDeviceConfigFlags { // Flag related to Privacy Indicators /** - * Whether the Permissions Hub is showing. + * Whether to show the complete ongoing app ops chip. */ public static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_2_enabled"; + /** + * Whether to show app ops chip for just microphone + camera. + */ + public static final String PROPERTY_MIC_CAMERA_ENABLED = "camera_mic_icons_enabled"; + // Flags related to Assistant /** diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java index b3ea118d9c73..2620ba0749a9 100644 --- a/core/java/com/android/internal/os/BatterySipper.java +++ b/core/java/com/android/internal/os/BatterySipper.java @@ -129,6 +129,7 @@ public class BatterySipper implements Comparable<BatterySipper> { public double videoPowerMah; public double wakeLockPowerMah; public double wifiPowerMah; + public double systemServiceCpuPowerMah; // **************** // This list must be kept current with atoms.proto (frameworks/base/cmds/statsd/src/atoms.proto) @@ -242,6 +243,7 @@ public class BatterySipper implements Comparable<BatterySipper> { videoPowerMah += other.videoPowerMah; proportionalSmearMah += other.proportionalSmearMah; totalSmearedPowerMah += other.totalSmearedPowerMah; + systemServiceCpuPowerMah += other.systemServiceCpuPowerMah; } /** @@ -253,7 +255,8 @@ public class BatterySipper implements Comparable<BatterySipper> { public double sumPower() { totalPowerMah = usagePowerMah + wifiPowerMah + gpsPowerMah + cpuPowerMah + sensorPowerMah + mobileRadioPowerMah + wakeLockPowerMah + cameraPowerMah + - flashlightPowerMah + bluetoothPowerMah + audioPowerMah + videoPowerMah; + flashlightPowerMah + bluetoothPowerMah + audioPowerMah + videoPowerMah + + systemServiceCpuPowerMah; totalSmearedPowerMah = totalPowerMah + screenPowerMah + proportionalSmearMah; return totalPowerMah; diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java index b131ab83cc79..3dfa3c3f6906 100644 --- a/core/java/com/android/internal/os/BatteryStatsHelper.java +++ b/core/java/com/android/internal/os/BatteryStatsHelper.java @@ -133,6 +133,7 @@ public class BatteryStatsHelper { private double mMaxDrainedPower; PowerCalculator mCpuPowerCalculator; + SystemServicePowerCalculator mSystemServicePowerCalculator; PowerCalculator mWakelockPowerCalculator; MobileRadioPowerCalculator mMobileRadioPowerCalculator; PowerCalculator mWifiPowerCalculator; @@ -396,6 +397,11 @@ public class BatteryStatsHelper { } mCpuPowerCalculator.reset(); + if (mSystemServicePowerCalculator == null) { + mSystemServicePowerCalculator = new SystemServicePowerCalculator(mPowerProfile, mStats); + } + mSystemServicePowerCalculator.reset(); + if (mMemoryPowerCalculator == null) { mMemoryPowerCalculator = new MemoryPowerCalculator(mPowerProfile); } @@ -588,6 +594,8 @@ public class BatteryStatsHelper { mFlashlightPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType); mMediaPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType); + mSystemServicePowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, + mStatsType); final double totalPower = app.sumPower(); if (DEBUG && totalPower != 0) { diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 58ba16bc61dd..84981515e133 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -144,6 +144,7 @@ public class BatteryStatsImpl extends BatteryStats { private static final boolean DEBUG = false; public static final boolean DEBUG_ENERGY = false; private static final boolean DEBUG_ENERGY_CPU = DEBUG_ENERGY; + private static final boolean DEBUG_BINDER_STATS = true; private static final boolean DEBUG_MEMORY = false; private static final boolean DEBUG_HISTORY = false; private static final boolean USE_OLD_HISTORY = false; // for debugging. @@ -154,7 +155,7 @@ public class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - static final int VERSION = 186 + (USE_OLD_HISTORY ? 1000 : 0); + static final int VERSION = 187 + (USE_OLD_HISTORY ? 1000 : 0); // The maximum number of names wakelocks we will keep track of // per uid; once the limit is reached, we batch the remaining wakelocks @@ -218,10 +219,13 @@ public class BatteryStatsImpl extends BatteryStats { new KernelCpuUidClusterTimeReader(true); @VisibleForTesting protected KernelSingleUidTimeReader mKernelSingleUidTimeReader; + @VisibleForTesting + protected SystemServerCpuThreadReader mSystemServerCpuThreadReader; private final KernelMemoryBandwidthStats mKernelMemoryBandwidthStats = new KernelMemoryBandwidthStats(); private final LongSparseArray<SamplingTimer> mKernelMemoryStats = new LongSparseArray<>(); + public LongSparseArray<SamplingTimer> getKernelMemoryStats() { return mKernelMemoryStats; } @@ -267,6 +271,7 @@ public class BatteryStatsImpl extends BatteryStats { /** Container for Rail Energy Data stats. */ private final RailStats mTmpRailStats = new RailStats(); + /** * Use a queue to delay removing UIDs from {@link KernelCpuUidUserSysTimeReader}, * {@link KernelCpuUidActiveTimeReader}, {@link KernelCpuUidClusterTimeReader}, @@ -1007,6 +1012,16 @@ public class BatteryStatsImpl extends BatteryStats { private long[] mCpuFreqs; + /** + * Times spent by the system server threads grouped by cluster and CPU speed. + */ + private LongSamplingCounter[][] mSystemServerThreadCpuTimesUs; + + /** + * Times spent by the system server threads handling incoming binder requests. + */ + private LongSamplingCounter[][] mBinderThreadCpuTimesUs; + @VisibleForTesting protected PowerProfile mPowerProfile; @@ -6131,10 +6146,77 @@ public class BatteryStatsImpl extends BatteryStats { * the power consumption to the calling app. */ public void noteBinderCallStats(int workSourceUid, long incrementalCallCount, - Collection<BinderCallsStats.CallStat> callStats) { + Collection<BinderCallsStats.CallStat> callStats, int[] binderThreadNativeTids) { synchronized (this) { getUidStatsLocked(workSourceUid).noteBinderCallStatsLocked(incrementalCallCount, callStats); + mSystemServerCpuThreadReader.setBinderThreadNativeTids(binderThreadNativeTids); + } + } + + /** + * Estimates the proportion of system server CPU activity handling incoming binder calls + * that can be attributed to each app + */ + @VisibleForTesting + public void updateSystemServiceCallStats() { + // Start off by computing the average duration of recorded binder calls, + // regardless of which binder or transaction. We will use this as a fallback + // for calls that were not sampled at all. + int totalRecordedCallCount = 0; + long totalRecordedCallTimeMicros = 0; + for (int i = 0; i < mUidStats.size(); i++) { + Uid uid = mUidStats.valueAt(i); + ArraySet<BinderCallStats> binderCallStats = uid.mBinderCallStats; + for (int j = binderCallStats.size() - 1; j >= 0; j--) { + BinderCallStats stats = binderCallStats.valueAt(j); + totalRecordedCallCount += stats.recordedCallCount; + totalRecordedCallTimeMicros += stats.recordedCpuTimeMicros; + } + } + + long totalSystemServiceTimeMicros = 0; + + // For every UID, use recorded durations of sampled binder calls to estimate + // the total time the system server spent handling requests from this UID. + for (int i = 0; i < mUidStats.size(); i++) { + Uid uid = mUidStats.valueAt(i); + + long totalTimeForUid = 0; + int totalCallCountForUid = 0; + ArraySet<BinderCallStats> binderCallStats = uid.mBinderCallStats; + for (int j = binderCallStats.size() - 1; j >= 0; j--) { + BinderCallStats stats = binderCallStats.valueAt(j); + totalCallCountForUid += stats.callCount; + if (stats.recordedCallCount > 0) { + totalTimeForUid += + stats.callCount * stats.recordedCpuTimeMicros / stats.recordedCallCount; + } else if (totalRecordedCallCount > 0) { + totalTimeForUid += + stats.callCount * totalRecordedCallTimeMicros / totalRecordedCallCount; + } + } + + if (totalCallCountForUid < uid.mBinderCallCount && totalRecordedCallCount > 0) { + // Estimate remaining calls, which were not tracked because of binder call + // stats sampling + totalTimeForUid += + (uid.mBinderCallCount - totalCallCountForUid) * totalRecordedCallTimeMicros + / totalRecordedCallCount; + } + + uid.mSystemServiceTimeUs = totalTimeForUid; + totalSystemServiceTimeMicros += totalTimeForUid; + } + + for (int i = 0; i < mUidStats.size(); i++) { + Uid uid = mUidStats.valueAt(i); + if (totalSystemServiceTimeMicros > 0) { + uid.mProportionalSystemServiceUsage = + (double) uid.mSystemServiceTimeUs / totalSystemServiceTimeMicros; + } else { + uid.mProportionalSystemServiceUsage = 0; + } } } @@ -6583,7 +6665,7 @@ public class BatteryStatsImpl extends BatteryStats { /** * Accumulates stats for a specific binder transaction. */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + @VisibleForTesting protected static class BinderCallStats { static final Comparator<BinderCallStats> COMPARATOR = Comparator.comparing(BinderCallStats::getClassName) @@ -6822,6 +6904,16 @@ public class BatteryStatsImpl extends BatteryStats { */ private final ArraySet<BinderCallStats> mBinderCallStats = new ArraySet<>(); + /** + * Estimated total time spent by the system server handling requests from this uid. + */ + private long mSystemServiceTimeUs; + + /** + * Estimated proportion of system server binder call CPU cost for this uid. + */ + private double mProportionalSystemServiceUsage; + public Uid(BatteryStatsImpl bsi, int uid) { mBsi = bsi; mUid = uid; @@ -6899,7 +6991,6 @@ public class BatteryStatsImpl extends BatteryStats { return nullIfAllZeros(mCpuClusterTimesMs, STATS_SINCE_CHARGED); } - @Override public long[] getCpuFreqTimes(int which, int procState) { if (which < 0 || which >= NUM_PROCESS_STATE) { @@ -6934,10 +7025,16 @@ public class BatteryStatsImpl extends BatteryStats { return mBinderCallCount; } + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) public ArraySet<BinderCallStats> getBinderCallStats() { return mBinderCallStats; } + @Override + public double getProportionalSystemServiceUsage() { + return mProportionalSystemServiceUsage; + } + public void addIsolatedUid(int isolatedUid) { if (mChildUids == null) { mChildUids = new IntArray(); @@ -8029,9 +8126,12 @@ public class BatteryStatsImpl extends BatteryStats { mBinderCallCount = 0; mBinderCallStats.clear(); + mProportionalSystemServiceUsage = 0; + mLastStepUserTime = mLastStepSystemTime = 0; mCurStepUserTime = mCurStepSystemTime = 0; + return !active; } @@ -8373,28 +8473,7 @@ public class BatteryStatsImpl extends BatteryStats { mUserCpuTime.writeToParcel(out); mSystemCpuTime.writeToParcel(out); - if (mCpuClusterSpeedTimesUs != null) { - out.writeInt(1); - out.writeInt(mCpuClusterSpeedTimesUs.length); - for (LongSamplingCounter[] cpuSpeeds : mCpuClusterSpeedTimesUs) { - if (cpuSpeeds != null) { - out.writeInt(1); - out.writeInt(cpuSpeeds.length); - for (LongSamplingCounter c : cpuSpeeds) { - if (c != null) { - out.writeInt(1); - c.writeToParcel(out); - } else { - out.writeInt(0); - } - } - } else { - out.writeInt(0); - } - } - } else { - out.writeInt(0); - } + mBsi.writeCpuSpeedCountersToParcel(out, mCpuClusterSpeedTimesUs); LongSamplingCounterArray.writeToParcel(out, mCpuFreqTimeMs); LongSamplingCounterArray.writeToParcel(out, mScreenOffCpuFreqTimeMs); @@ -8432,6 +8511,7 @@ public class BatteryStatsImpl extends BatteryStats { } else { out.writeInt(0); } + out.writeDouble(mProportionalSystemServiceUsage); } void readJobCompletionsFromParcelLocked(Parcel in) { @@ -8692,36 +8772,7 @@ public class BatteryStatsImpl extends BatteryStats { mUserCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in); mSystemCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in); - if (in.readInt() != 0) { - int numCpuClusters = in.readInt(); - if (mBsi.mPowerProfile != null && mBsi.mPowerProfile.getNumCpuClusters() != numCpuClusters) { - throw new ParcelFormatException("Incompatible number of cpu clusters"); - } - - mCpuClusterSpeedTimesUs = new LongSamplingCounter[numCpuClusters][]; - for (int cluster = 0; cluster < numCpuClusters; cluster++) { - if (in.readInt() != 0) { - int numSpeeds = in.readInt(); - if (mBsi.mPowerProfile != null && - mBsi.mPowerProfile.getNumSpeedStepsInCpuCluster(cluster) != numSpeeds) { - throw new ParcelFormatException("Incompatible number of cpu speeds"); - } - - final LongSamplingCounter[] cpuSpeeds = new LongSamplingCounter[numSpeeds]; - mCpuClusterSpeedTimesUs[cluster] = cpuSpeeds; - for (int speed = 0; speed < numSpeeds; speed++) { - if (in.readInt() != 0) { - cpuSpeeds[speed] = new LongSamplingCounter( - mBsi.mOnBatteryTimeBase, in); - } - } - } else { - mCpuClusterSpeedTimesUs[cluster] = null; - } - } - } else { - mCpuClusterSpeedTimesUs = null; - } + mCpuClusterSpeedTimesUs = mBsi.readCpuSpeedCountersFromParcel(in); mCpuFreqTimeMs = LongSamplingCounterArray.readFromParcel(in, mBsi.mOnBatteryTimeBase); mScreenOffCpuFreqTimeMs = LongSamplingCounterArray.readFromParcel( @@ -8762,6 +8813,8 @@ public class BatteryStatsImpl extends BatteryStats { } else { mWifiRadioApWakeupCount = null; } + + mProportionalSystemServiceUsage = in.readDouble(); } public void noteJobsDeferredLocked(int numDeferred, long sinceLast) { @@ -9904,7 +9957,6 @@ public class BatteryStatsImpl extends BatteryStats { UserInfoProvider userInfoProvider) { init(clocks); - if (systemDir == null) { mStatsFile = null; mBatteryStatsHistory = new BatteryStatsHistory(this, mHistoryBuffer); @@ -10046,6 +10098,8 @@ public class BatteryStatsImpl extends BatteryStats { firstCpuOfCluster += mPowerProfile.getNumCoresInCpuCluster(i); } + mSystemServerCpuThreadReader = SystemServerCpuThreadReader.create(); + if (mEstimatedBatteryCapacity == -1) { // Initialize the estimated battery capacity to a known preset one. mEstimatedBatteryCapacity = (int) mPowerProfile.getBatteryCapacity(); @@ -10726,6 +10780,9 @@ public class BatteryStatsImpl extends BatteryStats { mTmpRailStats.reset(); + resetIfNotNull(mSystemServerThreadCpuTimesUs, false); + resetIfNotNull(mBinderThreadCpuTimesUs, false); + mLastHistoryStepDetails = null; mLastStepCpuUserTime = mLastStepCpuSystemTime = 0; mCurStepCpuUserTime = mCurStepCpuSystemTime = 0; @@ -10853,7 +10910,7 @@ public class BatteryStatsImpl extends BatteryStats { return null; } - /** + /** * Distribute WiFi energy info and network traffic to apps. * @param info The energy information from the WiFi controller. */ @@ -11772,6 +11829,7 @@ public class BatteryStatsImpl extends BatteryStats { for (int cluster = mKernelCpuSpeedReaders.length - 1; cluster >= 0; --cluster) { mKernelCpuSpeedReaders[cluster].readDelta(); } + mSystemServerCpuThreadReader.readDelta(); return; } @@ -11791,6 +11849,87 @@ public class BatteryStatsImpl extends BatteryStats { readKernelUidCpuClusterTimesLocked(onBattery); mNumAllUidCpuTimeReads += 2; } + + updateSystemServerThreadStats(); + } + + /** + * Estimates the proportion of the System Server CPU activity (per cluster per speed) + * spent on handling incoming binder calls. + */ + @VisibleForTesting + public void updateSystemServerThreadStats() { + // There are some simplifying assumptions made in this algorithm + // 1) We assume that if a thread handles incoming binder calls, all of its activity + // is spent doing that. Most incoming calls are handled by threads allocated + // by the native layer in the binder thread pool, so this assumption is reasonable. + // 2) We use the aggregate CPU time spent in different threads as a proxy for the CPU + // cost. In reality, in multi-core CPUs, the CPU cost may not be linearly + // affected by additional threads. + + SystemServerCpuThreadReader.SystemServiceCpuThreadTimes systemServiceCpuThreadTimes = + mSystemServerCpuThreadReader.readDelta(); + + int index = 0; + int numCpuClusters = mPowerProfile.getNumCpuClusters(); + if (mSystemServerThreadCpuTimesUs == null) { + mSystemServerThreadCpuTimesUs = new LongSamplingCounter[numCpuClusters][]; + mBinderThreadCpuTimesUs = new LongSamplingCounter[numCpuClusters][]; + } + for (int cluster = 0; cluster < numCpuClusters; cluster++) { + int numSpeeds = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster); + if (mSystemServerThreadCpuTimesUs[cluster] == null) { + mSystemServerThreadCpuTimesUs[cluster] = new LongSamplingCounter[numSpeeds]; + mBinderThreadCpuTimesUs[cluster] = new LongSamplingCounter[numSpeeds]; + for (int speed = 0; speed < numSpeeds; speed++) { + mSystemServerThreadCpuTimesUs[cluster][speed] = + new LongSamplingCounter(mOnBatteryTimeBase); + mBinderThreadCpuTimesUs[cluster][speed] = + new LongSamplingCounter(mOnBatteryTimeBase); + } + } + for (int speed = 0; speed < numSpeeds; speed++) { + mSystemServerThreadCpuTimesUs[cluster][speed].addCountLocked( + systemServiceCpuThreadTimes.threadCpuTimesUs[index]); + mBinderThreadCpuTimesUs[cluster][speed].addCountLocked( + systemServiceCpuThreadTimes.binderThreadCpuTimesUs[index]); + index++; + } + } + if (DEBUG_BINDER_STATS) { + Slog.d(TAG, "System server threads per CPU cluster (binder threads/total threads/%)"); + long binderThreadTime = 0; + long totalThreadTime = 0; + int cpuIndex = 0; + for (int cluster = 0; cluster < numCpuClusters; cluster++) { + StringBuilder sb = new StringBuilder(); + sb.append("cpu").append(cpuIndex).append(": ["); + int numSpeeds = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster); + for (int speed = 0; speed < numSpeeds; speed++) { + if (speed != 0) { + sb.append(", "); + } + long totalCount = mSystemServerThreadCpuTimesUs[cluster][speed].getCountLocked( + 0) / 1000; + long binderCount = mBinderThreadCpuTimesUs[cluster][speed].getCountLocked(0) + / 1000; + sb.append(String.format("%d/%d(%.1f%%)", + binderCount, + totalCount, + totalCount != 0 ? (double) binderCount * 100 / totalCount : 0)); + + totalThreadTime += totalCount; + binderThreadTime += binderCount; + index++; + } + cpuIndex += mPowerProfile.getNumCoresInCpuCluster(cluster); + Slog.d(TAG, sb.toString()); + } + Slog.d(TAG, "Total system server thread time (ms): " + totalThreadTime); + Slog.d(TAG, String.format("Total Binder thread time (ms): %d (%.1f%%)", + binderThreadTime, + binderThreadTime != 0 ? (double) binderThreadTime * 100 / totalThreadTime : 0)); + } } /** @@ -12998,6 +13137,75 @@ public class BatteryStatsImpl extends BatteryStats { } } + + @Override + public long getSystemServiceTimeAtCpuSpeed(int cluster, int step) { + // Estimates the time spent by the system server handling incoming binder requests. + // + // The data that we can get from the kernel is this: + // - CPU duration for a (thread - cluster - CPU speed) combination + // - CPU duration for a (UID - cluster - CPU speed) combination + // + // The configuration we have in the Power Profile is this: + // - Average CPU power for a (cluster - CPU speed) combination. + // + // The model used by BatteryStats can be illustrated with this example: + // + // - Let's say the system server has 10 threads. + // - These 10 threads spent 1000 ms of CPU time in aggregate + // - Of the 10 threads 4 were execute exclusively incoming binder calls. + // - These 4 "binder" threads consumed 600 ms of CPU time in aggregate + // - The real time spent by the system server UID doing all of this is, say, 200 ms. + // + // We will assume that power consumption is proportional to the time spent by the CPU + // across all threads. This is a crude assumption, but we don't have more detailed data. + // Thus, + // binderRealTime = realTime * aggregateBinderThreadTime / aggregateAllThreadTime + // + // In our example, + // binderRealTime = 200 * 600 / 1000 = 120ms + // + // We can then multiply this estimated time by the average power to obtain an estimate + // of the total power consumed by incoming binder calls for the given cluster/speed + // combination. + + if (mSystemServerThreadCpuTimesUs == null) { + return 0; + } + + if (cluster < 0 || cluster >= mSystemServerThreadCpuTimesUs.length) { + return 0; + } + + final LongSamplingCounter[] threadTimesForCluster = mSystemServerThreadCpuTimesUs[cluster]; + + if (step < 0 || step >= threadTimesForCluster.length) { + return 0; + } + + Uid systemUid = mUidStats.get(Process.SYSTEM_UID); + if (systemUid == null) { + return 0; + } + + final long uidTimeAtCpuSpeed = systemUid.getTimeAtCpuSpeed(cluster, step, + BatteryStats.STATS_SINCE_CHARGED); + if (uidTimeAtCpuSpeed == 0) { + return 0; + } + + final long uidThreadTime = + threadTimesForCluster[step].getCountLocked(BatteryStats.STATS_SINCE_CHARGED); + + if (uidThreadTime == 0) { + return 0; + } + + final long binderThreadTime = mBinderThreadCpuTimesUs[cluster][step].getCountLocked( + BatteryStats.STATS_SINCE_CHARGED); + return uidTimeAtCpuSpeed * binderThreadTime / uidThreadTime; + } + /** * Retrieve the statistics object for a particular uid, creating if needed. */ @@ -13327,45 +13535,7 @@ public class BatteryStatsImpl extends BatteryStats { pw.print(uid.getUserCpuTimeUs(STATS_SINCE_CHARGED) / 1000); pw.print(" "); pw.println(uid.getSystemCpuTimeUs(STATS_SINCE_CHARGED) / 1000); } - pw.println("Per UID system service calls:"); - BinderTransactionNameResolver nameResolver = new BinderTransactionNameResolver(); - for (int i = 0; i < size; i++) { - int u = mUidStats.keyAt(i); - Uid uid = mUidStats.get(u); - long binderCallCount = uid.getBinderCallCount(); - if (binderCallCount != 0) { - pw.print(" "); - pw.print(u); - pw.print(" system service calls: "); - pw.print(binderCallCount); - ArraySet<BinderCallStats> binderCallStats = uid.getBinderCallStats(); - if (!binderCallStats.isEmpty()) { - pw.println(", including"); - BinderCallStats[] bcss = new BinderCallStats[binderCallStats.size()]; - binderCallStats.toArray(bcss); - for (BinderCallStats bcs : bcss) { - bcs.ensureMethodName(nameResolver); - } - Arrays.sort(bcss, BinderCallStats.COMPARATOR); - for (BinderCallStats callStats : bcss) { - pw.print(" "); - pw.print(callStats.getClassName()); - pw.print('#'); - pw.print(callStats.getMethodName()); - pw.print(" calls: "); - pw.print(callStats.callCount); - if (callStats.recordedCallCount != 0) { - pw.print(" time: "); - pw.print(callStats.callCount * callStats.recordedCpuTimeMicros - / callStats.recordedCallCount / 1000); - } - pw.println(); - } - } else { - pw.println(); - } - } - } + pw.println("Per UID CPU active time in ms:"); for (int i = 0; i < size; i++) { int u = mUidStats.keyAt(i); @@ -13390,6 +13560,30 @@ public class BatteryStatsImpl extends BatteryStats { pw.print(" "); pw.print(u); pw.print(": "); pw.println(Arrays.toString(times)); } } + + updateSystemServiceCallStats(); + if (mSystemServerThreadCpuTimesUs != null) { + pw.println("Per UID System server binder time in ms:"); + for (int i = 0; i < size; i++) { + int u = mUidStats.keyAt(i); + Uid uid = mUidStats.get(u); + double proportionalSystemServiceUsage = uid.getProportionalSystemServiceUsage(); + + long time = 0; + for (int cluster = 0; cluster < mSystemServerThreadCpuTimesUs.length; cluster++) { + int numSpeeds = mSystemServerThreadCpuTimesUs[cluster].length; + for (int speed = 0; speed < numSpeeds; speed++) { + time += getSystemServiceTimeAtCpuSpeed(cluster, speed) + * proportionalSystemServiceUsage; + } + } + + pw.print(" "); + pw.print(u); + pw.print(": "); + pw.println(time); + } + } } final ReentrantLock mWriteLock = new ReentrantLock(); @@ -14908,6 +15102,9 @@ public class BatteryStatsImpl extends BatteryStats { u.readFromParcelLocked(mOnBatteryTimeBase, mOnBatteryScreenOffTimeBase, in); mUidStats.append(uid, u); } + + mSystemServerThreadCpuTimesUs = readCpuSpeedCountersFromParcel(in); + mBinderThreadCpuTimesUs = readCpuSpeedCountersFromParcel(in); } public void writeToParcel(Parcel out, int flags) { @@ -14923,6 +15120,8 @@ public class BatteryStatsImpl extends BatteryStats { // Need to update with current kernel wake lock counts. pullPendingStateUpdatesLocked(); + updateSystemServiceCallStats(); + // Pull the clock time. This may update the time and make a new history entry // if we had originally pulled a time before the RTC was set. getStartClockTime(); @@ -15105,6 +15304,73 @@ public class BatteryStatsImpl extends BatteryStats { } else { out.writeInt(0); } + writeCpuSpeedCountersToParcel(out, mSystemServerThreadCpuTimesUs); + writeCpuSpeedCountersToParcel(out, mBinderThreadCpuTimesUs); + } + + private void writeCpuSpeedCountersToParcel(Parcel out, LongSamplingCounter[][] counters) { + if (counters == null) { + out.writeInt(0); + return; + } + + out.writeInt(1); + out.writeInt(counters.length); + for (int i = 0; i < counters.length; i++) { + LongSamplingCounter[] counterArray = counters[i]; + if (counterArray == null) { + out.writeInt(0); + continue; + } + + out.writeInt(1); + out.writeInt(counterArray.length); + for (int j = 0; j < counterArray.length; j++) { + LongSamplingCounter c = counterArray[j]; + if (c != null) { + out.writeInt(1); + c.writeToParcel(out); + } else { + out.writeInt(0); + } + } + } + } + + private LongSamplingCounter[][] readCpuSpeedCountersFromParcel(Parcel in) { + LongSamplingCounter[][] counters; + if (in.readInt() != 0) { + int numCpuClusters = in.readInt(); + if (mPowerProfile != null + && mPowerProfile.getNumCpuClusters() != numCpuClusters) { + throw new ParcelFormatException("Incompatible number of cpu clusters"); + } + + counters = new LongSamplingCounter[numCpuClusters][]; + for (int cluster = 0; cluster < numCpuClusters; cluster++) { + if (in.readInt() != 0) { + int numSpeeds = in.readInt(); + if (mPowerProfile != null + && mPowerProfile.getNumSpeedStepsInCpuCluster(cluster) != numSpeeds) { + throw new ParcelFormatException("Incompatible number of cpu speeds"); + } + + final LongSamplingCounter[] cpuSpeeds = new LongSamplingCounter[numSpeeds]; + counters[cluster] = cpuSpeeds; + for (int speed = 0; speed < numSpeeds; speed++) { + if (in.readInt() != 0) { + cpuSpeeds[speed] = new LongSamplingCounter(mOnBatteryTimeBase, in); + } + } + } else { + counters[cluster] = null; + } + } + } else { + counters = null; + } + + return counters; } @UnsupportedAppUsage diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java index e09ef49acd10..201626abd820 100644 --- a/core/java/com/android/internal/os/BinderCallsStats.java +++ b/core/java/com/android/internal/os/BinderCallsStats.java @@ -115,7 +115,8 @@ public class BinderCallsStats implements BinderInternal.Observer { if (uidEntry != null) { ArrayMap<CallStatKey, CallStat> callStats = uidEntry.mCallStats; mCallStatsObserver.noteCallStats(uidEntry.workSourceUid, - uidEntry.incrementalCallCount, callStats.values()); + uidEntry.incrementalCallCount, callStats.values(), + mNativeTids.toArray()); uidEntry.incrementalCallCount = 0; for (int j = callStats.size() - 1; j >= 0; j--) { callStats.valueAt(j).incrementalCallCount = 0; diff --git a/core/java/com/android/internal/os/BinderInternal.java b/core/java/com/android/internal/os/BinderInternal.java index feb5aab94adc..f14d5f2bbbeb 100644 --- a/core/java/com/android/internal/os/BinderInternal.java +++ b/core/java/com/android/internal/os/BinderInternal.java @@ -142,7 +142,8 @@ public class BinderInternal { * Notes incoming binder call stats associated with this work source UID. */ void noteCallStats(int workSourceUid, long incrementalCallCount, - Collection<BinderCallsStats.CallStat> callStats); + Collection<BinderCallsStats.CallStat> callStats, + int[] binderThreadNativeTids); } /** diff --git a/core/java/com/android/internal/os/KernelCpuThreadReader.java b/core/java/com/android/internal/os/KernelCpuThreadReader.java index 34076700cd95..2ba372a47cf3 100644 --- a/core/java/com/android/internal/os/KernelCpuThreadReader.java +++ b/core/java/com/android/internal/os/KernelCpuThreadReader.java @@ -225,19 +225,22 @@ public class KernelCpuThreadReader { /** Set the number of frequency buckets to use */ void setNumBuckets(int numBuckets) { - if (numBuckets < 1) { - Slog.w(TAG, "Number of buckets must be at least 1, but was " + numBuckets); - return; - } // If `numBuckets` hasn't changed since the last set, do nothing if (mFrequenciesKhz != null && mFrequenciesKhz.length == numBuckets) { return; } - mFrequencyBucketCreator = - new FrequencyBucketCreator(mProcTimeInStateReader.getFrequenciesKhz(), numBuckets); - mFrequenciesKhz = - mFrequencyBucketCreator.bucketFrequencies( - mProcTimeInStateReader.getFrequenciesKhz()); + + final long[] frequenciesKhz = mProcTimeInStateReader.getFrequenciesKhz(); + if (numBuckets != 0) { + mFrequencyBucketCreator = new FrequencyBucketCreator(frequenciesKhz, numBuckets); + mFrequenciesKhz = mFrequencyBucketCreator.bucketFrequencies(frequenciesKhz); + } else { + mFrequencyBucketCreator = null; + mFrequenciesKhz = new int[frequenciesKhz.length]; + for (int i = 0; i < frequenciesKhz.length; i++) { + mFrequenciesKhz[i] = (int) frequenciesKhz[i]; + } + } } /** Set the UID predicate for {@link #getProcessCpuUsage} */ @@ -320,8 +323,15 @@ public class KernelCpuThreadReader { if (cpuUsagesLong == null) { return null; } - int[] cpuUsages = mFrequencyBucketCreator.bucketValues(cpuUsagesLong); - + final int[] cpuUsages; + if (mFrequencyBucketCreator != null) { + cpuUsages = mFrequencyBucketCreator.bucketValues(cpuUsagesLong); + } else { + cpuUsages = new int[cpuUsagesLong.length]; + for (int i = 0; i < cpuUsagesLong.length; i++) { + cpuUsages[i] = (int) cpuUsagesLong[i]; + } + } return new ThreadCpuUsage(threadId, threadName, cpuUsages); } diff --git a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java new file mode 100644 index 000000000000..1cdd42c7403e --- /dev/null +++ b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java @@ -0,0 +1,151 @@ +/* + * 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.os; + +import android.os.Process; + +import com.android.internal.annotations.VisibleForTesting; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; + +/** + * Reads /proc/UID/task/TID/time_in_state files to obtain statistics on CPU usage + * by various threads of the System Server. + */ +public class SystemServerCpuThreadReader { + private KernelCpuThreadReader mKernelCpuThreadReader; + private int[] mBinderThreadNativeTids; + + private int[] mThreadCpuTimesUs; + private int[] mBinderThreadCpuTimesUs; + private long[] mLastThreadCpuTimesUs; + private long[] mLastBinderThreadCpuTimesUs; + + /** + * Times (in microseconds) spent by the system server UID. + */ + public static class SystemServiceCpuThreadTimes { + // All threads + public long[] threadCpuTimesUs; + // Just the threads handling incoming binder calls + public long[] binderThreadCpuTimesUs; + } + + private SystemServiceCpuThreadTimes mDeltaCpuThreadTimes = new SystemServiceCpuThreadTimes(); + + /** + * Creates a configured instance of SystemServerCpuThreadReader. + */ + public static SystemServerCpuThreadReader create() { + return new SystemServerCpuThreadReader( + KernelCpuThreadReader.create(0, uid -> uid == Process.myUid())); + } + + @VisibleForTesting + public SystemServerCpuThreadReader(Path procPath, int systemServerUid) throws IOException { + this(new KernelCpuThreadReader(0, uid -> uid == systemServerUid, null, null, + new KernelCpuThreadReader.Injector() { + @Override + public int getUidForPid(int pid) { + return systemServerUid; + } + })); + } + + @VisibleForTesting + public SystemServerCpuThreadReader(KernelCpuThreadReader kernelCpuThreadReader) { + mKernelCpuThreadReader = kernelCpuThreadReader; + } + + public void setBinderThreadNativeTids(int[] nativeTids) { + mBinderThreadNativeTids = nativeTids; + } + + /** + * Returns delta of CPU times, per thread, since the previous call to this method. + */ + public SystemServiceCpuThreadTimes readDelta() { + if (mBinderThreadCpuTimesUs == null) { + int numCpuFrequencies = mKernelCpuThreadReader.getCpuFrequenciesKhz().length; + mThreadCpuTimesUs = new int[numCpuFrequencies]; + mBinderThreadCpuTimesUs = new int[numCpuFrequencies]; + + mLastThreadCpuTimesUs = new long[numCpuFrequencies]; + mLastBinderThreadCpuTimesUs = new long[numCpuFrequencies]; + + mDeltaCpuThreadTimes.threadCpuTimesUs = new long[numCpuFrequencies]; + mDeltaCpuThreadTimes.binderThreadCpuTimesUs = new long[numCpuFrequencies]; + } + + Arrays.fill(mThreadCpuTimesUs, 0); + Arrays.fill(mBinderThreadCpuTimesUs, 0); + + ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsage = + mKernelCpuThreadReader.getProcessCpuUsage(); + int processCpuUsageSize = processCpuUsage.size(); + for (int i = 0; i < processCpuUsageSize; i++) { + KernelCpuThreadReader.ProcessCpuUsage pcu = processCpuUsage.get(i); + ArrayList<KernelCpuThreadReader.ThreadCpuUsage> threadCpuUsages = pcu.threadCpuUsages; + if (threadCpuUsages != null) { + int threadCpuUsagesSize = threadCpuUsages.size(); + for (int j = 0; j < threadCpuUsagesSize; j++) { + KernelCpuThreadReader.ThreadCpuUsage tcu = threadCpuUsages.get(j); + boolean isBinderThread = isBinderThread(tcu.threadId); + + final int len = Math.min(tcu.usageTimesMillis.length, mThreadCpuTimesUs.length); + for (int k = 0; k < len; k++) { + int usageTimeUs = tcu.usageTimesMillis[k] * 1000; + mThreadCpuTimesUs[k] += usageTimeUs; + if (isBinderThread) { + mBinderThreadCpuTimesUs[k] += usageTimeUs; + } + } + } + } + } + + for (int i = 0; i < mThreadCpuTimesUs.length; i++) { + if (mThreadCpuTimesUs[i] < mLastThreadCpuTimesUs[i]) { + mDeltaCpuThreadTimes.threadCpuTimesUs[i] = mThreadCpuTimesUs[i]; + mDeltaCpuThreadTimes.binderThreadCpuTimesUs[i] = mBinderThreadCpuTimesUs[i]; + } else { + mDeltaCpuThreadTimes.threadCpuTimesUs[i] = + mThreadCpuTimesUs[i] - mLastThreadCpuTimesUs[i]; + mDeltaCpuThreadTimes.binderThreadCpuTimesUs[i] = + mBinderThreadCpuTimesUs[i] - mLastBinderThreadCpuTimesUs[i]; + } + mLastThreadCpuTimesUs[i] = mThreadCpuTimesUs[i]; + mLastBinderThreadCpuTimesUs[i] = mBinderThreadCpuTimesUs[i]; + } + + return mDeltaCpuThreadTimes; + } + + private boolean isBinderThread(int threadId) { + if (mBinderThreadNativeTids != null) { + for (int i = 0; i < mBinderThreadNativeTids.length; i++) { + if (threadId == mBinderThreadNativeTids[i]) { + return true; + } + } + } + return false; + } +} diff --git a/core/java/com/android/internal/os/SystemServicePowerCalculator.java b/core/java/com/android/internal/os/SystemServicePowerCalculator.java new file mode 100644 index 000000000000..481b901b3c69 --- /dev/null +++ b/core/java/com/android/internal/os/SystemServicePowerCalculator.java @@ -0,0 +1,91 @@ +/* + * 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.os; + +import android.os.BatteryStats; +import android.util.Log; + +import java.util.Arrays; + +/** + * Estimates the amount of power consumed by the System Server handling requests from + * a given app. + */ +public class SystemServicePowerCalculator extends PowerCalculator { + private static final boolean DEBUG = false; + private static final String TAG = "SystemServicePowerCalc"; + + private static final long MICROSEC_IN_HR = (long) 60 * 60 * 1000 * 1000; + + private final PowerProfile mPowerProfile; + private final BatteryStats mBatteryStats; + // Tracks system server CPU [cluster][speed] power in milliAmp-microseconds + private double[][] mSystemServicePowerMaUs; + + public SystemServicePowerCalculator(PowerProfile powerProfile, BatteryStats batteryStats) { + mPowerProfile = powerProfile; + mBatteryStats = batteryStats; + } + + @Override + public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, + long rawUptimeUs, int statsType) { + final double proportionalUsage = u.getProportionalSystemServiceUsage(); + if (proportionalUsage > 0) { + if (mSystemServicePowerMaUs == null) { + updateSystemServicePower(); + } + + double cpuPowerMaUs = 0; + int numCpuClusters = mPowerProfile.getNumCpuClusters(); + for (int cluster = 0; cluster < numCpuClusters; cluster++) { + final int numSpeeds = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster); + for (int speed = 0; speed < numSpeeds; speed++) { + cpuPowerMaUs += mSystemServicePowerMaUs[cluster][speed] * proportionalUsage; + } + } + + app.systemServiceCpuPowerMah = cpuPowerMaUs / MICROSEC_IN_HR; + } + } + + private void updateSystemServicePower() { + final int numCpuClusters = mPowerProfile.getNumCpuClusters(); + mSystemServicePowerMaUs = new double[numCpuClusters][]; + for (int cluster = 0; cluster < numCpuClusters; cluster++) { + final int numSpeeds = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster); + mSystemServicePowerMaUs[cluster] = new double[numSpeeds]; + for (int speed = 0; speed < numSpeeds; speed++) { + mSystemServicePowerMaUs[cluster][speed] = + mBatteryStats.getSystemServiceTimeAtCpuSpeed(cluster, speed) + * mPowerProfile.getAveragePowerForCpuCore(cluster, speed); + } + } + if (DEBUG) { + Log.d(TAG, "System service power per CPU cluster and frequency"); + for (int cluster = 0; cluster < numCpuClusters; cluster++) { + Log.d(TAG, "Cluster[" + cluster + "]: " + + Arrays.toString(mSystemServicePowerMaUs[cluster])); + } + } + } + + @Override + public void reset() { + mSystemServicePowerMaUs = null; + } +} diff --git a/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java b/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java index ba60fa590792..b42ea7d0b769 100644 --- a/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java +++ b/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java @@ -16,14 +16,8 @@ package com.android.internal.os.logging; -import android.content.ComponentName; -import android.content.Context; -import android.content.pm.PackageManager.NameNotFoundException; -import android.util.Pair; import android.view.WindowManager.LayoutParams; -import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.FrameworkStatsLog; /** @@ -32,81 +26,6 @@ import com.android.internal.util.FrameworkStatsLog; */ public class MetricsLoggerWrapper { - private static final int METRIC_VALUE_DISMISSED_BY_TAP = 0; - private static final int METRIC_VALUE_DISMISSED_BY_DRAG = 1; - - public static void logPictureInPictureDismissByTap(Context context, - Pair<ComponentName, Integer> topActivityInfo) { - MetricsLogger.action(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED, - METRIC_VALUE_DISMISSED_BY_TAP); - FrameworkStatsLog.write(FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED, - getUid(context, topActivityInfo.first, topActivityInfo.second), - topActivityInfo.first.flattenToString(), - FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__DISMISSED); - } - - public static void logPictureInPictureDismissByDrag(Context context, - Pair<ComponentName, Integer> topActivityInfo) { - MetricsLogger.action(context, - MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED, - METRIC_VALUE_DISMISSED_BY_DRAG); - FrameworkStatsLog.write(FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED, - getUid(context, topActivityInfo.first, topActivityInfo.second), - topActivityInfo.first.flattenToString(), - FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__DISMISSED); - } - - public static void logPictureInPictureMinimize(Context context, boolean isMinimized, - Pair<ComponentName, Integer> topActivityInfo) { - MetricsLogger.action(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_MINIMIZED, - isMinimized); - FrameworkStatsLog.write(FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED, - getUid(context, topActivityInfo.first, topActivityInfo.second), - topActivityInfo.first.flattenToString(), - FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__MINIMIZED); - } - - /** - * Get uid from component name and user Id - * @return uid. -1 if not found. - */ - private static int getUid(Context context, ComponentName componentName, int userId) { - int uid = -1; - if (componentName == null) { - return uid; - } - try { - uid = context.getPackageManager().getApplicationInfoAsUser( - componentName.getPackageName(), 0, userId).uid; - } catch (NameNotFoundException e) { - } - return uid; - } - - public static void logPictureInPictureMenuVisible(Context context, boolean menuStateFull) { - MetricsLogger.visibility(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_MENU, - menuStateFull); - } - - public static void logPictureInPictureEnter(Context context, - int uid, String shortComponentName, boolean supportsEnterPipOnTaskSwitch) { - MetricsLogger.action(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_ENTERED, - supportsEnterPipOnTaskSwitch); - FrameworkStatsLog.write(FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED, uid, - shortComponentName, - FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__ENTERED); - } - - public static void logPictureInPictureFullScreen(Context context, int uid, - String shortComponentName) { - MetricsLogger.action(context, - MetricsEvent.ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN); - FrameworkStatsLog.write(FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED, - uid, - shortComponentName, - FrameworkStatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__EXPANDED_TO_FULL_SCREEN); - } - public static void logAppOverlayEnter(int uid, String packageName, boolean changed, int type, boolean usingAlertWindow) { if (changed) { if (type != LayoutParams.TYPE_APPLICATION_OVERLAY) { diff --git a/core/jni/core_jni_helpers.h b/core/jni/core_jni_helpers.h index eeda275b811c..d629e0dae6dd 100644 --- a/core/jni/core_jni_helpers.h +++ b/core/jni/core_jni_helpers.h @@ -104,6 +104,31 @@ static inline std::string getStringField(JNIEnv* env, jobject obj, jfieldID fiel return std::string(defaultValue); } +static inline JNIEnv* GetJNIEnvironment(JavaVM* vm, jint version = JNI_VERSION_1_4) { + JNIEnv* env; + if (vm->GetEnv(reinterpret_cast<void**>(&env), version) != JNI_OK) { + return nullptr; + } + return env; +} + +static inline JNIEnv* GetOrAttachJNIEnvironment(JavaVM* jvm, jint version = JNI_VERSION_1_4) { + JNIEnv* env = GetJNIEnvironment(jvm, version); + if (!env) { + int result = jvm->AttachCurrentThread(&env, nullptr); + LOG_ALWAYS_FATAL_IF(result != JNI_OK, "JVM thread attach failed."); + struct VmDetacher { + VmDetacher(JavaVM* vm) : mVm(vm) {} + ~VmDetacher() { mVm->DetachCurrentThread(); } + + private: + JavaVM* const mVm; + }; + static thread_local VmDetacher detacher(jvm); + } + return env; +} + } // namespace android #endif // CORE_JNI_HELPERS diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto index 5b22e3126eaf..1014fbb73877 100644 --- a/core/proto/android/app/settings_enums.proto +++ b/core/proto/android/app/settings_enums.proto @@ -2698,4 +2698,9 @@ enum PageId { // CATEGORY: SETTINGS // OS: S EMERGENCY_SOS_GESTURE_SETTINGS = 1847; + + // OPEN: Settings > System > Gestures > Double tap + // CATEGORY: SETTINGS + // OS: S + SETTINGS_COLUMBUS = 1848; } diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index cff1218bd81b..56736c0d6780 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -976,7 +976,7 @@ <string name="save_password_remember" msgid="6490888932657708341">"Запомніць"</string> <string name="save_password_never" msgid="6776808375903410659">"Ніколі"</string> <string name="open_permission_deny" msgid="5136793905306987251">"У вас няма дазволу на адкрыццё гэтай старонкі."</string> - <string name="text_copied" msgid="2531420577879738860">"Тэкст скапіяваны ў буфер абмену."</string> + <string name="text_copied" msgid="2531420577879738860">"Тэкст скапіраваны ў буфер абмену."</string> <string name="copied" msgid="4675902854553014676">"Скапіравана"</string> <string name="more_item_label" msgid="7419249600215749115">"Больш"</string> <string name="prepend_shortcut_label" msgid="1743716737502867951">"Меню+"</string> diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index 27ff3f0d4531..84f324356cd1 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -1937,7 +1937,7 @@ <item quantity="one">Una sugerencia de Autocompletar</item> </plurals> <string name="autofill_save_title" msgid="7719802414283739775">"¿Quieres guardar en "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string> - <string name="autofill_save_title_with_type" msgid="3002460014579799605">"¿Quieres guardar <xliff:g id="TYPE">%1$s</xliff:g> en "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string> + <string name="autofill_save_title_with_type" msgid="3002460014579799605">"¿Quieres guardar la <xliff:g id="TYPE">%1$s</xliff:g> en "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string> <string name="autofill_save_title_with_2types" msgid="3783270967447869241">"¿Quieres guardar <xliff:g id="TYPE_0">%1$s</xliff:g> y <xliff:g id="TYPE_1">%2$s</xliff:g> en "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string> <string name="autofill_save_title_with_3types" msgid="6598228952100102578">"¿Quieres guardar <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> y <xliff:g id="TYPE_2">%3$s</xliff:g> en "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string> <string name="autofill_update_title" msgid="3630695947047069136">"¿Quieres actualizar en "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string> diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index b115025fd63d..264a8fcb343d 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -1894,7 +1894,7 @@ <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Այս հավելվածը ստեղծվել է Android-ի ավելի հին տարբերակի համար և կարող է պատշաճ չաշխատել: Ստուգեք թարմացումների առկայությունը կամ դիմեք մշակողին:"</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Ստուգել նոր տարբերակի առկայությունը"</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Դուք ունեք նոր հաղորդագրություններ"</string> - <string name="new_sms_notification_content" msgid="3197949934153460639">"Դիտելու համար բացել SMS հավելվածը"</string> + <string name="new_sms_notification_content" msgid="3197949934153460639">"Դիտելու համար բացել SMS-ների փոխանակման հավելվածը"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Որոշ գործառույթներ կարող են չաշխատել"</string> <string name="profile_encrypted_detail" msgid="5279730442756849055">"Աշխատանքային պրոֆիլը կողպված է"</string> <string name="profile_encrypted_message" msgid="1128512616293157802">"Հպեք՝ այն ապակողպելու համար"</string> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index 688996d87dba..085df65907f8 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -866,12 +866,12 @@ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="3069635524964070596">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali salah menggambar pola pembuka kunci. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya gagal, Anda akan diminta membuka kunci tablet menggunakan proses masuk Google.\n\nCoba lagi dalam <xliff:g id="NUMBER_2">%3$d</xliff:g> detik."</string> <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="6399092175942158529">"Sudah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali Anda salah menggambar pola pembuka kunci. Setelah gagal <xliff:g id="NUMBER_1">%2$d</xliff:g> kali lagi, Anda akan diminta membuka kunci perangkat Android TV menggunakan login Google.\n\n Coba lagi dalam <xliff:g id="NUMBER_2">%3$d</xliff:g> detik."</string> <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="5691623136957148335">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali salah menggambar pola pembuka kunci. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya gagal, Anda akan diminta membuka kunci ponsel menggunakan proses masuk Google.\n\nCoba lagi dalam <xliff:g id="NUMBER_2">%3$d</xliff:g> detik."</string> - <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="7914445759242151426">"Anda telah gagal mencoba membuka gembok tablet sebanyak <xliff:g id="NUMBER_0">%1$d</xliff:g> kali. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> upaya gagal lagi, tablet akan disetel ulang ke setelan default pabrik dan semua data pengguna hilang."</string> + <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="7914445759242151426">"Anda telah gagal mencoba membuka gembok tablet sebanyak <xliff:g id="NUMBER_0">%1$d</xliff:g> kali. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> upaya gagal lagi, tablet akan direset ke setelan default pabrik dan semua data pengguna hilang."</string> <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="4275591249631864248">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali gagal membuka kunci perangkat Android TV. Setelah gagal <xliff:g id="NUMBER_1">%2$d</xliff:g> kali lagi, perangkat Android TV akan direset ke default pabrik dan semua data pengguna akan hilang."</string> - <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="1166532464798446579">"Anda telah gagal mencoba membuka gembok ponsel sebanyak <xliff:g id="NUMBER_0">%1$d</xliff:g> kali. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> upaya gagal lagi, ponsel akan disetel ulang ke setelan default pabrik dan semua data pengguna hilang."</string> - <string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="8682445539263683414">"Anda telah gagal mencoba membuka gembok tablet sebanyak <xliff:g id="NUMBER">%d</xliff:g> kali. Kini tablet akan disetel ulang ke setelan default pabrik."</string> + <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="1166532464798446579">"Anda telah gagal mencoba membuka gembok ponsel sebanyak <xliff:g id="NUMBER_0">%1$d</xliff:g> kali. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> upaya gagal lagi, ponsel akan direset ke setelan default pabrik dan semua data pengguna hilang."</string> + <string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="8682445539263683414">"Anda telah gagal mencoba membuka gembok tablet sebanyak <xliff:g id="NUMBER">%d</xliff:g> kali. Kini tablet akan direset ke setelan default pabrik."</string> <string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="2205435033340091883">"Anda telah <xliff:g id="NUMBER">%d</xliff:g> kali gagal membuka kunci perangkat Android TV. Perangkat Android TV sekarang akan direset ke default pabrik."</string> - <string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="2203704707679895487">"Anda telah gagal mencoba membuka gembok ponsel sebanyak <xliff:g id="NUMBER">%d</xliff:g> kali. Kini ponsel akan disetel ulang ke setelan default pabrik."</string> + <string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="2203704707679895487">"Anda telah gagal mencoba membuka gembok ponsel sebanyak <xliff:g id="NUMBER">%d</xliff:g> kali. Kini ponsel akan direset ke setelan default pabrik."</string> <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6807200118164539589">"Coba lagi dalam <xliff:g id="NUMBER">%d</xliff:g> detik."</string> <string name="lockscreen_forgot_pattern_button_text" msgid="8362442730606839031">"Lupa pola?"</string> <string name="lockscreen_glogin_forgot_pattern" msgid="9218940117797602518">"Pembuka kunci akun"</string> @@ -1438,7 +1438,7 @@ <string name="vpn_lockdown_config" msgid="8331697329868252169">"Ubah setelan jaringan atau VPN"</string> <string name="upload_file" msgid="8651942222301634271">"Pilih file"</string> <string name="no_file_chosen" msgid="4146295695162318057">"Tidak ada file yang dipilih"</string> - <string name="reset" msgid="3865826612628171429">"Setel ulang"</string> + <string name="reset" msgid="3865826612628171429">"Reset"</string> <string name="submit" msgid="862795280643405865">"Kirim"</string> <string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Aplikasi mengemudi sedang berjalan"</string> <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Ketuk untuk keluar dari aplikasi mengemudi."</string> @@ -1608,12 +1608,12 @@ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="23741434207544038">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali salah mengetik PIN. \n\nCoba lagi dalam <xliff:g id="NUMBER_1">%2$d</xliff:g> detik."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="3328686432962224215">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali salah mengetik sandi. \n\nCoba lagi dalam <xliff:g id="NUMBER_1">%2$d</xliff:g> detik."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="7357404233979139075">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali salah menggambar pola pembuka kunci. \n\nCoba lagi dalam <xliff:g id="NUMBER_1">%2$d</xliff:g> detik."</string> - <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="3479940221343361587">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali gagal saat berusaha membuka kunci tablet. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya gagal, tablet akan disetel ulang ke setelan default pabrik dan semua data pengguna akan hilang."</string> + <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="3479940221343361587">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali gagal saat berusaha membuka kunci tablet. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya gagal, tablet akan direset ke setelan default pabrik dan semua data pengguna akan hilang."</string> <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="9064457748587850217">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali gagal membuka kunci perangkat Android TV. Setelah gagal <xliff:g id="NUMBER_1">%2$d</xliff:g> kali lagi, perangkat Android TV akan direset ke default pabrik dan semua data pengguna akan hilang."</string> - <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="5955398963754432548">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali gagal saat berusaha membuka kunci ponsel. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya gagal, ponsel akan disetel ulang ke setelan default pabrik dan semua data pengguna akan hilang."</string> - <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2299099385175083308">"Anda telah <xliff:g id="NUMBER">%d</xliff:g> kali gagal saat berusaha membuka kunci tablet. Kini tablet akan disetel ulang ke setelan default pabrik."</string> + <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="5955398963754432548">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali gagal saat berusaha membuka kunci ponsel. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya gagal, ponsel akan direset ke setelan default pabrik dan semua data pengguna akan hilang."</string> + <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2299099385175083308">"Anda telah <xliff:g id="NUMBER">%d</xliff:g> kali gagal saat berusaha membuka kunci tablet. Kini tablet akan direset ke setelan default pabrik."</string> <string name="kg_failed_attempts_now_wiping" product="tv" msgid="5045460916106267585">"Anda telah <xliff:g id="NUMBER">%d</xliff:g> kali gagal membuka kunci perangkat Android TV. Perangkat Android TV sekarang akan direset ke default pabrik."</string> - <string name="kg_failed_attempts_now_wiping" product="default" msgid="5043730590446071189">"Anda telah <xliff:g id="NUMBER">%d</xliff:g> kali gagal saat berusaha untuk membuka kunci ponsel. Kini ponsel akan disetel ulang ke setelan default pabrik."</string> + <string name="kg_failed_attempts_now_wiping" product="default" msgid="5043730590446071189">"Anda telah <xliff:g id="NUMBER">%d</xliff:g> kali gagal saat berusaha untuk membuka kunci ponsel. Kini ponsel akan direset ke setelan default pabrik."</string> <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="7086799295109717623">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali salah menggambar pola pembuka kunci. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya gagal, Anda akan diminta membuka kunci tablet menggunakan akun email.\n\nCoba lagi dalam <xliff:g id="NUMBER_2">%3$d</xliff:g> detik."</string> <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4670840383567106114">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali salah menggambar pola pembuka kunci. Setelah gagal <xliff:g id="NUMBER_1">%2$d</xliff:g> kali lagi, Anda akan diminta membuka kunci perangkat Android TV menggunakan akun email.\n\n Coba lagi dalam <xliff:g id="NUMBER_2">%3$d</xliff:g> detik."</string> <string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali salah menggambar pola pembuka kunci. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya gagal, Anda akan diminta membuka kunci ponsel menggunakan akun email.\n\nCoba lagi dalam <xliff:g id="NUMBER_2">%3$d</xliff:g> detik."</string> @@ -1907,7 +1907,7 @@ <string name="app_info" msgid="6113278084877079851">"Info aplikasi"</string> <string name="negative_duration" msgid="1938335096972945232">"−<xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="demo_starting_message" msgid="6577581216125805905">"Memulai demo..."</string> - <string name="demo_restarting_message" msgid="1160053183701746766">"Menyetel ulang perangkat..."</string> + <string name="demo_restarting_message" msgid="1160053183701746766">"Mereset perangkat..."</string> <string name="suspended_widget_accessibility" msgid="6331451091851326101">"<xliff:g id="LABEL">%1$s</xliff:g> dinonaktifkan"</string> <string name="conference_call" msgid="5731633152336490471">"Konferensi Telepon"</string> <string name="tooltip_popup_title" msgid="7863719020269945722">"Keterangan alat"</string> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index 400c44a4ef9c..14bfa03bb706 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -1937,7 +1937,7 @@ <item quantity="one">Un suggerimento di Compilazione automatica</item> </plurals> <string name="autofill_save_title" msgid="7719802414283739775">"Vuoi salvare su "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string> - <string name="autofill_save_title_with_type" msgid="3002460014579799605">"Vuoi salvare <xliff:g id="TYPE">%1$s</xliff:g> su "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string> + <string name="autofill_save_title_with_type" msgid="3002460014579799605">"Vuoi salvare la <xliff:g id="TYPE">%1$s</xliff:g> su "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string> <string name="autofill_save_title_with_2types" msgid="3783270967447869241">"Vuoi salvare <xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g> su "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string> <string name="autofill_save_title_with_3types" msgid="6598228952100102578">"Vuoi salvare <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> e <xliff:g id="TYPE_2">%3$s</xliff:g> su "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string> <string name="autofill_update_title" msgid="3630695947047069136">"Vuoi aggiornare su "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index 2c4274532a02..2b6d22681f52 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -1202,7 +1202,7 @@ <string name="aerr_application_repeated" msgid="7804378743218496566">"האפליקציה <xliff:g id="APPLICATION">%1$s</xliff:g> נעצרת שוב ושוב"</string> <string name="aerr_process_repeated" msgid="1153152413537954974">"האפליקציה <xliff:g id="PROCESS">%1$s</xliff:g> נעצרת שוב ושוב"</string> <string name="aerr_restart" msgid="2789618625210505419">"פתח שוב את האפליקציה"</string> - <string name="aerr_report" msgid="3095644466849299308">"משוב"</string> + <string name="aerr_report" msgid="3095644466849299308">"שליחת משוב"</string> <string name="aerr_close" msgid="3398336821267021852">"סגירה"</string> <string name="aerr_mute" msgid="2304972923480211376">"השתק עד הפעלה מחדש של המכשיר"</string> <string name="aerr_wait" msgid="3198677780474548217">"המתן"</string> diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index 9f23a76de1d2..3ef51a7e6dab 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -1165,7 +1165,7 @@ <string name="capital_off" msgid="7443704171014626777">"IZKLOPLJENO"</string> <string name="checked" msgid="9179896827054513119">"potrjeno"</string> <string name="not_checked" msgid="7972320087569023342">"ni potrjeno"</string> - <string name="whichApplication" msgid="5432266899591255759">"Dokončanje dejanja z"</string> + <string name="whichApplication" msgid="5432266899591255759">"Dokončanje dejanja z aplikacijo"</string> <string name="whichApplicationNamed" msgid="6969946041713975681">"Dokončanje dejanja z aplikacijo %1$s"</string> <string name="whichApplicationLabel" msgid="7852182961472531728">"Izvedba dejanja"</string> <string name="whichViewApplication" msgid="5733194231473132945">"Odpiranje z aplikacijo"</string> diff --git a/core/tests/coretests/src/android/app/backup/BackupAgentTest.java b/core/tests/coretests/src/android/app/backup/BackupAgentTest.java new file mode 100644 index 000000000000..ea903f2b61eb --- /dev/null +++ b/core/tests/coretests/src/android/app/backup/BackupAgentTest.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 android.app.backup; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.when; + +import android.app.backup.BackupAgent.IncludeExcludeRules; +import android.app.backup.BackupManager.OperationType; +import android.app.backup.FullBackup.BackupScheme.PathWithRequiredFlags; +import android.os.ParcelFileDescriptor; +import android.os.UserHandle; +import android.platform.test.annotations.Presubmit; +import android.util.ArraySet; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.io.IOException; +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +@Presubmit +@RunWith(AndroidJUnit4.class) +public class BackupAgentTest { + // An arbitrary user. + private static final UserHandle USER_HANDLE = new UserHandle(15); + + @Mock FullBackup.BackupScheme mBackupScheme; + + private BackupAgent mBackupAgent; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testGetIncludeExcludeRules_isMigration_returnsEmptyRules() throws Exception { + mBackupAgent = getAgentForOperationType(OperationType.MIGRATION); + + IncludeExcludeRules rules = mBackupAgent.getIncludeExcludeRules(mBackupScheme); + assertThat(rules).isEqualTo(IncludeExcludeRules.emptyRules()); + } + + @Test + public void testGetIncludeExcludeRules_isNotMigration_returnsRules() throws Exception { + PathWithRequiredFlags path = new PathWithRequiredFlags("path", /* requiredFlags */ 0); + Map<String, Set<PathWithRequiredFlags>> includePaths = Collections.singletonMap("test", + Collections.singleton(path)); + ArraySet<PathWithRequiredFlags> excludePaths = new ArraySet<>(); + excludePaths.add(path); + IncludeExcludeRules expectedRules = new IncludeExcludeRules(includePaths, excludePaths); + + mBackupAgent = getAgentForOperationType(OperationType.BACKUP); + when(mBackupScheme.maybeParseAndGetCanonicalExcludePaths()).thenReturn(excludePaths); + when(mBackupScheme.maybeParseAndGetCanonicalIncludePaths()).thenReturn(includePaths); + + IncludeExcludeRules rules = mBackupAgent.getIncludeExcludeRules(mBackupScheme); + assertThat(rules).isEqualTo(expectedRules); + } + + private BackupAgent getAgentForOperationType(@OperationType int operationType) { + BackupAgent agent = new TestFullBackupAgent(); + agent.onCreate(USER_HANDLE, operationType); + return agent; + } + + private static class TestFullBackupAgent extends BackupAgent { + + @Override + public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, + ParcelFileDescriptor newState) throws IOException { + // Left empty as this is a full backup agent. + } + + @Override + public void onRestore(BackupDataInput data, int appVersionCode, + ParcelFileDescriptor newState) throws IOException { + // Left empty as this is a full backup agent. + } + } +} diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java index f11adef81793..e0d702e98595 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java @@ -494,7 +494,8 @@ public class TransactionParcelTests { @Override public void scheduleCreateBackupAgent(ApplicationInfo applicationInfo, - CompatibilityInfo compatibilityInfo, int i, int userId) throws RemoteException { + CompatibilityInfo compatibilityInfo, int i, int userId, int operatioType) + throws RemoteException { } @Override diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java index 801cd4ddb94e..af02b7bdbd90 100644 --- a/core/tests/coretests/src/android/view/InsetsControllerTest.java +++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java @@ -27,6 +27,7 @@ import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR; import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL; import static android.view.WindowInsets.Type.ime; +import static android.view.WindowInsets.Type.navigationBars; import static android.view.WindowInsets.Type.statusBars; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; @@ -40,8 +41,11 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.notNull; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.content.Context; @@ -124,7 +128,7 @@ public class InsetsControllerTest { } mTestClock = new OffsettableClock(); mTestHandler = new TestHandler(null, mTestClock); - mTestHost = new TestHost(mViewRoot); + mTestHost = spy(new TestHost(mViewRoot)); mController = new InsetsController(mTestHost, (controller, type) -> { if (type == ITYPE_IME) { return new InsetsSourceConsumer(type, controller.getState(), @@ -745,6 +749,99 @@ public class InsetsControllerTest { }); } + @Test + public void testInsetsChangedCount_controlSystemBars() { + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + prepareControls(); + + // Hiding visible system bars should only causes insets change once for each bar. + clearInvocations(mTestHost); + mController.hide(statusBars() | navigationBars()); + verify(mTestHost, times(2)).notifyInsetsChanged(); + + // Sending the same insets state should not cause insets change. + // This simulates the callback from server after hiding system bars. + clearInvocations(mTestHost); + mController.onStateChanged(mController.getState()); + verify(mTestHost, never()).notifyInsetsChanged(); + + // Showing invisible system bars should only causes insets change once for each bar. + clearInvocations(mTestHost); + mController.show(statusBars() | navigationBars()); + verify(mTestHost, times(2)).notifyInsetsChanged(); + + // Sending the same insets state should not cause insets change. + // This simulates the callback from server after showing system bars. + clearInvocations(mTestHost); + mController.onStateChanged(mController.getState()); + verify(mTestHost, never()).notifyInsetsChanged(); + }); + } + + @Test + public void testInsetsChangedCount_controlIme() { + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + prepareControls(); + + // Showing invisible ime should only causes insets change once. + clearInvocations(mTestHost); + mController.show(ime(), true /* fromIme */); + verify(mTestHost, times(1)).notifyInsetsChanged(); + + // Sending the same insets state should not cause insets change. + // This simulates the callback from server after showing ime. + clearInvocations(mTestHost); + mController.onStateChanged(mController.getState()); + verify(mTestHost, never()).notifyInsetsChanged(); + + // Hiding visible ime should only causes insets change once. + clearInvocations(mTestHost); + mController.hide(ime()); + verify(mTestHost, times(1)).notifyInsetsChanged(); + + // Sending the same insets state should not cause insets change. + // This simulates the callback from server after hiding ime. + clearInvocations(mTestHost); + mController.onStateChanged(mController.getState()); + verify(mTestHost, never()).notifyInsetsChanged(); + }); + } + + @Test + public void testInsetsChangedCount_onStateChanged() { + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + final InsetsState localState = mController.getState(); + + // Changing status bar frame should cause notifyInsetsChanged. + clearInvocations(mTestHost); + InsetsState newState = new InsetsState(localState, true /* copySources */); + newState.getSource(ITYPE_STATUS_BAR).getFrame().bottom++; + mController.onStateChanged(newState); + verify(mTestHost, times(1)).notifyInsetsChanged(); + + // Changing status bar visibility should cause notifyInsetsChanged. + clearInvocations(mTestHost); + newState = new InsetsState(localState, true /* copySources */); + newState.getSource(ITYPE_STATUS_BAR).setVisible(false); + mController.onStateChanged(newState); + verify(mTestHost, times(1)).notifyInsetsChanged(); + + // Changing invisible IME frame should not cause notifyInsetsChanged. + clearInvocations(mTestHost); + newState = new InsetsState(localState, true /* copySources */); + newState.getSource(ITYPE_IME).getFrame().top--; + mController.onStateChanged(newState); + verify(mTestHost, never()).notifyInsetsChanged(); + + // Changing IME visibility should cause notifyInsetsChanged. + clearInvocations(mTestHost); + newState = new InsetsState(localState, true /* copySources */); + newState.getSource(ITYPE_IME).setVisible(true); + mController.onStateChanged(newState); + verify(mTestHost, times(1)).notifyInsetsChanged(); + }); + } + private void waitUntilNextFrame() throws Exception { final CountDownLatch latch = new CountDownLatch(1); Choreographer.getMainThreadInstance().postCallback(Choreographer.CALLBACK_COMMIT, @@ -777,7 +874,7 @@ public class InsetsControllerTest { return controls; } - private static class TestHost extends ViewRootInsetsControllerHost { + public static class TestHost extends ViewRootInsetsControllerHost { private InsetsState mModifiedState = new InsetsState(); diff --git a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java index df2946c97d20..c37a34a68549 100644 --- a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java +++ b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java @@ -201,6 +201,38 @@ public class EditorCursorDragTest { } @Test + public void testCursorDrag_diagonal_thresholdConfig() throws Throwable { + TextView tv = mActivity.findViewById(R.id.textview); + Editor editor = tv.getEditorForTesting(); + + StringBuilder sb = new StringBuilder(); + for (int i = 1; i <= 9; i++) { + sb.append("here is some text").append(i).append("\n"); + } + sb.append(Strings.repeat("abcdefghij\n", 400)).append("Last"); + String text = sb.toString(); + onView(withId(R.id.textview)).perform(replaceText(text)); + + int index = text.indexOf("text9"); + onView(withId(R.id.textview)).perform(clickOnTextAtIndex(index)); + onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(index)); + + // Configure the drag direction threshold to require the drag to be exactly horizontal. With + // this set, a swipe that is slightly off horizontal should not trigger cursor drag. + editor.setCursorDragMinAngleFromVertical(90); + int startIdx = text.indexOf("5"); + int endIdx = text.indexOf("here is some text3"); + onView(withId(R.id.textview)).perform(dragOnText(startIdx, endIdx)); + onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(index)); + + // Configure the drag direction threshold to require the drag to be 45 degrees or more from + // vertical. With this set, the same swipe gesture as above should now trigger cursor drag. + editor.setCursorDragMinAngleFromVertical(45); + onView(withId(R.id.textview)).perform(dragOnText(startIdx, endIdx)); + onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(endIdx)); + } + + @Test public void testCursorDrag_vertical_whenTextViewContentsFitOnScreen() throws Throwable { String text = "012345_aaa\n" + "0123456789\n" diff --git a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java index 35fd4bd7dc14..94f43def240d 100644 --- a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java +++ b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java @@ -165,7 +165,7 @@ public class EditorTouchStateTest { long event2Time = 1001; MotionEvent event2 = moveEvent(event1Time, event2Time, 200f, 31f); mTouchState.update(event2, mConfig); - assertDrag(mTouchState, 20f, 30f, 0, 0, false); + assertDrag(mTouchState, 20f, 30f, 0, 0, 180f); // Simulate an ACTION_UP event with a delay that's longer than the double-tap timeout. long event3Time = 5000; @@ -280,7 +280,7 @@ public class EditorTouchStateTest { long event3Time = 1002; MotionEvent event3 = moveEvent(event3Time, event3Time, newX, newY); mTouchState.update(event3, mConfig); - assertDrag(mTouchState, 20f, 30f, 0, 0, false); + assertDrag(mTouchState, 20f, 30f, 0, 0, Float.MAX_VALUE); // Simulate an ACTION_UP event. long event4Time = 1003; @@ -301,15 +301,15 @@ public class EditorTouchStateTest { long event2Time = 1002; MotionEvent event2 = moveEvent(event1Time, event2Time, 100f, 174f); mTouchState.update(event2, mConfig); - assertDrag(mTouchState, 0f, 0f, 0, 0, true); + assertDrag(mTouchState, 0f, 0f, 0, 0, 100f / 174f); // Simulate another ACTION_MOVE event that is horizontal from the original down event. - // The value of `isDragCloseToVertical` should NOT change since it should only reflect the - // initial direction of movement. + // The drag direction ratio should NOT change since it should only reflect the initial + // direction of movement. long event3Time = 1003; MotionEvent event3 = moveEvent(event1Time, event3Time, 200f, 0f); mTouchState.update(event3, mConfig); - assertDrag(mTouchState, 0f, 0f, 0, 0, true); + assertDrag(mTouchState, 0f, 0f, 0, 0, 100f / 174f); // Simulate an ACTION_UP event. long event4Time = 1004; @@ -330,15 +330,15 @@ public class EditorTouchStateTest { long event2Time = 1002; MotionEvent event2 = moveEvent(event1Time, event2Time, 100f, 90f); mTouchState.update(event2, mConfig); - assertDrag(mTouchState, 0f, 0f, 0, 0, false); + assertDrag(mTouchState, 0f, 0f, 0, 0, 100f / 90f); // Simulate another ACTION_MOVE event that is vertical from the original down event. - // The value of `isDragCloseToVertical` should NOT change since it should only reflect the - // initial direction of movement. + // The drag direction ratio should NOT change since it should only reflect the initial + // direction of movement. long event3Time = 1003; MotionEvent event3 = moveEvent(event1Time, event3Time, 0f, 200f); mTouchState.update(event3, mConfig); - assertDrag(mTouchState, 0f, 0f, 0, 0, false); + assertDrag(mTouchState, 0f, 0f, 0, 0, 100f / 90f); // Simulate an ACTION_UP event. long event4Time = 1004; @@ -374,7 +374,7 @@ public class EditorTouchStateTest { long event2Time = 1002; MotionEvent event2 = moveEvent(event2Time, event2Time, 200f, 30f); mTouchState.update(event2, mConfig); - assertDrag(mTouchState, 20f, 30f, 0, 0, false); + assertDrag(mTouchState, 20f, 30f, 0, 0, Float.MAX_VALUE); // Simulate an ACTION_CANCEL event. long event3Time = 1003; @@ -411,6 +411,84 @@ public class EditorTouchStateTest { assertSingleTap(mTouchState, 22f, 33f, 20f, 30f); } + @Test + public void testGetXYRatio() throws Exception { + doTestGetXYRatio(-1, 0.0f); + doTestGetXYRatio(0, 0.0f); + doTestGetXYRatio(30, 0.58f); + doTestGetXYRatio(45, 1.0f); + doTestGetXYRatio(60, 1.73f); + doTestGetXYRatio(90, Float.MAX_VALUE); + doTestGetXYRatio(91, Float.MAX_VALUE); + } + + private void doTestGetXYRatio(int angleFromVerticalInDegrees, float expectedXYRatioRounded) { + float result = EditorTouchState.getXYRatio(angleFromVerticalInDegrees); + String msg = String.format( + "%d deg should give an x/y ratio of %f; actual unrounded result is %f", + angleFromVerticalInDegrees, expectedXYRatioRounded, result); + float roundedResult = (result == 0.0f || result == Float.MAX_VALUE) ? result : + Math.round(result * 100) / 100f; + assertThat(msg, roundedResult, is(expectedXYRatioRounded)); + } + + @Test + public void testUpdate_dragDirection() throws Exception { + // Simulate moving straight up. + doTestDragDirection(100f, 100f, 100f, 50f, 0f); + + // Simulate moving straight down. + doTestDragDirection(100f, 100f, 100f, 150f, 0f); + + // Simulate moving straight left. + doTestDragDirection(100f, 100f, 50f, 100f, Float.MAX_VALUE); + + // Simulate moving straight right. + doTestDragDirection(100f, 100f, 150f, 100f, Float.MAX_VALUE); + + // Simulate moving up and right, < 45 deg from vertical. + doTestDragDirection(100f, 100f, 110f, 50f, 10f / 50f); + + // Simulate moving up and right, > 45 deg from vertical. + doTestDragDirection(100f, 100f, 150f, 90f, 50f / 10f); + + // Simulate moving down and right, < 45 deg from vertical. + doTestDragDirection(100f, 100f, 110f, 150f, 10f / 50f); + + // Simulate moving down and right, > 45 deg from vertical. + doTestDragDirection(100f, 100f, 150f, 110f, 50f / 10f); + + // Simulate moving down and left, < 45 deg from vertical. + doTestDragDirection(100f, 100f, 90f, 150f, 10f / 50f); + + // Simulate moving down and left, > 45 deg from vertical. + doTestDragDirection(100f, 100f, 50f, 110f, 50f / 10f); + + // Simulate moving up and left, < 45 deg from vertical. + doTestDragDirection(100f, 100f, 90f, 50f, 10f / 50f); + + // Simulate moving up and left, > 45 deg from vertical. + doTestDragDirection(100f, 100f, 50f, 90f, 50f / 10f); + } + + private void doTestDragDirection(float downX, float downY, float moveX, float moveY, + float expectedInitialDragDirectionXYRatio) { + EditorTouchState touchState = new EditorTouchState(); + + // Simulate an ACTION_DOWN event. + long event1Time = 1001; + MotionEvent event1 = downEvent(event1Time, event1Time, downX, downY); + touchState.update(event1, mConfig); + + // Simulate an ACTION_MOVE event. + long event2Time = 1002; + MotionEvent event2 = moveEvent(event1Time, event2Time, moveX, moveY); + touchState.update(event2, mConfig); + String msg = String.format("(%.0f,%.0f)=>(%.0f,%.0f)", downX, downY, moveX, moveY); + assertThat(msg, touchState.getInitialDragDirectionXYRatio(), + is(expectedInitialDragDirectionXYRatio)); + } + private static MotionEvent downEvent(long downTime, long eventTime, float x, float y) { return MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_DOWN, x, y, 0); } @@ -441,7 +519,7 @@ public class EditorTouchStateTest { } private static void assertDrag(EditorTouchState touchState, float lastDownX, - float lastDownY, float lastUpX, float lastUpY, boolean isDragCloseToVertical) { + float lastDownY, float lastUpX, float lastUpY, float initialDragDirectionXYRatio) { assertThat(touchState.getLastDownX(), is(lastDownX)); assertThat(touchState.getLastDownY(), is(lastDownY)); assertThat(touchState.getLastUpX(), is(lastUpX)); @@ -451,7 +529,7 @@ public class EditorTouchStateTest { assertThat(touchState.isMultiTap(), is(false)); assertThat(touchState.isMultiTapInSameArea(), is(false)); assertThat(touchState.isMovedEnoughForDrag(), is(true)); - assertThat(touchState.isDragCloseToVertical(), is(isDragCloseToVertical)); + assertThat(touchState.getInitialDragDirectionXYRatio(), is(initialDragDirectionXYRatio)); } private static void assertMultiTap(EditorTouchState touchState, @@ -467,6 +545,5 @@ public class EditorTouchStateTest { || multiTapStatus == MultiTapStatus.TRIPLE_CLICK)); assertThat(touchState.isMultiTapInSameArea(), is(isMultiTapInSameArea)); assertThat(touchState.isMovedEnoughForDrag(), is(false)); - assertThat(touchState.isDragCloseToVertical(), is(false)); } } diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java index 3e67b8bffa63..22c41f3c9622 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java @@ -38,7 +38,8 @@ import java.util.Collection; @SmallTest public class BatteryStatsBinderCallStatsTest extends TestCase { - private static final int TRANSACTION_CODE = 100; + private static final int TRANSACTION_CODE1 = 100; + private static final int TRANSACTION_CODE2 = 101; /** * Test BatteryStatsImpl.Uid.noteBinderCallStats. @@ -53,23 +54,23 @@ public class BatteryStatsBinderCallStatsTest extends TestCase { Collection<BinderCallsStats.CallStat> callStats = new ArrayList<>(); BinderCallsStats.CallStat stat1 = new BinderCallsStats.CallStat(callingUid, - MockBinder.class, TRANSACTION_CODE, true /*screenInteractive */); + MockBinder.class, TRANSACTION_CODE1, true /*screenInteractive */); stat1.incrementalCallCount = 21; stat1.recordedCallCount = 5; stat1.cpuTimeMicros = 1000; callStats.add(stat1); - bi.noteBinderCallStats(workSourceUid, 42, callStats); + bi.noteBinderCallStats(workSourceUid, 42, callStats, null); callStats.clear(); BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(callingUid, - MockBinder.class, TRANSACTION_CODE, true /*screenInteractive */); + MockBinder.class, TRANSACTION_CODE1, true /*screenInteractive */); stat2.incrementalCallCount = 9; stat2.recordedCallCount = 8; stat2.cpuTimeMicros = 500; callStats.add(stat2); - bi.noteBinderCallStats(workSourceUid, 8, callStats); + bi.noteBinderCallStats(workSourceUid, 8, callStats, null); BatteryStatsImpl.Uid uid = bi.getUidStatsLocked(workSourceUid); assertEquals(42 + 8, uid.getBinderCallCount()); @@ -85,9 +86,62 @@ public class BatteryStatsBinderCallStatsTest extends TestCase { assertEquals(500, value.recordedCpuTimeMicros); } + + @Test + public void testProportionalSystemServiceUsage_noStatsForSomeMethods() throws Exception { + final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms + MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks); + + int callingUid = Process.FIRST_APPLICATION_UID + 1; + int workSourceUid1 = Process.FIRST_APPLICATION_UID + 1; + int workSourceUid2 = Process.FIRST_APPLICATION_UID + 2; + int workSourceUid3 = Process.FIRST_APPLICATION_UID + 3; + + Collection<BinderCallsStats.CallStat> callStats = new ArrayList<>(); + BinderCallsStats.CallStat stat1a = new BinderCallsStats.CallStat(callingUid, + MockBinder.class, TRANSACTION_CODE1, true /*screenInteractive */); + stat1a.incrementalCallCount = 10; + stat1a.recordedCallCount = 5; + stat1a.cpuTimeMicros = 1000; + callStats.add(stat1a); + + BinderCallsStats.CallStat stat1b = new BinderCallsStats.CallStat(callingUid, + MockBinder.class, TRANSACTION_CODE2, true /*screenInteractive */); + stat1b.incrementalCallCount = 30; + stat1b.recordedCallCount = 15; + stat1b.cpuTimeMicros = 1500; + callStats.add(stat1b); + + bi.noteBinderCallStats(workSourceUid1, 65, callStats, null); + + // No recorded stats for some methods. Must use the global average. + callStats.clear(); + BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(callingUid, + MockBinder.class, TRANSACTION_CODE1, true /*screenInteractive */); + stat2.incrementalCallCount = 10; + callStats.add(stat2); + + bi.noteBinderCallStats(workSourceUid2, 40, callStats, null); + + // No stats for any calls. Must use the global average + callStats.clear(); + bi.noteBinderCallStats(workSourceUid3, 50, callStats, null); + + bi.updateSystemServiceCallStats(); + + double prop1 = bi.getUidStatsLocked(workSourceUid1).getProportionalSystemServiceUsage(); + double prop2 = bi.getUidStatsLocked(workSourceUid2).getProportionalSystemServiceUsage(); + double prop3 = bi.getUidStatsLocked(workSourceUid3).getProportionalSystemServiceUsage(); + + assertEquals(0.419, prop1, 0.01); + assertEquals(0.258, prop2, 0.01); + assertEquals(0.323, prop3, 0.01); + assertEquals(1.000, prop1 + prop2 + prop3, 0.01); + } + private static class MockBinder extends Binder { public static String getDefaultTransactionName(int txCode) { - return txCode == TRANSACTION_CODE ? "testMethod" : "unknown"; + return txCode == TRANSACTION_CODE1 ? "testMethod" : "unknown"; } } } diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java index a5117a3e7cc3..188ba9e0ca0e 100644 --- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java @@ -768,8 +768,8 @@ public class BinderCallsStatsTest { final ArrayList<BinderCallsStats.CallStat> callStatsList = new ArrayList<>(); bcs.setCallStatsObserver( - (workSourceUid, incrementalCallCount, callStats) -> callStatsList.addAll( - callStats)); + (workSourceUid, incrementalCallCount, callStats, binderThreadIds) -> + callStatsList.addAll(callStats)); Binder binder = new Binder(); diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java index bc0e0a496d80..75dd7fb82f30 100644 --- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java +++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java @@ -133,6 +133,12 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { return this; } + public MockBatteryStatsImpl setSystemServerCpuThreadReader( + SystemServerCpuThreadReader systemServerCpuThreadReader) { + mSystemServerCpuThreadReader = systemServerCpuThreadReader; + return this; + } + public MockBatteryStatsImpl setUserInfoProvider(UserInfoProvider provider) { mUserInfoProvider = provider; return this; diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java new file mode 100644 index 000000000000..10ba54865dbe --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java @@ -0,0 +1,136 @@ +/* + * 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.os; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertTrue; + +import android.content.Context; +import android.os.FileUtils; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class SystemServerCpuThreadReaderTest { + private File mProcDirectory; + + @Before + public void setUp() { + Context context = InstrumentationRegistry.getContext(); + mProcDirectory = context.getDir("proc", Context.MODE_PRIVATE); + } + + @After + public void tearDown() throws Exception { + FileUtils.deleteContents(mProcDirectory); + } + + @Test + public void testReaderDelta_firstTime() throws IOException { + int uid = 42; + setupDirectory( + mProcDirectory.toPath().resolve(String.valueOf(uid)), + new int[]{42, 1, 2, 3}, + new int[]{1000, 2000}, + // Units are 10ms aka 10000Us + new int[][]{{100, 200}, {0, 200}, {0, 300}, {0, 400}}); + + SystemServerCpuThreadReader reader = new SystemServerCpuThreadReader( + mProcDirectory.toPath(), uid); + reader.setBinderThreadNativeTids(new int[]{1, 3}); + SystemServerCpuThreadReader.SystemServiceCpuThreadTimes systemServiceCpuThreadTimes = + reader.readDelta(); + assertArrayEquals(new long[]{100 * 10000, 1100 * 10000}, + systemServiceCpuThreadTimes.threadCpuTimesUs); + assertArrayEquals(new long[]{0, 600 * 10000}, + systemServiceCpuThreadTimes.binderThreadCpuTimesUs); + } + + @Test + public void testReaderDelta_nextTime() throws IOException { + int uid = 42; + setupDirectory( + mProcDirectory.toPath().resolve(String.valueOf(uid)), + new int[]{42, 1, 2, 3}, + new int[]{1000, 2000}, + new int[][]{{100, 200}, {0, 200}, {0, 300}, {0, 400}}); + + SystemServerCpuThreadReader reader = new SystemServerCpuThreadReader( + mProcDirectory.toPath(), uid); + reader.setBinderThreadNativeTids(new int[]{1, 3}); + + // First time, populate "last" snapshot + reader.readDelta(); + + FileUtils.deleteContents(mProcDirectory); + setupDirectory( + mProcDirectory.toPath().resolve(String.valueOf(uid)), + new int[]{42, 1, 2, 3}, + new int[]{1000, 2000}, + new int[][]{{500, 600}, {700, 800}, {900, 1000}, {1100, 1200}}); + + // Second time, get the actual delta + SystemServerCpuThreadReader.SystemServiceCpuThreadTimes systemServiceCpuThreadTimes = + reader.readDelta(); + + assertArrayEquals(new long[]{3100 * 10000, 2500 * 10000}, + systemServiceCpuThreadTimes.threadCpuTimesUs); + assertArrayEquals(new long[]{1800 * 10000, 1400 * 10000}, + systemServiceCpuThreadTimes.binderThreadCpuTimesUs); + } + + private void setupDirectory(Path processPath, int[] threadIds, int[] cpuFrequencies, + int[][] cpuTimes) throws IOException { + // Make /proc/$PID + assertTrue(processPath.toFile().mkdirs()); + + // Make /proc/$PID/task + final Path selfThreadsPath = processPath.resolve("task"); + assertTrue(selfThreadsPath.toFile().mkdirs()); + + // Make thread directories in reverse order, as they are read in order of creation by + // CpuThreadProcReader + for (int i = 0; i < threadIds.length; i++) { + // Make /proc/$PID/task/$TID + final Path threadPath = selfThreadsPath.resolve(String.valueOf(threadIds[i])); + assertTrue(threadPath.toFile().mkdirs()); + + // Make /proc/$PID/task/$TID/time_in_state + final OutputStream timeInStateStream = + Files.newOutputStream(threadPath.resolve("time_in_state")); + for (int j = 0; j < cpuFrequencies.length; j++) { + final String line = cpuFrequencies[j] + " " + cpuTimes[i][j] + "\n"; + timeInStateStream.write(line.getBytes()); + } + timeInStateStream.close(); + } + } +} diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java new file mode 100644 index 000000000000..ac5443e1c7ce --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java @@ -0,0 +1,173 @@ +/* + * 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.os; + +import static org.junit.Assert.assertEquals; + +import android.content.Context; +import android.os.BatteryStats; +import android.os.Binder; +import android.os.Process; + +import androidx.annotation.Nullable; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class SystemServicePowerCalculatorTest { + + private PowerProfile mProfile; + private MockBatteryStatsImpl mMockBatteryStats; + private MockKernelCpuUidFreqTimeReader mMockCpuUidFreqTimeReader; + private MockServerCpuThreadReader mMockServerCpuThreadReader; + private SystemServicePowerCalculator mSystemServicePowerCalculator; + + @Before + public void setUp() throws IOException { + Context context = InstrumentationRegistry.getContext(); + mProfile = new PowerProfile(context, true /* forTest */); + mMockServerCpuThreadReader = new MockServerCpuThreadReader(); + mMockCpuUidFreqTimeReader = new MockKernelCpuUidFreqTimeReader(); + mMockBatteryStats = new MockBatteryStatsImpl(new MockClocks()) + .setPowerProfile(mProfile) + .setSystemServerCpuThreadReader(mMockServerCpuThreadReader) + .setKernelCpuUidFreqTimeReader(mMockCpuUidFreqTimeReader) + .setUserInfoProvider(new MockUserInfoProvider()); + mMockBatteryStats.getOnBatteryTimeBase().setRunning(true, 0, 0); + mSystemServicePowerCalculator = + new SystemServicePowerCalculator(mProfile, mMockBatteryStats); + } + + @Test + public void testCalculateApp() { + // Test Power Profile has two CPU clusters with 3 and 4 speeds, thus 7 freq times total + mMockServerCpuThreadReader.setThreadTimes( + new long[]{30000, 40000, 50000, 60000, 70000, 80000, 90000}, + new long[]{20000, 30000, 40000, 50000, 60000, 70000, 80000}); + + mMockCpuUidFreqTimeReader.setSystemServerCpuTimes( + new long[]{10000, 20000, 30000, 40000, 50000, 60000, 70000} + ); + + mMockBatteryStats.readKernelUidCpuFreqTimesLocked(null, true, false); + + int workSourceUid1 = 100; + int workSourceUid2 = 200; + int transactionCode = 42; + + Collection<BinderCallsStats.CallStat> callStats = new ArrayList<>(); + BinderCallsStats.CallStat stat1 = new BinderCallsStats.CallStat(workSourceUid1, + Binder.class, transactionCode, true /*screenInteractive */); + stat1.incrementalCallCount = 100; + stat1.recordedCallCount = 100; + stat1.cpuTimeMicros = 1000000; + callStats.add(stat1); + + mMockBatteryStats.noteBinderCallStats(workSourceUid1, 100, callStats, null); + + callStats.clear(); + BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(workSourceUid2, + Binder.class, transactionCode, true /*screenInteractive */); + stat2.incrementalCallCount = 100; + stat2.recordedCallCount = 100; + stat2.cpuTimeMicros = 9000000; + callStats.add(stat2); + + mMockBatteryStats.noteBinderCallStats(workSourceUid2, 100, callStats, null); + + mMockBatteryStats.updateSystemServiceCallStats(); + mMockBatteryStats.updateSystemServerThreadStats(); + + BatterySipper app1 = new BatterySipper(BatterySipper.DrainType.APP, + mMockBatteryStats.getUidStatsLocked(workSourceUid1), 0); + mSystemServicePowerCalculator.calculateApp(app1, app1.uidObj, 0, 0, + BatteryStats.STATS_SINCE_CHARGED); + assertEquals(0.27162, app1.systemServiceCpuPowerMah, 0.00001); + + BatterySipper app2 = new BatterySipper(BatterySipper.DrainType.APP, + mMockBatteryStats.getUidStatsLocked(workSourceUid2), 0); + mSystemServicePowerCalculator.calculateApp(app2, app2.uidObj, 0, 0, + BatteryStats.STATS_SINCE_CHARGED); + assertEquals(2.44458, app2.systemServiceCpuPowerMah, 0.00001); + } + + private static class MockKernelCpuUidFreqTimeReader extends + KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader { + private long[] mSystemServerCpuTimes; + + MockKernelCpuUidFreqTimeReader() { + super(/*throttle */false); + } + + void setSystemServerCpuTimes(long[] systemServerCpuTimes) { + mSystemServerCpuTimes = systemServerCpuTimes; + } + + @Override + public boolean perClusterTimesAvailable() { + return true; + } + + @Override + public void readDelta(@Nullable Callback<long[]> cb) { + if (cb != null) { + cb.onUidCpuTime(Process.SYSTEM_UID, mSystemServerCpuTimes); + } + } + } + + private static class MockServerCpuThreadReader extends SystemServerCpuThreadReader { + private SystemServiceCpuThreadTimes mThreadTimes = new SystemServiceCpuThreadTimes(); + + MockServerCpuThreadReader() { + super(null); + } + + public void setThreadTimes(long[] threadCpuTimesUs, long[] binderThreadCpuTimesUs) { + mThreadTimes.threadCpuTimesUs = threadCpuTimesUs; + mThreadTimes.binderThreadCpuTimesUs = binderThreadCpuTimesUs; + } + + @Override + public SystemServiceCpuThreadTimes readDelta() { + return mThreadTimes; + } + } + + private static class MockUserInfoProvider extends BatteryStatsImpl.UserInfoProvider { + @Nullable + @Override + protected int[] getUserIds() { + return new int[0]; + } + + @Override + public boolean exists(int userId) { + return true; + } + } +} diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index f834ce798a27..602ccfeca2f2 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -143,6 +143,7 @@ applications that come with the platform <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" /> <permission name="android.permission.PACKAGE_USAGE_STATS" /> <permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" /> + <permission name="android.permission.MODIFY_AUDIO_ROUTING" /> <!-- For permission hub 2 debugging only --> <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/> @@ -432,18 +433,6 @@ applications that come with the platform <permission name="android.permission.CAPTURE_AUDIO_OUTPUT" /> <!-- Permissions required for CTS test - AdbManagerTest --> <permission name="android.permission.MANAGE_DEBUGGING" /> - <!-- Permissions required for ATS tests - AtsCarHostTestCases, AtsCarDeviceApp --> - <permission name="android.car.permission.CAR_DRIVING_STATE" /> - <!-- Permissions required for ATS tests - AtsDeviceInfo, AtsAudioDeviceTestCases --> - <permission name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME" /> - <!-- Permissions required for ATS tests - AtsDeviceInfo --> - <permission name="android.car.permission.CAR_DIAGNOSTICS" /> - <!-- Permissions required for ATS tests - AtsDeviceInfo --> - <permission name="android.car.permission.CLEAR_CAR_DIAGNOSTICS" /> - <!-- Permissions required for ATS tests - AtsCarHostTestCases --> - <permission name="android.car.permission.CONTROL_APP_BLOCKING" /> - <!-- Permissions required for ATS tests - AtsCarHostTestCases --> - <permission name="android.car.permission.CAR_UX_RESTRICTIONS_CONFIGURATION" /> </privapp-permissions> <privapp-permissions package="com.android.statementservice"> diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl index 5711f98e3b75..bd2d4af54c83 100644 --- a/data/keyboards/Generic.kl +++ b/data/keyboards/Generic.kl @@ -345,10 +345,10 @@ key 377 TV # key 395 "KEY_LIST" # key 396 "KEY_MEMO" key 397 CALENDAR -# key 398 "KEY_RED" -# key 399 "KEY_GREEN" -# key 400 "KEY_YELLOW" -# key 401 "KEY_BLUE" +key 398 PROG_RED +key 399 PROG_GREEN +key 400 PROG_YELLOW +key 401 PROG_BLUE key 402 CHANNEL_UP key 403 CHANNEL_DOWN # key 404 "KEY_FIRST" diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/UidChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/UidChecker.java index 3ffb8ea40789..a2ee065cd1cc 100644 --- a/errorprone/java/com/google/errorprone/bugpatterns/android/UidChecker.java +++ b/errorprone/java/com/google/errorprone/bugpatterns/android/UidChecker.java @@ -23,12 +23,15 @@ import com.google.errorprone.BugPattern; import com.google.errorprone.VisitorState; import com.google.errorprone.bugpatterns.BugChecker; import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher; +import com.google.errorprone.bugpatterns.BugChecker.NewClassTreeMatcher; import com.google.errorprone.matchers.Description; import com.google.errorprone.util.ASTHelpers; import com.sun.source.tree.ExpressionTree; import com.sun.source.tree.IdentifierTree; import com.sun.source.tree.MemberSelectTree; import com.sun.source.tree.MethodInvocationTree; +import com.sun.source.tree.NewClassTree; +import com.sun.source.tree.Tree; import com.sun.tools.javac.code.Symbol.VarSymbol; import java.util.List; @@ -44,11 +47,20 @@ import java.util.regex.Pattern; name = "AndroidFrameworkUid", summary = "Verifies that PID, UID and user ID arguments aren't crossed", severity = WARNING) -public final class UidChecker extends BugChecker implements MethodInvocationTreeMatcher { +public final class UidChecker extends BugChecker implements MethodInvocationTreeMatcher, + NewClassTreeMatcher { @Override public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) { - final List<VarSymbol> vars = ASTHelpers.getSymbol(tree).params(); - final List<? extends ExpressionTree> args = tree.getArguments(); + return matchArguments(ASTHelpers.getSymbol(tree).params(), tree.getArguments(), tree); + } + + @Override + public Description matchNewClass(NewClassTree tree, VisitorState state) { + return matchArguments(ASTHelpers.getSymbol(tree).params(), tree.getArguments(), tree); + } + + private Description matchArguments(List<VarSymbol> vars, + List<? extends ExpressionTree> args, Tree tree) { for (int i = 0; i < Math.min(vars.size(), args.size()); i++) { final Flavor varFlavor = getFlavor(vars.get(i)); final Flavor argFlavor = getFlavor(args.get(i)); diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UidCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UidCheckerTest.java index 74da94731092..75341fd1f317 100644 --- a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UidCheckerTest.java +++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UidCheckerTest.java @@ -34,7 +34,7 @@ public class UidCheckerTest { } @Test - public void testTypical() { + public void testTypical_methodInvocation() { compilationHelper .addSourceLines("Example.java", "public abstract class Example {", @@ -57,7 +57,30 @@ public class UidCheckerTest { } @Test - public void testCallingUid() { + public void testTypical_newClass() { + compilationHelper + .addSourceLines("Example.java", + "public abstract class Example {", + " class Bar { Bar(int pid, int uid, int userId) {} }", + " abstract int getUserId();", + " void foo(int pid, int uid, int userId, int unrelated) {", + " new Bar(0, 0, 0);", + " new Bar(pid, uid, userId);", + " new Bar(pid, uid, getUserId());", + " new Bar(unrelated, unrelated, unrelated);", + " // BUG: Diagnostic contains:", + " new Bar(uid, pid, userId);", + " // BUG: Diagnostic contains:", + " new Bar(pid, userId, uid);", + " // BUG: Diagnostic contains:", + " new Bar(getUserId(), 0, 0);", + " }", + "}") + .doTest(); + } + + @Test + public void testCallingUid_methodInvocation() { compilationHelper .addSourceFile("/android/os/Binder.java") .addSourceFile("/android/os/UserHandle.java") @@ -98,4 +121,48 @@ public class UidCheckerTest { "}") .doTest(); } + + + @Test + public void testCallingUid_newClass() { + compilationHelper + .addSourceFile("/android/os/Binder.java") + .addSourceFile("/android/os/UserHandle.java") + .addSourceLines("Example.java", + "import android.os.Binder;", + "import android.os.UserHandle;", + "public abstract class Example {", + " int callingUserId;", + " int callingUid;", + " class Foo { Foo(int callingUserId) {} }", + " class Bar { Bar(int callingUid) {} }", + " void doUserId(int callingUserId) {", + " new Foo(UserHandle.getUserId(Binder.getCallingUid()));", + " new Foo(this.callingUserId);", + " new Foo(callingUserId);", + " // BUG: Diagnostic contains:", + " new Foo(Binder.getCallingUid());", + " // BUG: Diagnostic contains:", + " new Foo(this.callingUid);", + " // BUG: Diagnostic contains:", + " new Foo(callingUid);", + " }", + " void doUid(int callingUserId) {", + " new Bar(Binder.getCallingUid());", + " new Bar(this.callingUid);", + " new Bar(callingUid);", + " // BUG: Diagnostic contains:", + " new Bar(UserHandle.getUserId(Binder.getCallingUid()));", + " // BUG: Diagnostic contains:", + " new Bar(this.callingUserId);", + " // BUG: Diagnostic contains:", + " new Bar(callingUserId);", + " }", + " void doInner() {", + " // BUG: Diagnostic contains:", + " new Foo(UserHandle.getUserId(callingUserId));", + " }", + "}") + .doTest(); + } } diff --git a/framework-jarjar-rules.txt b/framework-jarjar-rules.txt index d8af726ffa72..70dedb8179b0 100644 --- a/framework-jarjar-rules.txt +++ b/framework-jarjar-rules.txt @@ -1,2 +1,6 @@ rule android.hidl.** android.internal.hidl.@1 rule android.net.wifi.WifiAnnotations* android.internal.wifi.WifiAnnotations@1 + +# Hide media mainline module implementation classes to avoid collisions with +# app-bundled ExoPlayer classes. +rule com.google.android.exoplayer2.** android.media.internal.exo.@1 diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java index b3103fd516dd..0452933328e2 100644 --- a/graphics/java/android/graphics/HardwareRenderer.java +++ b/graphics/java/android/graphics/HardwareRenderer.java @@ -634,6 +634,19 @@ public class HardwareRenderer { } /** + * Sets the colormode with the desired SDR white point. + * + * The white point only applies if the color mode is an HDR mode + * + * @hide + */ + public void setColorMode(@ActivityInfo.ColorMode int colorMode, float whitePoint) { + nSetSdrWhitePoint(mNativeProxy, whitePoint); + mColorMode = colorMode; + nSetColorMode(mNativeProxy, colorMode); + } + + /** * Blocks until all previously queued work has completed. * * TODO: Only used for draw finished listeners, but the FrameCompleteCallback does that @@ -1227,6 +1240,8 @@ public class HardwareRenderer { private static native void nSetColorMode(long nativeProxy, int colorMode); + private static native void nSetSdrWhitePoint(long nativeProxy, float whitePoint); + private static native int nSyncAndDrawFrame(long nativeProxy, long[] frameInfo, int size); private static native void nDestroy(long nativeProxy, long rootRenderNode); diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index 446e81e65bb8..ba44d056dda3 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -78,6 +78,7 @@ bool Properties::isolatedProcess = false; int Properties::contextPriority = 0; int Properties::defaultRenderAhead = -1; +float Properties::defaultSdrWhitePoint = 200.f; bool Properties::load() { bool prevDebugLayersUpdates = debugLayersUpdates; diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index c8f6b3b7ff99..85a0f4aa7809 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -249,6 +249,8 @@ public: static int defaultRenderAhead; + static float defaultSdrWhitePoint; + private: static ProfileType sProfileType; static bool sDisableProfileBars; diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp index 7d6875f59d17..fc594da19708 100644 --- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp +++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp @@ -223,6 +223,11 @@ static void android_view_ThreadedRenderer_setColorMode(JNIEnv* env, jobject claz proxy->setColorMode(static_cast<ColorMode>(colorMode)); } +static void android_view_ThreadedRenderer_setSdrWhitePoint(JNIEnv* env, jobject clazz, + jlong proxyPtr, jfloat sdrWhitePoint) { + Properties::defaultSdrWhitePoint = sdrWhitePoint; +} + static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz, jlong proxyPtr, jlongArray frameInfo, jint frameInfoSize) { LOG_ALWAYS_FATAL_IF(frameInfoSize != UI_THREAD_FRAME_INFO_SIZE, @@ -671,6 +676,7 @@ static const JNINativeMethod gMethods[] = { {"nSetLightGeometry", "(JFFFF)V", (void*)android_view_ThreadedRenderer_setLightGeometry}, {"nSetOpaque", "(JZ)V", (void*)android_view_ThreadedRenderer_setOpaque}, {"nSetColorMode", "(JI)V", (void*)android_view_ThreadedRenderer_setColorMode}, + {"nSetSdrWhitePoint", "(JF)V", (void*)android_view_ThreadedRenderer_setSdrWhitePoint}, {"nSyncAndDrawFrame", "(J[JI)I", (void*)android_view_ThreadedRenderer_syncAndDrawFrame}, {"nDestroy", "(JJ)V", (void*)android_view_ThreadedRenderer_destroy}, {"nRegisterAnimatingRenderNode", "(JJ)V", diff --git a/libs/hwui/service/GraphicsStatsService.cpp b/libs/hwui/service/GraphicsStatsService.cpp index 644d5fbd5bf9..e4198017aee0 100644 --- a/libs/hwui/service/GraphicsStatsService.cpp +++ b/libs/hwui/service/GraphicsStatsService.cpp @@ -559,6 +559,7 @@ void GraphicsStatsService::finishDumpInMemory(Dump* dump, AStatsEventList* data, AStatsEvent_writeBool(event, !lastFullDay); AStatsEvent_build(event); } + delete dump; } diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp index eff34a83af1b..87512f0354c8 100644 --- a/libs/hwui/utils/Color.cpp +++ b/libs/hwui/utils/Color.cpp @@ -26,6 +26,7 @@ #include <algorithm> #include <cmath> +#include <Properties.h> namespace android { namespace uirenderer { @@ -344,13 +345,9 @@ SkColor LabToSRGB(const Lab& lab, SkAlpha alpha) { static_cast<uint8_t>(rgb.b * 255)); } -// Note that SkColorSpace doesn't have the notion of an unspecified SDR white -// level. -static constexpr float kDefaultSDRWhiteLevel = 150.f; - skcms_TransferFunction GetPQSkTransferFunction(float sdr_white_level) { if (sdr_white_level <= 0.f) { - sdr_white_level = kDefaultSDRWhiteLevel; + sdr_white_level = Properties::defaultSdrWhitePoint; } // The generic PQ transfer function produces normalized luminance values i.e. // the range 0-1 represents 0-10000 nits for the reference display, but we diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 5252cd041199..dca35012cbdd 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -16,6 +16,9 @@ cc_library_shared { name: "libinputservice", srcs: [ "PointerController.cpp", + "PointerControllerContext.cpp", + "MouseCursorController.cpp", + "TouchSpotController.cpp", "SpriteController.cpp", "SpriteIcon.cpp", ], diff --git a/libs/input/MouseCursorController.cpp b/libs/input/MouseCursorController.cpp new file mode 100644 index 000000000000..80b555be97dd --- /dev/null +++ b/libs/input/MouseCursorController.cpp @@ -0,0 +1,460 @@ +/* + * 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. + */ + +#define LOG_TAG "MouseCursorController" +//#define LOG_NDEBUG 0 + +// Log debug messages about pointer updates +#define DEBUG_MOUSE_CURSOR_UPDATES 0 + +#include "MouseCursorController.h" + +#include <log/log.h> + +#include <SkBitmap.h> +#include <SkBlendMode.h> +#include <SkCanvas.h> +#include <SkColor.h> +#include <SkPaint.h> + +namespace { +// Time to spend fading out the pointer completely. +const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms +} // namespace + +namespace android { + +// --- MouseCursorController --- + +MouseCursorController::MouseCursorController(PointerControllerContext& context) + : mContext(context) { + std::scoped_lock lock(mLock); + + mLocked.animationFrameIndex = 0; + mLocked.lastFrameUpdatedTime = 0; + + mLocked.pointerFadeDirection = 0; + mLocked.pointerX = 0; + mLocked.pointerY = 0; + mLocked.pointerAlpha = 0.0f; // pointer is initially faded + mLocked.pointerSprite = mContext.getSpriteController()->createSprite(); + mLocked.updatePointerIcon = false; + mLocked.requestedPointerType = mContext.getPolicy()->getDefaultPointerIconId(); + + mLocked.resourcesLoaded = false; + + mLocked.buttonState = 0; +} + +MouseCursorController::~MouseCursorController() { + std::scoped_lock lock(mLock); + + mLocked.pointerSprite.clear(); +} + +bool MouseCursorController::getBounds(float* outMinX, float* outMinY, float* outMaxX, + float* outMaxY) const { + std::scoped_lock lock(mLock); + + return getBoundsLocked(outMinX, outMinY, outMaxX, outMaxY); +} + +bool MouseCursorController::getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, + float* outMaxY) const REQUIRES(mLock) { + if (!mLocked.viewport.isValid()) { + return false; + } + + *outMinX = mLocked.viewport.logicalLeft; + *outMinY = mLocked.viewport.logicalTop; + *outMaxX = mLocked.viewport.logicalRight - 1; + *outMaxY = mLocked.viewport.logicalBottom - 1; + return true; +} + +void MouseCursorController::move(float deltaX, float deltaY) { +#if DEBUG_MOUSE_CURSOR_UPDATES + ALOGD("Move pointer by deltaX=%0.3f, deltaY=%0.3f", deltaX, deltaY); +#endif + if (deltaX == 0.0f && deltaY == 0.0f) { + return; + } + + std::scoped_lock lock(mLock); + + setPositionLocked(mLocked.pointerX + deltaX, mLocked.pointerY + deltaY); +} + +void MouseCursorController::setButtonState(int32_t buttonState) { +#if DEBUG_MOUSE_CURSOR_UPDATES + ALOGD("Set button state 0x%08x", buttonState); +#endif + std::scoped_lock lock(mLock); + + if (mLocked.buttonState != buttonState) { + mLocked.buttonState = buttonState; + } +} + +int32_t MouseCursorController::getButtonState() const { + std::scoped_lock lock(mLock); + return mLocked.buttonState; +} + +void MouseCursorController::setPosition(float x, float y) { +#if DEBUG_MOUSE_CURSOR_UPDATES + ALOGD("Set pointer position to x=%0.3f, y=%0.3f", x, y); +#endif + std::scoped_lock lock(mLock); + setPositionLocked(x, y); +} + +void MouseCursorController::setPositionLocked(float x, float y) REQUIRES(mLock) { + float minX, minY, maxX, maxY; + if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) { + if (x <= minX) { + mLocked.pointerX = minX; + } else if (x >= maxX) { + mLocked.pointerX = maxX; + } else { + mLocked.pointerX = x; + } + if (y <= minY) { + mLocked.pointerY = minY; + } else if (y >= maxY) { + mLocked.pointerY = maxY; + } else { + mLocked.pointerY = y; + } + updatePointerLocked(); + } +} + +void MouseCursorController::getPosition(float* outX, float* outY) const { + std::scoped_lock lock(mLock); + + *outX = mLocked.pointerX; + *outY = mLocked.pointerY; +} + +int32_t MouseCursorController::getDisplayId() const { + std::scoped_lock lock(mLock); + return mLocked.viewport.displayId; +} + +void MouseCursorController::fade(PointerControllerInterface::Transition transition) { + std::scoped_lock lock(mLock); + + // Remove the inactivity timeout, since we are fading now. + mContext.removeInactivityTimeout(); + + // Start fading. + if (transition == PointerControllerInterface::Transition::IMMEDIATE) { + mLocked.pointerFadeDirection = 0; + mLocked.pointerAlpha = 0.0f; + updatePointerLocked(); + } else { + mLocked.pointerFadeDirection = -1; + mContext.startAnimation(); + } +} + +void MouseCursorController::unfade(PointerControllerInterface::Transition transition) { + std::scoped_lock lock(mLock); + + // Always reset the inactivity timer. + mContext.resetInactivityTimeout(); + + // Start unfading. + if (transition == PointerControllerInterface::Transition::IMMEDIATE) { + mLocked.pointerFadeDirection = 0; + mLocked.pointerAlpha = 1.0f; + updatePointerLocked(); + } else { + mLocked.pointerFadeDirection = 1; + mContext.startAnimation(); + } +} + +void MouseCursorController::reloadPointerResources(bool getAdditionalMouseResources) { + std::scoped_lock lock(mLock); + + loadResourcesLocked(getAdditionalMouseResources); + updatePointerLocked(); +} + +/** + * The viewport values for deviceHeight and deviceWidth have already been adjusted for rotation, + * so here we are getting the dimensions in the original, unrotated orientation (orientation 0). + */ +static void getNonRotatedSize(const DisplayViewport& viewport, int32_t& width, int32_t& height) { + width = viewport.deviceWidth; + height = viewport.deviceHeight; + + if (viewport.orientation == DISPLAY_ORIENTATION_90 || + viewport.orientation == DISPLAY_ORIENTATION_270) { + std::swap(width, height); + } +} + +void MouseCursorController::setDisplayViewport(const DisplayViewport& viewport, + bool getAdditionalMouseResources) { + std::scoped_lock lock(mLock); + + if (viewport == mLocked.viewport) { + return; + } + + const DisplayViewport oldViewport = mLocked.viewport; + mLocked.viewport = viewport; + + int32_t oldDisplayWidth, oldDisplayHeight; + getNonRotatedSize(oldViewport, oldDisplayWidth, oldDisplayHeight); + int32_t newDisplayWidth, newDisplayHeight; + getNonRotatedSize(viewport, newDisplayWidth, newDisplayHeight); + + // Reset cursor position to center if size or display changed. + if (oldViewport.displayId != viewport.displayId || oldDisplayWidth != newDisplayWidth || + oldDisplayHeight != newDisplayHeight) { + float minX, minY, maxX, maxY; + if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) { + mLocked.pointerX = (minX + maxX) * 0.5f; + mLocked.pointerY = (minY + maxY) * 0.5f; + // Reload icon resources for density may be changed. + loadResourcesLocked(getAdditionalMouseResources); + } else { + mLocked.pointerX = 0; + mLocked.pointerY = 0; + } + } else if (oldViewport.orientation != viewport.orientation) { + // Apply offsets to convert from the pixel top-left corner position to the pixel center. + // This creates an invariant frame of reference that we can easily rotate when + // taking into account that the pointer may be located at fractional pixel offsets. + float x = mLocked.pointerX + 0.5f; + float y = mLocked.pointerY + 0.5f; + float temp; + + // Undo the previous rotation. + switch (oldViewport.orientation) { + case DISPLAY_ORIENTATION_90: + temp = x; + x = oldViewport.deviceHeight - y; + y = temp; + break; + case DISPLAY_ORIENTATION_180: + x = oldViewport.deviceWidth - x; + y = oldViewport.deviceHeight - y; + break; + case DISPLAY_ORIENTATION_270: + temp = x; + x = y; + y = oldViewport.deviceWidth - temp; + break; + } + + // Perform the new rotation. + switch (viewport.orientation) { + case DISPLAY_ORIENTATION_90: + temp = x; + x = y; + y = viewport.deviceHeight - temp; + break; + case DISPLAY_ORIENTATION_180: + x = viewport.deviceWidth - x; + y = viewport.deviceHeight - y; + break; + case DISPLAY_ORIENTATION_270: + temp = x; + x = viewport.deviceWidth - y; + y = temp; + break; + } + + // Apply offsets to convert from the pixel center to the pixel top-left corner position + // and save the results. + mLocked.pointerX = x - 0.5f; + mLocked.pointerY = y - 0.5f; + } + + updatePointerLocked(); +} + +void MouseCursorController::updatePointerIcon(int32_t iconId) { + std::scoped_lock lock(mLock); + + if (mLocked.requestedPointerType != iconId) { + mLocked.requestedPointerType = iconId; + mLocked.updatePointerIcon = true; + updatePointerLocked(); + } +} + +void MouseCursorController::setCustomPointerIcon(const SpriteIcon& icon) { + std::scoped_lock lock(mLock); + + const int32_t iconId = mContext.getPolicy()->getCustomPointerIconId(); + mLocked.additionalMouseResources[iconId] = icon; + mLocked.requestedPointerType = iconId; + mLocked.updatePointerIcon = true; + updatePointerLocked(); +} + +bool MouseCursorController::doFadingAnimation(nsecs_t timestamp, bool keepAnimating) { + nsecs_t frameDelay = timestamp - mContext.getAnimationTime(); + + std::scoped_lock lock(mLock); + + // Animate pointer fade. + if (mLocked.pointerFadeDirection < 0) { + mLocked.pointerAlpha -= float(frameDelay) / POINTER_FADE_DURATION; + if (mLocked.pointerAlpha <= 0.0f) { + mLocked.pointerAlpha = 0.0f; + mLocked.pointerFadeDirection = 0; + } else { + keepAnimating = true; + } + updatePointerLocked(); + } else if (mLocked.pointerFadeDirection > 0) { + mLocked.pointerAlpha += float(frameDelay) / POINTER_FADE_DURATION; + if (mLocked.pointerAlpha >= 1.0f) { + mLocked.pointerAlpha = 1.0f; + mLocked.pointerFadeDirection = 0; + } else { + keepAnimating = true; + } + updatePointerLocked(); + } + + return keepAnimating; +} + +bool MouseCursorController::doBitmapAnimation(nsecs_t timestamp) { + std::scoped_lock lock(mLock); + + std::map<int32_t, PointerAnimation>::const_iterator iter = + mLocked.animationResources.find(mLocked.requestedPointerType); + if (iter == mLocked.animationResources.end()) { + return false; + } + + if (timestamp - mLocked.lastFrameUpdatedTime > iter->second.durationPerFrame) { + sp<SpriteController> spriteController = mContext.getSpriteController(); + spriteController->openTransaction(); + + int incr = (timestamp - mLocked.lastFrameUpdatedTime) / iter->second.durationPerFrame; + mLocked.animationFrameIndex += incr; + mLocked.lastFrameUpdatedTime += iter->second.durationPerFrame * incr; + while (mLocked.animationFrameIndex >= iter->second.animationFrames.size()) { + mLocked.animationFrameIndex -= iter->second.animationFrames.size(); + } + mLocked.pointerSprite->setIcon(iter->second.animationFrames[mLocked.animationFrameIndex]); + + spriteController->closeTransaction(); + } + + // Keep animating. + return true; +} + +void MouseCursorController::updatePointerLocked() REQUIRES(mLock) { + if (!mLocked.viewport.isValid()) { + return; + } + sp<SpriteController> spriteController = mContext.getSpriteController(); + spriteController->openTransaction(); + + mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER); + mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY); + mLocked.pointerSprite->setDisplayId(mLocked.viewport.displayId); + + if (mLocked.pointerAlpha > 0) { + mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha); + mLocked.pointerSprite->setVisible(true); + } else { + mLocked.pointerSprite->setVisible(false); + } + + if (mLocked.updatePointerIcon) { + if (mLocked.requestedPointerType == mContext.getPolicy()->getDefaultPointerIconId()) { + mLocked.pointerSprite->setIcon(mLocked.pointerIcon); + } else { + std::map<int32_t, SpriteIcon>::const_iterator iter = + mLocked.additionalMouseResources.find(mLocked.requestedPointerType); + if (iter != mLocked.additionalMouseResources.end()) { + std::map<int32_t, PointerAnimation>::const_iterator anim_iter = + mLocked.animationResources.find(mLocked.requestedPointerType); + if (anim_iter != mLocked.animationResources.end()) { + mLocked.animationFrameIndex = 0; + mLocked.lastFrameUpdatedTime = systemTime(SYSTEM_TIME_MONOTONIC); + mContext.startAnimation(); + } + mLocked.pointerSprite->setIcon(iter->second); + } else { + ALOGW("Can't find the resource for icon id %d", mLocked.requestedPointerType); + mLocked.pointerSprite->setIcon(mLocked.pointerIcon); + } + } + mLocked.updatePointerIcon = false; + } + + spriteController->closeTransaction(); +} + +void MouseCursorController::loadResourcesLocked(bool getAdditionalMouseResources) REQUIRES(mLock) { + if (!mLocked.viewport.isValid()) { + return; + } + + if (!mLocked.resourcesLoaded) mLocked.resourcesLoaded = true; + + sp<PointerControllerPolicyInterface> policy = mContext.getPolicy(); + policy->loadPointerResources(&mResources, mLocked.viewport.displayId); + policy->loadPointerIcon(&mLocked.pointerIcon, mLocked.viewport.displayId); + + mLocked.additionalMouseResources.clear(); + mLocked.animationResources.clear(); + if (getAdditionalMouseResources) { + policy->loadAdditionalMouseResources(&mLocked.additionalMouseResources, + &mLocked.animationResources, + mLocked.viewport.displayId); + } + + mLocked.updatePointerIcon = true; +} + +bool MouseCursorController::isViewportValid() { + std::scoped_lock lock(mLock); + return mLocked.viewport.isValid(); +} + +void MouseCursorController::getAdditionalMouseResources() { + std::scoped_lock lock(mLock); + + if (mLocked.additionalMouseResources.empty()) { + mContext.getPolicy()->loadAdditionalMouseResources(&mLocked.additionalMouseResources, + &mLocked.animationResources, + mLocked.viewport.displayId); + } + mLocked.updatePointerIcon = true; + updatePointerLocked(); +} + +bool MouseCursorController::resourcesLoaded() { + std::scoped_lock lock(mLock); + return mLocked.resourcesLoaded; +} + +} // namespace android diff --git a/libs/input/MouseCursorController.h b/libs/input/MouseCursorController.h new file mode 100644 index 000000000000..448165b5ac46 --- /dev/null +++ b/libs/input/MouseCursorController.h @@ -0,0 +1,111 @@ +/* + * 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. + */ + +#ifndef _UI_MOUSE_CURSOR_CONTROLLER_H +#define _UI_MOUSE_CURSOR_CONTROLLER_H + +#include <gui/DisplayEventReceiver.h> +#include <input/DisplayViewport.h> +#include <input/Input.h> +#include <ui/DisplayInfo.h> +#include <utils/BitSet.h> +#include <utils/Looper.h> +#include <utils/RefBase.h> + +#include <map> +#include <memory> +#include <vector> + +#include "PointerControllerContext.h" +#include "SpriteController.h" + +namespace android { + +/* + * Helper class for PointerController that specifically handles + * mouse cursor resources and actions. + */ +class MouseCursorController { +public: + MouseCursorController(PointerControllerContext& context); + ~MouseCursorController(); + + bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const; + void move(float deltaX, float deltaY); + void setButtonState(int32_t buttonState); + int32_t getButtonState() const; + void setPosition(float x, float y); + void getPosition(float* outX, float* outY) const; + int32_t getDisplayId() const; + void fade(PointerControllerInterface::Transition transition); + void unfade(PointerControllerInterface::Transition transition); + void setDisplayViewport(const DisplayViewport& viewport, bool getAdditionalMouseResources); + + void updatePointerIcon(int32_t iconId); + void setCustomPointerIcon(const SpriteIcon& icon); + void reloadPointerResources(bool getAdditionalMouseResources); + + void getAdditionalMouseResources(); + bool isViewportValid(); + + bool doBitmapAnimation(nsecs_t timestamp); + bool doFadingAnimation(nsecs_t timestamp, bool keepAnimating); + + bool resourcesLoaded(); + +private: + mutable std::mutex mLock; + + PointerResources mResources; + + PointerControllerContext& mContext; + + struct Locked { + DisplayViewport viewport; + + size_t animationFrameIndex; + nsecs_t lastFrameUpdatedTime; + + int32_t pointerFadeDirection; + float pointerX; + float pointerY; + float pointerAlpha; + sp<Sprite> pointerSprite; + SpriteIcon pointerIcon; + bool updatePointerIcon; + + bool resourcesLoaded; + + std::map<int32_t, SpriteIcon> additionalMouseResources; + std::map<int32_t, PointerAnimation> animationResources; + + int32_t requestedPointerType; + + int32_t buttonState; + + } mLocked GUARDED_BY(mLock); + + bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const; + void setPositionLocked(float x, float y); + + void updatePointerLocked(); + + void loadResourcesLocked(bool getAdditionalMouseResources); +}; + +} // namespace android + +#endif // _UI_MOUSE_CURSOR_CONTROLLER_H diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp index 5e480a66c355..14c96cefd462 100644 --- a/libs/input/PointerController.cpp +++ b/libs/input/PointerController.cpp @@ -21,31 +21,26 @@ #define DEBUG_POINTER_UPDATES 0 #include "PointerController.h" +#include "MouseCursorController.h" +#include "PointerControllerContext.h" +#include "TouchSpotController.h" #include <log/log.h> -#include <memory> +#include <SkBitmap.h> +#include <SkBlendMode.h> +#include <SkCanvas.h> +#include <SkColor.h> +#include <SkPaint.h> namespace android { // --- PointerController --- -// Time to wait before starting the fade when the pointer is inactive. -static const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL = 15 * 1000 * 1000000LL; // 15 seconds -static const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_SHORT = 3 * 1000 * 1000000LL; // 3 seconds - -// Time to spend fading out the spot completely. -static const nsecs_t SPOT_FADE_DURATION = 200 * 1000000LL; // 200 ms - -// Time to spend fading out the pointer completely. -static const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms - -// The number of events to be read at once for DisplayEventReceiver. -static const int EVENT_BUFFER_SIZE = 100; - std::shared_ptr<PointerController> PointerController::create( const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper, const sp<SpriteController>& spriteController) { + // using 'new' to access non-public constructor std::shared_ptr<PointerController> controller = std::shared_ptr<PointerController>( new PointerController(policy, looper, spriteController)); @@ -60,758 +55,175 @@ std::shared_ptr<PointerController> PointerController::create( * weak_ptr's which themselves cannot be constructed until there's at least one shared_ptr. */ - controller->mHandler->pointerController = controller; - controller->mCallback->pointerController = controller; - if (controller->mDisplayEventReceiver.initCheck() == NO_ERROR) { - controller->mLooper->addFd(controller->mDisplayEventReceiver.getFd(), Looper::POLL_CALLBACK, - Looper::EVENT_INPUT, controller->mCallback, nullptr); - } else { - ALOGE("Failed to initialize DisplayEventReceiver."); - } + controller->mContext.setHandlerController(controller); + controller->mContext.setCallbackController(controller); + controller->mContext.initializeDisplayEventReceiver(); return controller; } PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper, const sp<SpriteController>& spriteController) - : mPolicy(policy), - mLooper(looper), - mSpriteController(spriteController), - mHandler(new MessageHandler()), - mCallback(new LooperCallback()) { - AutoMutex _l(mLock); - - mLocked.animationPending = false; - - mLocked.presentation = Presentation::POINTER; - mLocked.presentationChanged = false; - - mLocked.inactivityTimeout = InactivityTimeout::NORMAL; - - mLocked.pointerFadeDirection = 0; - mLocked.pointerX = 0; - mLocked.pointerY = 0; - mLocked.pointerAlpha = 0.0f; // pointer is initially faded - mLocked.pointerSprite = mSpriteController->createSprite(); - mLocked.pointerIconChanged = false; - mLocked.requestedPointerType = mPolicy->getDefaultPointerIconId(); - - mLocked.animationFrameIndex = 0; - mLocked.lastFrameUpdatedTime = 0; - - mLocked.buttonState = 0; + : mContext(policy, looper, spriteController, *this), mCursorController(mContext) { + std::scoped_lock lock(mLock); + mLocked.presentation = Presentation::SPOT; } -PointerController::~PointerController() { - mLooper->removeMessages(mHandler); - - AutoMutex _l(mLock); - - mLocked.pointerSprite.clear(); - - for (auto& it : mLocked.spotsByDisplay) { - const std::vector<Spot*>& spots = it.second; - size_t numSpots = spots.size(); - for (size_t i = 0; i < numSpots; i++) { - delete spots[i]; - } - } - mLocked.spotsByDisplay.clear(); - mLocked.recycledSprites.clear(); -} - -bool PointerController::getBounds(float* outMinX, float* outMinY, - float* outMaxX, float* outMaxY) const { - AutoMutex _l(mLock); - - return getBoundsLocked(outMinX, outMinY, outMaxX, outMaxY); -} - -bool PointerController::getBoundsLocked(float* outMinX, float* outMinY, - float* outMaxX, float* outMaxY) const { - - if (!mLocked.viewport.isValid()) { - return false; - } - - *outMinX = mLocked.viewport.logicalLeft; - *outMinY = mLocked.viewport.logicalTop; - *outMaxX = mLocked.viewport.logicalRight - 1; - *outMaxY = mLocked.viewport.logicalBottom - 1; - return true; +bool PointerController::getBounds(float* outMinX, float* outMinY, float* outMaxX, + float* outMaxY) const { + return mCursorController.getBounds(outMinX, outMinY, outMaxX, outMaxY); } void PointerController::move(float deltaX, float deltaY) { -#if DEBUG_POINTER_UPDATES - ALOGD("Move pointer by deltaX=%0.3f, deltaY=%0.3f", deltaX, deltaY); -#endif - if (deltaX == 0.0f && deltaY == 0.0f) { - return; - } - - AutoMutex _l(mLock); - - setPositionLocked(mLocked.pointerX + deltaX, mLocked.pointerY + deltaY); + mCursorController.move(deltaX, deltaY); } void PointerController::setButtonState(int32_t buttonState) { -#if DEBUG_POINTER_UPDATES - ALOGD("Set button state 0x%08x", buttonState); -#endif - AutoMutex _l(mLock); - - if (mLocked.buttonState != buttonState) { - mLocked.buttonState = buttonState; - } + mCursorController.setButtonState(buttonState); } int32_t PointerController::getButtonState() const { - AutoMutex _l(mLock); - - return mLocked.buttonState; + return mCursorController.getButtonState(); } void PointerController::setPosition(float x, float y) { -#if DEBUG_POINTER_UPDATES - ALOGD("Set pointer position to x=%0.3f, y=%0.3f", x, y); -#endif - AutoMutex _l(mLock); - - setPositionLocked(x, y); -} - -void PointerController::setPositionLocked(float x, float y) { - float minX, minY, maxX, maxY; - if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) { - if (x <= minX) { - mLocked.pointerX = minX; - } else if (x >= maxX) { - mLocked.pointerX = maxX; - } else { - mLocked.pointerX = x; - } - if (y <= minY) { - mLocked.pointerY = minY; - } else if (y >= maxY) { - mLocked.pointerY = maxY; - } else { - mLocked.pointerY = y; - } - updatePointerLocked(); - } + std::scoped_lock lock(mLock); + mCursorController.setPosition(x, y); } void PointerController::getPosition(float* outX, float* outY) const { - AutoMutex _l(mLock); - - *outX = mLocked.pointerX; - *outY = mLocked.pointerY; + mCursorController.getPosition(outX, outY); } int32_t PointerController::getDisplayId() const { - AutoMutex _l(mLock); - - return mLocked.viewport.displayId; + return mCursorController.getDisplayId(); } void PointerController::fade(Transition transition) { - AutoMutex _l(mLock); - - // Remove the inactivity timeout, since we are fading now. - removeInactivityTimeoutLocked(); - - // Start fading. - if (transition == Transition::IMMEDIATE) { - mLocked.pointerFadeDirection = 0; - mLocked.pointerAlpha = 0.0f; - updatePointerLocked(); - } else { - mLocked.pointerFadeDirection = -1; - startAnimationLocked(); - } + std::scoped_lock lock(mLock); + mCursorController.fade(transition); } void PointerController::unfade(Transition transition) { - AutoMutex _l(mLock); - - // Always reset the inactivity timer. - resetInactivityTimeoutLocked(); - - // Start unfading. - if (transition == Transition::IMMEDIATE) { - mLocked.pointerFadeDirection = 0; - mLocked.pointerAlpha = 1.0f; - updatePointerLocked(); - } else { - mLocked.pointerFadeDirection = 1; - startAnimationLocked(); - } + std::scoped_lock lock(mLock); + mCursorController.unfade(transition); } void PointerController::setPresentation(Presentation presentation) { - AutoMutex _l(mLock); + std::scoped_lock lock(mLock); if (mLocked.presentation == presentation) { return; } mLocked.presentation = presentation; - mLocked.presentationChanged = true; - if (!mLocked.viewport.isValid()) { + if (!mCursorController.isViewportValid()) { return; } if (presentation == Presentation::POINTER) { - if (mLocked.additionalMouseResources.empty()) { - mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources, - &mLocked.animationResources, - mLocked.viewport.displayId); - } - fadeOutAndReleaseAllSpotsLocked(); - updatePointerLocked(); + mCursorController.getAdditionalMouseResources(); + clearSpotsLocked(); } } -void PointerController::setSpots(const PointerCoords* spotCoords, - const uint32_t* spotIdToIndex, BitSet32 spotIdBits, int32_t displayId) { -#if DEBUG_POINTER_UPDATES - ALOGD("setSpots: idBits=%08x", spotIdBits.value); - for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) { - uint32_t id = idBits.firstMarkedBit(); - idBits.clearBit(id); - const PointerCoords& c = spotCoords[spotIdToIndex[id]]; - ALOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f, displayId=%" PRId32 ".", id, - c.getAxisValue(AMOTION_EVENT_AXIS_X), - c.getAxisValue(AMOTION_EVENT_AXIS_Y), - c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), - displayId); +void PointerController::setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, + BitSet32 spotIdBits, int32_t displayId) { + std::scoped_lock lock(mLock); + auto it = mLocked.spotControllers.find(displayId); + if (it == mLocked.spotControllers.end()) { + mLocked.spotControllers.try_emplace(displayId, displayId, mContext); } -#endif - - AutoMutex _l(mLock); - if (!mLocked.viewport.isValid()) { - return; - } - - std::vector<Spot*> newSpots; - std::map<int32_t, std::vector<Spot*>>::const_iterator iter = - mLocked.spotsByDisplay.find(displayId); - if (iter != mLocked.spotsByDisplay.end()) { - newSpots = iter->second; - } - - mSpriteController->openTransaction(); - - // Add or move spots for fingers that are down. - for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) { - uint32_t id = idBits.clearFirstMarkedBit(); - const PointerCoords& c = spotCoords[spotIdToIndex[id]]; - const SpriteIcon& icon = c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE) > 0 - ? mResources.spotTouch : mResources.spotHover; - float x = c.getAxisValue(AMOTION_EVENT_AXIS_X); - float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y); - - Spot* spot = getSpot(id, newSpots); - if (!spot) { - spot = createAndAddSpotLocked(id, newSpots); - } - - spot->updateSprite(&icon, x, y, displayId); - } - - // Remove spots for fingers that went up. - for (size_t i = 0; i < newSpots.size(); i++) { - Spot* spot = newSpots[i]; - if (spot->id != Spot::INVALID_ID - && !spotIdBits.hasBit(spot->id)) { - fadeOutAndReleaseSpotLocked(spot); - } - } - - mSpriteController->closeTransaction(); - mLocked.spotsByDisplay[displayId] = newSpots; + mLocked.spotControllers.at(displayId).setSpots(spotCoords, spotIdToIndex, spotIdBits); } void PointerController::clearSpots() { -#if DEBUG_POINTER_UPDATES - ALOGD("clearSpots"); -#endif + std::scoped_lock lock(mLock); + clearSpotsLocked(); +} - AutoMutex _l(mLock); - if (!mLocked.viewport.isValid()) { - return; +void PointerController::clearSpotsLocked() REQUIRES(mLock) { + for (auto& [displayID, spotController] : mLocked.spotControllers) { + spotController.clearSpots(); } - - fadeOutAndReleaseAllSpotsLocked(); } void PointerController::setInactivityTimeout(InactivityTimeout inactivityTimeout) { - AutoMutex _l(mLock); - - if (mLocked.inactivityTimeout != inactivityTimeout) { - mLocked.inactivityTimeout = inactivityTimeout; - resetInactivityTimeoutLocked(); - } + mContext.setInactivityTimeout(inactivityTimeout); } void PointerController::reloadPointerResources() { - AutoMutex _l(mLock); + std::scoped_lock lock(mLock); - loadResourcesLocked(); - updatePointerLocked(); -} - -/** - * The viewport values for deviceHeight and deviceWidth have already been adjusted for rotation, - * so here we are getting the dimensions in the original, unrotated orientation (orientation 0). - */ -static void getNonRotatedSize(const DisplayViewport& viewport, int32_t& width, int32_t& height) { - width = viewport.deviceWidth; - height = viewport.deviceHeight; + for (auto& [displayID, spotController] : mLocked.spotControllers) { + spotController.reloadSpotResources(); + } - if (viewport.orientation == DISPLAY_ORIENTATION_90 - || viewport.orientation == DISPLAY_ORIENTATION_270) { - std::swap(width, height); + if (mCursorController.resourcesLoaded()) { + bool getAdditionalMouseResources = false; + if (mLocked.presentation == PointerController::Presentation::POINTER) { + getAdditionalMouseResources = true; + } + mCursorController.reloadPointerResources(getAdditionalMouseResources); } } void PointerController::setDisplayViewport(const DisplayViewport& viewport) { - AutoMutex _l(mLock); - if (viewport == mLocked.viewport) { - return; - } - - const DisplayViewport oldViewport = mLocked.viewport; - mLocked.viewport = viewport; - - int32_t oldDisplayWidth, oldDisplayHeight; - getNonRotatedSize(oldViewport, oldDisplayWidth, oldDisplayHeight); - int32_t newDisplayWidth, newDisplayHeight; - getNonRotatedSize(viewport, newDisplayWidth, newDisplayHeight); - - // Reset cursor position to center if size or display changed. - if (oldViewport.displayId != viewport.displayId - || oldDisplayWidth != newDisplayWidth - || oldDisplayHeight != newDisplayHeight) { - - float minX, minY, maxX, maxY; - if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) { - mLocked.pointerX = (minX + maxX) * 0.5f; - mLocked.pointerY = (minY + maxY) * 0.5f; - // Reload icon resources for density may be changed. - loadResourcesLocked(); - } else { - mLocked.pointerX = 0; - mLocked.pointerY = 0; - } + std::scoped_lock lock(mLock); - fadeOutAndReleaseAllSpotsLocked(); - } else if (oldViewport.orientation != viewport.orientation) { - // Apply offsets to convert from the pixel top-left corner position to the pixel center. - // This creates an invariant frame of reference that we can easily rotate when - // taking into account that the pointer may be located at fractional pixel offsets. - float x = mLocked.pointerX + 0.5f; - float y = mLocked.pointerY + 0.5f; - float temp; - - // Undo the previous rotation. - switch (oldViewport.orientation) { - case DISPLAY_ORIENTATION_90: - temp = x; - x = oldViewport.deviceHeight - y; - y = temp; - break; - case DISPLAY_ORIENTATION_180: - x = oldViewport.deviceWidth - x; - y = oldViewport.deviceHeight - y; - break; - case DISPLAY_ORIENTATION_270: - temp = x; - x = y; - y = oldViewport.deviceWidth - temp; - break; - } - - // Perform the new rotation. - switch (viewport.orientation) { - case DISPLAY_ORIENTATION_90: - temp = x; - x = y; - y = viewport.deviceHeight - temp; - break; - case DISPLAY_ORIENTATION_180: - x = viewport.deviceWidth - x; - y = viewport.deviceHeight - y; - break; - case DISPLAY_ORIENTATION_270: - temp = x; - x = viewport.deviceWidth - y; - y = temp; - break; - } - - // Apply offsets to convert from the pixel center to the pixel top-left corner position - // and save the results. - mLocked.pointerX = x - 0.5f; - mLocked.pointerY = y - 0.5f; + bool getAdditionalMouseResources = false; + if (mLocked.presentation == PointerController::Presentation::POINTER) { + getAdditionalMouseResources = true; } - - updatePointerLocked(); + mCursorController.setDisplayViewport(viewport, getAdditionalMouseResources); } void PointerController::updatePointerIcon(int32_t iconId) { - AutoMutex _l(mLock); - if (mLocked.requestedPointerType != iconId) { - mLocked.requestedPointerType = iconId; - mLocked.presentationChanged = true; - updatePointerLocked(); - } + std::scoped_lock lock(mLock); + mCursorController.updatePointerIcon(iconId); } void PointerController::setCustomPointerIcon(const SpriteIcon& icon) { - AutoMutex _l(mLock); - - const int32_t iconId = mPolicy->getCustomPointerIconId(); - mLocked.additionalMouseResources[iconId] = icon; - mLocked.requestedPointerType = iconId; - mLocked.presentationChanged = true; - - updatePointerLocked(); -} - -void PointerController::MessageHandler::handleMessage(const Message& message) { - std::shared_ptr<PointerController> controller = pointerController.lock(); - - if (controller == nullptr) { - ALOGE("PointerController instance was released before processing message: what=%d", - message.what); - return; - } - switch (message.what) { - case MSG_INACTIVITY_TIMEOUT: - controller->doInactivityTimeout(); - break; - } -} - -int PointerController::LooperCallback::handleEvent(int /* fd */, int events, void* /* data */) { - std::shared_ptr<PointerController> controller = pointerController.lock(); - if (controller == nullptr) { - ALOGW("PointerController instance was released with pending callbacks. events=0x%x", - events); - return 0; // Remove the callback, the PointerController is gone anyways - } - if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) { - ALOGE("Display event receiver pipe was closed or an error occurred. events=0x%x", events); - return 0; // remove the callback - } - - if (!(events & Looper::EVENT_INPUT)) { - ALOGW("Received spurious callback for unhandled poll event. events=0x%x", events); - return 1; // keep the callback - } - - bool gotVsync = false; - ssize_t n; - nsecs_t timestamp; - DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE]; - while ((n = controller->mDisplayEventReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) { - for (size_t i = 0; i < static_cast<size_t>(n); ++i) { - if (buf[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) { - timestamp = buf[i].header.timestamp; - gotVsync = true; - } - } - } - if (gotVsync) { - controller->doAnimate(timestamp); - } - return 1; // keep the callback + std::scoped_lock lock(mLock); + mCursorController.setCustomPointerIcon(icon); } void PointerController::doAnimate(nsecs_t timestamp) { - AutoMutex _l(mLock); - - mLocked.animationPending = false; + std::scoped_lock lock(mLock); - bool keepFading = doFadingAnimationLocked(timestamp); - bool keepBitmapFlipping = doBitmapAnimationLocked(timestamp); - if (keepFading || keepBitmapFlipping) { - startAnimationLocked(); - } -} + mContext.setAnimationPending(false); -bool PointerController::doFadingAnimationLocked(nsecs_t timestamp) { - bool keepAnimating = false; - nsecs_t frameDelay = timestamp - mLocked.animationTime; + bool keepFading = false; + keepFading = mCursorController.doFadingAnimation(timestamp, keepFading); - // Animate pointer fade. - if (mLocked.pointerFadeDirection < 0) { - mLocked.pointerAlpha -= float(frameDelay) / POINTER_FADE_DURATION; - if (mLocked.pointerAlpha <= 0.0f) { - mLocked.pointerAlpha = 0.0f; - mLocked.pointerFadeDirection = 0; - } else { - keepAnimating = true; - } - updatePointerLocked(); - } else if (mLocked.pointerFadeDirection > 0) { - mLocked.pointerAlpha += float(frameDelay) / POINTER_FADE_DURATION; - if (mLocked.pointerAlpha >= 1.0f) { - mLocked.pointerAlpha = 1.0f; - mLocked.pointerFadeDirection = 0; - } else { - keepAnimating = true; - } - updatePointerLocked(); + for (auto& [displayID, spotController] : mLocked.spotControllers) { + keepFading = spotController.doFadingAnimation(timestamp, keepFading); } - // Animate spots that are fading out and being removed. - for(auto it = mLocked.spotsByDisplay.begin(); it != mLocked.spotsByDisplay.end();) { - std::vector<Spot*>& spots = it->second; - size_t numSpots = spots.size(); - for (size_t i = 0; i < numSpots;) { - Spot* spot = spots[i]; - if (spot->id == Spot::INVALID_ID) { - spot->alpha -= float(frameDelay) / SPOT_FADE_DURATION; - if (spot->alpha <= 0) { - spots.erase(spots.begin() + i); - releaseSpotLocked(spot); - numSpots--; - continue; - } else { - spot->sprite->setAlpha(spot->alpha); - keepAnimating = true; - } - } - ++i; - } - - if (spots.size() == 0) { - it = mLocked.spotsByDisplay.erase(it); - } else { - ++it; - } - } - - return keepAnimating; -} - -bool PointerController::doBitmapAnimationLocked(nsecs_t timestamp) { - std::map<int32_t, PointerAnimation>::const_iterator iter = mLocked.animationResources.find( - mLocked.requestedPointerType); - if (iter == mLocked.animationResources.end()) { - return false; - } - - if (timestamp - mLocked.lastFrameUpdatedTime > iter->second.durationPerFrame) { - mSpriteController->openTransaction(); - - int incr = (timestamp - mLocked.lastFrameUpdatedTime) / iter->second.durationPerFrame; - mLocked.animationFrameIndex += incr; - mLocked.lastFrameUpdatedTime += iter->second.durationPerFrame * incr; - while (mLocked.animationFrameIndex >= iter->second.animationFrames.size()) { - mLocked.animationFrameIndex -= iter->second.animationFrames.size(); - } - mLocked.pointerSprite->setIcon(iter->second.animationFrames[mLocked.animationFrameIndex]); - - mSpriteController->closeTransaction(); + bool keepBitmapFlipping = mCursorController.doBitmapAnimation(timestamp); + if (keepFading || keepBitmapFlipping) { + mContext.startAnimation(); } - - // Keep animating. - return true; } void PointerController::doInactivityTimeout() { fade(Transition::GRADUAL); } -void PointerController::startAnimationLocked() { - if (!mLocked.animationPending) { - mLocked.animationPending = true; - mLocked.animationTime = systemTime(SYSTEM_TIME_MONOTONIC); - mDisplayEventReceiver.requestNextVsync(); - } -} - -void PointerController::resetInactivityTimeoutLocked() { - mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT); - - nsecs_t timeout = mLocked.inactivityTimeout == InactivityTimeout::SHORT - ? INACTIVITY_TIMEOUT_DELAY_TIME_SHORT - : INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL; - mLooper->sendMessageDelayed(timeout, mHandler, MSG_INACTIVITY_TIMEOUT); -} - -void PointerController::removeInactivityTimeoutLocked() { - mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT); -} - -void PointerController::updatePointerLocked() REQUIRES(mLock) { - if (!mLocked.viewport.isValid()) { - return; - } - - mSpriteController->openTransaction(); - - mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER); - mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY); - mLocked.pointerSprite->setDisplayId(mLocked.viewport.displayId); - - if (mLocked.pointerAlpha > 0) { - mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha); - mLocked.pointerSprite->setVisible(true); - } else { - mLocked.pointerSprite->setVisible(false); - } - - if (mLocked.pointerIconChanged || mLocked.presentationChanged) { - if (mLocked.presentation == Presentation::POINTER) { - if (mLocked.requestedPointerType == mPolicy->getDefaultPointerIconId()) { - mLocked.pointerSprite->setIcon(mLocked.pointerIcon); - } else { - std::map<int32_t, SpriteIcon>::const_iterator iter = - mLocked.additionalMouseResources.find(mLocked.requestedPointerType); - if (iter != mLocked.additionalMouseResources.end()) { - std::map<int32_t, PointerAnimation>::const_iterator anim_iter = - mLocked.animationResources.find(mLocked.requestedPointerType); - if (anim_iter != mLocked.animationResources.end()) { - mLocked.animationFrameIndex = 0; - mLocked.lastFrameUpdatedTime = systemTime(SYSTEM_TIME_MONOTONIC); - startAnimationLocked(); - } - mLocked.pointerSprite->setIcon(iter->second); - } else { - ALOGW("Can't find the resource for icon id %d", mLocked.requestedPointerType); - mLocked.pointerSprite->setIcon(mLocked.pointerIcon); - } - } - } else { - mLocked.pointerSprite->setIcon(mResources.spotAnchor); - } - mLocked.pointerIconChanged = false; - mLocked.presentationChanged = false; - } - - mSpriteController->closeTransaction(); -} - -PointerController::Spot* PointerController::getSpot(uint32_t id, const std::vector<Spot*>& spots) { - for (size_t i = 0; i < spots.size(); i++) { - Spot* spot = spots[i]; - if (spot->id == id) { - return spot; - } +void PointerController::onDisplayViewportsUpdated(std::vector<DisplayViewport>& viewports) { + std::unordered_set<int32_t> displayIdSet; + for (DisplayViewport viewport : viewports) { + displayIdSet.insert(viewport.displayId); } - return nullptr; -} - -PointerController::Spot* PointerController::createAndAddSpotLocked(uint32_t id, - std::vector<Spot*>& spots) { - // Remove spots until we have fewer than MAX_SPOTS remaining. - while (spots.size() >= MAX_SPOTS) { - Spot* spot = removeFirstFadingSpotLocked(spots); - if (!spot) { - spot = spots[0]; - spots.erase(spots.begin()); - } - releaseSpotLocked(spot); - } - - // Obtain a sprite from the recycled pool. - sp<Sprite> sprite; - if (! mLocked.recycledSprites.empty()) { - sprite = mLocked.recycledSprites.back(); - mLocked.recycledSprites.pop_back(); - } else { - sprite = mSpriteController->createSprite(); - } - - // Return the new spot. - Spot* spot = new Spot(id, sprite); - spots.push_back(spot); - return spot; -} - -PointerController::Spot* PointerController::removeFirstFadingSpotLocked(std::vector<Spot*>& spots) { - for (size_t i = 0; i < spots.size(); i++) { - Spot* spot = spots[i]; - if (spot->id == Spot::INVALID_ID) { - spots.erase(spots.begin() + i); - return spot; - } - } - return nullptr; -} - -void PointerController::releaseSpotLocked(Spot* spot) { - spot->sprite->clearIcon(); - - if (mLocked.recycledSprites.size() < MAX_RECYCLED_SPRITES) { - mLocked.recycledSprites.push_back(spot->sprite); - } - - delete spot; -} - -void PointerController::fadeOutAndReleaseSpotLocked(Spot* spot) { - if (spot->id != Spot::INVALID_ID) { - spot->id = Spot::INVALID_ID; - startAnimationLocked(); - } -} - -void PointerController::fadeOutAndReleaseAllSpotsLocked() { - for (auto& it : mLocked.spotsByDisplay) { - const std::vector<Spot*>& spots = it.second; - size_t numSpots = spots.size(); - for (size_t i = 0; i < numSpots; i++) { - Spot* spot = spots[i]; - fadeOutAndReleaseSpotLocked(spot); - } - } -} - -void PointerController::loadResourcesLocked() REQUIRES(mLock) { - if (!mLocked.viewport.isValid()) { - return; - } - - mPolicy->loadPointerResources(&mResources, mLocked.viewport.displayId); - mPolicy->loadPointerIcon(&mLocked.pointerIcon, mLocked.viewport.displayId); - - mLocked.additionalMouseResources.clear(); - mLocked.animationResources.clear(); - if (mLocked.presentation == Presentation::POINTER) { - mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources, - &mLocked.animationResources, mLocked.viewport.displayId); - } - - mLocked.pointerIconChanged = true; -} - - -// --- PointerController::Spot --- - -void PointerController::Spot::updateSprite(const SpriteIcon* icon, float x, float y, - int32_t displayId) { - sprite->setLayer(Sprite::BASE_LAYER_SPOT + id); - sprite->setAlpha(alpha); - sprite->setTransformationMatrix(SpriteTransformationMatrix(scale, 0.0f, 0.0f, scale)); - sprite->setPosition(x, y); - sprite->setDisplayId(displayId); - this->x = x; - this->y = y; - - if (icon != lastIcon) { - lastIcon = icon; - if (icon) { - sprite->setIcon(*icon); - sprite->setVisible(true); + std::scoped_lock lock(mLock); + for (auto it = mLocked.spotControllers.begin(); it != mLocked.spotControllers.end();) { + int32_t displayID = it->first; + if (!displayIdSet.count(displayID)) { + it = mLocked.spotControllers.erase(it); } else { - sprite->setVisible(false); + ++it; } } } diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h index 14c0679654c6..1f561da333b1 100644 --- a/libs/input/PointerController.h +++ b/libs/input/PointerController.h @@ -30,48 +30,14 @@ #include <memory> #include <vector> +#include "MouseCursorController.h" +#include "PointerControllerContext.h" #include "SpriteController.h" +#include "TouchSpotController.h" namespace android { /* - * Pointer resources. - */ -struct PointerResources { - SpriteIcon spotHover; - SpriteIcon spotTouch; - SpriteIcon spotAnchor; -}; - -struct PointerAnimation { - std::vector<SpriteIcon> animationFrames; - nsecs_t durationPerFrame; -}; - -/* - * Pointer controller policy interface. - * - * The pointer controller policy is used by the pointer controller to interact with - * the Window Manager and other system components. - * - * The actual implementation is partially supported by callbacks into the DVM - * via JNI. This interface is also mocked in the unit tests. - */ -class PointerControllerPolicyInterface : public virtual RefBase { -protected: - PointerControllerPolicyInterface() { } - virtual ~PointerControllerPolicyInterface() { } - -public: - virtual void loadPointerIcon(SpriteIcon* icon, int32_t displayId) = 0; - virtual void loadPointerResources(PointerResources* outResources, int32_t displayId) = 0; - virtual void loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources, - std::map<int32_t, PointerAnimation>* outAnimationResources, int32_t displayId) = 0; - virtual int32_t getDefaultPointerIconId() = 0; - virtual int32_t getCustomPointerIconId() = 0; -}; - -/* * Tracks pointer movements and draws the pointer sprite to a surface. * * Handles pointer acceleration and animation. @@ -81,15 +47,10 @@ public: static std::shared_ptr<PointerController> create( const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper, const sp<SpriteController>& spriteController); - enum class InactivityTimeout { - NORMAL = 0, - SHORT = 1, - }; - virtual ~PointerController(); + virtual ~PointerController() = default; - virtual bool getBounds(float* outMinX, float* outMinY, - float* outMaxX, float* outMaxY) const; + virtual bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const; virtual void move(float deltaX, float deltaY); virtual void setButtonState(int32_t buttonState); virtual int32_t getButtonState() const; @@ -101,129 +62,37 @@ public: virtual void setDisplayViewport(const DisplayViewport& viewport); virtual void setPresentation(Presentation presentation); - virtual void setSpots(const PointerCoords* spotCoords, - const uint32_t* spotIdToIndex, BitSet32 spotIdBits, int32_t displayId); + virtual void setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, + BitSet32 spotIdBits, int32_t displayId); virtual void clearSpots(); void updatePointerIcon(int32_t iconId); void setCustomPointerIcon(const SpriteIcon& icon); void setInactivityTimeout(InactivityTimeout inactivityTimeout); + void doInactivityTimeout(); + void doAnimate(nsecs_t timestamp); void reloadPointerResources(); + void onDisplayViewportsUpdated(std::vector<DisplayViewport>& viewports); private: - static constexpr size_t MAX_RECYCLED_SPRITES = 12; - static constexpr size_t MAX_SPOTS = 12; - - enum { - MSG_INACTIVITY_TIMEOUT, - }; - - struct Spot { - static const uint32_t INVALID_ID = 0xffffffff; - - uint32_t id; - sp<Sprite> sprite; - float alpha; - float scale; - float x, y; - - inline Spot(uint32_t id, const sp<Sprite>& sprite) - : id(id), - sprite(sprite), - alpha(1.0f), - scale(1.0f), - x(0.0f), - y(0.0f), - lastIcon(nullptr) {} - - void updateSprite(const SpriteIcon* icon, float x, float y, int32_t displayId); + friend PointerControllerContext::LooperCallback; + friend PointerControllerContext::MessageHandler; - private: - const SpriteIcon* lastIcon; - }; + mutable std::mutex mLock; - class MessageHandler : public virtual android::MessageHandler { - public: - void handleMessage(const Message& message) override; - std::weak_ptr<PointerController> pointerController; - }; + PointerControllerContext mContext; - class LooperCallback : public virtual android::LooperCallback { - public: - int handleEvent(int fd, int events, void* data) override; - std::weak_ptr<PointerController> pointerController; - }; - - mutable Mutex mLock; - - sp<PointerControllerPolicyInterface> mPolicy; - sp<Looper> mLooper; - sp<SpriteController> mSpriteController; - sp<MessageHandler> mHandler; - sp<LooperCallback> mCallback; - - DisplayEventReceiver mDisplayEventReceiver; - - PointerResources mResources; + MouseCursorController mCursorController; struct Locked { - bool animationPending; - nsecs_t animationTime; - - size_t animationFrameIndex; - nsecs_t lastFrameUpdatedTime; - - DisplayViewport viewport; - - InactivityTimeout inactivityTimeout; - Presentation presentation; - bool presentationChanged; - - int32_t pointerFadeDirection; - float pointerX; - float pointerY; - float pointerAlpha; - sp<Sprite> pointerSprite; - SpriteIcon pointerIcon; - bool pointerIconChanged; - - std::map<int32_t, SpriteIcon> additionalMouseResources; - std::map<int32_t, PointerAnimation> animationResources; - int32_t requestedPointerType; - - int32_t buttonState; - - std::map<int32_t /* displayId */, std::vector<Spot*>> spotsByDisplay; - std::vector<sp<Sprite>> recycledSprites; + std::unordered_map<int32_t /* displayId */, TouchSpotController> spotControllers; } mLocked GUARDED_BY(mLock); PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper, const sp<SpriteController>& spriteController); - - bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const; - void setPositionLocked(float x, float y); - - void doAnimate(nsecs_t timestamp); - bool doFadingAnimationLocked(nsecs_t timestamp); - bool doBitmapAnimationLocked(nsecs_t timestamp); - void doInactivityTimeout(); - - void startAnimationLocked(); - - void resetInactivityTimeoutLocked(); - void removeInactivityTimeoutLocked(); - void updatePointerLocked(); - - Spot* getSpot(uint32_t id, const std::vector<Spot*>& spots); - Spot* createAndAddSpotLocked(uint32_t id, std::vector<Spot*>& spots); - Spot* removeFirstFadingSpotLocked(std::vector<Spot*>& spots); - void releaseSpotLocked(Spot* spot); - void fadeOutAndReleaseSpotLocked(Spot* spot); - void fadeOutAndReleaseAllSpotsLocked(); - - void loadResourcesLocked(); + void clearSpotsLocked(); }; } // namespace android diff --git a/libs/input/PointerControllerContext.cpp b/libs/input/PointerControllerContext.cpp new file mode 100644 index 000000000000..2d7e22b01112 --- /dev/null +++ b/libs/input/PointerControllerContext.cpp @@ -0,0 +1,179 @@ +/* + * 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. + */ + +#include "PointerControllerContext.h" +#include "PointerController.h" + +namespace { +// Time to wait before starting the fade when the pointer is inactive. +const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL = 15 * 1000 * 1000000LL; // 15 seconds +const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_SHORT = 3 * 1000 * 1000000LL; // 3 seconds + +// The number of events to be read at once for DisplayEventReceiver. +const int EVENT_BUFFER_SIZE = 100; +} // namespace + +namespace android { + +// --- PointerControllerContext --- + +PointerControllerContext::PointerControllerContext( + const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper, + const sp<SpriteController>& spriteController, PointerController& controller) + : mPolicy(policy), + mLooper(looper), + mSpriteController(spriteController), + mHandler(new MessageHandler()), + mCallback(new LooperCallback()), + mController(controller) { + std::scoped_lock lock(mLock); + mLocked.inactivityTimeout = InactivityTimeout::NORMAL; + mLocked.animationPending = false; +} + +PointerControllerContext::~PointerControllerContext() { + mLooper->removeMessages(mHandler); +} + +void PointerControllerContext::setInactivityTimeout(InactivityTimeout inactivityTimeout) { + std::scoped_lock lock(mLock); + + if (mLocked.inactivityTimeout != inactivityTimeout) { + mLocked.inactivityTimeout = inactivityTimeout; + resetInactivityTimeoutLocked(); + } +} + +void PointerControllerContext::startAnimation() { + std::scoped_lock lock(mLock); + if (!mLocked.animationPending) { + mLocked.animationPending = true; + mLocked.animationTime = systemTime(SYSTEM_TIME_MONOTONIC); + mDisplayEventReceiver.requestNextVsync(); + } +} + +void PointerControllerContext::resetInactivityTimeout() { + std::scoped_lock lock(mLock); + resetInactivityTimeoutLocked(); +} + +void PointerControllerContext::resetInactivityTimeoutLocked() REQUIRES(mLock) { + mLooper->removeMessages(mHandler, MessageHandler::MSG_INACTIVITY_TIMEOUT); + + nsecs_t timeout = mLocked.inactivityTimeout == InactivityTimeout::SHORT + ? INACTIVITY_TIMEOUT_DELAY_TIME_SHORT + : INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL; + mLooper->sendMessageDelayed(timeout, mHandler, MessageHandler::MSG_INACTIVITY_TIMEOUT); +} + +void PointerControllerContext::removeInactivityTimeout() { + std::scoped_lock lock(mLock); + mLooper->removeMessages(mHandler, MessageHandler::MSG_INACTIVITY_TIMEOUT); +} + +void PointerControllerContext::setAnimationPending(bool animationPending) { + std::scoped_lock lock(mLock); + mLocked.animationPending = animationPending; +} + +nsecs_t PointerControllerContext::getAnimationTime() { + std::scoped_lock lock(mLock); + return mLocked.animationTime; +} + +void PointerControllerContext::setHandlerController(std::shared_ptr<PointerController> controller) { + mHandler->pointerController = controller; +} + +void PointerControllerContext::setCallbackController( + std::shared_ptr<PointerController> controller) { + mCallback->pointerController = controller; +} + +sp<PointerControllerPolicyInterface> PointerControllerContext::getPolicy() { + return mPolicy; +} + +sp<SpriteController> PointerControllerContext::getSpriteController() { + return mSpriteController; +} + +void PointerControllerContext::initializeDisplayEventReceiver() { + if (mDisplayEventReceiver.initCheck() == NO_ERROR) { + mLooper->addFd(mDisplayEventReceiver.getFd(), Looper::POLL_CALLBACK, Looper::EVENT_INPUT, + mCallback, nullptr); + } else { + ALOGE("Failed to initialize DisplayEventReceiver."); + } +} + +void PointerControllerContext::handleDisplayEvents() { + bool gotVsync = false; + ssize_t n; + nsecs_t timestamp; + DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE]; + while ((n = mDisplayEventReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) { + for (size_t i = 0; i < static_cast<size_t>(n); ++i) { + if (buf[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) { + timestamp = buf[i].header.timestamp; + gotVsync = true; + } + } + } + if (gotVsync) { + mController.doAnimate(timestamp); + } +} + +void PointerControllerContext::MessageHandler::handleMessage(const Message& message) { + std::shared_ptr<PointerController> controller = pointerController.lock(); + + if (controller == nullptr) { + ALOGE("PointerController instance was released before processing message: what=%d", + message.what); + return; + } + switch (message.what) { + case MSG_INACTIVITY_TIMEOUT: + controller->doInactivityTimeout(); + break; + } +} + +int PointerControllerContext::LooperCallback::handleEvent(int /* fd */, int events, + void* /* data */) { + std::shared_ptr<PointerController> controller = pointerController.lock(); + if (controller == nullptr) { + ALOGW("PointerController instance was released with pending callbacks. events=0x%x", + events); + return 0; // Remove the callback, the PointerController is gone anyways + } + if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) { + ALOGE("Display event receiver pipe was closed or an error occurred. events=0x%x", events); + return 0; // remove the callback + } + + if (!(events & Looper::EVENT_INPUT)) { + ALOGW("Received spurious callback for unhandled poll event. events=0x%x", events); + return 1; // keep the callback + } + + controller->mContext.handleDisplayEvents(); + return 1; // keep the callback +} + +} // namespace android diff --git a/libs/input/PointerControllerContext.h b/libs/input/PointerControllerContext.h new file mode 100644 index 000000000000..92e1bda25f56 --- /dev/null +++ b/libs/input/PointerControllerContext.h @@ -0,0 +1,154 @@ +/* + * 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. + */ + +#ifndef _UI_POINTER_CONTROLLER_CONTEXT_H +#define _UI_POINTER_CONTROLLER_CONTEXT_H + +#include <PointerControllerInterface.h> +#include <gui/DisplayEventReceiver.h> +#include <input/DisplayViewport.h> +#include <input/Input.h> +#include <ui/DisplayInfo.h> +#include <utils/BitSet.h> +#include <utils/Looper.h> +#include <utils/RefBase.h> + +#include <map> +#include <memory> +#include <vector> + +#include "SpriteController.h" + +namespace android { + +class PointerController; + +/* + * Pointer resources. + */ +struct PointerResources { + SpriteIcon spotHover; + SpriteIcon spotTouch; + SpriteIcon spotAnchor; +}; + +struct PointerAnimation { + std::vector<SpriteIcon> animationFrames; + nsecs_t durationPerFrame; +}; + +enum class InactivityTimeout { + NORMAL = 0, + SHORT = 1, +}; + +/* + * Pointer controller policy interface. + * + * The pointer controller policy is used by the pointer controller to interact with + * the Window Manager and other system components. + * + * The actual implementation is partially supported by callbacks into the DVM + * via JNI. This interface is also mocked in the unit tests. + */ +class PointerControllerPolicyInterface : public virtual RefBase { +protected: + PointerControllerPolicyInterface() {} + virtual ~PointerControllerPolicyInterface() {} + +public: + virtual void loadPointerIcon(SpriteIcon* icon, int32_t displayId) = 0; + virtual void loadPointerResources(PointerResources* outResources, int32_t displayId) = 0; + virtual void loadAdditionalMouseResources( + std::map<int32_t, SpriteIcon>* outResources, + std::map<int32_t, PointerAnimation>* outAnimationResources, int32_t displayId) = 0; + virtual int32_t getDefaultPointerIconId() = 0; + virtual int32_t getCustomPointerIconId() = 0; +}; + +/* + * Contains logic and resources shared among PointerController, + * MouseCursorController, and TouchSpotController. + */ + +class PointerControllerContext { +public: + PointerControllerContext(const sp<PointerControllerPolicyInterface>& policy, + const sp<Looper>& looper, const sp<SpriteController>& spriteController, + PointerController& controller); + ~PointerControllerContext(); + + void removeInactivityTimeout(); + void resetInactivityTimeout(); + void startAnimation(); + void setInactivityTimeout(InactivityTimeout inactivityTimeout); + + void setAnimationPending(bool animationPending); + nsecs_t getAnimationTime(); + + void clearSpotsByDisplay(int32_t displayId); + + void setHandlerController(std::shared_ptr<PointerController> controller); + void setCallbackController(std::shared_ptr<PointerController> controller); + + sp<PointerControllerPolicyInterface> getPolicy(); + sp<SpriteController> getSpriteController(); + + void initializeDisplayEventReceiver(); + void handleDisplayEvents(); + + class MessageHandler : public virtual android::MessageHandler { + public: + enum { + MSG_INACTIVITY_TIMEOUT, + }; + + void handleMessage(const Message& message) override; + std::weak_ptr<PointerController> pointerController; + }; + + class LooperCallback : public virtual android::LooperCallback { + public: + int handleEvent(int fd, int events, void* data) override; + std::weak_ptr<PointerController> pointerController; + }; + +private: + sp<PointerControllerPolicyInterface> mPolicy; + sp<Looper> mLooper; + sp<SpriteController> mSpriteController; + sp<MessageHandler> mHandler; + sp<LooperCallback> mCallback; + + DisplayEventReceiver mDisplayEventReceiver; + + PointerController& mController; + + mutable std::mutex mLock; + + struct Locked { + bool animationPending; + nsecs_t animationTime; + + InactivityTimeout inactivityTimeout; + } mLocked GUARDED_BY(mLock); + + void resetInactivityTimeoutLocked(); +}; + +} // namespace android + +#endif // _UI_POINTER_CONTROLLER_CONTEXT_H diff --git a/libs/input/TouchSpotController.cpp b/libs/input/TouchSpotController.cpp new file mode 100644 index 000000000000..c7430ceead41 --- /dev/null +++ b/libs/input/TouchSpotController.cpp @@ -0,0 +1,236 @@ +/* + * 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. + */ + +#define LOG_TAG "TouchSpotController" + +// Log debug messages about pointer updates +#define DEBUG_SPOT_UPDATES 0 + +#include "TouchSpotController.h" + +#include <log/log.h> + +#include <SkBitmap.h> +#include <SkBlendMode.h> +#include <SkCanvas.h> +#include <SkColor.h> +#include <SkPaint.h> + +namespace { +// Time to spend fading out the spot completely. +const nsecs_t SPOT_FADE_DURATION = 200 * 1000000LL; // 200 ms +} // namespace + +namespace android { + +// --- Spot --- + +void TouchSpotController::Spot::updateSprite(const SpriteIcon* icon, float x, float y, + int32_t displayId) { + sprite->setLayer(Sprite::BASE_LAYER_SPOT + id); + sprite->setAlpha(alpha); + sprite->setTransformationMatrix(SpriteTransformationMatrix(scale, 0.0f, 0.0f, scale)); + sprite->setPosition(x, y); + sprite->setDisplayId(displayId); + this->x = x; + this->y = y; + + if (icon != mLastIcon) { + mLastIcon = icon; + if (icon) { + sprite->setIcon(*icon); + sprite->setVisible(true); + } else { + sprite->setVisible(false); + } + } +} + +// --- TouchSpotController --- + +TouchSpotController::TouchSpotController(int32_t displayId, PointerControllerContext& context) + : mDisplayId(displayId), mContext(context) { + mContext.getPolicy()->loadPointerResources(&mResources, mDisplayId); +} + +TouchSpotController::~TouchSpotController() { + std::scoped_lock lock(mLock); + + size_t numSpots = mLocked.displaySpots.size(); + for (size_t i = 0; i < numSpots; i++) { + delete mLocked.displaySpots[i]; + } + mLocked.displaySpots.clear(); +} + +void TouchSpotController::setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, + BitSet32 spotIdBits) { +#if DEBUG_SPOT_UPDATES + ALOGD("setSpots: idBits=%08x", spotIdBits.value); + for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) { + uint32_t id = idBits.firstMarkedBit(); + idBits.clearBit(id); + const PointerCoords& c = spotCoords[spotIdToIndex[id]]; + ALOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f, displayId=%" PRId32 ".", id, + c.getAxisValue(AMOTION_EVENT_AXIS_X), c.getAxisValue(AMOTION_EVENT_AXIS_Y), + c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), displayId); + } +#endif + + std::scoped_lock lock(mLock); + sp<SpriteController> spriteController = mContext.getSpriteController(); + spriteController->openTransaction(); + + // Add or move spots for fingers that are down. + for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + const PointerCoords& c = spotCoords[spotIdToIndex[id]]; + const SpriteIcon& icon = c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE) > 0 + ? mResources.spotTouch + : mResources.spotHover; + float x = c.getAxisValue(AMOTION_EVENT_AXIS_X); + float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y); + + Spot* spot = getSpot(id, mLocked.displaySpots); + if (!spot) { + spot = createAndAddSpotLocked(id, mLocked.displaySpots); + } + + spot->updateSprite(&icon, x, y, mDisplayId); + } + + for (Spot* spot : mLocked.displaySpots) { + if (spot->id != Spot::INVALID_ID && !spotIdBits.hasBit(spot->id)) { + fadeOutAndReleaseSpotLocked(spot); + } + } + + spriteController->closeTransaction(); +} + +void TouchSpotController::clearSpots() { +#if DEBUG_SPOT_UPDATES + ALOGD("clearSpots"); +#endif + + std::scoped_lock lock(mLock); + fadeOutAndReleaseAllSpotsLocked(); +} + +TouchSpotController::Spot* TouchSpotController::getSpot(uint32_t id, + const std::vector<Spot*>& spots) { + for (size_t i = 0; i < spots.size(); i++) { + Spot* spot = spots[i]; + if (spot->id == id) { + return spot; + } + } + return nullptr; +} + +TouchSpotController::Spot* TouchSpotController::createAndAddSpotLocked(uint32_t id, + std::vector<Spot*>& spots) { + // Remove spots until we have fewer than MAX_SPOTS remaining. + while (spots.size() >= MAX_SPOTS) { + Spot* spot = removeFirstFadingSpotLocked(spots); + if (!spot) { + spot = spots[0]; + spots.erase(spots.begin()); + } + releaseSpotLocked(spot); + } + + // Obtain a sprite from the recycled pool. + sp<Sprite> sprite; + if (!mLocked.recycledSprites.empty()) { + sprite = mLocked.recycledSprites.back(); + mLocked.recycledSprites.pop_back(); + } else { + sprite = mContext.getSpriteController()->createSprite(); + } + + // Return the new spot. + Spot* spot = new Spot(id, sprite); + spots.push_back(spot); + return spot; +} + +TouchSpotController::Spot* TouchSpotController::removeFirstFadingSpotLocked( + std::vector<Spot*>& spots) REQUIRES(mLock) { + for (size_t i = 0; i < spots.size(); i++) { + Spot* spot = spots[i]; + if (spot->id == Spot::INVALID_ID) { + spots.erase(spots.begin() + i); + return spot; + } + } + return NULL; +} + +void TouchSpotController::releaseSpotLocked(Spot* spot) REQUIRES(mLock) { + spot->sprite->clearIcon(); + + if (mLocked.recycledSprites.size() < MAX_RECYCLED_SPRITES) { + mLocked.recycledSprites.push_back(spot->sprite); + } + + delete spot; +} + +void TouchSpotController::fadeOutAndReleaseSpotLocked(Spot* spot) REQUIRES(mLock) { + if (spot->id != Spot::INVALID_ID) { + spot->id = Spot::INVALID_ID; + mContext.startAnimation(); + } +} + +void TouchSpotController::fadeOutAndReleaseAllSpotsLocked() REQUIRES(mLock) { + size_t numSpots = mLocked.displaySpots.size(); + for (size_t i = 0; i < numSpots; i++) { + Spot* spot = mLocked.displaySpots[i]; + fadeOutAndReleaseSpotLocked(spot); + } +} + +void TouchSpotController::reloadSpotResources() { + mContext.getPolicy()->loadPointerResources(&mResources, mDisplayId); +} + +bool TouchSpotController::doFadingAnimation(nsecs_t timestamp, bool keepAnimating) { + std::scoped_lock lock(mLock); + nsecs_t animationTime = mContext.getAnimationTime(); + nsecs_t frameDelay = timestamp - animationTime; + size_t numSpots = mLocked.displaySpots.size(); + for (size_t i = 0; i < numSpots;) { + Spot* spot = mLocked.displaySpots[i]; + if (spot->id == Spot::INVALID_ID) { + spot->alpha -= float(frameDelay) / SPOT_FADE_DURATION; + if (spot->alpha <= 0) { + mLocked.displaySpots.erase(mLocked.displaySpots.begin() + i); + releaseSpotLocked(spot); + numSpots--; + continue; + } else { + spot->sprite->setAlpha(spot->alpha); + keepAnimating = true; + } + } + ++i; + } + return keepAnimating; +} + +} // namespace android diff --git a/libs/input/TouchSpotController.h b/libs/input/TouchSpotController.h new file mode 100644 index 000000000000..f3b355010bee --- /dev/null +++ b/libs/input/TouchSpotController.h @@ -0,0 +1,91 @@ +/* + * 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. + */ + +#ifndef _UI_TOUCH_SPOT_CONTROLLER_H +#define _UI_TOUCH_SPOT_CONTROLLER_H + +#include "PointerControllerContext.h" + +namespace android { + +/* + * Helper class for PointerController that specifically handles + * touch spot resources and actions for a single display. + */ +class TouchSpotController { +public: + TouchSpotController(int32_t displayId, PointerControllerContext& context); + ~TouchSpotController(); + void setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, + BitSet32 spotIdBits); + void clearSpots(); + + void reloadSpotResources(); + bool doFadingAnimation(nsecs_t timestamp, bool keepAnimating); + +private: + struct Spot { + static const uint32_t INVALID_ID = 0xffffffff; + + uint32_t id; + sp<Sprite> sprite; + float alpha; + float scale; + float x, y; + + inline Spot(uint32_t id, const sp<Sprite>& sprite) + : id(id), + sprite(sprite), + alpha(1.0f), + scale(1.0f), + x(0.0f), + y(0.0f), + mLastIcon(nullptr) {} + + void updateSprite(const SpriteIcon* icon, float x, float y, int32_t displayId); + + private: + const SpriteIcon* mLastIcon; + }; + + int32_t mDisplayId; + + mutable std::mutex mLock; + + PointerResources mResources; + + PointerControllerContext& mContext; + + static constexpr size_t MAX_RECYCLED_SPRITES = 12; + static constexpr size_t MAX_SPOTS = 12; + + struct Locked { + std::vector<Spot*> displaySpots; + std::vector<sp<Sprite>> recycledSprites; + + } mLocked GUARDED_BY(mLock); + + Spot* getSpot(uint32_t id, const std::vector<Spot*>& spots); + Spot* createAndAddSpotLocked(uint32_t id, std::vector<Spot*>& spots); + Spot* removeFirstFadingSpotLocked(std::vector<Spot*>& spots); + void releaseSpotLocked(Spot* spot); + void fadeOutAndReleaseSpotLocked(Spot* spot); + void fadeOutAndReleaseAllSpotsLocked(); +}; + +} // namespace android + +#endif // _UI_TOUCH_SPOT_CONTROLLER_H diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp index 6e129a064385..b67088a389b6 100644 --- a/libs/input/tests/PointerController_test.cpp +++ b/libs/input/tests/PointerController_test.cpp @@ -178,9 +178,6 @@ void PointerControllerTest::ensureDisplayViewportIsSet() { viewport.deviceWidth = 400; viewport.deviceHeight = 300; mPointerController->setDisplayViewport(viewport); - - // The first call to setDisplayViewport should trigger the loading of the necessary resources. - EXPECT_TRUE(mPolicy->allResourcesAreLoaded()); } void PointerControllerTest::loopThread() { @@ -208,6 +205,7 @@ TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) { TEST_F(PointerControllerTest, updatePointerIcon) { ensureDisplayViewportIsSet(); + mPointerController->setPresentation(PointerController::Presentation::POINTER); mPointerController->unfade(PointerController::Transition::IMMEDIATE); int32_t type = CURSOR_TYPE_ADDITIONAL; @@ -247,8 +245,6 @@ TEST_F(PointerControllerTest, setCustomPointerIcon) { TEST_F(PointerControllerTest, doesNotGetResourcesBeforeSettingViewport) { mPointerController->setPresentation(PointerController::Presentation::POINTER); - mPointerController->setSpots(nullptr, nullptr, BitSet32(), -1); - mPointerController->clearSpots(); mPointerController->setPosition(1.0f, 1.0f); mPointerController->move(1.0f, 1.0f); mPointerController->unfade(PointerController::Transition::IMMEDIATE); diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java index 6fa378724240..2b3f420cd834 100644 --- a/media/java/android/media/MediaRouter.java +++ b/media/java/android/media/MediaRouter.java @@ -1616,9 +1616,9 @@ public class MediaRouter { Drawable mIcon; // playback information int mPlaybackType = PLAYBACK_TYPE_LOCAL; - int mVolumeMax = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME; - int mVolume = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME; - int mVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING; + int mVolumeMax = DEFAULT_PLAYBACK_MAX_VOLUME; + int mVolume = DEFAULT_PLAYBACK_VOLUME; + int mVolumeHandling = PLAYBACK_VOLUME_VARIABLE; int mPlaybackStream = AudioManager.STREAM_MUSIC; VolumeCallbackInfo mVcb; Display mPresentationDisplay; @@ -1722,6 +1722,21 @@ public class MediaRouter { */ public final static int PLAYBACK_VOLUME_VARIABLE = 1; + /** + * Default playback max volume if not set. + * Hard-coded to the same number of steps as AudioService.MAX_STREAM_VOLUME[STREAM_MUSIC] + * + * @see #getVolumeMax() + */ + private static final int DEFAULT_PLAYBACK_MAX_VOLUME = 15; + + /** + * Default playback volume if not set. + * + * @see #getVolume() + */ + private static final int DEFAULT_PLAYBACK_VOLUME = DEFAULT_PLAYBACK_MAX_VOLUME; + RouteInfo(RouteCategory category) { mCategory = category; mDeviceType = DEVICE_TYPE_UNKNOWN; @@ -2430,13 +2445,13 @@ public class MediaRouter { } return; } - if (mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE) { + if (mPlaybackType == PLAYBACK_TYPE_REMOTE) { int volumeControl = VolumeProvider.VOLUME_CONTROL_FIXED; switch (mVolumeHandling) { - case RemoteControlClient.PLAYBACK_VOLUME_VARIABLE: + case PLAYBACK_VOLUME_VARIABLE: volumeControl = VolumeProvider.VOLUME_CONTROL_ABSOLUTE; break; - case RemoteControlClient.PLAYBACK_VOLUME_FIXED: + case PLAYBACK_VOLUME_FIXED: default: break; } diff --git a/media/java/android/media/audiopolicy/AudioPolicyConfig.java b/media/java/android/media/audiopolicy/AudioPolicyConfig.java index 561a8847feed..697d80c6b78e 100644 --- a/media/java/android/media/audiopolicy/AudioPolicyConfig.java +++ b/media/java/android/media/audiopolicy/AudioPolicyConfig.java @@ -220,6 +220,34 @@ public class AudioPolicyConfig implements Parcelable { return textDump; } + /** + * Very short dump of configuration + * @return a condensed dump of configuration, uniquely identifies a policy in a log + */ + public String toCompactLogString() { + String compactDump = "reg:" + mRegistrationId; + int mixNum = 0; + for (AudioMix mix : mMixes) { + compactDump += " Mix:" + mixNum + "-Typ:" + mixTypePrefix(mix.getMixType()) + + "-Rul:" + mix.getRule().getCriteria().size(); + mixNum++; + } + return compactDump; + } + + private static String mixTypePrefix(int mixType) { + switch (mixType) { + case AudioMix.MIX_TYPE_PLAYERS: + return "p"; + case AudioMix.MIX_TYPE_RECORDERS: + return "r"; + case AudioMix.MIX_TYPE_INVALID: + default: + return "#"; + + } + } + protected void reset() { mMixCounter = 0; } diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java index 8bf462c5a5cf..70bd1609ddbd 100644 --- a/media/java/android/media/session/MediaSession.java +++ b/media/java/android/media/session/MediaSession.java @@ -19,6 +19,7 @@ package android.media.session; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.app.Activity; import android.app.PendingIntent; import android.compat.annotation.UnsupportedAppUsage; @@ -105,6 +106,7 @@ public final class MediaSession { * * @hide */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public static final int FLAG_EXCLUSIVE_GLOBAL_PRIORITY = 1 << 16; /** diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt index 54618a5da3a3..70ef69d8ce72 100644 --- a/non-updatable-api/current.txt +++ b/non-updatable-api/current.txt @@ -11900,6 +11900,7 @@ package android.content.pm { field public static final int INVALID_ID = -1; // 0xffffffff field public static final int STAGED_SESSION_ACTIVATION_FAILED = 2; // 0x2 field public static final int STAGED_SESSION_NO_ERROR = 0; // 0x0 + field public static final int STAGED_SESSION_OTHER_ERROR = 4; // 0x4 field public static final int STAGED_SESSION_UNKNOWN = 3; // 0x3 field public static final int STAGED_SESSION_VERIFICATION_FAILED = 1; // 0x1 } diff --git a/non-updatable-api/module-lib-current.txt b/non-updatable-api/module-lib-current.txt index 3c0b955119a6..86ac3e477dc4 100644 --- a/non-updatable-api/module-lib-current.txt +++ b/non-updatable-api/module-lib-current.txt @@ -39,6 +39,14 @@ package android.media { } +package android.media.session { + + public final class MediaSession { + field public static final int FLAG_EXCLUSIVE_GLOBAL_PRIORITY = 65536; // 0x10000 + } + +} + package android.os { public class Binder implements android.os.IBinder { diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt index 844e929774a1..10887fabb5c7 100644 --- a/non-updatable-api/system-current.txt +++ b/non-updatable-api/system-current.txt @@ -11575,6 +11575,9 @@ package android.webkit { public interface PacProcessor { method @Nullable public String findProxyForUrl(@NonNull String); method @NonNull public static android.webkit.PacProcessor getInstance(); + method @NonNull public static android.webkit.PacProcessor getInstanceForNetwork(long); + method public default long getNetworkHandle(); + method public default void releasePacProcessor(); method public boolean setProxyScript(@NonNull String); } @@ -11714,6 +11717,7 @@ package android.webkit { method public android.webkit.CookieManager getCookieManager(); method public android.webkit.GeolocationPermissions getGeolocationPermissions(); method @NonNull public default android.webkit.PacProcessor getPacProcessor(); + method @NonNull public default android.webkit.PacProcessor getPacProcessorForNetwork(long); method public android.webkit.ServiceWorkerController getServiceWorkerController(); method public android.webkit.WebViewFactoryProvider.Statics getStatics(); method @Deprecated public android.webkit.TokenBindingService getTokenBindingService(); diff --git a/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppDetector.java b/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppDetector.java index 5dcb9de4755e..a2cd0446d1e8 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppDetector.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppDetector.java @@ -39,7 +39,7 @@ import javax.inject.Singleton; /** * A class that detects unsafe apps. - * An app is considered safe if is a system app or installed through whitelisted sources. + * An app is considered safe if is a system app or installed through allowed sources. */ @Singleton public class SideLoadedAppDetector { diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java index 31f1170c9603..f77294e37b98 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java @@ -19,6 +19,8 @@ package com.android.systemui.car.voicerecognition; import static com.android.systemui.car.voicerecognition.ConnectedDeviceVoiceRecognitionNotifier.INVALID_VALUE; import static com.android.systemui.car.voicerecognition.ConnectedDeviceVoiceRecognitionNotifier.VOICE_RECOGNITION_STARTED; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; @@ -40,11 +42,13 @@ import com.android.systemui.car.CarSystemUiTest; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; @CarSystemUiTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @SmallTest +// TODO(b/162866441): Refactor to use the Executor pattern instead. public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase { private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; @@ -52,13 +56,15 @@ public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase { private ConnectedDeviceVoiceRecognitionNotifier mVoiceRecognitionNotifier; private TestableLooper mTestableLooper; + private Handler mHandler; private Handler mTestHandler; private BluetoothDevice mBluetoothDevice; @Before public void setUp() throws Exception { mTestableLooper = TestableLooper.get(this); - mTestHandler = spy(new Handler(mTestableLooper.getLooper())); + mHandler = new Handler(mTestableLooper.getLooper()); + mTestHandler = spy(mHandler); mBluetoothDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice( BLUETOOTH_REMOTE_ADDRESS); mVoiceRecognitionNotifier = new ConnectedDeviceVoiceRecognitionNotifier( @@ -74,8 +80,14 @@ public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase { mContext.sendBroadcast(intent, BLUETOOTH_PERM); mTestableLooper.processAllMessages(); - - verify(mTestHandler).post(any()); + waitForIdleSync(); + + mHandler.post(() -> { + ArgumentCaptor<Runnable> argumentCaptor = ArgumentCaptor.forClass(Runnable.class); + verify(mTestHandler).post(argumentCaptor.capture()); + assertThat(argumentCaptor.getValue()).isNotNull(); + assertThat(argumentCaptor.getValue()).isNotEqualTo(this); + }); } @Test @@ -86,8 +98,11 @@ public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase { mContext.sendBroadcast(intent, BLUETOOTH_PERM); mTestableLooper.processAllMessages(); + waitForIdleSync(); - verify(mTestHandler, never()).post(any()); + mHandler.post(() -> { + verify(mTestHandler, never()).post(any()); + }); } @Test @@ -97,8 +112,11 @@ public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase { mContext.sendBroadcast(intent, BLUETOOTH_PERM); mTestableLooper.processAllMessages(); + waitForIdleSync(); - verify(mTestHandler, never()).post(any()); + mHandler.post(() -> { + verify(mTestHandler, never()).post(any()); + }); } @Test @@ -108,7 +126,10 @@ public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase { mContext.sendBroadcast(intent, BLUETOOTH_PERM); mTestableLooper.processAllMessages(); + waitForIdleSync(); - verify(mTestHandler, never()).post(any()); + mHandler.post(() -> { + verify(mTestHandler, never()).post(any()); + }); } } diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/MasterSwitchController.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/PrimarySwitchController.java index a12aa83e9d4b..a08f566b8375 100644 --- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/MasterSwitchController.java +++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/PrimarySwitchController.java @@ -19,9 +19,9 @@ package com.android.settingslib.drawer; import android.os.Bundle; /** - * A controller that manages event for master switch. + * A controller that manages event for Primary switch. */ -public abstract class MasterSwitchController extends SwitchController { +public abstract class PrimarySwitchController extends SwitchController { @Override protected final MetaData getMetaData() { diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/SwitchesProvider.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/SwitchesProvider.java index 73f1a904b04b..f2b3e30dc252 100644 --- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/SwitchesProvider.java +++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/SwitchesProvider.java @@ -88,7 +88,7 @@ public abstract class SwitchesProvider extends ContentProvider { controller.setAuthority(mAuthority); mControllerMap.put(key, controller); - if (!(controller instanceof MasterSwitchController)) { + if (!(controller instanceof PrimarySwitchController)) { mSwitchDataList.add(controller.getBundle()); } }); @@ -116,7 +116,7 @@ public abstract class SwitchesProvider extends ContentProvider { switch (method) { case METHOD_GET_SWITCH_DATA: - if (!(controller instanceof MasterSwitchController)) { + if (!(controller instanceof PrimarySwitchController)) { return controller.getBundle(); } break; diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java index 1e4c7cac4404..52d2b3c919d9 100644 --- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java +++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java @@ -376,8 +376,12 @@ public abstract class Tile implements Parcelable { * Check whether tile only has primary profile. */ public boolean isPrimaryProfileOnly() { - String profile = mMetaData != null - ? mMetaData.getString(META_DATA_KEY_PROFILE) : PROFILE_ALL; + return isPrimaryProfileOnly(mMetaData); + } + + static boolean isPrimaryProfileOnly(Bundle metaData) { + String profile = metaData != null + ? metaData.getString(META_DATA_KEY_PROFILE) : PROFILE_ALL; profile = (profile != null ? profile : PROFILE_ALL); return TextUtils.equals(profile, PROFILE_PRIMARY); } diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java index ace50f30663d..49f6bd8c3334 100644 --- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java +++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java @@ -339,6 +339,16 @@ public class TileUtils { private static void loadTile(UserHandle user, Map<Pair<String, String>, Tile> addedCache, String defaultCategory, List<Tile> outTiles, Intent intent, Bundle metaData, ComponentInfo componentInfo) { + // Skip loading tile if the component is tagged primary_profile_only but not running on + // the current user. + if (user.getIdentifier() != ActivityManager.getCurrentUser() + && Tile.isPrimaryProfileOnly(componentInfo.metaData)) { + Log.w(LOG_TAG, "Found " + componentInfo.name + " for intent " + + intent + " is primary profile only, skip loading tile for uid " + + user.getIdentifier()); + return; + } + String categoryKey = defaultCategory; // Load category if ((metaData == null || !metaData.containsKey(EXTRA_CATEGORY_KEY)) diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index 704d264d1983..6751fa4583c5 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Wys Bluetooth-toestelle sonder name"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Deaktiveer absolute volume"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Aktiveer Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Verbeterde konnektiwiteit"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP-weergawe"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Kies Bluetooth AVRCP-weergawe"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP-weergawe"</string> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index 585924d7efb9..470e780fc049 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"የብሉቱዝ መሣሪያዎችን ያለ ስሞች አሳይ"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ፍጹማዊ ድምፅን አሰናክል"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorscheን አንቃ"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"የተሻሻለ ተገናኝነት"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"የብሉቱዝ AVRCP ስሪት"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"የብሉቱዝ AVRCP ስሪት ይምረጡ"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"የብሉቱዝ MAP ስሪት"</string> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index 39777cd5f881..9acfa0da7747 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -152,7 +152,7 @@ <string name="user_guest" msgid="6939192779649870792">"ضيف"</string> <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_some" msgid="3631650616557252926">"تم ضبط بعض الإعدادات التلقائية"</string> <string name="launch_defaults_none" msgid="8049374306261262709">"لم يتم تعيين إعدادات تلقائية"</string> <string name="tts_settings" msgid="8130616705989351312">"إعدادات تحويل النص إلى كلام"</string> <string name="tts_settings_title" msgid="7602210956640483039">"تحويل النص إلى كلام"</string> @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"عرض أجهزة البلوتوث بدون أسماء"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"إيقاف مستوى الصوت المطلق"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"تفعيل Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"إمكانية اتصال محسّن"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"إصدار Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"اختيار إصدار Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"إصدار Bluetooth MAP"</string> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index e0455cb5f19c..f993dba39476 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"নামবিহীন ব্লুটুথ ডিভাইচসমূহ দেখুৱাওক"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"পূৰ্ণ মাত্ৰাৰ ভলিউম অক্ষম কৰক"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche সক্ষম কৰক"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"উন্নত সংযোগ"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ব্লুটুথ AVRCP সংস্কৰণ"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ব্লুটুথ AVRCP সংস্কৰণ বাছনি কৰক"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ব্লুটুথ MAP সংস্কৰণ"</string> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index 6565d530f6a1..6a506614f970 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth cihazlarını adsız göstərin"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Mütləq səs həcmi deaktiv edin"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche\'ni aktiv edin"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Təkmilləşdirilmiş Bağlantı"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP Versiya"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP Versiyasını seçin"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP Versiyası"</string> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index 3ced29b47dd6..cf988ab3589f 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Prikaži Bluetooth uređaje bez naziva"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Onemogući glavno podešavanje jačine zvuka"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Omogući Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Poboljšano povezivanje"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Verzija Bluetooth AVRCP-a"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Izaberite verziju Bluetooth AVRCP-a"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Verzija Bluetooth MAP-a"</string> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index 01d7682416fa..8f71509772fc 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Паказваць прылады Bluetooth без назваў"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Адключыць абсалютны гук"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Уключыць Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Палепшанае падключэнне"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Версія Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Выбраць версію Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Версія Bluetooth MAP"</string> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index d042c0f89b1e..747cb266b6de 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Показване на устройствата с Bluetooth без имена"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Деактивиране на пълната сила на звука"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Активиране на Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Подобрена свързаност"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Версия на AVRCP за Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Избиране на версия на AVRCP за Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"MAP версия за Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index 2db23f73e683..87f3b7a4f2e8 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"নামহীন ব্লুটুথ ডিভাইসগুলি দেখুন"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"চূড়ান্ত ভলিউম অক্ষম করুন"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche ফিচার চালু করুন"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"কানেক্টিভিটি উন্নত করা হয়েছে"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ব্লুটুথ AVRCP ভার্সন"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ব্লুটুথ AVRCP ভার্সন বেছে নিন"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ব্লুটুথ MAP ভার্সন"</string> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index f26fe9d58533..e329c993eedb 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Prikaži Bluetooth uređaje bez naziva"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Onemogući apsolutnu jačinu zvuka"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Omogući Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Poboljšana povezivost"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP verzija"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Odaberite Bluetooth AVRCP verziju"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP verzija"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index 3ca9d5272d48..5ffdacd19c22 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostra els dispositius Bluetooth sense el nom"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Desactiva el volum absolut"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Activa Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Connectivitat millorada"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versió AVRCP de Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecciona la versió AVRCP de Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versió MAP de Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index a5532e0bff02..0aef99feb04f 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Zobrazovat zařízení Bluetooth bez názvů"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Zakázat absolutní hlasitost"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Zapnout funkci Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Lepší připojování"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Verze profilu Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Vyberte verzi profilu Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Verze MAP pro Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index 8ca22d7ba797..98068cb172fa 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Vis Bluetooth-enheder uden navne"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Deaktiver absolut lydstyrke"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Aktivér Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Enhanced Connectivity"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"AVRCP-version for Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Vælg AVRCP-version for Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"MAP-version for Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index 6b0ae2e24bb6..0837ad33ab06 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth-Geräte ohne Namen anzeigen"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Absolute Lautstärkeregelung deaktivieren"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Bluetooth-Gabeldorsche aktivieren"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Verbesserte Konnektivität"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP-Version"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP-Version auswählen"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP-Version"</string> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index 4d7c88287f72..1f9d97764016 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Εμφάνιση συσκευών Bluetooth χωρίς ονόματα"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Απενεργοποίηση απόλυτης έντασης"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Ενεργοποίηση Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Βελτιωμένη συνδεσιμότητα"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Έκδοση AVRCP Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Επιλογή έκδοσης AVRCP Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Έκδοση MAP Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index cc3b2aa33695..abe61a97096c 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Show Bluetooth devices without names"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Disable absolute volume"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Enable Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Enhanced connectivity"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP version"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Select Bluetooth AVRCP Version"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP version"</string> diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml index a9f039a6522c..959ad46bcf61 100644 --- a/packages/SettingsLib/res/values-en-rCA/strings.xml +++ b/packages/SettingsLib/res/values-en-rCA/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Show Bluetooth devices without names"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Disable absolute volume"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Enable Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Enhanced connectivity"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP version"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Select Bluetooth AVRCP Version"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP version"</string> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index cc3b2aa33695..abe61a97096c 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Show Bluetooth devices without names"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Disable absolute volume"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Enable Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Enhanced connectivity"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP version"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Select Bluetooth AVRCP Version"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP version"</string> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index cc3b2aa33695..abe61a97096c 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Show Bluetooth devices without names"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Disable absolute volume"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Enable Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Enhanced connectivity"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP version"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Select Bluetooth AVRCP Version"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP version"</string> diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml index 41c20e0c915f..738dd2a2ba31 100644 --- a/packages/SettingsLib/res/values-en-rXC/strings.xml +++ b/packages/SettingsLib/res/values-en-rXC/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Show Bluetooth devices without names"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Disable absolute volume"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Enable Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Enhanced Connectivity"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP Version"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Select Bluetooth AVRCP Version"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP Version"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index 287a1aca2fbd..d1e4fb5607a8 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostrar dispositivos Bluetooth sin nombre"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Inhabilitar volumen absoluto"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Habilitar Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Conectividad mejorada"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versión de AVRCP del Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecciona la versión de AVRCP del Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versión de MAP de Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index 9d3455766f6b..9ad71e2766a3 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostrar dispositivos Bluetooth sin nombre"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Inhabilitar volumen absoluto"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Habilitar Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Conectividad mejorada"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versión AVRCP de Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecciona la versión AVRCP de Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versión de MAP de Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index d003ef0b9c71..14d3b5782862 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Kuva ilma nimedeta Bluetoothi seadmed"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Keela absoluutne helitugevus"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Luba Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Täiustatud ühenduvus"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetoothi AVRCP versioon"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Valige Bluetoothi AVRCP versioon"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetoothi MAP-i versioon"</string> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index 0042321a3654..dcdadbdf3454 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Erakutsi Bluetooth bidezko gailuak izenik gabe"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Desgaitu bolumen absolutua"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gaitu Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Konexio hobeak"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP bertsioa"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Hautatu Bluetooth AVRCP bertsioa"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAParen bertsioa"</string> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index 1c0881531f89..f3b22d355b77 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"نمایش دستگاههای بلوتوث بدون نام"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"غیرفعال کردن میزان صدای مطلق"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"فعال کردن Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"اتصال بهبودیافته"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"نسخه AVRCP بلوتوث"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"انتخاب نسخه AVRCP بلوتوث"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"نسخه MAP بلوتوث"</string> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index 3945e5542eb7..3d28f1d0a1f5 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Näytä nimettömät Bluetooth-laitteet"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Poista yleinen äänenvoimakkuuden säätö käytöstä"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Ota Gabeldorsche käyttöön"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Parannetut yhteydet"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetoothin AVRCP-versio"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Valitse Bluetoothin AVRCP-versio"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetoothin MAP-versio"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index 140d4cee6ad7..87d3de16f216 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Afficher les appareils Bluetooth sans nom"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Désactiver le volume absolu"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Activer le Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Connectivité améliorée"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Version du profil Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Sélectionner la version du profil Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Version du profil Bluetooth MAP"</string> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index 1b1ae8ed55d4..ddf2bc0991a4 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -251,13 +251,12 @@ <string name="wifi_display_certification" msgid="1805579519992520381">"Certification affichage sans fil"</string> <string name="wifi_verbose_logging" msgid="1785910450009679371">"Autoriser l\'enregistrement d\'infos Wi-Fi détaillées"</string> <string name="wifi_scan_throttling" msgid="2985624788509913617">"Limiter la recherche Wi‑Fi"</string> - <string name="wifi_enhanced_mac_randomization" msgid="5437378364995776979">"Chgt aléatoire d\'adresse MAC sur Wi-Fi"</string> + <string name="wifi_enhanced_mac_randomization" msgid="5437378364995776979">"Chgt aléatoire d\'adresse MAC en Wi-Fi"</string> <string name="mobile_data_always_on" msgid="8275958101875563572">"Données mobiles toujours actives"</string> <string name="tethering_hardware_offload" msgid="4116053719006939161">"Accélération matérielle pour le partage de connexion"</string> <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Afficher les appareils Bluetooth sans nom"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Désactiver le volume absolu"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Activer Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Connectivité améliorée"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Version Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Sélectionner la version Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Version Bluetooth MAP"</string> @@ -284,7 +283,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> - <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_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Lorsque ce mode est activé, l\'adresse MAC de cet appareil peut changer lors de chaque connexion à un réseau Wi-Fi 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> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index f9d57c453f69..af430991083d 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostrar dispositivos Bluetooth sen nomes"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Desactivar volume absoluto"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Activar Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Conectividade mellorada"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versión de Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecciona a versión de Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versión de MAP de Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index aa1f9605b07d..3261f69b1105 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"નામ વિનાના બ્લૂટૂથ ઉપકરણો બતાવો"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ચોક્કસ વૉલ્યૂમને અક્ષમ કરો"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche ચાલુ કરો"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"વિસ્તૃત કનેક્ટિવિટી"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"બ્લૂટૂથ AVRCP સંસ્કરણ"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"બ્લૂટૂથ AVRCP સંસ્કરણ પસંદ કરો"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"બ્લૂટૂથ MAP વર્ઝન"</string> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 9b6a27ae8620..904a70e73958 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"बिना नाम वाले ब्लूटूथ डिवाइस दिखाएं"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ब्लूटूथ से आवाज़ के नियंत्रण की सुविधा रोकें"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche चालू करें"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"कनेक्टिविटी बेहतर बनाएं"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ब्लूटूथ एवीआरसीपी वर्शन"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ब्लूटूथ AVRCP वर्शन चुनें"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ब्लूटूथ का MAP वर्शन"</string> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index 14e3330a1670..3edc4527fb01 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Prikaži Bluetooth uređaje bez naziva"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Onemogući apsolutnu glasnoću"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Omogući Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Poboljšana povezivost"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Verzija AVRCP-a za Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Odaberite verziju AVRCP-a za Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Verzija MAP-a za Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index d16ff03b0903..fec2dd6d523f 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Név nélküli Bluetooth-eszközök megjelenítése"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Abszolút hangerő funkció letiltása"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"A Gabeldorsche engedélyezése"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Enhanced Connectivity"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"A Bluetooth AVRCP-verziója"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"A Bluetooth AVRCP-verziójának kiválasztása"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"A Bluetooth MAP-verziója"</string> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index b010b504b00c..f219d248e641 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Ցուցադրել Bluetooth սարքերն առանց անունների"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Անջատել ձայնի բացարձակ ուժգնությունը"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Միացնել Gabeldorsche-ը"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Տվյալների լավացված փոխանակում"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP տարբերակը"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Ընտրել Bluetooth AVRCP տարբերակը"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP-ի տարբերակ"</string> diff --git a/packages/SettingsLib/res/values-in/arrays.xml b/packages/SettingsLib/res/values-in/arrays.xml index 37cf189f26c0..3ab50cc948eb 100644 --- a/packages/SettingsLib/res/values-in/arrays.xml +++ b/packages/SettingsLib/res/values-in/arrays.xml @@ -31,7 +31,7 @@ <item msgid="7852381437933824454">"Memutus sambungan..."</item> <item msgid="5046795712175415059">"Sambungan terputus"</item> <item msgid="2473654476624070462">"Gagal"</item> - <item msgid="9146847076036105115">"Dicekal"</item> + <item msgid="9146847076036105115">"Diblokir"</item> <item msgid="4543924085816294893">"Menghindari sambungan buruk untuk sementara"</item> </string-array> <string-array name="wifi_status_with_ssid"> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index 42ccd5310913..a6f88465a673 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -180,8 +180,8 @@ <string name="tts_engine_settings_button" msgid="477155276199968948">"Luncurkan setelan mesin"</string> <string name="tts_engine_preference_section_title" msgid="3861562305498624904">"Mesin yang dipilih"</string> <string name="tts_general_section_title" msgid="8919671529502364567">"Umum"</string> - <string name="tts_reset_speech_pitch_title" msgid="7149398585468413246">"Setel ulang tinggi nada ucapan"</string> - <string name="tts_reset_speech_pitch_summary" msgid="6822904157021406449">"Setel ulang tinggi nada diucapkannya teks menjadi default."</string> + <string name="tts_reset_speech_pitch_title" msgid="7149398585468413246">"Reset tinggi nada ucapan"</string> + <string name="tts_reset_speech_pitch_summary" msgid="6822904157021406449">"Reset tinggi nada diucapkannya teks menjadi default."</string> <string-array name="tts_rate_entries"> <item msgid="9004239613505400644">"Sangat lambat"</item> <item msgid="1815382991399815061">"Lambat"</item> @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Tampilkan perangkat Bluetooth tanpa nama"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Nonaktifkan volume absolut"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Aktifkan Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Konektivitas Yang Disempurnakan"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versi AVRCP Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Pilih Versi AVRCP Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versi MAP Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index 0ebc341a9541..caf2323813ca 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Sýna Bluetooth-tæki án heita"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Slökkva á samstillingu hljóðstyrks"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Virkja Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Aukin tengigeta"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP-útgáfa"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Velja Bluetooth AVRCP-útgáfu"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP-útgáfa"</string> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index 50fdfc9a9136..8d18727e384c 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostra dispositivi Bluetooth senza nome"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Disattiva volume assoluto"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Attiva Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Connettività migliorata"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versione Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Seleziona versione Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versione Bluetooth MAP"</string> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index fb7d00f866b1..fff881c46433 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"הצגת מכשירי Bluetooth ללא שמות"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"השבת עוצמת קול מוחלטת"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"הפעלת Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"קישוריות משופרת"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth גרסה AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"בחר Bluetooth גרסה AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"גרסת Bluetooth MAP"</string> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index 3537ceaf8204..5e579b758f09 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth デバイスを名前なしで表示"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"絶対音量を無効にする"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche を有効にする"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"接続強化"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP バージョン"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP バージョンを選択する"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP バージョン"</string> diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml index 9b671a864e15..1b5fae9781b9 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth-მოწყობილობების ჩვენება სახელების გარეშე"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ხმის აბსოლუტური სიძლიერის გათიშვა"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche-ის ჩართვა"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"კავშირის გაძლიერებული შესაძლებლობა"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth-ის AVRCP-ის ვერსია"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"აირჩიეთ Bluetooth-ის AVRCP-ის ვერსია"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP-ის ვერსია"</string> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index 279aca0731f7..9c290e90dd8d 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth құрылғыларын атаусыз көрсету"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Абсолютті дыбыс деңгейін өшіру"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche функциясын іске қосу"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Жетілдірілген байланыс"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP нұсқасы"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP нұсқасын таңдау"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP нұсқасы"</string> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index 03065e896da2..2878db192d18 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"បង្ហាញឧបករណ៍ប្ល៊ូធូសគ្មានឈ្មោះ"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"បិទកម្រិតសំឡេងលឺខ្លាំង"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"បើក Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"ការតភ្ជាប់ដែលបានធ្វើឱ្យប្រសើរឡើង"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"កំណែប្ល៊ូធូស AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ជ្រើសរើសកំណែប្ល៊ូធូស AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"កំណែប៊្លូធូស MAP"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index 71c5e491d927..7b010564effa 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"ಹೆಸರುಗಳಿಲ್ಲದ ಬ್ಲೂಟೂತ್ ಸಾಧನಗಳನ್ನು ತೋರಿಸಿ"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ಸಂಪೂರ್ಣ ವಾಲ್ಯೂಮ್ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"ವರ್ಧಿತ ಸಂಪರ್ಕ"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ಬ್ಲೂಟೂತ್ AVRCP ಆವೃತ್ತಿ"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ಬ್ಲೂಟೂತ್ AVRCP ಆವೃತ್ತಿಯನ್ನು ಆಯ್ಕೆ ಮಾಡಿ"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ಬ್ಲೂಟೂತ್ MAP ಆವೃತ್ತಿ"</string> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index 5d82eae2fcba..696ed2955daa 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"이름이 없는 블루투스 기기 표시"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"절대 볼륨 사용 안함"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche 사용 설정"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"향상된 연결"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"블루투스 AVRCP 버전"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"블루투스 AVRCP 버전 선택"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"블루투스 MAP 버전"</string> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index 2702392c108f..c4b5f7e7477f 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Аталышсыз Bluetooth түзмөктөрү көрсөтүлсүн"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Үндүн абсолюттук деңгээли өчүрүлсүн"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche функциясын иштетүү"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Жакшыртылган туташуу"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP версиясы"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP версиясын тандоо"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP версиясы"</string> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index 7a2c338ef008..a72861ee6aac 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"ສະແດງອຸປະກອນ Bluetooth ທີ່ບໍ່ມີຊື່"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ປິດໃຊ້ລະດັບສຽງສົມບູນ"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"ເປີດໃຊ້ Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"ການເຊື່ອມຕໍ່ທີ່ເສີມແຕ່ງແລ້ວ"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ເວີຊັນ Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ເລືອກເວີຊັນ Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ເວີຊັນ Bluetooth MAP"</string> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index b73aa66ae54b..c72bf21d8b29 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Rodyti „Bluetooth“ įrenginius be pavadinimų"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Išjungti didžiausią garsą"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Įgalinti „Gabeldorsche“"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Patobulintas ryšys"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"„Bluetooth“ AVRCP versija"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Pasirinkite „Bluetooth“ AVRCP versiją"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"„Bluetooth“ MRK versija"</string> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index 8e5d24cfa81a..d95e57df320e 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Rādīt Bluetooth ierīces bez nosaukumiem"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Atspējot absolūto skaļumu"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Iespējot Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Uzlabota savienojamība"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP versija"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Atlasiet Bluetooth AVRCP versiju"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP versija"</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index 34299d801946..fb7b63410a5f 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Прикажувај уреди со Bluetooth без имиња"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Оневозможете апсолутна јачина на звук"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Овозможи Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Подобрена поврзливост"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Верзија Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Изберете верзија Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Верзија на Bluetooth MAP"</string> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index c95f8bf2fe39..3c281c8d972f 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"പേരില്ലാത്ത Bluetooth ഉപകരണങ്ങൾ കാണിക്കുക"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"അബ്സൊല്യൂട്ട് വോളിയം പ്രവർത്തനരഹിതമാക്കുക"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche പ്രവർത്തനക്ഷമമാക്കുക"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"മെച്ചപ്പെടുത്തിയ കണക്റ്റിവിറ്റി"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP പതിപ്പ്"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP പതിപ്പ് തിരഞ്ഞെടുക്കുക"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP പതിപ്പ്"</string> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index 8407db6d3a08..37fc5b47619c 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Нэргүй Bluetooth төхөөрөмжийг харуулах"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Үнэмлэхүй дууны түвшинг идэвхгүй болгох"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche-г идэвхжүүлэх"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Сайжруулсан холболт"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP хувилбар"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP хувилбарыг сонгох"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP хувилбар"</string> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index c50f365f68e3..360f15897ebe 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"नावांशिवाय ब्लूटूथ डिव्हाइस दाखवा"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"संपूर्ण आवाज बंद करा"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"गाबलडॉर्ष सुरू करा"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"वर्धित कनेक्टिव्हिटी"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ब्लूटूथ AVRCP आवृत्ती"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ब्लूटूथ AVRCP आवृत्ती निवडा"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ब्लूटूथ MAP आवृत्ती"</string> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index a0a434f05959..68356df25ef9 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Tunjukkan peranti Bluetooth tanpa nama"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Lumpuhkan kelantangan mutlak"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Dayakan Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Kesambungan Dipertingkat"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versi AVRCP Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Pilih Versi AVRCP Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versi MAP Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index fa499293ceab..3729a8352abd 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"အမည်မရှိသော ဘလူးတုသ်စက်ပစ္စည်းများကို ပြသရန်"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ပကတိ အသံနှုန်း သတ်မှတ်ချက် ပိတ်ရန်"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche ကို ဖွင့်ရန်"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"အရည်အသွေးမြှင့်တင်ထားသော ချိတ်ဆက်နိုင်မှု"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ဘလူးတုသ် AVRCP ဗားရှင်း"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ဘလူးတုသ် AVRCP ဗားရှင်းကို ရွေးပါ"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ဘလူးတုသ် MAP ဗားရှင်း"</string> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index aeaba31691f9..0e0e7617b06d 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Vis Bluetooth-enheter uten navn"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Slå av funksjonen for absolutt volum"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Aktiver Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Forbedret tilkobling"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP-versjon"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Velg Bluetooth AVRCP-versjon"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP-versjon"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index 4a2c1719aeb7..762d8304dd64 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"नामकरण नगरिएका ब्लुटुथ यन्त्रहरू देखाउनुहोस्"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"निरपेक्ष आवाज असक्षम गर्नुहोस्"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche सक्षम पार्नुहोस्"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"परिष्कृत जडान"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ब्लुटुथको AVRCP संस्करण"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ब्लुटुथको AVRCP संस्करण चयन गर्नुहोस्"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ब्लुटुथको MAP संस्करण"</string> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index 32cc39ec5b4f..83b72e925fe1 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth-apparaten zonder namen weergeven"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Absoluut volume uitschakelen"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche inschakelen"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Verbeterde connectiviteit"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth-AVRCP-versie"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth-AVRCP-versie selecteren"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"MAP-versie voor bluetooth"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index 8e5bf25a19ca..d200f502879e 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"ବ୍ଲୁଟୂଥ୍ ଡିଭାଇସ୍ଗୁଡ଼ିକୁ ନାମ ବିନା ଦେଖନ୍ତୁ"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ପୂର୍ଣ୍ଣ ଭଲ୍ୟୁମ୍ ଅକ୍ଷମ କରନ୍ତୁ"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"ଗାବେଲ୍ଡୋର୍ସ ସକ୍ରିୟ କରନ୍ତୁ"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"ଏନହାନ୍ସଡ୍ କନେକ୍ଟିଭିଟି"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ବ୍ଲୁଟୂଥ୍ AVRCP ଭର୍ସନ୍"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ବ୍ଲୁଟୂଥ୍ AVRCP ଭର୍ସନ୍"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"ବ୍ଲୁଟୁଥ୍ MAP ସଂସ୍କରଣ"</string> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index 15700130cf78..354ee126d9af 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"ਅਨਾਮ ਬਲੂਟੁੱਥ ਡੀਵਾਈਸਾਂ ਦਿਖਾਓ"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ਪੂਰਨ ਅਵਾਜ਼ ਨੂੰ ਬੰਦ ਕਰੋ"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche ਨੂੰ ਚਾਲੂ ਕਰੋ"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"ਵਿਸਤ੍ਰਿਤ ਕਨੈਕਟੀਵਿਟੀ"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ਬਲੂਟੁੱਥ AVRCP ਵਰਜਨ"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ਬਲੂਟੁੱਥ AVRCP ਵਰਜਨ ਚੁਣੋ"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP ਵਰਜਨ"</string> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index 1120c504717f..095412c94502 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Pokaż urządzenia Bluetooth bez nazw"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Wyłącz głośność bezwzględną"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Włącz Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Lepsza obsługa połączeń"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Wersja AVRCP Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Wybierz wersję AVRCP Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Wersja MAP Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index 4214a2720813..895a9872f4d4 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostrar dispositivos Bluetooth sem nomes"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Desativar volume absoluto"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Ativar Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Conectividade melhorada"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versão do Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecionar versão do Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versão MAP do Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index 508cbfccffe9..2d9f037297fb 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostrar dispositivos Bluetooth sem nomes"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Desativar volume absoluto"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Ativar o Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Conetividade melhorada"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versão de Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecionar versão de Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versão do MAP do Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index 4214a2720813..895a9872f4d4 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Mostrar dispositivos Bluetooth sem nomes"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Desativar volume absoluto"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Ativar Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Conectividade melhorada"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versão do Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selecionar versão do Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versão MAP do Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index 663d3f702ae5..728db174cd7b 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Afișați dispozitivele Bluetooth fără nume"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Dezactivați volumul absolut"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Activați Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Conectivitate îmbunătățită"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versiunea AVRCP pentru Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selectați versiunea AVRCP pentru Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versiunea MAP pentru Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index 9c0a2f5beb8b..ff2115ee3ff0 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Показывать Bluetooth-устройства без названий"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Отключить абсолютный уровень громкости"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Включить Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Улучшенный обмен данными"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Версия Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Выберите версию Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Версия Bluetooth MAP"</string> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index 8d0e93e4b5f7..a883cc60d8e1 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"නම් නොමැති බ්ලූටූත් උපාංග පෙන්වන්න"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"නිරපේක්ෂ හඩ පරිමාව අබල කරන්න"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche සබල කරන්න"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"වැඩිදියුණු කළ සබැඳුම් හැකියාව"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"බ්ලූටූත් AVRCP අනුවාදය"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"බ්ලූටූත් AVRCP අනුවාදය තෝරන්න"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP අනුවාදය"</string> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index f39a741e3a4b..05c63795382b 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Zobrazovať zariadenia Bluetooth bez názvov"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Zakázať absolútnu hlasitosť"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Povoliť Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Zlepšené možnosti pripojenia"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Verzia rozhrania Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Zvoľte verziu rozhrania Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Verzia profilu Bluetooth MAP"</string> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index 233c8e48635a..fd216e83d927 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Prikaži naprave Bluetooth brez imen"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Onemogočanje absolutne glasnosti"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Omogoči Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Izboljšana povezljivost"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Različica profila AVRCP za Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Izberite različico profila AVRCP za Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Različica profila MAP za Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index 6af1062a98f8..002c7fcda363 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Shfaq pajisjet me Bluetooth pa emra"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Çaktivizo volumin absolut"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Aktivizo Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Lidhshmëria e përmirësuar"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versioni AVRCP i Bluetooth-it"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Zgjidh versionin AVRCP të Bluetooth-it"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versioni MAP i Bluetooth-it"</string> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index 74c2aec7c174..25a1beb7af68 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Прикажи Bluetooth уређаје без назива"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Онемогући главно подешавање јачине звука"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Омогући Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Побољшано повезивање"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Верзија Bluetooth AVRCP-а"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Изаберите верзију Bluetooth AVRCP-а"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Верзија Bluetooth MAP-а"</string> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index fe1b0a856802..352cb0ab7d19 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Visa namnlösa Bluetooth-enheter"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Inaktivera Absolute volume"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Aktivera Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Förbättrad anslutning"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"AVRCP-version för Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Välj AVRCP-version för Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"MAP-version för Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index 5c80627003cd..d2891a06402a 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Onyesha vifaa vya Bluetooth visivyo na majina"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Zima sauti kamili"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Washa Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Muunganisho Ulioboreshwa"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Toleo la Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Chagua Toleo la Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Toleo la Ramani ya Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index 241644f631d3..7837dd8d46df 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"பெயர்கள் இல்லாத புளூடூத் சாதனங்களைக் காட்டு"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"அப்சல்யூட் ஒலியளவு அம்சத்தை முடக்கு"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorscheவை இயக்கு"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"மேம்படுத்தப்பட்ட இணைப்புநிலை"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"புளூடூத் AVRCP பதிப்பு"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"புளூடூத் AVRCP பதிப்பைத் தேர்ந்தெடு"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"புளூடூத்தின் MAP பதிப்பு"</string> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index a9ec2ea95ac5..60001a0b0ecd 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"పేర్లు లేని బ్లూటూత్ పరికరాలు చూపించు"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"సంపూర్ణ వాల్యూమ్ను నిలిపివేయి"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorscheను ఎనేబుల్ చేయి"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"మెరుగైన కనెక్టివిటీ"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"బ్లూటూత్ AVRCP వెర్షన్"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"బ్లూటూత్ AVRCP సంస్కరణను ఎంచుకోండి"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"బ్లూటూత్ MAP వెర్షన్"</string> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index b8343c6a82e7..defc33ee9233 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"แสดงอุปกรณ์บลูทูธที่ไม่มีชื่อ"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ปิดใช้การควบคุมระดับเสียงของอุปกรณ์อื่น"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"เปิดใช้ Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"การเชื่อมต่อที่ปรับปรุงแล้ว"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"เวอร์ชันของบลูทูธ AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"เลือกเวอร์ชันของบลูทูธ AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"เวอร์ชัน MAP ของบลูทูธ"</string> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index 8aeb39256748..5d4e9752fd93 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Ipakita ang mga Bluetooth device na walang pangalan"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"I-disable ang absolute volume"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"I-enable ang Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Pinagandang Pagkakonekta"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bersyon ng AVRCP ng Bluetooth"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Pumili ng Bersyon ng AVRCP ng Bluetooth"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bersyon ng MAP ng Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index e6d938047fcf..f01f3faed73e 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Adsız Bluetooth cihazlarını göster"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Mutlak sesi iptal et"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche\'yi etkileştir"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Gelişmiş Bağlantı"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP Sürümü"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP Sürümünü seçin"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP Sürümü"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index cf1fafd8f190..9ca2f062127a 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Показувати пристрої Bluetooth без назв"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Вимкнути абсолютну гучність"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Увімкнути Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Покращене з\'єднання"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Версія Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Виберіть версію Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Версія Bluetooth MAP"</string> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index b7fbe6fad392..8953f50078f1 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"بغیر نام والے بلوٹوتھ آلات دکھائیں"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"مطلق والیوم کو غیر فعال کریں"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche فعال کریں"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"بہتر کردہ کنیکٹوٹی"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"بلوٹوتھ AVRCP ورژن"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"بلوٹوتھ AVRCP ورژن منتخب کریں"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"بلوٹوتھ MAP ورژن"</string> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index f81731ab2723..f25b3ac3c37f 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bluetooth qurilmalarini nomlarisiz ko‘rsatish"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Tovush balandligining mutlaq darajasini faolsizlantirish"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche funksiyasini yoqish"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Kuchaytirilgan aloqa"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP versiyasi"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Bluetooth AVRCP versiyasini tanlang"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Bluetooth MAP versiyasi"</string> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index b7ccf8d85677..b5798f31d0f9 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Hiển thị các thiết bị Bluetooth không có tên"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Vô hiệu hóa âm lượng tuyệt đối"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Bật tính năng Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Kết nối nâng cao"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Phiên bản Bluetooth AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Chọn phiên bản Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Phiên bản Bluetooth MAP"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index 75c1333c37c1..c4dcfff1b579 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"显示没有名称的蓝牙设备"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"停用绝对音量功能"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"启用“Gabeldorsche”"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"增强连接性"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"蓝牙 AVRCP 版本"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"选择蓝牙 AVRCP 版本"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"蓝牙 MAP 版本"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index 26ddfb13717b..e04651cac5f6 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"顯示沒有名稱的藍牙裝置"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"停用絕對音量功能"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"啟用 Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"強化連線功能"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"藍牙 AVRCP 版本"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"選擇藍牙 AVRCP 版本"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"藍牙 MAP 版本"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index 72ea0439b1f6..a1ae6b6c27a1 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"顯示沒有名稱的藍牙裝置"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"停用絕對音量功能"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"啟用 Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"加強型連線"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"藍牙 AVRCP 版本"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"選取藍牙 AVRCP 版本"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"藍牙 MAP 版本"</string> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index 6b8739fd017a..2dafad8114d8 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -257,7 +257,6 @@ <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Bonisa amadivayisi e-Bluetooth ngaphandle kwamagama"</string> <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Khubaza ivolumu ngokuphelele"</string> <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Nika amandla i-Gabeldorsche"</string> - <string name="enhanced_connectivity" msgid="7201127377781666804">"Ukuxhumeka Okuthuthukisiwe"</string> <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Inguqulo ye-Bluetooth ye-AVRCP"</string> <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Khetha inguqulo ye-Bluetooth AVRCP"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Inguqulo ye-Bluetooth MAP"</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java index f757aa4e4dab..b29595eab5c7 100644 --- a/packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java +++ b/packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java @@ -24,7 +24,7 @@ import androidx.preference.PreferenceScreen; import com.android.settingslib.core.AbstractPreferenceController; /** - * This controller is used handle changes for the master switch in the developer options page. + * This controller is used handle changes for the primary switch in the developer options page. * * All Preference Controllers that are a part of the developer options page should inherit this * class. diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerWhitelistBackend.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java index 3c647a7ec465..c501b3aab4d4 100644 --- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerWhitelistBackend.java +++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java @@ -34,44 +34,50 @@ import com.android.internal.telephony.SmsApplication; import com.android.internal.util.ArrayUtils; /** - * Handles getting/changing the whitelist for the exceptions to battery saving features. + * Handles getting/changing the allowlist for the exceptions to battery saving features. */ -public class PowerWhitelistBackend { +public class PowerAllowlistBackend { - private static final String TAG = "PowerWhitelistBackend"; + private static final String TAG = "PowerAllowlistBackend"; private static final String DEVICE_IDLE_SERVICE = "deviceidle"; - private static PowerWhitelistBackend sInstance; + private static PowerAllowlistBackend sInstance; private final Context mAppContext; private final IDeviceIdleController mDeviceIdleService; - private final ArraySet<String> mWhitelistedApps = new ArraySet<>(); - private final ArraySet<String> mSysWhitelistedApps = new ArraySet<>(); + private final ArraySet<String> mAllowlistedApps = new ArraySet<>(); + private final ArraySet<String> mSysAllowlistedApps = new ArraySet<>(); private final ArraySet<String> mDefaultActiveApps = new ArraySet<>(); - public PowerWhitelistBackend(Context context) { + public PowerAllowlistBackend(Context context) { this(context, IDeviceIdleController.Stub.asInterface( ServiceManager.getService(DEVICE_IDLE_SERVICE))); } @VisibleForTesting - PowerWhitelistBackend(Context context, IDeviceIdleController deviceIdleService) { + PowerAllowlistBackend(Context context, IDeviceIdleController deviceIdleService) { mAppContext = context.getApplicationContext(); mDeviceIdleService = deviceIdleService; refreshList(); } - public int getWhitelistSize() { - return mWhitelistedApps.size(); + public int getAllowlistSize() { + return mAllowlistedApps.size(); } - public boolean isSysWhitelisted(String pkg) { - return mSysWhitelistedApps.contains(pkg); + /** + * Check if target package is in System allow list + */ + public boolean isSysAllowlisted(String pkg) { + return mSysAllowlistedApps.contains(pkg); } - public boolean isWhitelisted(String pkg) { - if (mWhitelistedApps.contains(pkg)) { + /** + * Check if target package is in allow list + */ + public boolean isAllowlisted(String pkg) { + if (mAllowlistedApps.contains(pkg)) { return true; } @@ -87,7 +93,7 @@ public class PowerWhitelistBackend { */ public boolean isDefaultActiveApp(String pkg) { // Additionally, check if pkg is default dialer/sms. They are considered essential apps and - // should be automatically whitelisted (otherwise user may be able to set restriction on + // should be automatically allowlisted (otherwise user may be able to set restriction on // them, leading to bad device behavior.) if (mDefaultActiveApps.contains(pkg)) { @@ -103,12 +109,17 @@ public class PowerWhitelistBackend { return false; } - public boolean isWhitelisted(String[] pkgs) { + /** + * + * @param pkgs a list of packageName + * @return true when one of package is in allow list + */ + public boolean isAllowlisted(String[] pkgs) { if (ArrayUtils.isEmpty(pkgs)) { return false; } for (String pkg : pkgs) { - if (isWhitelisted(pkg)) { + if (isAllowlisted(pkg)) { return true; } } @@ -116,40 +127,51 @@ public class PowerWhitelistBackend { return false; } + /** + * Add app into power save allow list. + * @param pkg packageName + */ public void addApp(String pkg) { try { mDeviceIdleService.addPowerSaveWhitelistApp(pkg); - mWhitelistedApps.add(pkg); + mAllowlistedApps.add(pkg); } catch (RemoteException e) { Log.w(TAG, "Unable to reach IDeviceIdleController", e); } } + /** + * Remove package from power save allow list. + * @param pkg + */ public void removeApp(String pkg) { try { mDeviceIdleService.removePowerSaveWhitelistApp(pkg); - mWhitelistedApps.remove(pkg); + mAllowlistedApps.remove(pkg); } catch (RemoteException e) { Log.w(TAG, "Unable to reach IDeviceIdleController", e); } } + /** + * Refresh all of lists + */ @VisibleForTesting public void refreshList() { - mSysWhitelistedApps.clear(); - mWhitelistedApps.clear(); + mSysAllowlistedApps.clear(); + mAllowlistedApps.clear(); mDefaultActiveApps.clear(); if (mDeviceIdleService == null) { return; } try { - final String[] whitelistedApps = mDeviceIdleService.getFullPowerWhitelist(); - for (String app : whitelistedApps) { - mWhitelistedApps.add(app); + final String[] allowlistedApps = mDeviceIdleService.getFullPowerWhitelist(); + for (String app : allowlistedApps) { + mAllowlistedApps.add(app); } - final String[] sysWhitelistedApps = mDeviceIdleService.getSystemPowerWhitelist(); - for (String app : sysWhitelistedApps) { - mSysWhitelistedApps.add(app); + final String[] sysAllowlistedApps = mDeviceIdleService.getSystemPowerWhitelist(); + for (String app : sysAllowlistedApps) { + mSysAllowlistedApps.add(app); } final boolean hasTelephony = mAppContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_TELEPHONY); @@ -171,9 +193,13 @@ public class PowerWhitelistBackend { } } - public static PowerWhitelistBackend getInstance(Context context) { + /** + * @param context + * @return a PowerAllowlistBackend object + */ + public static PowerAllowlistBackend getInstance(Context context) { if (sInstance == null) { - sInstance = new PowerWhitelistBackend(context); + sInstance = new PowerAllowlistBackend(context); } return sInstance; } diff --git a/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java b/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java index 4941f7e42bf6..8ac434957cd9 100644 --- a/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java +++ b/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java @@ -135,7 +135,7 @@ public class AppRestrictionsHelper { // Ignore } } else { - // Blacklist all other apps, system or downloaded + // Denylist all other apps, system or downloaded try { ApplicationInfo info = mIPm.getApplicationInfo(packageName, 0, userId); if (info != null) { @@ -258,11 +258,11 @@ public class AppRestrictionsHelper { } } - // Establish master/slave relationship for entries that share a package name + // Establish primary/secondary relationship for entries that share a package name HashMap<String,SelectableAppInfo> packageMap = new HashMap<String,SelectableAppInfo>(); for (SelectableAppInfo info : mVisibleApps) { if (packageMap.containsKey(info.packageName)) { - info.masterEntry = packageMap.get(info.packageName); + info.primaryEntry = packageMap.get(info.packageName); } else { packageMap.put(info.packageName, info); } @@ -366,12 +366,12 @@ public class AppRestrictionsHelper { public CharSequence appName; public CharSequence activityName; public Drawable icon; - public SelectableAppInfo masterEntry; + public SelectableAppInfo primaryEntry; @Override public String toString() { return packageName + ": appName=" + appName + "; activityName=" + activityName - + "; icon=" + icon + "; masterEntry=" + masterEntry; + + "; icon=" + icon + "; primaryEntry=" + primaryEntry; } } diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/TestAccessPointBuilder.java b/packages/SettingsLib/src/com/android/settingslib/wifi/TestAccessPointBuilder.java index 2fb2481ac117..df98b1717b9b 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/TestAccessPointBuilder.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/TestAccessPointBuilder.java @@ -108,8 +108,8 @@ public class TestAccessPointBuilder { public TestAccessPointBuilder setActive(boolean active) { if (active) { mNetworkInfo = new NetworkInfo( - ConnectivityManager.TYPE_DUMMY, - ConnectivityManager.TYPE_DUMMY, + ConnectivityManager.TYPE_WIFI, + ConnectivityManager.TYPE_WIFI, "TestNetwork", "TestNetwork"); } else { diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java index 11c799ea9df5..94e28f2b5e22 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java @@ -290,12 +290,12 @@ public class RestrictedLockUtilsTest { @Test public void sendShowAdminSupportDetailsIntent_extraRestrictionProvided() { EnforcedAdmin enforcedAdmin = new EnforcedAdmin(); - enforcedAdmin.enforcedRestriction = "Dummy"; + enforcedAdmin.enforcedRestriction = "Fake"; RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, enforcedAdmin); ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); verify(mContext).startActivityAsUser(intentCaptor.capture(), any()); - assertThat(intentCaptor.getValue().getExtra(EXTRA_RESTRICTION)).isEqualTo("Dummy"); + assertThat(intentCaptor.getValue().getExtra(EXTRA_RESTRICTION)).isEqualTo("Fake"); } @Test diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/MasterSwitchControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/PrimarySwitchControllerTest.java index 69d0f2e71c17..9e4cde866ef2 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/MasterSwitchControllerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/PrimarySwitchControllerTest.java @@ -23,16 +23,16 @@ import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; @RunWith(RobolectricTestRunner.class) -public class MasterSwitchControllerTest { +public class PrimarySwitchControllerTest { @Rule public final ExpectedException thrown = ExpectedException.none(); - private MasterSwitchController mController; + private PrimarySwitchController mController; @Before public void setUp() { - mController = new TestMasterSwitchController("123"); + mController = new TestPrimarySwitchController("123"); } @Test @@ -49,11 +49,11 @@ public class MasterSwitchControllerTest { mController.getBundle(); } - static class TestMasterSwitchController extends MasterSwitchController { + static class TestPrimarySwitchController extends PrimarySwitchController { private String mKey; - TestMasterSwitchController(String key) { + TestPrimarySwitchController(String key) { mKey = key; } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/SwitchesProviderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/SwitchesProviderTest.java index a740e683642a..bd0100b67a94 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/SwitchesProviderTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/SwitchesProviderTest.java @@ -35,7 +35,7 @@ import android.content.Context; import android.content.pm.ProviderInfo; import android.os.Bundle; -import com.android.settingslib.drawer.MasterSwitchControllerTest.TestMasterSwitchController; +import com.android.settingslib.drawer.PrimarySwitchControllerTest.TestPrimarySwitchController; import com.android.settingslib.drawer.SwitchController.MetaData; import org.junit.Before; @@ -124,8 +124,8 @@ public class SwitchesProviderTest { } @Test - public void getSwitchData_shouldNotReturnMasterSwitchData() { - final SwitchController controller = new TestMasterSwitchController("123"); + public void getSwitchData_shouldNotReturnPrimarySwitchData() { + final SwitchController controller = new TestPrimarySwitchController("123"); mSwitchesProvider.addSwitchController(controller); mSwitchesProvider.attachInfo(mContext, mProviderInfo); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java index 9b4b97e7f55d..176905305506 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java @@ -17,11 +17,14 @@ package com.android.settingslib.drawer; import static com.android.settingslib.drawer.TileUtils.IA_SETTINGS_ACTION; +import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_PROFILE; import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON; import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_URI; import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT; import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY; import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY_URI; +import static com.android.settingslib.drawer.TileUtils.PROFILE_ALL; +import static com.android.settingslib.drawer.TileUtils.PROFILE_PRIMARY; import static com.google.common.truth.Truth.assertThat; @@ -189,7 +192,7 @@ public class TileUtilsTest { List<Tile> outTiles = new ArrayList<>(); List<ResolveInfo> info = new ArrayList<>(); ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON, - URI_GET_SUMMARY, "my title", 0); + URI_GET_SUMMARY, "my title", 0, PROFILE_ALL); info.add(resolveInfo); when(mPackageManager.queryIntentActivitiesAsUser(any(Intent.class), anyInt(), anyInt())) @@ -211,7 +214,7 @@ public class TileUtilsTest { List<Tile> outTiles = new ArrayList<>(); List<ResolveInfo> info = new ArrayList<>(); ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON, - URI_GET_SUMMARY, null, 123); + URI_GET_SUMMARY, null, 123, PROFILE_ALL); info.add(resolveInfo); when(mPackageManager.queryIntentActivitiesAsUser(any(Intent.class), anyInt(), anyInt())) @@ -235,7 +238,7 @@ public class TileUtilsTest { List<Tile> outTiles = new ArrayList<>(); List<ResolveInfo> info = new ArrayList<>(); ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON, - URI_GET_SUMMARY, null, 123); + URI_GET_SUMMARY, null, 123, PROFILE_ALL); resolveInfo.activityInfo.packageName = "com.android.settings"; resolveInfo.activityInfo.applicationInfo.packageName = "com.android.settings"; info.add(resolveInfo); @@ -258,7 +261,7 @@ public class TileUtilsTest { final List<Tile> outTiles = new ArrayList<>(); final List<ResolveInfo> info = new ArrayList<>(); final ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON, - URI_GET_SUMMARY, null, 123); + URI_GET_SUMMARY, null, 123, PROFILE_ALL); resolveInfo.activityInfo.packageName = "com.android.settings"; resolveInfo.activityInfo.applicationInfo.packageName = "com.android.settings"; info.add(resolveInfo); @@ -290,7 +293,7 @@ public class TileUtilsTest { List<Tile> outTiles = new ArrayList<>(); List<ResolveInfo> info = new ArrayList<>(); ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON, - URI_GET_SUMMARY, null, 123); + URI_GET_SUMMARY, null, 123, PROFILE_ALL); resolveInfo.activityInfo.metaData .putBoolean(TileUtils.META_DATA_PREFERENCE_ICON_TINTABLE, true); info.add(resolveInfo); @@ -327,6 +330,26 @@ public class TileUtilsTest { assertThat(outTiles).hasSize(2); } + @Test + public void loadTilesForAction_isPrimaryProfileOnly_shouldSkipNonPrimaryUserTiles() { + Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>(); + List<Tile> outTiles = new ArrayList<>(); + List<ResolveInfo> info = new ArrayList<>(); + ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON, + URI_GET_SUMMARY, null, 123, PROFILE_PRIMARY); + info.add(resolveInfo); + + when(mPackageManager.queryIntentActivitiesAsUser(any(Intent.class), anyInt(), anyInt())) + .thenReturn(info); + when(mPackageManager.queryIntentContentProvidersAsUser(any(Intent.class), anyInt(), + anyInt())).thenReturn(info); + + TileUtils.loadTilesForAction(mContext, new UserHandle(10), IA_SETTINGS_ACTION, + addedCache, null /* defaultCategory */, outTiles, false /* requiresSettings */); + + assertThat(outTiles).isEmpty(); + } + public static ResolveInfo newInfo(boolean systemApp, String category) { return newInfo(systemApp, category, null); } @@ -337,14 +360,14 @@ public class TileUtilsTest { private static ResolveInfo newInfo(boolean systemApp, String category, String keyHint, String iconUri, String summaryUri) { - return newInfo(systemApp, category, keyHint, iconUri, summaryUri, null, 0); + return newInfo(systemApp, category, keyHint, iconUri, summaryUri, null, 0, PROFILE_ALL); } private static ResolveInfo newInfo(boolean systemApp, String category, String keyHint, - String iconUri, String summaryUri, String title, int titleResId) { + String iconUri, String summaryUri, String title, int titleResId, String profile) { final Bundle metaData = newMetaData(category, keyHint, iconUri, summaryUri, title, - titleResId); + titleResId, profile); final ResolveInfo info = new ResolveInfo(); info.system = systemApp; @@ -358,6 +381,7 @@ public class TileUtilsTest { info.providerInfo.packageName = "abc"; info.providerInfo.name = "456"; info.providerInfo.authority = "auth"; + info.providerInfo.metaData = metaData; ShadowTileUtils.setMetaData(metaData); info.providerInfo.applicationInfo = new ApplicationInfo(); @@ -369,7 +393,7 @@ public class TileUtilsTest { } private static Bundle newMetaData(String category, String keyHint, String iconUri, - String summaryUri, String title, int titleResId) { + String summaryUri, String title, int titleResId, String profile) { final Bundle metaData = new Bundle(); metaData.putString("com.android.settings.category", category); metaData.putInt(META_DATA_PREFERENCE_ICON, 314159); @@ -388,6 +412,9 @@ public class TileUtilsTest { } else if (title != null) { metaData.putString(TileUtils.META_DATA_PREFERENCE_TITLE, title); } + if (profile != null) { + metaData.putString(META_DATA_KEY_PROFILE, profile); + } return metaData; } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerAllowlistBackendTest.java index 20908925feff..4f11fb1f782f 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerAllowlistBackendTest.java @@ -47,7 +47,7 @@ import org.robolectric.shadows.ShadowPackageManager; @RunWith(RobolectricTestRunner.class) @Config(shadows = {ShadowDefaultDialerManager.class, ShadowSmsApplication.class}) -public class PowerWhitelistBackendTest { +public class PowerAllowlistBackendTest { private static final String PACKAGE_ONE = "com.example.packageone"; private static final String PACKAGE_TWO = "com.example.packagetwo"; @@ -56,7 +56,7 @@ public class PowerWhitelistBackendTest { private IDeviceIdleController mDeviceIdleService; @Mock private DevicePolicyManager mDevicePolicyManager; - private PowerWhitelistBackend mPowerWhitelistBackend; + private PowerAllowlistBackend mPowerAllowlistBackend; private ShadowPackageManager mPackageManager; private Context mContext; @@ -74,81 +74,81 @@ public class PowerWhitelistBackendTest { mPackageManager.setSystemFeature(PackageManager.FEATURE_TELEPHONY, true); doReturn(mDevicePolicyManager).when(mContext).getSystemService(DevicePolicyManager.class); - mPowerWhitelistBackend = new PowerWhitelistBackend(mContext, mDeviceIdleService); + mPowerAllowlistBackend = new PowerAllowlistBackend(mContext, mDeviceIdleService); } @Test - public void testIsWhitelisted() throws Exception { + public void testIsAllowlisted() throws Exception { doReturn(new String[] {PACKAGE_ONE}).when(mDeviceIdleService).getFullPowerWhitelist(); - mPowerWhitelistBackend.refreshList(); + mPowerAllowlistBackend.refreshList(); - assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isTrue(); - assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_TWO)).isFalse(); - assertThat(mPowerWhitelistBackend.isWhitelisted(new String[] {PACKAGE_ONE})).isTrue(); - assertThat(mPowerWhitelistBackend.isWhitelisted(new String[] {PACKAGE_TWO})).isFalse(); + assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isTrue(); + assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_TWO)).isFalse(); + assertThat(mPowerAllowlistBackend.isAllowlisted(new String[] {PACKAGE_ONE})).isTrue(); + assertThat(mPowerAllowlistBackend.isAllowlisted(new String[] {PACKAGE_TWO})).isFalse(); - mPowerWhitelistBackend.addApp(PACKAGE_TWO); + mPowerAllowlistBackend.addApp(PACKAGE_TWO); verify(mDeviceIdleService, atLeastOnce()).addPowerSaveWhitelistApp(PACKAGE_TWO); - assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isTrue(); - assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_TWO)).isTrue(); - assertThat(mPowerWhitelistBackend.isWhitelisted( + assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isTrue(); + assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_TWO)).isTrue(); + assertThat(mPowerAllowlistBackend.isAllowlisted( new String[] {PACKAGE_ONE, PACKAGE_TWO})).isTrue(); - mPowerWhitelistBackend.removeApp(PACKAGE_TWO); + mPowerAllowlistBackend.removeApp(PACKAGE_TWO); verify(mDeviceIdleService, atLeastOnce()).removePowerSaveWhitelistApp(PACKAGE_TWO); - assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isTrue(); - assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_TWO)).isFalse(); - assertThat(mPowerWhitelistBackend.isWhitelisted(new String[] {PACKAGE_ONE})).isTrue(); - assertThat(mPowerWhitelistBackend.isWhitelisted(new String[] {PACKAGE_TWO})).isFalse(); + assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isTrue(); + assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_TWO)).isFalse(); + assertThat(mPowerAllowlistBackend.isAllowlisted(new String[] {PACKAGE_ONE})).isTrue(); + assertThat(mPowerAllowlistBackend.isAllowlisted(new String[] {PACKAGE_TWO})).isFalse(); - mPowerWhitelistBackend.removeApp(PACKAGE_ONE); + mPowerAllowlistBackend.removeApp(PACKAGE_ONE); verify(mDeviceIdleService, atLeastOnce()).removePowerSaveWhitelistApp(PACKAGE_ONE); - assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isFalse(); - assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_TWO)).isFalse(); - assertThat(mPowerWhitelistBackend.isWhitelisted( + assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isFalse(); + assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_TWO)).isFalse(); + assertThat(mPowerAllowlistBackend.isAllowlisted( new String[] {PACKAGE_ONE, PACKAGE_TWO})).isFalse(); } @Test - public void isWhitelisted_shouldWhitelistDefaultSms() { + public void isAllowlisted_shouldAllowlistDefaultSms() { final String testSms = "com.android.test.defaultsms"; ShadowSmsApplication.setDefaultSmsApplication(new ComponentName(testSms, "receiver")); - mPowerWhitelistBackend.refreshList(); + mPowerAllowlistBackend.refreshList(); - assertThat(mPowerWhitelistBackend.isWhitelisted(testSms)).isTrue(); - assertThat(mPowerWhitelistBackend.isDefaultActiveApp(testSms)).isTrue(); + assertThat(mPowerAllowlistBackend.isAllowlisted(testSms)).isTrue(); + assertThat(mPowerAllowlistBackend.isDefaultActiveApp(testSms)).isTrue(); } @Test - public void isWhitelisted_shouldWhitelistDefaultDialer() { + public void isAllowlisted_shouldAllowlistDefaultDialer() { final String testDialer = "com.android.test.defaultdialer"; ShadowDefaultDialerManager.setDefaultDialerApplication(testDialer); - mPowerWhitelistBackend.refreshList(); + mPowerAllowlistBackend.refreshList(); - assertThat(mPowerWhitelistBackend.isWhitelisted(testDialer)).isTrue(); - assertThat(mPowerWhitelistBackend.isDefaultActiveApp(testDialer)).isTrue(); + assertThat(mPowerAllowlistBackend.isAllowlisted(testDialer)).isTrue(); + assertThat(mPowerAllowlistBackend.isDefaultActiveApp(testDialer)).isTrue(); } @Test - public void isWhitelisted_shouldWhitelistActiveDeviceAdminApp() { + public void isAllowlisted_shouldAllowlistActiveDeviceAdminApp() { doReturn(true).when(mDevicePolicyManager).packageHasActiveAdmins(PACKAGE_ONE); - assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isTrue(); - assertThat(mPowerWhitelistBackend.isDefaultActiveApp(PACKAGE_ONE)).isTrue(); + assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isTrue(); + assertThat(mPowerAllowlistBackend.isDefaultActiveApp(PACKAGE_ONE)).isTrue(); } @Test - public void testIsSystemWhitelisted() throws Exception { + public void testIsSystemAllowlisted() throws Exception { doReturn(new String[] {PACKAGE_ONE}).when(mDeviceIdleService).getSystemPowerWhitelist(); - mPowerWhitelistBackend.refreshList(); + mPowerAllowlistBackend.refreshList(); - assertThat(mPowerWhitelistBackend.isSysWhitelisted(PACKAGE_ONE)).isTrue(); - assertThat(mPowerWhitelistBackend.isSysWhitelisted(PACKAGE_TWO)).isFalse(); - assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isFalse(); + assertThat(mPowerAllowlistBackend.isSysAllowlisted(PACKAGE_ONE)).isTrue(); + assertThat(mPowerAllowlistBackend.isSysAllowlisted(PACKAGE_TWO)).isFalse(); + assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isFalse(); } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java index b930aa6ee1bd..84d722ad16df 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java @@ -197,61 +197,61 @@ public class InputMethodAndSubtypeUtilCompatTest { public void isValidSystemNonAuxAsciiCapableIme() { // System IME w/ no subtype assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme( - createDummyIme(true, false))) + createFakeIme(true, false))) .isFalse(); // System IME w/ non-Aux and non-ASCII-capable "keyboard" subtype assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme( - createDummyIme(true, false, createDummySubtype("keyboard", false, false)))) + createFakeIme(true, false, createFakeSubtype("keyboard", false, false)))) .isFalse(); // System IME w/ non-Aux and ASCII-capable "keyboard" subtype assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme( - createDummyIme(true, false, createDummySubtype("keyboard", false, true)))) + createFakeIme(true, false, createFakeSubtype("keyboard", false, true)))) .isTrue(); // System IME w/ Aux and ASCII-capable "keyboard" subtype assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme( - createDummyIme(true, true, createDummySubtype("keyboard", true, true)))) + createFakeIme(true, true, createFakeSubtype("keyboard", true, true)))) .isFalse(); // System IME w/ non-Aux and ASCII-capable "voice" subtype assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme( - createDummyIme(true, false, createDummySubtype("voice", false, true)))) + createFakeIme(true, false, createFakeSubtype("voice", false, true)))) .isFalse(); // System IME w/ non-Aux and non-ASCII-capable subtype + Non-Aux and ASCII-capable subtype assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme( - createDummyIme(true, false, - createDummySubtype("keyboard", false, true), - createDummySubtype("keyboard", false, false)))) + createFakeIme(true, false, + createFakeSubtype("keyboard", false, true), + createFakeSubtype("keyboard", false, false)))) .isTrue(); // Non-system IME w/ non-Aux and ASCII-capable "keyboard" subtype assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme( - createDummyIme(false, false, createDummySubtype("keyboard", false, true)))) + createFakeIme(false, false, createFakeSubtype("keyboard", false, true)))) .isFalse(); } - private static InputMethodInfo createDummyIme(boolean isSystem, boolean isAuxIme, + private static InputMethodInfo createFakeIme(boolean isSystem, boolean isAuxIme, InputMethodSubtype... subtypes) { final ResolveInfo ri = new ResolveInfo(); final ServiceInfo si = new ServiceInfo(); final ApplicationInfo ai = new ApplicationInfo(); - ai.packageName = "com.example.android.dummyime"; + ai.packageName = "com.example.android.fakeime"; ai.enabled = true; ai.flags |= (isSystem ? ApplicationInfo.FLAG_SYSTEM : 0); si.applicationInfo = ai; si.enabled = true; - si.packageName = "com.example.android.dummyime"; - si.name = "Dummy IME"; + si.packageName = "com.example.android.fakeime"; + si.name = "Fake IME"; si.exported = true; - si.nonLocalizedLabel = "Dummy IME"; + si.nonLocalizedLabel = "Fake IME"; ri.serviceInfo = si; return new InputMethodInfo(ri, isAuxIme, "", Arrays.asList(subtypes), 1, false); } - private static InputMethodSubtype createDummySubtype( + private static InputMethodSubtype createFakeSubtype( String mode, boolean isAuxiliary, boolean isAsciiCapable) { return new InputMethodSubtypeBuilder() .setSubtypeNameResId(0) diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java index 5171dda9bff7..97d87051402e 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java @@ -195,55 +195,55 @@ public class InputMethodAndSubtypeUtilTest { public void isValidNonAuxAsciiCapableIme() { // IME w/ no subtype assertThat(InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme( - createDummyIme(false))) + createFakeIme(false))) .isFalse(); // IME w/ non-Aux and non-ASCII-capable "keyboard" subtype assertThat(InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme( - createDummyIme(false, createDummySubtype("keyboard", false, false)))) + createFakeIme(false, createFakeSubtype("keyboard", false, false)))) .isFalse(); // IME w/ non-Aux and ASCII-capable "keyboard" subtype assertThat(InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme( - createDummyIme(false, createDummySubtype("keyboard", false, true)))) + createFakeIme(false, createFakeSubtype("keyboard", false, true)))) .isTrue(); // IME w/ Aux and ASCII-capable "keyboard" subtype assertThat(InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme( - createDummyIme(true, createDummySubtype("keyboard", true, true)))) + createFakeIme(true, createFakeSubtype("keyboard", true, true)))) .isFalse(); // IME w/ non-Aux and ASCII-capable "voice" subtype assertThat(InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme( - createDummyIme(false, createDummySubtype("voice", false, true)))) + createFakeIme(false, createFakeSubtype("voice", false, true)))) .isFalse(); // IME w/ non-Aux and non-ASCII-capable subtype + Non-Aux and ASCII-capable subtype assertThat(InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme( - createDummyIme(false, - createDummySubtype("keyboard", false, true), - createDummySubtype("keyboard", false, false)))) + createFakeIme(false, + createFakeSubtype("keyboard", false, true), + createFakeSubtype("keyboard", false, false)))) .isTrue(); } - private static InputMethodInfo createDummyIme(boolean isAuxIme, + private static InputMethodInfo createFakeIme(boolean isAuxIme, InputMethodSubtype... subtypes) { final ResolveInfo ri = new ResolveInfo(); final ServiceInfo si = new ServiceInfo(); final ApplicationInfo ai = new ApplicationInfo(); - ai.packageName = "com.example.android.dummyime"; + ai.packageName = "com.example.android.fakeime"; ai.enabled = true; si.applicationInfo = ai; si.enabled = true; - si.packageName = "com.example.android.dummyime"; - si.name = "Dummy IME"; + si.packageName = "com.example.android.fakeime"; + si.name = "Fake IME"; si.exported = true; - si.nonLocalizedLabel = "Dummy IME"; + si.nonLocalizedLabel = "Fake IME"; ri.serviceInfo = si; return new InputMethodInfo(ri, isAuxIme, "", Arrays.asList(subtypes), 1, false); } - private static InputMethodSubtype createDummySubtype( + private static InputMethodSubtype createFakeSubtype( String mode, boolean isAuxiliary, boolean isAsciiCapable) { return new InputMethodSubtypeBuilder() .setSubtypeNameResId(0) diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index aa960875ec6f..319b44ce216f 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -320,19 +320,6 @@ <!-- Permissions required for CTS test - AdbManagerTest --> <uses-permission android:name="android.permission.MANAGE_DEBUGGING" /> - <!-- Permissions required for ATS tests - AtsCarHostTestCases, AtsCarDeviceApp --> - <uses-permission android:name="android.car.permission.CAR_DRIVING_STATE" /> - <!-- Permissions required for ATS tests - AtsDeviceInfo, AtsAudioDeviceTestCases --> - <uses-permission android:name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME" /> - <!-- Permissions required for ATS tests - AtsDeviceInfo --> - <uses-permission android:name="android.car.permission.CAR_DIAGNOSTICS" /> - <!-- Permissions required for ATS tests - AtsDeviceInfo --> - <uses-permission android:name="android.car.permission.CLEAR_CAR_DIAGNOSTICS" /> - <!-- Permissions required for ATS tests - AtsCarHostTestCases --> - <uses-permission android:name="android.car.permission.CONTROL_APP_BLOCKING" /> - <!-- Permissions required for ATS tests - AtsCarHostTestCases --> - <uses-permission android:name="android.car.permission.CAR_UX_RESTRICTIONS_CONFIGURATION" /> - <application android:label="@string/app_label" android:theme="@android:style/Theme.DeviceDefault.DayNight" android:defaultToDeviceProtectedStorage="true" diff --git a/packages/SimAppDialog/Android.bp b/packages/SimAppDialog/Android.bp index 176035f73b65..1c680bb9d25e 100644 --- a/packages/SimAppDialog/Android.bp +++ b/packages/SimAppDialog/Android.bp @@ -7,7 +7,8 @@ android_app { static_libs: [ "androidx.legacy_legacy-support-v4", - "setup-wizard-lib", + "setupcompat", + "setupdesign", ], resource_dirs: ["res"], diff --git a/packages/SimAppDialog/AndroidManifest.xml b/packages/SimAppDialog/AndroidManifest.xml index 873f6c5bac54..e7368f35ed5a 100644 --- a/packages/SimAppDialog/AndroidManifest.xml +++ b/packages/SimAppDialog/AndroidManifest.xml @@ -23,7 +23,7 @@ android:name=".InstallCarrierAppActivity" android:exported="true" android:permission="android.permission.NETWORK_SETTINGS" - android:theme="@style/SuwThemeGlif.Light"> + android:theme="@style/SudThemeGlif.Light"> </activity> </application> </manifest> diff --git a/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml b/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml index 12f9bb6b13ea..68113dbf5df0 100644 --- a/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml +++ b/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml @@ -14,18 +14,17 @@ See the License for the specific language governing permissions and limitations under the License. --> -<com.android.setupwizardlib.GlifLayout +<com.google.android.setupdesign.GlifLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/setup_wizard_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:icon="@drawable/ic_signal_cellular_alt_rounded" - app:suwHeaderText="@string/install_carrier_app_title" - app:suwFooter="@layout/install_carrier_app_footer"> + app:sucHeaderText="@string/install_carrier_app_title"> <LinearLayout - style="@style/SuwContentFrame" + style="@style/SudContentFrame" android:id="@+id/content_frame" android:layout_width="match_parent" android:layout_height="wrap_content" @@ -33,12 +32,12 @@ <TextView android:id="@+id/install_carrier_app_description" - style="@style/SuwDescription.Glif" + style="@style/SudDescription.Glif" android:text="@string/install_carrier_app_description_default" android:layout_width="match_parent" android:layout_height="wrap_content"/> - <com.android.setupwizardlib.view.FillContentLayout + <com.google.android.setupdesign.view.FillContentLayout android:id="@+id/illo_container" android:layout_width="match_parent" android:layout_height="wrap_content" @@ -47,12 +46,12 @@ <ImageView android:src="@drawable/illo_sim_app_dialog" - style="@style/SuwContentIllustration" + style="@style/SudContentIllustration" android:contentDescription="@string/install_carrier_app_image_content_description" android:layout_width="match_parent" android:layout_height="match_parent"/> - </com.android.setupwizardlib.view.FillContentLayout> -</LinearLayout> + </com.google.android.setupdesign.view.FillContentLayout> + </LinearLayout> -</com.android.setupwizardlib.GlifLayout> +</com.google.android.setupdesign.GlifLayout> diff --git a/packages/SimAppDialog/res/layout/install_carrier_app_footer.xml b/packages/SimAppDialog/res/layout/install_carrier_app_footer.xml deleted file mode 100644 index 10dcb77a6584..000000000000 --- a/packages/SimAppDialog/res/layout/install_carrier_app_footer.xml +++ /dev/null @@ -1,43 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2018 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. ---> - -<com.android.setupwizardlib.view.ButtonBarLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/footer" - style="@style/SuwGlifButtonBar.Stackable" - android:layout_width="match_parent" - android:layout_height="wrap_content"> - - <Button - android:id="@+id/skip_button" - style="@style/SuwGlifButton.Secondary" - android:text="@string/install_carrier_app_defer_action" - android:layout_width="wrap_content" - android:layout_height="wrap_content"/> - - <Space - android:layout_width="0dp" - android:layout_height="0dp" - android:layout_weight="1"/> - - <Button - android:id="@+id/download_button" - style="@style/SuwGlifButton.Primary" - android:text="@string/install_carrier_app_download_action" - android:layout_width="wrap_content" - android:layout_height="wrap_content"/> -</com.android.setupwizardlib.view.ButtonBarLayout> diff --git a/packages/SimAppDialog/res/values/styles.xml b/packages/SimAppDialog/res/values/styles.xml new file mode 100644 index 000000000000..824e3802aca1 --- /dev/null +++ b/packages/SimAppDialog/res/values/styles.xml @@ -0,0 +1,26 @@ +<?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. +--> +<resources> + + <style name="SetupWizardPartnerResource"> + <!-- Disable to use partner overlay theme for outside setupwizard flow. --> + <item name="sucUsePartnerResource">false</item> + <!-- Enable heavy theme style inside setupwizard flow. --> + <item name="sudUsePartnerHeavyTheme">true</item> + </style> + +</resources> diff --git a/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java b/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java index abe82a885a94..0b6f9bb4f9e0 100644 --- a/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java +++ b/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java @@ -17,14 +17,17 @@ package com.android.simappdialog; import android.app.Activity; import android.content.Intent; +import android.content.res.Resources; import android.os.Bundle; import android.sysprop.SetupWizardProperties; import android.text.TextUtils; import android.view.View; -import android.widget.Button; import android.widget.TextView; -import com.android.setupwizardlib.util.WizardManagerHelper; +import com.google.android.setupcompat.template.FooterBarMixin; +import com.google.android.setupcompat.template.FooterButton; +import com.google.android.setupdesign.GlifLayout; +import com.google.android.setupdesign.util.ThemeResolver; /** * Activity that gives a user the choice to download the SIM app or defer until a later time @@ -35,7 +38,7 @@ import com.android.setupwizardlib.util.WizardManagerHelper; * Can display the carrier app name if its passed into the intent with key * {@link #BUNDLE_KEY_CARRIER_NAME} */ -public class InstallCarrierAppActivity extends Activity implements View.OnClickListener { +public class InstallCarrierAppActivity extends Activity { /** * Key for the carrier app name that will be displayed as the app to download. If unset, a * default description will be used @@ -50,20 +53,33 @@ public class InstallCarrierAppActivity extends Activity implements View.OnClickL protected void onCreate(Bundle icicle) { // Setup theme for aosp/pixel setTheme( - WizardManagerHelper.getThemeRes( - SetupWizardProperties.theme().orElse(""), - R.style.SuwThemeGlif_Light - ) - ); + new ThemeResolver.Builder() + .setDefaultTheme(R.style.SudThemeGlifV3_Light) + .build() + .resolve(SetupWizardProperties.theme().orElse(""), + /* suppressDayNight= */ false)); super.onCreate(icicle); setContentView(R.layout.install_carrier_app_activity); - Button notNowButton = findViewById(R.id.skip_button); - notNowButton.setOnClickListener(this); + GlifLayout layout = findViewById(R.id.setup_wizard_layout); + FooterBarMixin mixin = layout.getMixin(FooterBarMixin.class); + mixin.setSecondaryButton( + new FooterButton.Builder(this) + .setText(R.string.install_carrier_app_defer_action) + .setListener(this::onSkipButtonClick) + .setButtonType(FooterButton.ButtonType.SKIP) + .setTheme(R.style.SudGlifButton_Secondary) + .build()); + + mixin.setPrimaryButton( + new FooterButton.Builder(this) + .setText(R.string.install_carrier_app_download_action) + .setListener(this::onDownloadButtonClick) + .setButtonType(FooterButton.ButtonType.OTHER) + .setTheme(R.style.SudGlifButton_Primary) + .build()); - Button downloadButton = findViewById(R.id.download_button); - downloadButton.setOnClickListener(this); // Show/hide illo depending on whether one was provided in a resource overlay boolean showIllo = getResources().getBoolean(R.bool.show_sim_app_dialog_illo); @@ -82,15 +98,17 @@ public class InstallCarrierAppActivity extends Activity implements View.OnClickL } @Override - public void onClick(View v) { - switch (v.getId()) { - case R.id.skip_button: - finish(DEFER_RESULT); - break; - case R.id.download_button: - finish(DOWNLOAD_RESULT); - break; - } + protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) { + theme.applyStyle(R.style.SetupWizardPartnerResource, true); + super.onApplyThemeResource(theme, resid, first); + } + + protected void onSkipButtonClick(View view) { + finish(DEFER_RESULT); + } + + protected void onDownloadButtonClick(View view) { + finish(DOWNLOAD_RESULT); } private void finish(int resultCode) { diff --git a/packages/SystemUI/res-product/values-in/strings.xml b/packages/SystemUI/res-product/values-in/strings.xml index 2e0580f568f9..1451e2c063c9 100644 --- a/packages/SystemUI/res-product/values-in/strings.xml +++ b/packages/SystemUI/res-product/values-in/strings.xml @@ -26,10 +26,10 @@ <string name="keyguard_missing_sim_message" product="tablet" msgid="5018086454277963787">"Tidak ada kartu SIM dalam tablet."</string> <string name="keyguard_missing_sim_message" product="default" msgid="7053347843877341391">"Tidak ada kartu SIM dalam ponsel."</string> <string name="kg_invalid_confirm_pin_hint" product="default" msgid="6278551068943958651">"Kode PIN tidak cocok"</string> - <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="302165994845009232">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali berupaya membuka kunci tablet dengan tidak benar. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya yang tidak berhasil, tablet ini akan disetel ulang, sehingga semua datanya akan dihapus."</string> - <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="2594813176164266847">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali berupaya membuka kunci ponsel dengan tidak benar. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya yang tidak berhasil, ponsel ini akan disetel ulang, sehingga semua datanya akan dihapus."</string> - <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="8710104080409538587">"Anda telah <xliff:g id="NUMBER">%d</xliff:g> kali berupaya membuka kunci tablet dengan tidak benar. Tablet ini akan disetel ulang, sehingga semua datanya akan dihapus."</string> - <string name="kg_failed_attempts_now_wiping" product="default" msgid="6381835450014881813">"Anda telah <xliff:g id="NUMBER">%d</xliff:g> kali berupaya membuka kunci ponsel dengan tidak benar. Ponsel ini akan disetel ulang, sehingga semua datanya akan dihapus."</string> + <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="302165994845009232">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali berupaya membuka kunci tablet dengan tidak benar. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya yang tidak berhasil, tablet ini akan direset, sehingga semua datanya akan dihapus."</string> + <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="2594813176164266847">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali berupaya membuka kunci ponsel dengan tidak benar. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya yang tidak berhasil, ponsel ini akan direset, sehingga semua datanya akan dihapus."</string> + <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="8710104080409538587">"Anda telah <xliff:g id="NUMBER">%d</xliff:g> kali berupaya membuka kunci tablet dengan tidak benar. Tablet ini akan direset, sehingga semua datanya akan dihapus."</string> + <string name="kg_failed_attempts_now_wiping" product="default" msgid="6381835450014881813">"Anda telah <xliff:g id="NUMBER">%d</xliff:g> kali berupaya membuka kunci ponsel dengan tidak benar. Ponsel ini akan direset, sehingga semua datanya akan dihapus."</string> <string name="kg_failed_attempts_almost_at_erase_user" product="tablet" msgid="7325071812832605911">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali berupaya membuka kunci tablet dengan tidak benar. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya yang tidak berhasil, pengguna ini akan dihapus, sehingga semua data pengguna akan dihapus."</string> <string name="kg_failed_attempts_almost_at_erase_user" product="default" msgid="8110939900089863103">"Anda telah <xliff:g id="NUMBER_0">%1$d</xliff:g> kali berupaya membuka kunci ponsel dengan tidak benar. Setelah <xliff:g id="NUMBER_1">%2$d</xliff:g> lagi upaya yang tidak berhasil, pengguna ini akan dihapus, sehingga semua data pengguna akan dihapus."</string> <string name="kg_failed_attempts_now_erasing_user" product="tablet" msgid="8509811676952707883">"Anda telah <xliff:g id="NUMBER">%d</xliff:g> kali berupaya membuka kunci tablet dengan tidak benar. Pengguna ini akan dihapus, sehingga semua data pengguna akan dihapus."</string> diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index 308fd88ee6cd..ee8700261db5 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Hou aan/af-skakelaar in om nuwe kontroles te sien"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Voeg kontroles by"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Wysig kontroles"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Gebruik eenhandmodus"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Swiep van die onderkant van die skerm af op of tik enige plek bo die program om uit te gaan"</string> </resources> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index f2bf5778b42f..03eb39659ef3 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"አዲስ መቆጣጠሪያዎችን ለማየት የኃይል አዝራር ይያዙ"</string> <string name="controls_menu_add" msgid="4447246119229920050">"መቆጣጠሪያዎችን አክል"</string> <string name="controls_menu_edit" msgid="890623986951347062">"መቆጣጠሪያዎችን ያርትዑ"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"ባለአንድ እጅ ሁነታን በመጠቀም ላይ"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"ለመውጣት ከማያው ግርጌ ወደ ላይ ይጥረጉ ወይም ከመተግበሪያው በላይ ማንኛውም ቦታ ላይ መታ ያድርጉ"</string> </resources> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index 36280e716c4e..95d54a2386dd 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -1108,4 +1108,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"اضغط مع الاستمرار على زر التشغيل لعرض عناصر التحكّم الجديدة."</string> <string name="controls_menu_add" msgid="4447246119229920050">"إضافة عناصر تحكّم"</string> <string name="controls_menu_edit" msgid="890623986951347062">"تعديل عناصر التحكّم"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"استخدام وضع \"التصفح بيد واحدة\""</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"للخروج، مرِّر سريعًا من أسفل الشاشة إلى أعلاها أو انقر في أي مكان فوق التطبيق."</string> </resources> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index a4cc17b822d5..3023a27b15b4 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -502,7 +502,7 @@ <string name="battery_saver_notification_title" msgid="8419266546034372562">"বেটাৰি সঞ্চয়কাৰী অন হৈ আছে"</string> <string name="battery_saver_notification_text" msgid="2617841636449016951">"কাৰ্যদক্ষতা আৰু নেপথ্য ডেটা হ্ৰাস কৰে"</string> <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"বেটাৰি সঞ্চয়কাৰী অফ কৰক"</string> - <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>এ আপোনাৰ স্ক্ৰীনত দৃশ্যমান হোৱা অথবা ৰেকর্ডিং অথবা কাষ্টিংৰ সময়ত আপোনাৰ ডিভাইচত প্লে\' কৰা সকলো তথ্যলৈ এক্সেছ পাব। এইটোত পাছৱর্ড, পৰিশোধৰ সবিশেষ, ফট\', বার্তাসমূহ আৰু আপুনি প্লে\' কৰা অডিঅ\'ৰ দৰে তথ্য অন্তর্ভুক্ত হয়।"</string> + <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>এ আপোনাৰ স্ক্ৰীনত দৃশ্যমান হোৱা অথবা ৰেকর্ডিং অথবা কাষ্টিঙৰ সময়ত আপোনাৰ ডিভাইচত প্লে\' কৰা সকলো তথ্যলৈ এক্সেছ পাব। এইটোত পাছৱর্ড, পৰিশোধৰ সবিশেষ, ফট\', বার্তাসমূহ আৰু আপুনি প্লে\' কৰা অডিঅ\'ৰ দৰে তথ্য অন্তর্ভুক্ত হয়।"</string> <string name="media_projection_dialog_service_text" msgid="958000992162214611">"এই সুবিধাটো প্ৰদান কৰা সেৱাটোৱে আপোনাৰ স্ক্ৰীনত দৃশ্যমান হোৱা অথবা ৰেকর্ডিং অথবা কাষ্টিংৰ সময়ত আপোনাৰ ডিভাইচত প্লে\' কৰা সকলো তথ্যলৈ এক্সেছ পাব। এইটোত পাছৱর্ড, পৰিশোধৰ সবিশেষ, ফট\', বার্তাসমূহ আৰু আপুনি প্লে\' কৰা অডিঅ\'ৰ দৰে তথ্য অন্তর্ভুক্ত হয়।"</string> <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"ৰেকর্ডিং অথবা কাষ্টিং আৰম্ভ কৰিবনে?"</string> <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ৰ জৰিয়তে ৰেকর্ডিং অথবা কাষ্টিং আৰম্ভ কৰিবনে ?"</string> @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"নতুন নিয়ন্ত্ৰণসমূহ চাবলৈ পাৱাৰৰ বুটামটো ধৰি ৰাখক"</string> <string name="controls_menu_add" msgid="4447246119229920050">"নিয়ন্ত্ৰণসমূহ যোগ দিয়ক"</string> <string name="controls_menu_edit" msgid="890623986951347062">"নিয়ন্ত্ৰণসমূহ সম্পাদনা কৰক"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"এখন হাতেৰে ব্যৱহাৰ কৰা ম’ড ব্যৱহাৰ কৰিবলৈ"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"বাহিৰ হ’বলৈ স্ক্রীনখনৰ একেবাৰে তলৰ পৰা ওপৰলৈ ছোৱাইপ কৰক অথবা এপ্টোৰ ওপৰত যিকোনো ঠাইত টিপক"</string> </resources> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index 9443ba8ac22c..3c8e04b7ba35 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Yeni nizamlayıcıları görmək üçün yandırıb-söndürmə düyməsinə basıb saxlayın"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Nizamlayıcılar əlavə edin"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Nizamlayıcıları redaktə edin"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Bir əlli rejimdən istifadə edilir"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Çıxmaq üçün ekranın aşağısından yuxarıya doğru sürüşdürün və ya tətbiqin yuxarısında istənilən yerə toxunun"</string> </resources> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index 5b8c44599eed..4b400eb479c9 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -1090,4 +1090,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Zadržite dugme za uključivanje da biste videli nove kontrole"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Dodaj kontrole"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Izmeni kontrole"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Korišćenje režima jednom rukom"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Da biste izašli, prevucite nagore od dna ekrana ili dodirnite bilo gde iznad aplikacije"</string> </resources> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index 6ee51682fe85..f1064fb7b1d7 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -1096,4 +1096,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Каб убачыць новыя элементы кіравання, утрымлівайце кнопку сілкавання націснутай"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Дадаць элементы кіравання"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Змяніць элементы кіравання"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Выкарыстоўваецца рэжым кіравання адной рукой"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Каб выйсці, правядзіце па экране пальцам знізу ўверх або націсніце ў любым месцы над праграмай"</string> </resources> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index c7bb3d08579c..8ba2a1e51769 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Задръжте бутона за захранване, за да видите новите контроли"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Добавяне на контроли"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Редактиране на контролите"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Използване на режима за работа с една ръка"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"За изход прекарайте пръст нагоре от долната част на екрана или докоснете произволно място над приложението"</string> </resources> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index 42e7523aef81..5861c44c1cbb 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -1084,4 +1084,8 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"নতুন কন্ট্রোল দেখতে পাওয়ার বোতাম টিপে ধরে থাকুন"</string> <string name="controls_menu_add" msgid="4447246119229920050">"কন্ট্রোল যোগ করুন"</string> <string name="controls_menu_edit" msgid="890623986951347062">"কন্ট্রোল এডিট করুন"</string> + <!-- no translation found for one_handed_tutorial_title (6312198435090726656) --> + <skip /> + <!-- no translation found for one_handed_tutorial_description (7674850272340517174) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index 16a1aa615c9b..984cbebd42eb 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -1090,4 +1090,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Zadržite dugme za uključivanje da vidite nove kontrole"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Dodaj kontrole"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Uredi kontrole"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Korištenje načina rada jednom rukom"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Da izađete, prevucite s dna ekrana prema gore ili dodirnite bilo gdje iznad aplikacije"</string> </resources> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index fdcec987f438..b8c41efa7813 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Mantén premut el botó d\'engegada per veure controls nous"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Afegeix controls"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Edita els controls"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"S\'està utilitzant el mode d\'una mà"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Per sortir, llisca cap amunt des de la part inferior de la pantalla o toca qualsevol lloc a sobre de l\'aplicació"</string> </resources> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index ab3b4fb2659c..f946cc4f9552 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -1096,4 +1096,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Nové ovládací prvky zobrazíte podržením vypínače"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Přidat ovládací prvky"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Upravit ovládací prvky"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Používáte režim jedné ruky"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Režim ukončíte, když přejedete prstem z dolní části obrazovky nahoru nebo klepnete kamkoli nad aplikaci"</string> </resources> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index 0fc9b9495388..a6de8a618f23 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Hold afbryderknappen nede for at se nye betjeningselementer"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Tilføj styring"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Rediger styring"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Brug af enhåndstilstand"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Du kan afslutte ved at stryge opad fra bunden af skærmen eller trykke et vilkårligt sted over appen"</string> </resources> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index a6b137a8e9e5..372cc1166fce 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -502,7 +502,7 @@ <string name="battery_saver_notification_title" msgid="8419266546034372562">"Energiesparmodus ist aktiviert"</string> <string name="battery_saver_notification_text" msgid="2617841636449016951">"Reduzierung der Leistung und Hintergrunddaten"</string> <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Energiesparmodus deaktivieren"</string> - <string name="media_projection_dialog_text" msgid="1755705274910034772">"Die App \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\" erhält Zugriff auf alle Informationen, die auf deinem Bildschirm sichtbar sind oder von deinem Gerät wiedergegeben werden, während du aufnimmst oder streamst. Dazu gehören beispielsweise Passwörter, Zahlungsdetails, Fotos, Nachrichten und Audioinhalte."</string> + <string name="media_projection_dialog_text" msgid="1755705274910034772">"Die App \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\" erhält Zugriff auf alle Informationen, die auf deinem Bildschirm sichtbar sind oder von deinem Gerät wiedergegeben werden, während du aufnimmst oder streamst. Dazu gehören beispielsweise angezeigte Passwörter und Zahlungsdetails, Fotos, Nachrichten und Audioinhalte."</string> <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Der Anbieter dieser App erhält Zugriff auf alle Informationen, die auf deinem Bildschirm sichtbar sind oder von deinem Gerät wiedergegeben werden, während du aufnimmst oder streamst. Dazu gehören beispielsweise Passwörter, Zahlungsdetails, Fotos, Nachrichten und Audioinhalte."</string> <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Aufnahme oder Stream starten?"</string> <string name="media_projection_dialog_title" msgid="3316063622495360646">"Aufnehmen oder Streamen mit der App \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\" starten?"</string> @@ -1084,4 +1084,8 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Zum Anzeigen der Karten für neue Geräte Ein-/Aus-Taste gedrückt halten"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Steuerelemente hinzufügen"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Steuerelemente bearbeiten"</string> + <!-- no translation found for one_handed_tutorial_title (6312198435090726656) --> + <skip /> + <!-- no translation found for one_handed_tutorial_description (7674850272340517174) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index bdd19a18b2dd..b4673476bad5 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Πατήστε το κουμπί λειτουργίας για να δείτε νέα στοιχεία ελέγχου."</string> <string name="controls_menu_add" msgid="4447246119229920050">"Προσθήκη στοιχείων ελέγχου"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Επεξεργασία στοιχείων ελέγχου"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Χρήση λειτουργίας ενός χεριού"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Για έξοδο, σύρετε προς τα πάνω από το κάτω μέρος της οθόνης ή πατήστε οπουδήποτε πάνω από την εφαρμογή."</string> </resources> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index 68a8d30477f6..7fa7f83c5f12 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -502,7 +502,7 @@ <string name="battery_saver_notification_title" msgid="8419266546034372562">"Battery Saver is on"</string> <string name="battery_saver_notification_text" msgid="2617841636449016951">"Reduces performance and background data"</string> <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Turn off Battery Saver"</string> - <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information, such as passwords, payment details, photos, messages and audio that you play."</string> + <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages and audio that you play."</string> <string name="media_projection_dialog_service_text" msgid="958000992162214611">"The service providing this function will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages and audio that you play."</string> <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Start recording or casting?"</string> <string name="media_projection_dialog_title" msgid="3316063622495360646">"Start recording or casting with <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string> @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Hold Power button to see new controls"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Add controls"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Edit controls"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Using one-handed mode"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"To exit, swipe up from the bottom of the screen or tap anywhere above the app"</string> </resources> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index e9856af6cb1e..f73dad3a8bef 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -502,7 +502,7 @@ <string name="battery_saver_notification_title" msgid="8419266546034372562">"Battery Saver is on"</string> <string name="battery_saver_notification_text" msgid="2617841636449016951">"Reduces performance and background data"</string> <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Turn off Battery Saver"</string> - <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information, such as passwords, payment details, photos, messages and audio that you play."</string> + <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages and audio that you play."</string> <string name="media_projection_dialog_service_text" msgid="958000992162214611">"The service providing this function will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages and audio that you play."</string> <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Start recording or casting?"</string> <string name="media_projection_dialog_title" msgid="3316063622495360646">"Start recording or casting with <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string> @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Hold Power button to see new controls"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Add controls"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Edit controls"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Using one-handed mode"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"To exit, swipe up from the bottom of the screen or tap anywhere above the app"</string> </resources> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index 68a8d30477f6..7fa7f83c5f12 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -502,7 +502,7 @@ <string name="battery_saver_notification_title" msgid="8419266546034372562">"Battery Saver is on"</string> <string name="battery_saver_notification_text" msgid="2617841636449016951">"Reduces performance and background data"</string> <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Turn off Battery Saver"</string> - <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information, such as passwords, payment details, photos, messages and audio that you play."</string> + <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages and audio that you play."</string> <string name="media_projection_dialog_service_text" msgid="958000992162214611">"The service providing this function will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages and audio that you play."</string> <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Start recording or casting?"</string> <string name="media_projection_dialog_title" msgid="3316063622495360646">"Start recording or casting with <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string> @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Hold Power button to see new controls"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Add controls"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Edit controls"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Using one-handed mode"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"To exit, swipe up from the bottom of the screen or tap anywhere above the app"</string> </resources> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index 68a8d30477f6..7fa7f83c5f12 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -502,7 +502,7 @@ <string name="battery_saver_notification_title" msgid="8419266546034372562">"Battery Saver is on"</string> <string name="battery_saver_notification_text" msgid="2617841636449016951">"Reduces performance and background data"</string> <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Turn off Battery Saver"</string> - <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information, such as passwords, payment details, photos, messages and audio that you play."</string> + <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages and audio that you play."</string> <string name="media_projection_dialog_service_text" msgid="958000992162214611">"The service providing this function will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages and audio that you play."</string> <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Start recording or casting?"</string> <string name="media_projection_dialog_title" msgid="3316063622495360646">"Start recording or casting with <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string> @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Hold Power button to see new controls"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Add controls"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Edit controls"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Using one-handed mode"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"To exit, swipe up from the bottom of the screen or tap anywhere above the app"</string> </resources> diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml index eacef2624b05..6140bf1eea3d 100644 --- a/packages/SystemUI/res/values-en-rXC/strings.xml +++ b/packages/SystemUI/res/values-en-rXC/strings.xml @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Hold Power button to see new controls"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Add controls"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Edit controls"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Using one-handed mode"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"To exit, swipe up from the bottom of the screen or tap anywhere above the app"</string> </resources> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index 94a464bb6afa..6818116ec9b5 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Mantén presionado el botón de encendido para ver los nuevos controles"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Agregar controles"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Editar controles"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Cómo usar el Modo de una mano"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Para salir, desliza el dedo hacia arriba desde la parte inferior de la pantalla o presiona cualquier parte arriba de la app"</string> </resources> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 0e7d376e5884..f09a0d24b26e 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -502,10 +502,10 @@ <string name="battery_saver_notification_title" msgid="8419266546034372562">"Ahorro de batería activado"</string> <string name="battery_saver_notification_text" msgid="2617841636449016951">"Reduce el rendimiento y los datos en segundo plano"</string> <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Desactivar Ahorro de batería"</string> - <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> tendrá acceso a toda la información que se muestre en pantalla o se reproduzca en el dispositivo mientras grabas o envías contenido, incluyendo contraseñas, detalles de pagos, fotos, mensajes y audios que reproduzcas."</string> - <string name="media_projection_dialog_service_text" msgid="958000992162214611">"El servicio que ofrece esta función tendrá acceso a toda la información que se muestre en pantalla o se reproduzca en el dispositivo mientras grabas o envías contenido, incluyendo contraseñas, detalles de pagos, fotos, mensajes y audios que reproduzcas."</string> + <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> tendrá acceso a toda la información que se muestre en la pantalla o se reproduzca en el dispositivo mientras grabas o envías contenido, incluyendo contraseñas, detalles de pagos, fotos, mensajes y audios que reproduzcas."</string> + <string name="media_projection_dialog_service_text" msgid="958000992162214611">"El servicio que ofrece esta función tendrá acceso a toda la información que se muestre en la pantalla o se reproduzca en el dispositivo mientras grabas o envías contenido, incluyendo contraseñas, detalles de pagos, fotos, mensajes y audios que reproduzcas."</string> <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"¿Empezar a grabar o enviar contenido?"</string> - <string name="media_projection_dialog_title" msgid="3316063622495360646">"¿Quieres iniciar la grabación o el envío de contenido con <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string> + <string name="media_projection_dialog_title" msgid="3316063622495360646">"¿Iniciar grabación o el envío de contenido en <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string> <string name="media_projection_remember_text" msgid="6896767327140422951">"No volver a mostrar"</string> <string name="clear_all_notifications_text" msgid="348312370303046130">"Borrar todo"</string> <string name="manage_notifications_text" msgid="6885645344647733116">"Gestionar"</string> @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Mantén pulsado el botón de encendido para ver los controles nuevos"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Añadir controles"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Editar controles"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Utilizar el modo una mano"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Para salir, desliza dos dedos hacia arriba desde la parte inferior de la pantalla o toca cualquier zona que haya encima de la aplicación."</string> </resources> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index fc9c73bb10bf..6236f251f55d 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Uute juhtelementide vaatamiseks hoidke all toitenuppu"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Lisa juhtelemente"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Muuda juhtelemente"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Ühekäerežiimi kasutamine"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Väljumiseks pühkige ekraani alaosast üles või puudutage ekraani rakenduse kohal"</string> </resources> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index 041beab2e74c..167a09c71ce3 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Eduki sakatuta etengailua kontrolatzeko aukera berriak ikusteko"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Gehitu aukerak"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Editatu aukerak"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Esku bakarreko modua erabiltzea"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Ateratzeko, pasatu hatza pantailaren behealdetik gora edo sakatu aplikazioaren gainean, edonon"</string> </resources> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index 05e076f55a12..055505200cc2 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -742,7 +742,7 @@ <string name="feedback_promoted" msgid="8075757485407091976">"سیستمْ این اعلان را ارتقا داده است."</string> <string name="feedback_demoted" msgid="5848066008939031913">"سیستمْ این اعلان را تنزل داده است."</string> <string name="feedback_prompt" msgid="2278631214125128281">"این مورد درست بود؟"</string> - <string name="feedback_response" msgid="4671729244976641339">"از بازخورد شما سپاسگزاریم!"</string> + <string name="feedback_response" msgid="4671729244976641339">"از بازخورد شما سپاسگذاریم!"</string> <string name="feedback_ok" msgid="6481426753298857144">"تأیید"</string> <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"کنترلهای اعلان برای <xliff:g id="APP_NAME">%1$s</xliff:g> باز شد"</string> <string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"کنترلهای اعلان برای <xliff:g id="APP_NAME">%1$s</xliff:g> بسته شد"</string> @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"برای دیدن کنترلهای جدید، دکمه روشن/خاموش را پایین نگه دارید"</string> <string name="controls_menu_add" msgid="4447246119229920050">"افزودن کنترلها"</string> <string name="controls_menu_edit" msgid="890623986951347062">"ویرایش کنترلها"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"استفاده از «حالت تک حرکت»"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"برای خارج شدن، از پایین صفحهنمایش تند بهطرف بالا بکشید یا در هر جایی از بالای برنامه که میخواهید ضربه بزنید"</string> </resources> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index 4fccaaca57f2..ec9bbc4a8929 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Paina virtapainiketta pitkään nähdäksesi uudet säätimet"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Lisää säätimiä"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Muokkaa säätimiä"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Yhden käden moodin käyttö"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Poistu pyyhkäisemällä ylös näytön alareunasta tai napauttamalla sovelluksen yllä"</string> </resources> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 4e2074c34a88..6c5387f63eda 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -502,7 +502,7 @@ <string name="battery_saver_notification_title" msgid="8419266546034372562">"Économiseur de pile activé"</string> <string name="battery_saver_notification_text" msgid="2617841636449016951">"Réduire les performances et de fond"</string> <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Désactiver la fonction Économiseur de pile"</string> - <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aura accès à toute l\'information visible sur votre écran ou qui joue sur votre appareil durant l\'enregistrement ou la diffusion. Cela comprend des renseignements comme les mots de passe, les détails du paiement, les photos, les messages et l\'audio que vous faites jouer."</string> + <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aura accès à toute l\'information visible sur votre écran ou qui joue sur votre appareil durant l\'enregistrement ou la diffusion. Cela comprend des renseignements comme les mots de passe, les détails du paiement, les photos, les messages et le contenu audio que vous faites jouer."</string> <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Le service offrant cette fonction aura accès à toute l\'information qui est visible sur votre écran ou sur ce qui joue sur votre appareil durant l\'enregistrement ou la diffusion. Cela comprend des renseignements comme les mots de passe, les détails du paiement, les photos, les messages et le contenu audio que vous faites jouer."</string> <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Commencer à enregistrer ou à diffuser?"</string> <string name="media_projection_dialog_title" msgid="3316063622495360646">"Commencer à enregistrer ou à diffuser avec <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string> @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Maintenez enfoncé l\'interrupteur pour afficher les nouvelles commandes"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Ajouter des commandes"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Modifier des commandes"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Utiliser le mode Une main"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Pour quitter, balayez l\'écran du bas vers le haut, ou touchez n\'importe où sur l\'écran en haut de l\'application"</string> </resources> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index fedec563edcf..3a2ae07bc64d 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -502,7 +502,7 @@ <string name="battery_saver_notification_title" msgid="8419266546034372562">"Économiseur de batterie activé"</string> <string name="battery_saver_notification_text" msgid="2617841636449016951">"Limite les performances et les données en arrière-plan."</string> <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Désactiver l\'économiseur de batterie"</string> - <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aura accès à toutes les informations visibles sur votre écran ou lues depuis votre appareil lors d\'un enregistrement ou d\'une diffusion de contenu. Par exemple, vos mots de passe, vos données de paiement, vos photos, vos messages ou encore vos contenus audio lus."</string> + <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aura accès à toutes les informations visibles sur votre écran ou lues depuis votre appareil pendant un enregistrement ou une diffusion de contenu. Il peut s\'agir de mots de passe, données de paiement, photos, messages ou encore contenus audio lus."</string> <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Le service qui fournit cette fonction aura accès à toutes les informations visibles sur votre écran ou lues depuis votre appareil lors d\'un enregistrement ou d\'une diffusion de contenu. Cela comprend, entre autres, vos mots de passe, vos données de paiement, vos photos, vos messages ou encore les contenus audio que vous lisez."</string> <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Démarrer l\'enregistrement ou la diffusion ?"</string> <string name="media_projection_dialog_title" msgid="3316063622495360646">"Démarrer l\'enregistrement ou la diffusion avec <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ?"</string> @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Appuyez de manière prolongée sur le bouton Marche/Arrêt pour afficher les nouvelles commandes"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Ajouter des commandes"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Modifier des commandes"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Utiliser le mode une main"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Pour quitter, balayez l\'écran de bas en haut ou appuyez n\'importe où au-dessus de l\'application"</string> </resources> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index c1b9024e8b44..25c7b4bfab59 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Mantén premido o botón de acendido para ver os novos controis"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Engadir controis"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Editar controis"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Como se usa o modo dunha soa man?"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Para saír, pasa o dedo cara arriba desde a parte inferior da pantalla ou toca calquera lugar da zona situada encima da aplicación"</string> </resources> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index 9c2f71790fb0..17cc5a45aeb9 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -1084,4 +1084,8 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"નવા નિયંત્રણ જોવા માટે પાવર બટનને દબાવી રાખો"</string> <string name="controls_menu_add" msgid="4447246119229920050">"નિયંત્રણો ઉમેરો"</string> <string name="controls_menu_edit" msgid="890623986951347062">"નિયંત્રણોમાં ફેરફાર કરો"</string> + <!-- no translation found for one_handed_tutorial_title (6312198435090726656) --> + <skip /> + <!-- no translation found for one_handed_tutorial_description (7674850272340517174) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index cc2faa2a598c..97ae7b6e8926 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -1086,4 +1086,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"नए कंट्रोल देखने के लिए पावर बटन दबाकर रखें"</string> <string name="controls_menu_add" msgid="4447246119229920050">"कंट्राेल जोड़ें"</string> <string name="controls_menu_edit" msgid="890623986951347062">"कंट्रोल मेन्यू में बदलाव करें"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"वन-हैंडेड मोड का इस्तेमाल करें"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"इसे बंद करने के लिए, स्क्रीन के सबसे निचले हिस्से से ऊपर की ओर स्वाइप करें या ऐप्लिकेशन के आइकॉन के ऊपर कहीं भी टैप करें"</string> </resources> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index 90251995002d..9e17735ef18d 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -505,10 +505,10 @@ <string name="battery_saver_notification_title" msgid="8419266546034372562">"Štednja baterije je uključena"</string> <string name="battery_saver_notification_text" msgid="2617841636449016951">"Smanjuje količinu rada i pozadinske podatke"</string> <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Isključite Štednju baterije"</string> - <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> imat će pristup svim podacima koji su vidljivi na vašem zaslonu ili koji se reproduciraju s vašeg uređaja tijekom snimanja ili emitiranja. To uključuje podatke kao što su zaporke, podaci o plaćanju, fotografije, poruke i audiozapisi koje reproducirate."</string> + <string name="media_projection_dialog_text" msgid="1755705274910034772">"Aplikacija <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> imat će pristup svim podacima koji su vidljivi na vašem zaslonu ili koji se reproduciraju s vašeg uređaja tijekom snimanja ili emitiranja. To uključuje podatke kao što su zaporke, podaci o plaćanju, fotografije, poruke i audiozapisi koje reproducirate."</string> <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Usluga koja pruža ovu funkcionalnost imat će pristup svim podacima koji su vidljivi na vašem zaslonu ili koji se reproduciraju s vašeg uređaja tijekom snimanja ili emitiranja. To uključuje podatke kao što su zaporke, podaci o plaćanju, fotografije, poruke i audiozapisi koje reproducirate."</string> <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Započeti snimanje ili emitiranje?"</string> - <string name="media_projection_dialog_title" msgid="3316063622495360646">"Započeti snimanje ili emitiranja pomoću aplikacije <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string> + <string name="media_projection_dialog_title" msgid="3316063622495360646">"Započeti snimanje ili emitiranje pomoću aplikacije <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string> <string name="media_projection_remember_text" msgid="6896767327140422951">"Ne prikazuj ponovo"</string> <string name="clear_all_notifications_text" msgid="348312370303046130">"Izbriši sve"</string> <string name="manage_notifications_text" msgid="6885645344647733116">"Upravljajte"</string> @@ -1090,4 +1090,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Zadržite tipku za uključivanje/isključivanje za prikaz novih kontrola"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Dodaj kontrole"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Uredi kontrole"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Korištenje načina rada jednom rukom"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Za izlaz prijeđite prstom od dna zaslona prema gore ili dodirnite bio gdje iznad aplikacije"</string> </resources> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index 689d86960c88..804587f9bdcd 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Az új vezérlők megtekintéséhez tartsa nyomva a bekapcsológombot"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Vezérlők hozzáadása"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Vezérlők szerkesztése"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Egykezes mód használata"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"A kilépéshez csúsztasson felfelé a képernyő aljáról, vagy koppintson az alkalmazás felett a képernyő bármelyik részére"</string> </resources> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index a574f26a514d..08ef489901ee 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Սեղմած պահեք սնուցման կոճակը՝ կառավարման նոր տարրերը տեսնելու համար։"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Ավելացնել կառավարման տարրեր"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Փոփոխել կառավարման տարրերը"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Ինչպես օգտվել մեկ ձեռքի ռեժիմից"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Դուրս գալու համար մատը սահեցրեք էկրանի ներքևից վերև կամ հպեք հավելվածի վերևում որևէ տեղ։"</string> </resources> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 7cc1b898dedb..ba9eca3f0de9 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -855,7 +855,7 @@ </string-array> <string name="menu_ime" msgid="5677467548258017952">"Pengalih keyboard"</string> <string name="save" msgid="3392754183673848006">"Simpan"</string> - <string name="reset" msgid="8715144064608810383">"Setel ulang"</string> + <string name="reset" msgid="8715144064608810383">"Reset"</string> <string name="adjust_button_width" msgid="8313444823666482197">"Sesuaikan lebar tombol"</string> <string name="clipboard" msgid="8517342737534284617">"Papan klip"</string> <string name="accessibility_key" msgid="3471162841552818281">"Tombol navigasi khusus"</string> @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Tahan Tombol daya untuk melihat kontrol baru"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Tambahkan kontrol"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Edit kontrol"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Menggunakan mode satu tangan"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Untuk keluar, geser layar dari bawah ke atas atau ketuk di mana saja di atas aplikasi"</string> </resources> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index 8b686b8deb96..629581346d46 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Haltu aflrofanum inni til að sjá nýjar stýringar"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Bæta við stýringum"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Breyta stýringum"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Notkun stillingar fyrir eina hönd"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Til að loka skaltu strjúka upp frá neðri hluta skjásins eða ýta hvar sem er fyrir ofan forritið"</string> </resources> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 35742fd4d4db..ef862cb0f919 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Tieni premuto il tasto di accensione per visualizzare i nuovi controlli"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Aggiungi controlli"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Modifica controlli"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Usare la modalità one-hand"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Per uscire, scorri verso l\'alto dalla parte inferiore dello schermo oppure tocca un punto qualsiasi sopra l\'app"</string> </resources> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index 1029bb2042db..358946d69472 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -508,7 +508,7 @@ <string name="battery_saver_notification_title" msgid="8419266546034372562">"תכונת החיסכון בסוללה פועלת"</string> <string name="battery_saver_notification_text" msgid="2617841636449016951">"מפחית את הביצועים ונתונים ברקע"</string> <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"כיבוי תכונת החיסכון בסוללה"</string> - <string name="media_projection_dialog_text" msgid="1755705274910034772">"לאפליקציה <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> תהיה גישה לכל הפרטים שגלויים במסך שלך או מופעלים מהמכשיר שלך בזמן הקלטה או העברה (cast). זה כולל פרטים כמו סיסמאות, פרטי תשלום, תמונות, הודעות ואודיו שמושמע מהמכשיר."</string> + <string name="media_projection_dialog_text" msgid="1755705274910034772">"לאפליקציית <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> תהיה גישה לכל המידע הגלוי במסך שלך ולכל תוכן שמופעל במכשיר שלך בזמן הקלטה או העברה (casting). המידע הזה כולל פרטים כמו סיסמאות, פרטי תשלום, תמונות, הודעות ואודיו שמושמע מהמכשיר."</string> <string name="media_projection_dialog_service_text" msgid="958000992162214611">"לשירות שמספק את הפונקציה הזו תהיה גישה לכל הפרטים שגלויים במסך שלך או מופעלים מהמכשיר שלך בזמן הקלטה או העברה (cast). זה כולל פרטים כמו סיסמאות, פרטי תשלום, תמונות, הודעות ואודיו שמושמע מהמכשיר."</string> <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"להתחיל להקליט או להעביר (cast)?"</string> <string name="media_projection_dialog_title" msgid="3316063622495360646">"להתחיל להקליט או להעביר (cast) באמצעות <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string> @@ -522,7 +522,7 @@ <string name="notification_section_header_conversations" msgid="821834744538345661">"שיחות"</string> <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"ניקוי כל ההתראות השקטות"</string> <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"התראות הושהו על ידי מצב \'נא לא להפריע\'"</string> - <string name="media_projection_action_text" msgid="3634906766918186440">"התחל כעת"</string> + <string name="media_projection_action_text" msgid="3634906766918186440">"כן, אפשר להתחיל"</string> <string name="empty_shade_text" msgid="8935967157319717412">"אין התראות"</string> <string name="profile_owned_footer" msgid="2756770645766113964">"ייתכן שהפרופיל נתון למעקב"</string> <string name="vpn_footer" msgid="3457155078010607471">"ייתכן שהרשת נמצאת במעקב"</string> @@ -1096,4 +1096,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"ניתן ללחוץ על לחצן ההפעלה כדי להציג פקדים חדשים"</string> <string name="controls_menu_add" msgid="4447246119229920050">"הוספת פקדים"</string> <string name="controls_menu_edit" msgid="890623986951347062">"עריכת פקדים"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"איך להשתמש במצב שימוש ביד אחת"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"כדי לצאת, יש להחליק למעלה מתחתית המסך או להקיש במקום כלשהו במסך מעל האפליקציה"</string> </resources> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index fedd9bb7ffc0..9cb872bf1fab 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -502,10 +502,10 @@ <string name="battery_saver_notification_title" msgid="8419266546034372562">"バッテリー セーバー ON"</string> <string name="battery_saver_notification_text" msgid="2617841636449016951">"パフォーマンスとバックグラウンドデータを制限します"</string> <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"バッテリー セーバーを OFF"</string> - <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>は、記録中やキャスト中に画面上に表示またはデバイスから再生されるすべての情報にアクセスできます。これには、パスワード、お支払いの詳細、写真、メッセージ、再生される音声などの情報が含まれます。"</string> - <string name="media_projection_dialog_service_text" msgid="958000992162214611">"この機能を提供するサービスは、記録中やキャスト中に画面上に表示またはデバイスから再生されるすべての情報にアクセスできます。これには、パスワード、お支払いの詳細、写真、メッセージ、再生される音声などの情報が含まれます。"</string> - <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"記録やキャストを開始しますか?"</string> - <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>で記録やキャストを開始しますか?"</string> + <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> は、録画中やキャスト中に画面に表示されたり、デバイスで再生されるすべての情報にアクセスできます。これには、パスワード、お支払いの詳細、写真、メッセージ、再生される音声などが含まれます。"</string> + <string name="media_projection_dialog_service_text" msgid="958000992162214611">"この機能を提供するサービスは、録画中やキャスト中に画面に表示されたり、デバイスで再生されるすべての情報にアクセスできます。これには、パスワード、お支払いの詳細、写真、メッセージ、再生される音声などが含まれます。"</string> + <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"録画やキャストを開始しますか?"</string> + <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> で録画やキャストを開始しますか?"</string> <string name="media_projection_remember_text" msgid="6896767327140422951">"次回から表示しない"</string> <string name="clear_all_notifications_text" msgid="348312370303046130">"すべて消去"</string> <string name="manage_notifications_text" msgid="6885645344647733116">"管理"</string> @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"電源ボタンを長押しすると、新しいコントロールが表示されます"</string> <string name="controls_menu_add" msgid="4447246119229920050">"コントロールを追加"</string> <string name="controls_menu_edit" msgid="890623986951347062">"コントロールを編集"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"片手モードの使用"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"終了するには、画面を下から上にスワイプするか、アプリの任意の場所をタップします"</string> </resources> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index 5c55112ea1fd..ef6ff128ed09 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"ხანგრძლივად დააჭირეთ ჩართვის ღილაკს მართვის ახალი საშუალებების სანახავად"</string> <string name="controls_menu_add" msgid="4447246119229920050">"მართვის საშუალებების დამატება"</string> <string name="controls_menu_edit" msgid="890623986951347062">"მართვის საშუალებათა რედაქტირება"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"ცალი ხელის რეჟიმის გამოყენება"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"გასასვლელად გადაფურცლეთ ეკრანის ქვედა კიდიდან ზემოთ ან შეეხეთ ნებისმიერ ადგილას აპის ზემოთ"</string> </resources> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index 94c8ea33a461..08b1f6262f1f 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -502,8 +502,8 @@ <string name="battery_saver_notification_title" msgid="8419266546034372562">"Battery saver қосулы"</string> <string name="battery_saver_notification_text" msgid="2617841636449016951">"Өнімділікті және фондық деректерді азайтады"</string> <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Battery saver функциясын өшіру"</string> - <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> экранда көрсетілетін немесе жазу не трансляциялау кезінде құрылғыда ойнатылған барлық ақпаратты пайдалана алады. Бұған құпия сөздер, төлем туралы мәліметтер, суреттер, хабарлар және ойнатылатын аудио сияқты ақпарат кіреді."</string> - <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Осы функцияны ұсынатын қызмет экранда көрсетілетін немесе жазу не трансляциялау кезінде құрылғыда ойнатылған барлық ақпаратты пайдалана алады. Бұған құпия сөздер, төлем туралы мәліметтер, суреттер, хабарлар және ойнатылатын аудио сияқты ақпарат кіреді."</string> + <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> жазу не трансляциялау кезінде экранда көрсетілетін немесе дыбысталатын барлық ақпаратты пайдалана алады. Бұған құпия сөздер, төлем туралы мәліметтер, суреттер, хабарлар және аудиоматериалдар кіреді."</string> + <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Осы функцияны ұсынатын қызмет жазу не трансляциялау кезінде экранда көрсетілетін немесе құрылғыда дыбысталатын ақпаратты пайдалана алады. Бұған құпия сөздер, төлем туралы мәліметтер, суреттер, хабарлар және аудиоматериалдар кіреді."</string> <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Жазу немесе трансляциялау басталсын ба?"</string> <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> арқылы жазу немесе трансляциялау басталсын ба?"</string> <string name="media_projection_remember_text" msgid="6896767327140422951">"Қайта көрсетпеу"</string> @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Жаңа басқару элементтерін көру үшін \"Қуат\" түймесін басып тұрыңыз."</string> <string name="controls_menu_add" msgid="4447246119229920050">"Басқару элементтерін енгізу"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Басқару элементтерін өзгерту"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Бір қолмен енгізу режимін пайдалану"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Шығу үшін экранның төменгі жағынан жоғары қарай сипаңыз немесе қолданбаның үстінен кез келген жерден түртіңіз."</string> </resources> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index 5ba460adebf3..6d80ee47413c 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -502,10 +502,10 @@ <string name="battery_saver_notification_title" msgid="8419266546034372562">"កម្មវិធីសន្សំថ្មបានបើក"</string> <string name="battery_saver_notification_text" msgid="2617841636449016951">"ការបន្ថយការប្រតិបត្តិ និងទិន្នន័យផ្ទៃខាងក្រោយ"</string> <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"បិទកម្មវិធីសន្សំថ្ម"</string> - <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> នឹងមានសិទ្ធិចូលប្រើព័ត៌មានទាំងអស់ដែលអាចមើលឃើញនៅលើអេក្រង់របស់អ្នក ឬដែលចាក់ពីឧបករណ៍របស់អ្នក នៅពេលកំពុងថត ឬបញ្ជូន។ ព័ត៌មាននេះមានដូចជា ពាក្យសម្ងាត់ ព័ត៌មានលម្អិតអំពីការទូទាត់ប្រាក់ រូបថត សារ និងសំឡេងដែលអ្នកចាក់ជាដើម។"</string> - <string name="media_projection_dialog_service_text" msgid="958000992162214611">"សេវាកម្មដែលផ្ដល់មុខងារនេះនឹងមានសិទ្ធិចូលប្រើព័ត៌មានទាំងអស់ដែលអាចមើលឃើញនៅលើអេក្រង់របស់អ្នក ឬដែលចាក់ពីឧបករណ៍របស់អ្នក នៅពេលកំពុងថត ឬបញ្ជូន។ ព័ត៌មាននេះមានដូចជា ពាក្យសម្ងាត់ ព័ត៌មានលម្អិតអំពីការទូទាត់ប្រាក់ រូបថត សារ និងសំឡេងដែលអ្នកចាក់ជាដើម។"</string> + <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> នឹងមានសិទ្ធិចូលប្រើព័ត៌មានទាំងអស់ដែលអាចមើលឃើញនៅលើអេក្រង់របស់អ្នក ឬដែលចាក់ពីឧបករណ៍របស់អ្នក នៅពេលកំពុងថត ឬភ្ជាប់។ ព័ត៌មាននេះមានដូចជា ពាក្យសម្ងាត់ ព័ត៌មានលម្អិតអំពីការទូទាត់ប្រាក់ រូបថត សារ និងសំឡេងដែលអ្នកចាក់ជាដើម។"</string> + <string name="media_projection_dialog_service_text" msgid="958000992162214611">"សេវាកម្មដែលផ្ដល់មុខងារនេះនឹងមានសិទ្ធិចូលប្រើព័ត៌មានទាំងអស់ដែលអាចមើលឃើញនៅលើអេក្រង់របស់អ្នក ឬដែលចាក់ពីឧបករណ៍របស់អ្នក នៅពេលកំពុងថត ឬភ្ជាប់។ ព័ត៌មាននេះមានដូចជា ពាក្យសម្ងាត់ ព័ត៌មានលម្អិតអំពីការទូទាត់ប្រាក់ រូបថត សារ និងសំឡេងដែលអ្នកចាក់ជាដើម។"</string> <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"ចាប់ផ្ដើមថត ឬបញ្ជូនមែនទេ?"</string> - <string name="media_projection_dialog_title" msgid="3316063622495360646">"ចាប់ផ្ដើមថត ឬបញ្ជូនដោយប្រើ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ឬ?"</string> + <string name="media_projection_dialog_title" msgid="3316063622495360646">"ចាប់ផ្ដើមថត ឬភ្ជាប់ដោយប្រើ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ឬ?"</string> <string name="media_projection_remember_text" msgid="6896767327140422951">"កុំបង្ហាញម្ដងទៀត"</string> <string name="clear_all_notifications_text" msgid="348312370303046130">"សម្អាតទាំងអស់"</string> <string name="manage_notifications_text" msgid="6885645344647733116">"គ្រប់គ្រង"</string> @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"សង្កត់ប៊ូតុងថាមពល ដើម្បីមើលឃើញការគ្រប់គ្រងថ្មីៗ"</string> <string name="controls_menu_add" msgid="4447246119229920050">"បញ្ចូលផ្ទាំងគ្រប់គ្រង"</string> <string name="controls_menu_edit" msgid="890623986951347062">"កែផ្ទាំងគ្រប់គ្រង"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"កំពុងប្រើមុខងារប្រើដៃម្ខាង"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"ដើម្បីចាកចេញ សូមអូសឡើងលើពីផ្នែកខាងក្រោមអេក្រង់ ឬចុចផ្នែកណាមួយនៅខាងលើកម្មវិធី"</string> </resources> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index 7b8ed88bfd19..db2086458286 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -502,10 +502,10 @@ <string name="battery_saver_notification_title" msgid="8419266546034372562">"ಬ್ಯಾಟರಿ ಸೇವರ್ ಆನ್ ಆಗಿದೆ"</string> <string name="battery_saver_notification_text" msgid="2617841636449016951">"ಕಾರ್ಯಕ್ಷಮತೆ ಮತ್ತು ಹಿನ್ನೆಲೆ ಡೇಟಾವನ್ನು ಕಡಿಮೆ ಮಾಡುತ್ತದೆ"</string> <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"ಬ್ಯಾಟರಿ ಸೇವರ್ ಆಫ್ ಮಾಡಿ"</string> - <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>, ಸ್ಕ್ರೀನ್ ಮೇಲೆ ಗೋಚರಿಸುವ ಅಥವಾ ರೆಕಾರ್ಡಿಂಗ್ ಅಥವಾ ಬಿತ್ತರಿಸುವಾಗ ಸಾಧನದಲ್ಲಿ ಪ್ಲೇ ಆಗುವ ಎಲ್ಲಾ ಮಾಹಿತಿಗಳಿಗೆ ಪ್ರವೇಶವನ್ನು ಹೊಂದಿರುತ್ತವೆ. ಪಾಸ್ವರ್ಡ್ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಫೋಟೋಗಳು, ಸಂದೇಶಗಳು ಮತ್ತು ಆಡಿಯೊ ಪ್ಲೇಬ್ಯಾಕ್ನಂತಹ ಮಾಹಿತಿಯನ್ನು ಇದು ಒಳಗೊಂಡಿದೆ."</string> + <string name="media_projection_dialog_text" msgid="1755705274910034772">"ರೆಕಾರ್ಡ್ ಮಾಡುವಾಗ ಅಥವಾ ಬಿತ್ತರಿಸುವಾಗ ಸ್ಕ್ರೀನ್ ಮೇಲೆ ಕಾಣಿಸುವ ಸಕಲ ಮಾಹಿತಿಗೂ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ಪ್ರವೇಶ ಹೊಂದಿರುತ್ತದೆ. ಇದು ಪಾಸ್ವರ್ಡ್ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಫೋಟೋಗಳು, ಸಂದೇಶಗಳು ಮತ್ತು ಆಡಿಯೊ ಪ್ಲೇಬ್ಯಾಕ್ನಂತಹ ಮಾಹಿತಿಯನ್ನು ಕೂಡ ಒಳಗೊಂಡಿರುತ್ತದೆ."</string> <string name="media_projection_dialog_service_text" msgid="958000992162214611">"ಈ ವೈಶಿಷ್ಟ್ಯವು ಒದಗಿಸುವ ಸೇವೆಗಳು, ಸ್ಕ್ರೀನ್ ಮೇಲೆ ಗೋಚರಿಸುವ ಅಥವಾ ರೆಕಾರ್ಡಿಂಗ್ ಅಥವಾ ಬಿತ್ತರಿಸುವಾಗ ಸಾಧನದಲ್ಲಿ ಪ್ಲೇ ಆಗುವ ಎಲ್ಲಾ ಮಾಹಿತಿಗಳಿಗೆ ಪ್ರವೇಶವನ್ನು ಹೊಂದಿರುತ್ತವೆ. ಪಾಸ್ವರ್ಡ್ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಫೋಟೋಗಳು, ಸಂದೇಶಗಳು ಮತ್ತು ಆಡಿಯೊ ಪ್ಲೇಬ್ಯಾಕ್ನಂತಹ ಮಾಹಿತಿಯನ್ನು ಇದು ಒಳಗೊಂಡಿದೆ."</string> <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"ರೆಕಾರ್ಡಿಂಗ್ ಅಥವಾ ಬಿತ್ತರಿಸುವಿಕೆಯನ್ನು ಪ್ರಾರಂಭಿಸಬೇಕೆ?"</string> - <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ಬಳಸಿಕೊಂಡು ರೆಕಾರ್ಡಿಂಗ್ ಅಥವಾ ಬಿತ್ತರಿಸುವುದನ್ನು ಪ್ರಾರಂಭಿಸುವುದೇ?"</string> + <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ಮೂಲಕ ರೆಕಾರ್ಡಿಂಗ್, ಬಿತ್ತರಿಸುವುದನ್ನು ಪ್ರಾರಂಭಿಸುವುದೇ?"</string> <string name="media_projection_remember_text" msgid="6896767327140422951">"ಮತ್ತೊಮ್ಮೆ ತೋರಿಸದಿರು"</string> <string name="clear_all_notifications_text" msgid="348312370303046130">"ಎಲ್ಲವನ್ನೂ ತೆರವುಗೊಳಿಸಿ"</string> <string name="manage_notifications_text" msgid="6885645344647733116">"ನಿರ್ವಹಿಸಿ"</string> @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"ಹೊಸ ನಿಯಂತ್ರಣಗಳನ್ನು ನೋಡಲು ಪವರ್ ಬಟನ್ ಹಿಡಿದುಕೊಳ್ಳಿ"</string> <string name="controls_menu_add" msgid="4447246119229920050">"ನಿಯಂತ್ರಣಗಳನ್ನು ಸೇರಿಸಿ"</string> <string name="controls_menu_edit" msgid="890623986951347062">"ನಿಯಂತ್ರಣಗಳನ್ನು ಎಡಿಟ್ ಮಾಡಿ"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"ಒಂದು ಕೈ ಮೋಡ್ ಅನ್ನು ಬಳಸಲಾಗುತ್ತಿದೆ"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"ನಿರ್ಗಮಿಸಲು, ಸ್ಕ್ರೀನ್ನ ಕೆಳಗಿನಿಂದ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ ಅಥವಾ ಆ್ಯಪ್ನ ಮೇಲೆ ಎಲ್ಲಿಯಾದರೂ ಟ್ಯಾಪ್ ಮಾಡಿ"</string> </resources> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index 8d1177bec0ea..b43eb410c46d 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"새 컨트롤을 보려면 전원 버튼을 길게 누르세요."</string> <string name="controls_menu_add" msgid="4447246119229920050">"컨트롤 추가"</string> <string name="controls_menu_edit" msgid="890623986951347062">"컨트롤 수정"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"한 손 사용 모드 사용하기"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"화면 하단에서 위로 스와이프하거나 앱 상단을 탭하여 종료합니다."</string> </resources> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index 794de98420d1..02e468a4f311 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -502,10 +502,10 @@ <string name="battery_saver_notification_title" msgid="8419266546034372562">"Батареяны үнөмдөгүч режими күйүк"</string> <string name="battery_saver_notification_text" msgid="2617841636449016951">"Иштин майнаптуулугун начарлатып, фондук дайын-даректерди чектейт"</string> <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Батареяны үнөмдөгүчтү өчүрүү"</string> - <string name="media_projection_dialog_text" msgid="1755705274910034772">"Бул функцияны аткарган <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> кызматы экраныңызда көрүнүп турган бардык маалыматты же жаздыруу жана тышкы экранга чыгаруу учурунда түзмөгүңүздө ойнотулган маалыматты колдоно алат. Буга сырсөздөр, төлөмдүн чоо-жайы, сүрөттөр, билдирүүлөр жана ойнотулган аудио кирет."</string> - <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Бул функцияны аткарган кызматка экраныңыздагы бардык маалымат же түзмөктө ойнотулуп жаткан нерсе, сырсөздөр, төлөмдөрдүн чоо-жайы, сүрөттөр, билдирүүлөр жана аудио файлдар жеткиликтүү болот."</string> + <string name="media_projection_dialog_text" msgid="1755705274910034772">"Жаздырып же тышкы экранга чыгарып жатканда, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> колдонмосу экраныңыздагы бардык маалыматты же түзмөктө ойнолуп жаткан бардык нерселерди (сырсөздөрдү, төлөмдүн чоо-жайын, сүрөттөрдү, билдирүүлөрдү жана угуп жаткан аудиофайлдарды) көрө алат."</string> + <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Жаздырып же тышкы экранга чыгарып жатканда, бул колдонмо экраныңыздагы бардык маалыматты же түзмөктө ойнолуп жаткан бардык нерселерди (сырсөздөрдү, төлөмдүн чоо-жайын, сүрөттөрдү, билдирүүлөрдү жана угуп жаткан аудиофайлдарды) көрө алат."</string> <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Жаздырып же тышкы экранга чыгарып баштайсызбы?"</string> - <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> менен жаздырылып же тышкы экранга чыгарылып башталсынбы?"</string> + <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> колдонмосу аркылуу жаздырып же тышкы экранга чыгарып баштайсызбы?"</string> <string name="media_projection_remember_text" msgid="6896767327140422951">"Экинчи көрүнбөсүн"</string> <string name="clear_all_notifications_text" msgid="348312370303046130">"Баарын тазалап салуу"</string> <string name="manage_notifications_text" msgid="6885645344647733116">"Башкаруу"</string> @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Башкаруу элементтерин көрүү үчүн күйгүзүү/өчүрүү баскычын коё бербей басып туруңуз"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Башкаруу элементтерин кошуу"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Башкаруу элементтерин түзөтүү"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Бир кол режимин колдонуу"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Чыгуу үчүн экранды ылдый жагынан өйдө көздөй сүрүңүз же колдонмонун өйдө жагын басыңыз"</string> </resources> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index 2fe3f8075dbb..030bdc1ae120 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"ກົດປຸ່ມເປີດປິດຄ້າງໄວ້ເພື່ອເບິ່ງການຄວບຄຸມໃໝ່"</string> <string name="controls_menu_add" msgid="4447246119229920050">"ເພີ່ມການຄວບຄຸມ"</string> <string name="controls_menu_edit" msgid="890623986951347062">"ແກ້ໄຂການຄວບຄຸມ"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"ກຳລັງໃຊ້ໂໝດມືດຽວ"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"ເພື່ອອອກ, ໃຫ້ປັດຂຶ້ນຈາກລຸ່ມສຸດຂອງໜ້າຈໍ ຫຼື ແຕະບ່ອນໃດກໍໄດ້ຢູ່ເທິງແອັບ"</string> </resources> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index 4489b7dd62ee..6f487416b322 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -508,7 +508,7 @@ <string name="battery_saver_notification_title" msgid="8419266546034372562">"Akumuliatoriaus tausojimo priemonė įjungta"</string> <string name="battery_saver_notification_text" msgid="2617841636449016951">"Sumažinamas našumas ir foninių duomenų naudojimas"</string> <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Išjungti Akumuliatoriaus tausojimo priemonę"</string> - <string name="media_projection_dialog_text" msgid="1755705274910034772">"„<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>“ galės pasiekti visą informaciją, matomą ekrane ir leidžiamą iš įrenginio įrašant ar perduodant turinį. Tai apima įvairią informaciją, pvz., slaptažodžius, išsamią mokėjimo informaciją, nuotraukas, pranešimus ir leidžiamus garso įrašus."</string> + <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> galės pasiekti visą informaciją, matomą ekrane ir leidžiamą iš įrenginio įrašant ar perduodant turinį. Tai apima įvairią informaciją, pvz., slaptažodžius, išsamią mokėjimo informaciją, nuotraukas, pranešimus ir leidžiamus garso įrašus."</string> <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Šią funkcija teikianti paslauga galės pasiekti visą informaciją, matomą ekrane ir leidžiamą iš įrenginio įrašant ar perduodant turinį. Tai apima įvairią informaciją, pvz., slaptažodžius, išsamią mokėjimo informaciją, nuotraukas, pranešimus ir leidžiamus garso įrašus."</string> <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Pradėti įrašyti ar perduoti turinį?"</string> <string name="media_projection_dialog_title" msgid="3316063622495360646">"Pradėti įrašyti ar perduoti turinį naudojant „<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>“?"</string> @@ -1096,4 +1096,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Jei norite peržiūrėti naujus valdiklius, laikykite paspaudę maitinimo mygtuką"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Pridėti valdiklių"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Redaguoti valdiklius"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Vienos rankos režimo naudojimas"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Jei norite išeiti, perbraukite aukštyn nuo ekrano apačios arba palieskite bet kur virš programos"</string> </resources> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index d4826e42f17c..bdea26a1a202 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -1090,4 +1090,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Nospiediet barošanas pogu un turiet to, lai skatītu jaunas vadīklas"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Pievienot vadīklas"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Rediģēt vadīklas"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Vienas rokas režīma izmantošana"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Lai izietu, velciet augšup no ekrāna apakšdaļas vai pieskarieties jebkurā vietā virs lietotnes"</string> </resources> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index 4836ecf1574a..c9f3777c6192 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -502,8 +502,8 @@ <string name="battery_saver_notification_title" msgid="8419266546034372562">"Штедачот на батерија е вклучен"</string> <string name="battery_saver_notification_text" msgid="2617841636449016951">"Ја намалува изведбата и податоците во заднина"</string> <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Исклучете го штедачот на батерија"</string> - <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ќе има пристап до сите информации што се видливи на екранот или пуштени од вашиот уред додека се снима или емитува. Ова вклучува информации како, на пример, лозинки, детали на исплатата, фотографии, пораки и аудио што го пуштате."</string> - <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Услугата што ја обезбедува функцијава ќе има пристап до сите информации што се видливи на екранот или пуштени од вашиот уред додека се снима или емитува. Ова вклучува информации како, на пример, лозинки, детали на исплатата, фотографии, пораки и аудио што го пуштате."</string> + <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ќе има пристап до сите податоци што се видливи на екранот или пуштени од вашиот уред додека се снима или емитува. Ова вклучува податоци како лозинки, детали за плаќање, фотографии, пораки, аудио што го пуштате итн."</string> + <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Услугата што ја обезбедува функцијава ќе има пристап до сите податоци што се видливи на екранот или пуштени од вашиот уред додека се снима или емитува. Ова вклучува информации како лозинки, детали за плаќање, фотографии, пораки, аудио што го пуштате итн."</string> <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Да почне снимање или емитување?"</string> <string name="media_projection_dialog_title" msgid="3316063622495360646">"Да почне снимање или емитување со <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string> <string name="media_projection_remember_text" msgid="6896767327140422951">"Не покажувај повторно"</string> @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Задржете го копчето за вклучување за да ги видите новите контроли"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Додајте контроли"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Изменете ги контролите"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Користење на режимот со една рака"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"За да излезете, повлечете нагоре од дното на екранот или допрете каде било над апликацијата"</string> </resources> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index 863ce1152752..c968c2827437 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -502,7 +502,7 @@ <string name="battery_saver_notification_title" msgid="8419266546034372562">"ബാറ്ററി ലാഭിക്കൽ ഓണാണ്"</string> <string name="battery_saver_notification_text" msgid="2617841636449016951">"പ്രവർത്തനവും പശ്ചാത്തല ഡാറ്റയും കുറയ്ക്കുന്നു"</string> <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"ബാറ്ററി ലാഭിക്കൽ ഓഫാക്കുക"</string> - <string name="media_projection_dialog_text" msgid="1755705274910034772">"റെക്കോർഡ് ചെയ്യുമ്പോഴോ കാസ്റ്റ് ചെയ്യുമ്പോഴോ നിങ്ങളുടെ ഉപകരണത്തിൽ നിന്ന് പ്ലേ ചെയ്യുന്നതോ നിങ്ങളുടെ സ്ക്രീനിൽ ദൃശ്യമാകുന്നതോ ആയ എല്ലാ വിവരങ്ങളിലേക്കും <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> എന്നതിന് ആക്സസ് ഉണ്ടായിരിക്കും. നിങ്ങൾ പ്ലേ ചെയ്യുന്ന ഒഡിയോ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, പേയ്മെന്റ് വിശദാംശങ്ങൾ, പാസ്വേഡുകൾ എന്നിവ പോലുള്ള വിവരങ്ങൾ ഇതിൽ ഉൾപ്പെടുന്നു."</string> + <string name="media_projection_dialog_text" msgid="1755705274910034772">"റെക്കോർഡ് ചെയ്യുമ്പോഴോ കാസ്റ്റ് ചെയ്യുമ്പോഴോ നിങ്ങളുടെ ഉപകരണത്തിൽ നിന്ന് പ്ലേ ചെയ്യുന്നതോ നിങ്ങളുടെ സ്ക്രീനിൽ ദൃശ്യമാകുന്നതോ ആയ എല്ലാ വിവരങ്ങളിലേക്കും <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>-ന് ആക്സസ് ഉണ്ടായിരിക്കും. നിങ്ങൾ പ്ലേ ചെയ്യുന്ന ഒഡിയോ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, പേയ്മെന്റ് വിശദാംശങ്ങൾ, പാസ്വേഡുകൾ എന്നിവ പോലുള്ള വിവരങ്ങൾ ഇതിൽ ഉൾപ്പെടുന്നു."</string> <string name="media_projection_dialog_service_text" msgid="958000992162214611">"റെക്കോർഡ് ചെയ്യുമ്പോഴോ കാസ്റ്റ് ചെയ്യുമ്പോഴോ നിങ്ങളുടെ ഉപകരണത്തിൽ നിന്ന് പ്ലേ ചെയ്യുന്നതോ നിങ്ങളുടെ സ്ക്രീനിൽ ദൃശ്യമാകുന്നതോ ആയ എല്ലാ വിവരങ്ങളിലേക്കും ഈ ഫംഗ്ഷൻ ലഭ്യമാക്കുന്ന സേവനത്തിന് ആക്സസ് ഉണ്ടായിരിക്കും. നിങ്ങൾ പ്ലേ ചെയ്യുന്ന ഓഡിയോ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, പേയ്മെന്റ് വിശദാംശങ്ങൾ, പാസ്വേഡുകൾ എന്നിവ പോലുള്ള വിവരങ്ങൾ ഇതിൽ ഉൾപ്പെടുന്നു."</string> <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"റെക്കോർഡ് ചെയ്യൽ അല്ലെങ്കിൽ കാസ്റ്റ് ചെയ്യൽ ആരംഭിക്കണോ?"</string> <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ഉപയോഗിച്ച് റെക്കോർഡ് ചെയ്യൽ അല്ലെങ്കിൽ കാസ്റ്റ് ചെയ്യൽ ആരംഭിക്കണോ?"</string> @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"പുതിയ നിയന്ത്രണങ്ങൾ കാണാൻ പവർ ബട്ടൺ പിടിക്കുക"</string> <string name="controls_menu_add" msgid="4447246119229920050">"നിയന്ത്രണങ്ങൾ ചേർക്കുക"</string> <string name="controls_menu_edit" msgid="890623986951347062">"നിയന്ത്രണങ്ങൾ എഡിറ്റ് ചെയ്യുക"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"ഒറ്റക്കൈ മോഡ് എങ്ങനെ ഉപയോഗിക്കാം"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"പുറത്ത് കടക്കാൻ, സ്ക്രീനിന്റെ ചുവടെ നിന്ന് മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്യുക അല്ലെങ്കിൽ ആപ്പിന് മുകളിലായി എവിടെയെങ്കിലും ടാപ്പ് ചെയ്യുക"</string> </resources> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index 107c3101a221..9ce6779e9ae4 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Шинэ хяналтыг харахын тулд асаах товчийг удаан дарна уу"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Хяналт нэмэх"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Хяналтыг өөрчлөх"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Нэг гарын горимыг ашиглаж байна"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Гарахын тулд дэлгэцийн доод хэсгээс дээш шудрах эсвэл аппын дээд хэсэгт хүссэн газраа товшино уу"</string> </resources> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index bd2c3a5d09e9..d7126795089f 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -504,8 +504,8 @@ <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"बॅटरी सेव्हर बंद करा"</string> <string name="media_projection_dialog_text" msgid="1755705274910034772">"तुमच्या स्क्रीनवर दृश्यमान असलेल्या किंवा रेकॉर्ड किंवा कास्ट करताना तुमच्या डिव्हाइसमधून प्ले केलेल्या सर्व माहितीचा अॅक्सेस <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ला असेल. यामध्ये पासवर्ड, पेमेंट तपशील, फोटो, मेसेज आणि तुम्ही प्ले केलेला ऑडिओ यासारख्या माहितीचा समावेश असतो."</string> <string name="media_projection_dialog_service_text" msgid="958000992162214611">"हे कार्य पुरवठा करणाऱ्या सेवेस तुमच्या स्क्रीनवर दृश्यमान असलेल्या किंवा रेकॉर्ड किंवा कास्ट करताना तुमच्या डिव्हाइसमधून प्ले केलेल्या सर्व माहितीचा अॅक्सेस असेल. यामध्ये पासवर्ड, पेमेंट तपशील, फोटो, मेसेज आणि तुम्ही प्ले केलेला ऑडिओ यासारख्या माहितीचा समावेश असतो."</string> - <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"रेकॉर्ड किंवा कास्ट करणे सुरू करायचे आहे का ?"</string> - <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ने रेकॉर्ड करणे किंवा कास्ट करणे सुरू करा?"</string> + <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"रेकॉर्ड करणे किंवा कास्ट करणे सुरू करायचे का ?"</string> + <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ने रेकॉर्ड करणे किंवा कास्ट करणे सुरू करायचे का?"</string> <string name="media_projection_remember_text" msgid="6896767327140422951">"पुन्हा दर्शवू नका"</string> <string name="clear_all_notifications_text" msgid="348312370303046130">"सर्व साफ करा"</string> <string name="manage_notifications_text" msgid="6885645344647733116">"व्यवस्थापित करा"</string> @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"नवीन नियंत्रणे पाहण्यासाठी पॉवर बटण धरून ठेवा"</string> <string name="controls_menu_add" msgid="4447246119229920050">"नियंत्रणे जोडा"</string> <string name="controls_menu_edit" msgid="890623986951347062">"नियंत्रणे व्यवस्थापित करा"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"एकहाती मोड वापरणे"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"बाहेर पडण्यासाठी स्क्रीनच्या खालून वरच्या दिशेने स्वाइप करा किंवा ॲप आयकनच्या वर कोठेही टॅप करा"</string> </resources> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index 029571b656ea..01e918ade0ee 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Tahan butang Kuasa untuk melihat kawalan baharu"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Tambah kawalan"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Edit kawalan"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Menggunakan mod sebelah tangan"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Untuk keluar, leret ke atas daripada bahagian bawah skrin atau ketik pada mana-mana di bahagian atas apl"</string> </resources> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index 6e632af3c5f4..23b0a06cca8f 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -502,8 +502,8 @@ <string name="battery_saver_notification_title" msgid="8419266546034372562">"ဘက်ထရီ အားထိန်းကို ဖွင့်ထားခြင်း"</string> <string name="battery_saver_notification_text" msgid="2617841636449016951">"လုပ်ကိုင်မှုကို လျှော့ချလျက် နောက်ခံ ဒေတာကို ကန့်သတ်သည်"</string> <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"ဘက်ထရီ အားထိန်းကို ပိတ်ရန်"</string> - <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> သည် အသံဖမ်းနေစဉ် (သို့) ကာစ်လုပ်နေစဉ် သင့်မျက်နှာပြင်တွင် မြင်ရသော (သို့) သင့်စက်တွင် ဖွင့်ထားသော အချက်အလက်မှန်သမျှကို သုံးနိုင်ပါမည်။ ၎င်းတွင် စကားဝှက်များ၊ ငွေပေးချေမှုအသေးစိတ်များ၊ ဓာတ်ပုံများ၊ မက်ဆေ့ဂျ်များနှင့် သင်ဖွင့်သည့်အသံကဲ့သို့သော အချက်အလက်များ ပါဝင်သည်။"</string> - <string name="media_projection_dialog_service_text" msgid="958000992162214611">"ဤလုပ်ရပ်အတွက် ဝန်ဆောင်မှုသည် အသံဖမ်းနေစဉ် (သို့) ကာစ်လုပ်နေစဉ် သင့်မျက်နှာပြင်တွင် မြင်ရသော (သို့) သင့်စက်တွင် ဖွင့်ထားသော အချက်အလက်မှန်သမျှကို သုံးနိုင်ပါမည်။ ၎င်းတွင် စကားဝှက်များ၊ ငွေပေးချေမှုအသေးစိတ်များ၊ ဓာတ်ပုံများ၊ မက်ဆေ့ဂျ်များနှင့် သင်ဖွင့်သည့်အသံကဲ့သို့သော အချက်အလက်များ ပါဝင်သည်။"</string> + <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> သည် အသံဖမ်းနေစဉ် (သို့) ကာစ်လုပ်နေစဉ် သင့်ဖန်သားပြင်တွင် မြင်ရသော (သို့) သင့်စက်တွင် ဖွင့်ထားသော အချက်အလက်မှန်သမျှကို သုံးနိုင်နိုင်ပါမည်။ ၎င်းတွင် စကားဝှက်များ၊ ငွေပေးချေမှုအသေးစိတ်များ၊ ဓာတ်ပုံများ၊ မက်ဆေ့ဂျ်များနှင့် သင်ဖွင့်သည့်အသံကဲ့သို့သော အချက်အလက်များ ပါဝင်သည်။"</string> + <string name="media_projection_dialog_service_text" msgid="958000992162214611">"ဤလုပ်ရပ်အတွက် ဝန်ဆောင်မှုသည် အသံဖမ်းနေစဉ် (သို့) ကာစ်လုပ်နေစဉ် သင့်ဖန်သားပြင်တွင် မြင်ရသော (သို့) သင့်စက်တွင် ဖွင့်ထားသော အချက်အလက်မှန်သမျှကို သုံးနိုင်ပြီး သင့်စက်မှ ဖွင့်နိုင်ပါမည်။ ၎င်းတွင် စကားဝှက်များ၊ ငွေပေးချေမှုအသေးစိတ်များ၊ ဓာတ်ပုံများ၊ မက်ဆေ့ဂျ်များနှင့် သင်ဖွင့်သည့်အသံကဲ့သို့သော အချက်အလက်များ ပါဝင်သည်။"</string> <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"ဖမ်းယူခြင်း သို့မဟုတ် ကာစ်လုပ်ခြင်း စတင်မလား။"</string> <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> နှင့် ဖမ်းယူခြင်း သို့မဟုတ် ကာစ်လုပ်ခြင်း စတင်မလား။"</string> <string name="media_projection_remember_text" msgid="6896767327140422951">"နောက်ထပ် မပြပါနှင့်"</string> @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"ထိန်းချုပ်မှုအသစ်များ ကြည့်ရန် ဖွင့်ပိတ်ခလုတ်ကို ဖိထားပါ"</string> <string name="controls_menu_add" msgid="4447246119229920050">"ထိန်းချုပ်မှုများ ထည့်ရန်"</string> <string name="controls_menu_edit" msgid="890623986951347062">"ထိန်းချုပ်မှုများ တည်းဖြတ်ရန်"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"လက်တစ်ဖက်သုံးမုဒ် အသုံးပြုခြင်း"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"ထွက်ရန် ဖန်သားပြင်၏အောက်ခြေမှ အပေါ်သို့ပွတ်ဆွဲပါ သို့မဟုတ် အက်ပ်အပေါ်ဘက် မည်သည့်နေရာတွင်မဆို တို့ပါ"</string> </resources> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index 5e29ca48bf7b..b0a593fe42f9 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Hold inne av/på-knappen for å se kontroller"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Legg til kontroller"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Endre kontroller"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Bruk av enhåndsmodus"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"For å avslutte, sveip opp fra bunnen av skjermen eller trykk hvor som helst over appen"</string> </resources> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index 098e0b1911d1..09462cf3ffcb 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -1084,4 +1084,8 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"नयाँ नियन्त्रण सुविधाहरू हेर्न पावर बटन थिचिराख्नुहोस्"</string> <string name="controls_menu_add" msgid="4447246119229920050">"नियन्त्रण सुविधाहरू थप्नुहोस्"</string> <string name="controls_menu_edit" msgid="890623986951347062">"नियन्त्रण सुविधाहरू सम्पादन गर्नु…"</string> + <!-- no translation found for one_handed_tutorial_title (6312198435090726656) --> + <skip /> + <!-- no translation found for one_handed_tutorial_description (7674850272340517174) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index e5012182eadc..2385017a0df7 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Houd de aan/uit-knop ingedrukt om nieuwe bedieningselementen te bekijken"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Bedieningselementen toevoegen"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Bedieningselementen bewerken"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Bediening met één hand gebruiken"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Als je wilt afsluiten, swipe je omhoog vanaf de onderkant van het scherm of tik je ergens boven de app"</string> </resources> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index bfd4bd77df31..d5c077b3ec1b 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -1084,4 +1084,8 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"ନୂଆ ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକୁ ଦେଖିବା ପାଇଁ ପାୱାର ବଟନକୁ ଧରି ରଖନ୍ତୁ"</string> <string name="controls_menu_add" msgid="4447246119229920050">"ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକ ଯୋଗ କରନ୍ତୁ"</string> <string name="controls_menu_edit" msgid="890623986951347062">"ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକ ସମ୍ପାଦନ କରନ୍ତୁ"</string> + <!-- no translation found for one_handed_tutorial_title (6312198435090726656) --> + <skip /> + <!-- no translation found for one_handed_tutorial_description (7674850272340517174) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index 1df919286a95..11567821397a 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -1084,4 +1084,8 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"ਨਵੇਂ ਕੰਟਰੋਲ ਦੇਖਣ ਲਈ ਪਾਵਰ ਬਟਨ ਦਬਾਈ ਰੱਖੋ"</string> <string name="controls_menu_add" msgid="4447246119229920050">"ਕੰਟਰੋਲ ਸ਼ਾਮਲ ਕਰੋ"</string> <string name="controls_menu_edit" msgid="890623986951347062">"ਕੰਟਰੋਲਾਂ ਦਾ ਸੰਪਾਦਨ ਕਰੋ"</string> + <!-- no translation found for one_handed_tutorial_title (6312198435090726656) --> + <skip /> + <!-- no translation found for one_handed_tutorial_description (7674850272340517174) --> + <skip /> </resources> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index 56fcbb20f5b5..1f6effe431e7 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -1096,4 +1096,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Przytrzymaj przycisk zasilania, by zobaczyć nowe elementy sterujące"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Dodaj elementy sterujące"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Edytuj elementy sterujące"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Korzystanie z trybu jednej ręki"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Aby zamknąć, przesuń palcem z dołu ekranu w górę lub kliknij dowolne miejsce nad aplikacją"</string> </resources> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index 88f603200dc5..14d7a84dab28 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -502,7 +502,7 @@ <string name="battery_saver_notification_title" msgid="8419266546034372562">"Economia de bateria ativada"</string> <string name="battery_saver_notification_text" msgid="2617841636449016951">"Reduz o desempenho e os dados em segundo plano"</string> <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Desativar a Economia de bateria"</string> - <string name="media_projection_dialog_text" msgid="1755705274910034772">"O app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> terá acesso a todas as informações visíveis na tela ou tocadas no dispositivo, como gravação ou transmissão Isso inclui informações como senhas, detalhes de pagamento, fotos, mensagens e áudio que você toca."</string> + <string name="media_projection_dialog_text" msgid="1755705274910034772">"O app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> terá acesso a todas as informações visíveis na tela ou tocadas no dispositivo, como gravação ou transmissão. Isso inclui informações como senhas, detalhes de pagamento, fotos, mensagens e o áudio que você tocar."</string> <string name="media_projection_dialog_service_text" msgid="958000992162214611">"O serviço que oferece essa função terá acesso a todas as informações visíveis na tela ou reproduzidas durante uma gravação ou transmissão. Isso inclui senhas, detalhes de pagamento, fotos, mensagens e áudio."</string> <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Iniciar gravação ou transmissão?"</string> <string name="media_projection_dialog_title" msgid="3316063622495360646">"Iniciar gravação ou transmissão com o app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string> @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Mantenha o botão liga/desliga pressionado para ver os novos controles"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Adicionar controles"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Editar controles"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Como usar o modo para uma mão"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Para sair, deslize de baixo para cima na tela ou toque em qualquer lugar acima do app"</string> </resources> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 5fdb2853551d..a6f7f004f411 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Mantenha premido o botão ligar/desligar para ver os novos controlos."</string> <string name="controls_menu_add" msgid="4447246119229920050">"Adicionar controlos"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Editar controlos"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Utilizar o modo para uma mão"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Para sair, deslize rapidamente para cima a partir da parte inferior do ecrã ou toque em qualquer ponto acima da app."</string> </resources> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 88f603200dc5..14d7a84dab28 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -502,7 +502,7 @@ <string name="battery_saver_notification_title" msgid="8419266546034372562">"Economia de bateria ativada"</string> <string name="battery_saver_notification_text" msgid="2617841636449016951">"Reduz o desempenho e os dados em segundo plano"</string> <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Desativar a Economia de bateria"</string> - <string name="media_projection_dialog_text" msgid="1755705274910034772">"O app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> terá acesso a todas as informações visíveis na tela ou tocadas no dispositivo, como gravação ou transmissão Isso inclui informações como senhas, detalhes de pagamento, fotos, mensagens e áudio que você toca."</string> + <string name="media_projection_dialog_text" msgid="1755705274910034772">"O app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> terá acesso a todas as informações visíveis na tela ou tocadas no dispositivo, como gravação ou transmissão. Isso inclui informações como senhas, detalhes de pagamento, fotos, mensagens e o áudio que você tocar."</string> <string name="media_projection_dialog_service_text" msgid="958000992162214611">"O serviço que oferece essa função terá acesso a todas as informações visíveis na tela ou reproduzidas durante uma gravação ou transmissão. Isso inclui senhas, detalhes de pagamento, fotos, mensagens e áudio."</string> <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Iniciar gravação ou transmissão?"</string> <string name="media_projection_dialog_title" msgid="3316063622495360646">"Iniciar gravação ou transmissão com o app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string> @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Mantenha o botão liga/desliga pressionado para ver os novos controles"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Adicionar controles"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Editar controles"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Como usar o modo para uma mão"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Para sair, deslize de baixo para cima na tela ou toque em qualquer lugar acima do app"</string> </resources> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index d5eecc76144c..0f15cfcd23f6 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -1090,4 +1090,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Apăsați butonul de alimentare pentru a vedea noile comenzi"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Adăugați comenzi"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Editați comenzile"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Folosirea modului cu o mână"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Pentru a ieși, glisați în sus din partea de jos a ecranului sau atingeți oriunde deasupra ferestrei aplicației"</string> </resources> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 50850225c891..6196f88a7bfb 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -1096,4 +1096,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Удерживайте кнопку питания, чтобы увидеть новые элементы управления"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Добавить виджеты"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Изменить виджеты"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Использование режима управления одной рукой"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Чтобы выйти, проведите по экрану снизу вверх или нажмите в любой области над значком приложения."</string> </resources> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index ff7e816f8336..8aca9f4e0dbd 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"නව පාලන බැලීමට බල බොත්තම අල්ලාගෙන සිටින්න"</string> <string name="controls_menu_add" msgid="4447246119229920050">"පාලන එක් කරන්න"</string> <string name="controls_menu_edit" msgid="890623986951347062">"පාලන සංස්කරණය කරන්න"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"තනි-අත් ප්රකාරය භාවිත කරමින්"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"පිටවීමට, තිරයේ පහළ සිට ඉහළට ස්වයිප් කරන්න හෝ යෙදුමට ඉහළින් ඕනෑම තැනක තට්ටු කරන්න"</string> </resources> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index a86da967ab62..45ee562b374c 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -1096,4 +1096,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Pridržaním vypínača zobrazíte nové ovládacie prvky"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Pridať ovládače"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Upraviť ovládače"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Používanie režimu jednej ruky"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Ukončíte potiahnutím z dolnej časti obrazovky nahor alebo klepnutím kdekoľvek nad aplikáciu"</string> </resources> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index b9039d8c3cb9..c7ad8ab92da3 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -1096,4 +1096,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Za ogled novih kontrolnikov pridržite gumb za vklop"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Dodaj kontrolnike"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Uredi kontrolnike"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Uporaba enoročnega načina"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Za izhod povlecite z dna zaslona navzgor ali se dotaknite na poljubnem mestu nad aplikacijo"</string> </resources> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index 2972c5383bee..3e0aecc16d4e 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -502,8 +502,8 @@ <string name="battery_saver_notification_title" msgid="8419266546034372562">"\"Kursyesi i baterisë\" është i aktivizuar"</string> <string name="battery_saver_notification_text" msgid="2617841636449016951">"Pakëson veprimtarinë dhe të dhënat në sfond"</string> <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Çaktivizo \"Kursyesin e baterisë\""</string> - <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> do të ketë qasje te të gjitha informacionet që janë të dukshme në ekran ose që luhen nga pajisja jote gjatë regjistrimit ose transmetimit. Kjo përfshin informacione si p.sh. fjalëkalimet, detajet e pagesave, fotografitë, mesazhet dhe audion që luan ti."</string> - <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Shërbimi që e ofron këtë funksion do të ketë qasje te të gjitha informacionet që janë të dukshme në ekran ose që luhen nga pajisja jote gjatë regjistrimit ose transmetimit. Kjo përfshin informacione si p.sh. fjalëkalimet, detajet e pagesave, fotografitë, mesazhet dhe audion që luan ti."</string> + <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> do të ketë qasje te të gjitha informacionet që janë të dukshme në ekran ose që luhen nga pajisja jote gjatë regjistrimit ose transmetimit. Kjo përfshin informacione, si p.sh.: fjalëkalimet, detajet e pagesave, fotografitë, mesazhet dhe audion që luan ti."</string> + <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Shërbimi që e ofron këtë funksion do të ketë qasje te të gjitha informacionet që janë të dukshme në ekran ose që luhen nga pajisja jote gjatë regjistrimit ose transmetimit. Kjo përfshin informacione, si p.sh.: fjalëkalimet, detajet e pagesave, fotografitë, mesazhet dhe audion që luan ti."</string> <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Do të fillosh regjistrimin ose transmetimin?"</string> <string name="media_projection_dialog_title" msgid="3316063622495360646">"Fillo regjistrimin ose transmetimin me <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string> <string name="media_projection_remember_text" msgid="6896767327140422951">"Mos e shfaq sërish"</string> @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Mbaj shtypur butonin e energjisë për të parë kontrollet e reja"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Shto kontrollet"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Modifiko kontrollet"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Po përdor modalitetin e përdorimit me një dorë"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Për të dalë, rrëshqit lart nga fundi i ekranit ose trokit diku mbi aplikacion"</string> </resources> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 77cbb96b3691..985041b3b24d 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -1090,4 +1090,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Задржите дугме за укључивање да бисте видели нове контроле"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Додај контроле"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Измени контроле"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Коришћење режима једном руком"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Да бисте изашли, превуците нагоре од дна екрана или додирните било где изнад апликације"</string> </resources> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index c7eda4567a68..2018a383b5ed 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"De nya snabbkontrollerna visas om du håller strömbrytaren nedtryckt"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Lägg till snabbkontroller"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Redigera snabbkontroller"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Använda enhandsläge"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Avsluta genom att svepa uppåt från skärmens nederkant eller trycka ovanför appen"</string> </resources> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index 2804f2f1e106..6eecc0e402fa 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Shikilia kitufe cha kuwasha/kuzima ili uone vidhibiti vipya"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Weka vidhibiti"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Badilisha vidhibiti"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Kutumia hali ya kutumia kwa mkono mmoja"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Ili ufunge, telezesha kidole juu kutoka sehemu ya chini ya skrini au uguse mahali popote juu ya programu"</string> </resources> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index ee176fa3c57d..a05e0e735620 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"புதிய கட்டுப்பாடுகளைப் பார்க்க பவர் பட்டனைப் பிடித்திருக்கவும்"</string> <string name="controls_menu_add" msgid="4447246119229920050">"கட்டுப்பாடுகளைச் சேர்த்தல்"</string> <string name="controls_menu_edit" msgid="890623986951347062">"கட்டுப்பாடுகளை மாற்றுதல்"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"ஒற்றைக் கைப் பயன்முறையைப் பயன்படுத்துதல்"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"வெளியேற, திரையின் கீழிருந்து மேல்நோக்கி ஸ்வைப் செய்யவும் அல்லது ஆப்ஸுக்கு மேலே எங்காவது தட்டவும்"</string> </resources> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index dc5ea1f29fb5..7d76abd0f20f 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"కొత్త నియంత్రణలను చూడడానికి పవర్ బటన్ని నొక్కి పట్టుకోండి"</string> <string name="controls_menu_add" msgid="4447246119229920050">"నియంత్రణలను జోడించండి"</string> <string name="controls_menu_edit" msgid="890623986951347062">"నియంత్రణలను ఎడిట్ చేయండి"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"వన్-హ్యాండెడ్ మోడ్ను ఉపయోగించడం"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"నిష్క్రమించడానికి, స్క్రీన్ కింది భాగం నుండి పైకి స్వైప్ చేయండి లేదా యాప్ పైన ఎక్కడైనా ట్యాప్ చేయండి"</string> </resources> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index ba9fd296f607..765c22424e89 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"กดปุ่มเปิด/ปิดค้างไว้เพื่อดูตัวควบคุมใหม่ๆ"</string> <string name="controls_menu_add" msgid="4447246119229920050">"เพิ่มตัวควบคุม"</string> <string name="controls_menu_edit" msgid="890623986951347062">"แก้ไขตัวควบคุม"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"การใช้โหมดมือเดียว"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"หากต้องการออก ให้เลื่อนขึ้นจากด้านล่างของหน้าจอหรือแตะที่ใดก็ได้เหนือแอป"</string> </resources> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index 069438f2caed..c2cae9024a60 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Pindutin nang matagal ang Power button para makita ang mga bagong kontrol"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Magdagdag ng mga kontrol"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Mag-edit ng mga kontrol"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Gamit ang one-hand mode"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Para lumabas, mag-swipe pataas mula sa ibaba ng screen o mag-tap kahit saan sa itaas ng app"</string> </resources> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index 649b9af53ff6..74b44f2079c6 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Yeni kontrolleri görmek için Güç düğmesini basılı tutun"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Denetim ekle"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Denetimleri düzenle"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Tek el modunu kullanma"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Çıkmak için ekranın alt kısmından yukarı kaydırın veya uygulamanın üzerinde herhangi bir yere dokunun"</string> </resources> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 6cb3ac8ce915..38c331ade344 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -1096,4 +1096,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Утримуйте кнопку живлення, щоб переглянути нові елементи керування"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Додати елементи керування"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Змінити елементи керування"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Як користуватись режимом керування однією рукою"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Щоб вийти, проведіть пальцем вверх від низу екрана або торкніться екрана над додатком"</string> </resources> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index 804c33436ea6..69ddabbe94ac 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"نئے کنٹرولز دیکھنے کے لیے پاور بٹن کو دبائے رکھیں"</string> <string name="controls_menu_add" msgid="4447246119229920050">"کنٹرولز شامل کریں"</string> <string name="controls_menu_edit" msgid="890623986951347062">"کنٹرولز میں ترمیم کریں"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"ایک ہاتھ کی وضع کا استعمال کرنا"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"باہر نکلنے کے لئے، اسکرین کے نیچے سے اوپر کی طرف سوائپ کریں یا ایپ کے اوپر کہیں بھی تھپتھپائیں"</string> </resources> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index 6193d33961e6..ed704b0f5597 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -505,7 +505,7 @@ <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ekranda chiqqan yoki yozib olish va translatsiya vaqtida ijro etilgan barcha axborotlarga ruxsat oladi. Bu axborotlar parollar, toʻlov tafsilotlari, rasmlar, xabarlar va ijro etilgan audiolardan iborat boʻlishi mumkin."</string> <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Bu funksiyani taʼminlovchi xizmat ekranda chiqqan yoki yozib olish va translatsiya vaqtida ijro etilgan barcha axborotlarga ruxsat oladi. Bu axborotlar parollar, toʻlov tafsilotlari, rasmlar, xabarlar va ijro etilgan audiolardan iborat boʻlishi mumkin."</string> <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Yozib olish yoki translatsiya boshlansinmi?"</string> - <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> bilan yozib olinsin yoki translatsiya qilinsinmi?"</string> + <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> orqali yozib olish yoki translatsiya boshlansinmi?"</string> <string name="media_projection_remember_text" msgid="6896767327140422951">"Boshqa ko‘rsatilmasin"</string> <string name="clear_all_notifications_text" msgid="348312370303046130">"Hammasini tozalash"</string> <string name="manage_notifications_text" msgid="6885645344647733116">"Boshqarish"</string> @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Yangi boshqaruv elementlari bilan tanishish uchun quvvat tugmasini bosib turing"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Element kiritish"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Elementlarni tahrirlash"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Ixcham rejimdan foydalaning"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Chiqish uchun ekran pastidan tepaga suring yoki ilovaning tepasidagi istalgan joyga bosing."</string> </resources> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index 97ffbc623e02..5cc209941771 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -502,8 +502,8 @@ <string name="battery_saver_notification_title" msgid="8419266546034372562">"Trình tiết kiệm pin đang bật"</string> <string name="battery_saver_notification_text" msgid="2617841636449016951">"Giảm hiệu suất và dữ liệu nền"</string> <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Tắt trình tiết kiệm pin"</string> - <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> sẽ có quyền truy cập vào tất cả các thông tin hiển thị trên màn hình của bạn hoặc phát từ thiết bị trong khi ghi âm/ghi hình hoặc truyền, bao gồm cả thông tin như mật khẩu, chi tiết thanh toán, ảnh, tin nhắn và âm thanh mà bạn phát."</string> - <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Dịch vụ cung cấp chức năng này có quyền truy cập vào tất cả các thông tin hiển thị trên màn hình của bạn hoặc phát từ thiết bị trong khi ghi âm/ghi hình hoặc truyền, bao gồm cả thông tin như mật khẩu, chi tiết thanh toán, ảnh, tin nhắn và âm thanh mà bạn phát."</string> + <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> sẽ có quyền truy cập vào tất cả các thông tin hiển thị trên màn hình của bạn hoặc phát trên thiết bị của bạn trong khi ghi âm/ghi hình hoặc truyền, bao gồm cả thông tin như mật khẩu, chi tiết thanh toán, ảnh, tin nhắn và âm thanh mà bạn phát."</string> + <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Dịch vụ cung cấp chức năng này có quyền truy cập vào tất cả các thông tin hiển thị trên màn hình của bạn hoặc phát trên thiết bị của bạn trong khi ghi âm/ghi hình hoặc truyền, bao gồm cả thông tin như mật khẩu, chi tiết thanh toán, ảnh, tin nhắn và âm thanh mà bạn phát."</string> <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Bắt đầu ghi âm/ghi hình hoặc truyền?"</string> <string name="media_projection_dialog_title" msgid="3316063622495360646">"Bắt đầu ghi âm/ghi hình hoặc truyền bằng <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string> <string name="media_projection_remember_text" msgid="6896767327140422951">"Không hiển thị lại"</string> @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Giữ nút Nguồn để xem các tùy chọn điều khiển mới"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Thêm các tùy chọn điều khiển"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Chỉnh sửa tùy chọn điều khiển"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Cách dùng chế độ một tay"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Để thoát, hãy vuốt lên từ cuối màn hình hoặc nhấn vào vị trí bất kỳ phía trên ứng dụng"</string> </resources> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index b9b146c9fb4b..f670bf682b64 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"按住电源按钮即可查看新控件"</string> <string name="controls_menu_add" msgid="4447246119229920050">"添加控件"</string> <string name="controls_menu_edit" msgid="890623986951347062">"修改控件"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"使用单手模式"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"如需退出,请从屏幕底部向上滑动,或点按应用上方的任意位置"</string> </resources> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index f2cb185078b5..2e26d6153a8f 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"按住「開關」按鈕以查看新控制項"</string> <string name="controls_menu_add" msgid="4447246119229920050">"新增控制項"</string> <string name="controls_menu_edit" msgid="890623986951347062">"編輯控制項"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"使用單手模式"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"如要退出,請從螢幕底部向上滑動,或輕按應用程式上方的任何位置"</string> </resources> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index 69b52946d698..0049d2ec1f56 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -502,7 +502,7 @@ <string name="battery_saver_notification_title" msgid="8419266546034372562">"省電模式已開啟"</string> <string name="battery_saver_notification_text" msgid="2617841636449016951">"降低效能並限制背景數據傳輸"</string> <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"關閉省電模式"</string> - <string name="media_projection_dialog_text" msgid="1755705274910034772">"在錄製或投放內容時,「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」可存取畫面上顯示的任何資訊或裝置播放的任何內容,包括密碼、付款詳情、相片、訊息和你播放的音訊。"</string> + <string name="media_projection_dialog_text" msgid="1755705274910034772">"在錄製或投放內容時,<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>可存取畫面上顯示的任何資訊或裝置播放的任何內容,包括密碼、付款詳情、相片、訊息和你播放的音訊。"</string> <string name="media_projection_dialog_service_text" msgid="958000992162214611">"在錄製或投放內容時,提供這項功能的服務可存取畫面上顯示的任何資訊或裝置播放的任何內容,包括密碼、付款詳情、相片、訊息和你播放的音訊。"</string> <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"要開始錄製或投放內容嗎?"</string> <string name="media_projection_dialog_title" msgid="3316063622495360646">"要使用「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」開始錄製或投放內容嗎?"</string> @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"按住電源按鈕即可查看新的控制項"</string> <string name="controls_menu_add" msgid="4447246119229920050">"新增控制項"</string> <string name="controls_menu_edit" msgid="890623986951347062">"編輯控制項"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"使用單手模式"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"如要退出,請從螢幕底部向上滑動,或輕觸應用程式上方的任何位置"</string> </resources> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index 5b899f93e5f9..c8c99b1c18c1 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -1084,4 +1084,6 @@ <string name="controls_added_tooltip" msgid="4842812921719153085">"Bamba Inkinobho yamandla ukuze ubone izilawuli ezintsha"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Engeza Izilawuli"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Hlela izilawuli"</string> + <string name="one_handed_tutorial_title" msgid="6312198435090726656">"Ukusebenzisa imodi yesandla esisodwa"</string> + <string name="one_handed_tutorial_description" msgid="7674850272340517174">"Ukuze uphume, swayipha ngaphezulu kusuka ngezansi kwesikrini noma thepha noma kuphi ngenhla kohlelo lokusebenza"</string> </resources> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index fa620df12b87..fba43a628387 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -515,13 +515,11 @@ <!-- Whether or not to add a "people" notifications section --> <bool name="config_usePeopleFiltering">false</bool> - <!-- Defines the blacklist for system icons. That is to say, the icons in the status bar that - are part of the blacklist are never displayed. Each item in the blacklist must be a string - defined in core/res/res/config.xml to properly blacklist the icon. - - TODO: See if we can rename this config variable. + <!-- Defines system icons to be excluded from the display. That is to say, the icons in the + status bar that are part of this list are never displayed. Each item in the list must be a + string defined in core/res/res/config.xml to properly exclude the icon. --> - <string-array name="config_statusBarIconBlackList" translatable="false"> + <string-array name="config_statusBarIconsToExclude" translatable="false"> <item>@*android:string/status_bar_rotate</item> <item>@*android:string/status_bar_headset</item> </string-array> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WallpaperEngineCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WallpaperEngineCompat.java index 4d968f1763ca..73dc60dbc7da 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WallpaperEngineCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WallpaperEngineCompat.java @@ -26,6 +26,17 @@ public class WallpaperEngineCompat { private static final String TAG = "WallpaperEngineCompat"; + /** + * Returns true if {@link IWallpaperEngine#scalePreview(Rect)} is available. + */ + public static boolean supportsScalePreview() { + try { + return IWallpaperEngine.class.getMethod("scalePreview", Rect.class) != null; + } catch (NoSuchMethodException | SecurityException e) { + return false; + } + } + private final IWallpaperEngine mWrappedEngine; public WallpaperEngineCompat(IWallpaperEngine wrappedEngine) { diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java index 4df66602bb7e..6512624f5064 100644 --- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java @@ -281,9 +281,11 @@ public class AppOpsControllerImpl implements AppOpsController, * @return {@code true} iff the app-op for should be shown to the user */ private boolean isUserVisible(int appOpCode, int uid, String packageName) { - // currently OP_SYSTEM_ALERT_WINDOW does not correspond to a platform permission - // which may be user senstive, so for now always show it to the user. - if (appOpCode == AppOpsManager.OP_SYSTEM_ALERT_WINDOW) { + // currently OP_SYSTEM_ALERT_WINDOW and OP_MONITOR_HIGH_POWER_LOCATION + // does not correspond to a platform permission + // which may be user sensitive, so for now always show it to the user. + if (appOpCode == AppOpsManager.OP_SYSTEM_ALERT_WINDOW + || appOpCode == AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION) { return true; } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java index d0f61817d1cc..fa0d2ba84b4e 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java @@ -310,7 +310,6 @@ public class BubbleExpandedView extends LinearLayout { // Set ActivityView's alpha value as zero, since there is no view content to be shown. setContentVisibility(false); - mActivityViewContainer.setBackgroundColor(Color.WHITE); mActivityViewContainer.setOutlineProvider(new ViewOutlineProvider() { @Override public void getOutline(View view, Outline outline) { @@ -439,9 +438,11 @@ public class BubbleExpandedView extends LinearLayout { } void applyThemeAttrs() { - final TypedArray ta = mContext.obtainStyledAttributes( - new int[] {android.R.attr.dialogCornerRadius}); + final TypedArray ta = mContext.obtainStyledAttributes(new int[] { + android.R.attr.dialogCornerRadius, + android.R.attr.colorBackgroundFloating}); mCornerRadius = ta.getDimensionPixelSize(0, 0); + mActivityViewContainer.setBackgroundColor(ta.getColor(1, Color.WHITE)); ta.recycle(); if (mActivityView != null && ScreenDecorationsUtils.supportsRoundedCornersOnWindows( diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java index ded386159dca..563684ad65c1 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java @@ -159,7 +159,7 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback, } else { float distance = (float) Math.hypot(mLastPos.x - mDownPos.x, mLastPos.y - mDownPos.y); - if (distance > mDragDistThreshold && mPassedSlop) { + if (distance > mDragDistThreshold) { mGestureEventCallback.onStop(); } } @@ -273,13 +273,13 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback, */ public interface OneHandedGestureEventCallback { /** - * Handle the start event event, and return whether the event was consumed. + * Handles the start gesture. */ - boolean onStart(); + void onStart(); /** - * Handle the exit event event, and return whether the event was consumed. + * Handles the exit gesture. */ - boolean onStop(); + void onStop(); } } diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java index 51e587586852..a3921ee54fec 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java @@ -24,6 +24,7 @@ import android.content.ComponentName; import android.content.Context; import android.graphics.Point; import android.graphics.Rect; +import android.view.KeyEvent; import androidx.annotation.NonNull; @@ -33,6 +34,7 @@ import com.android.systemui.R; import com.android.systemui.model.SysUiState; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.TaskStackChangeListener; +import com.android.systemui.statusbar.CommandQueue; import com.android.wm.shell.common.DisplayChangeController; import com.android.wm.shell.common.DisplayController; @@ -50,9 +52,11 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable { private static final String TAG = "OneHandedManager"; private boolean mIsOneHandedEnabled; + private boolean mIsSwipeToNotificationEnabled; private boolean mTaskChangeToExit; private float mOffSetFraction; + private final CommandQueue mCommandQueue; private final DisplayController mDisplayController; private final OneHandedGestureHandler mGestureHandler; private final OneHandedTimeoutHandler mTimeoutHandler; @@ -60,11 +64,7 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable { private final OneHandedTutorialHandler mTutorialHandler; private final SysUiState mSysUiFlagContainer; - private Context mContext; private OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer; - private OneHandedGestureHandler.OneHandedGestureEventCallback mGestureEventCallback; - private OneHandedTouchHandler.OneHandedTouchEventCallback mTouchEventCallback; - private OneHandedTransitionCallback mTransitionCallback; /** * Handler for system task stack changes, exit when user lunch new task or bring task to front @@ -105,13 +105,14 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable { */ @Inject public OneHandedManagerImpl(Context context, + CommandQueue commandQueue, DisplayController displayController, OneHandedDisplayAreaOrganizer displayAreaOrganizer, OneHandedTouchHandler touchHandler, OneHandedTutorialHandler tutorialHandler, OneHandedGestureHandler gestureHandler, SysUiState sysUiState) { - mContext = context; + mCommandQueue = commandQueue; mDisplayAreaOrganizer = displayAreaOrganizer; mDisplayController = displayController; mDisplayController.addDisplayChangingController(mRotationController); @@ -120,6 +121,8 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable { context.getResources().getFraction(R.fraction.config_one_handed_offset, 1, 1); mIsOneHandedEnabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled( context.getContentResolver()); + mIsSwipeToNotificationEnabled = OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( + context.getContentResolver()); mTimeoutHandler = OneHandedTimeoutHandler.get(); mTouchHandler = touchHandler; mTutorialHandler = tutorialHandler; @@ -148,11 +151,19 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable { } /** - * Start one handed mode + * Sets whether to enable swipe bottom to notification gesture when user update settings. + */ + public void setSwipeToNotificationEnabled(boolean enabled) { + mIsSwipeToNotificationEnabled = enabled; + updateOneHandedEnabled(); + } + + /** + * Enters one handed mode. */ @Override public void startOneHanded() { - if (!mDisplayAreaOrganizer.isInOneHanded() && mIsOneHandedEnabled) { + if (!mDisplayAreaOrganizer.isInOneHanded()) { final int yOffSet = Math.round(getDisplaySize().y * mOffSetFraction); mDisplayAreaOrganizer.scheduleOffset(0, yOffSet); mTimeoutHandler.resetTimer(); @@ -160,7 +171,7 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable { } /** - * Stop one handed mode + * Exits one handed mode. */ @Override public void stopOneHanded() { @@ -171,53 +182,45 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable { } private void setupGestures() { - mTouchEventCallback = new OneHandedTouchHandler.OneHandedTouchEventCallback() { - @Override - public boolean onStart() { - boolean result = false; - if (!mDisplayAreaOrganizer.isInOneHanded()) { - startOneHanded(); - result = true; - } - return result; - } + mTouchHandler.registerTouchEventListener( + new OneHandedTouchHandler.OneHandedTouchEventCallback() { + @Override + public void onStart() { + if (mIsOneHandedEnabled) { + startOneHanded(); + } + } - @Override - public boolean onStop() { - boolean result = false; - if (mDisplayAreaOrganizer.isInOneHanded()) { - stopOneHanded(); - result = true; - } - return result; - } - }; - mTouchHandler.registerTouchEventListener(mTouchEventCallback); + @Override + public void onStop() { + if (mIsOneHandedEnabled) { + stopOneHanded(); + } + } + }); - mGestureEventCallback = new OneHandedGestureHandler.OneHandedGestureEventCallback() { - @Override - public boolean onStart() { - boolean result = false; - if (!mDisplayAreaOrganizer.isInOneHanded()) { - startOneHanded(); - result = true; - } - return result; - } + mGestureHandler.setGestureEventListener( + new OneHandedGestureHandler.OneHandedGestureEventCallback() { + @Override + public void onStart() { + if (mIsOneHandedEnabled) { + startOneHanded(); + } else if (mIsSwipeToNotificationEnabled) { + mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN); + } + } - @Override - public boolean onStop() { - boolean result = false; - if (mDisplayAreaOrganizer.isInOneHanded()) { - stopOneHanded(); - result = true; - } - return result; - } - }; - mGestureHandler.setGestureEventListener(mGestureEventCallback); + @Override + public void onStop() { + if (mIsOneHandedEnabled) { + stopOneHanded(); + } else if (mIsSwipeToNotificationEnabled) { + mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP); + } + } + }); - mTransitionCallback = new OneHandedTransitionCallback() { + mDisplayAreaOrganizer.registerTransitionCallback(new OneHandedTransitionCallback() { @Override public void onStartFinished(Rect bounds) { mSysUiFlagContainer.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE, @@ -229,8 +232,8 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable { mSysUiFlagContainer.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE, false).commitUpdate(DEFAULT_DISPLAY); } - }; - mDisplayAreaOrganizer.registerTransitionCallback(mTransitionCallback); + }); + mDisplayAreaOrganizer.registerTransitionCallback(mTouchHandler); mDisplayAreaOrganizer.registerTransitionCallback(mGestureHandler); mDisplayAreaOrganizer.registerTransitionCallback(mTutorialHandler); @@ -264,7 +267,7 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable { ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener); } mTouchHandler.onOneHandedEnabled(mIsOneHandedEnabled); - mGestureHandler.onOneHandedEnabled(mIsOneHandedEnabled); + mGestureHandler.onOneHandedEnabled(mIsOneHandedEnabled || mIsSwipeToNotificationEnabled); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedSettingsUtil.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedSettingsUtil.java index 9b232cd0a19d..1b6ec04193f0 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedSettingsUtil.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedSettingsUtil.java @@ -132,6 +132,14 @@ public final class OneHandedSettingsUtil { Settings.Secure.ONE_HANDED_MODE_TIMEOUT, ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS); } + /** + * Returns whether swipe bottom to notification gesture enabled or not. + */ + public static boolean getSettingsSwipeToNotificationEnabled(ContentResolver resolver) { + return Settings.Secure.getInt(resolver, + Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 0) == 1; + } + protected static void dump(PrintWriter pw, String prefix, ContentResolver resolver) { final String innerPrefix = prefix + " "; pw.println(prefix + TAG); diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java index d616a3a45b90..1446e5a431c6 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java @@ -175,13 +175,13 @@ public class OneHandedTouchHandler implements OneHandedTransitionCallback, Dumpa */ public interface OneHandedTouchEventCallback { /** - * Handle the start event event, and return whether the event was consumed. + * Handle the start event. */ - boolean onStart(); + void onStart(); /** - * Handle the exit event event, and return whether the event was consumed. + * Handle the exit event. */ - boolean onStop(); + void onStop(); } } diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java index 8a67da53e6a2..b28730d004f6 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java @@ -16,11 +16,13 @@ package com.android.systemui.onehanded; +import android.content.ContentResolver; import android.content.Context; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; import android.os.Handler; +import android.provider.Settings; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; @@ -48,12 +50,15 @@ import javax.inject.Singleton; @Singleton public class OneHandedTutorialHandler implements OneHandedTransitionCallback, Dumpable { private static final String TAG = "OneHandedTutorialHandler"; + private static final int MAX_TUTORIAL_SHOW_COUNT = 2; private final Rect mLastUpdatedBounds = new Rect(); private final WindowManager mWindowManager; private View mTutorialView; private Point mDisplaySize = new Point(); private Handler mUpdateHandler; + private ContentResolver mContentResolver; + private boolean mCanShowTutorial; /** * Container of the tutorial panel showing at outside region when one handed starting @@ -71,6 +76,7 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback, Du @Inject public OneHandedTutorialHandler(Context context) { context.getDisplay().getRealSize(mDisplaySize); + mContentResolver = context.getContentResolver(); mUpdateHandler = new Handler(); mWindowManager = context.getSystemService(WindowManager.class); mTargetViewContainer = new FrameLayout(context); @@ -79,12 +85,20 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback, Du R.fraction.config_one_handed_offset, 1, 1)); mTutorialView = LayoutInflater.from(context).inflate(R.xml.one_handed_tutorial, null); mTargetViewContainer.addView(mTutorialView); - createOrUpdateTutorialTarget(); + mCanShowTutorial = (Settings.Secure.getInt(mContentResolver, + Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, 0) >= MAX_TUTORIAL_SHOW_COUNT) + ? false : true; + if (mCanShowTutorial) { + createOrUpdateTutorialTarget(); + } } @Override public void onStartFinished(Rect bounds) { - mUpdateHandler.post(() -> updateFinished(View.VISIBLE, 0f)); + mUpdateHandler.post(() -> { + updateFinished(View.VISIBLE, 0f); + updateTutorialCount(); + }); } @Override @@ -94,10 +108,23 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback, Du } private void updateFinished(int visible, float finalPosition) { + if (!canShowTutorial()) { + return; + } + mTargetViewContainer.setVisibility(visible); mTargetViewContainer.setTranslationY(finalPosition); } + private void updateTutorialCount() { + int showCount = Settings.Secure.getInt(mContentResolver, + Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, 0); + showCount = Math.min(MAX_TUTORIAL_SHOW_COUNT, showCount + 1); + mCanShowTutorial = showCount < MAX_TUTORIAL_SHOW_COUNT; + Settings.Secure.putInt(mContentResolver, + Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, showCount); + } + /** * Adds the tutorial target view to the WindowManager and update its layout, so it's ready * to be animated in. @@ -153,7 +180,19 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback, Du pw.println(mLastUpdatedBounds); } + private boolean canShowTutorial() { + if (!mCanShowTutorial) { + mTargetViewContainer.setVisibility(View.GONE); + return false; + } + + return true; + } + private void onAnimationUpdate(float value) { + if (!canShowTutorial()) { + return; + } mTargetViewContainer.setVisibility(View.VISIBLE); mTargetViewContainer.setTransitionGroup(true); mTargetViewContainer.setTranslationY(value - mTargetViewContainer.getHeight()); diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java index 9239435f1622..0903c0e5c512 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java @@ -40,7 +40,6 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.Dependency; import com.android.systemui.Dumpable; import com.android.systemui.SystemUI; -import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.statusbar.CommandQueue; @@ -80,7 +79,10 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum mOneHandedManager.setOneHandedEnabled(enabled); } - setEnabledGesturalOverlay(enabled); + // Also checks swipe to notification settings since they all need gesture overlay. + setEnabledGesturalOverlay( + enabled || OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( + mContext.getContentResolver())); } }; @@ -130,11 +132,28 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum } }; + private final ContentObserver mSwipeToNotificationEnabledObserver = + new ContentObserver(mMainHandler) { + @Override + public void onChange(boolean selfChange) { + final boolean enabled = + OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( + mContext.getContentResolver()); + if (mOneHandedManager != null) { + mOneHandedManager.setSwipeToNotificationEnabled(enabled); + } + + // Also checks one handed mode settings since they all need gesture overlay. + setEnabledGesturalOverlay( + enabled || OneHandedSettingsUtil.getSettingsOneHandedModeEnabled( + mContext.getContentResolver())); + } + }; + @Inject public OneHandedUI(Context context, CommandQueue commandQueue, OneHandedManagerImpl oneHandedManager, - DumpManager dumpManager, OneHandedSettingsUtil settingsUtil, ScreenLifecycle screenLifecycle) { super(context); @@ -239,6 +258,9 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum mContext.getContentResolver(), mTimeoutObserver); mSettingUtil.registerSettingsKeyObserver(Settings.Secure.TAPS_APP_TO_EXIT, mContext.getContentResolver(), mTaskChangeExitObserver); + mSettingUtil.registerSettingsKeyObserver( + Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, + mContext.getContentResolver(), mSwipeToNotificationEnabledObserver); } private void updateSettings() { @@ -248,6 +270,8 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum mSettingUtil.getSettingsOneHandedModeTimeout(mContext.getContentResolver())); mOneHandedManager.setTaskChangeToExit( mSettingUtil.getSettingsTapsAppToExit(mContext.getContentResolver())); + mOneHandedManager.setSwipeToNotificationEnabled( + mSettingUtil.getSettingsSwipeToNotificationEnabled(mContext.getContentResolver())); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java index 118e0bd5f5f8..025341cf4fba 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java @@ -99,6 +99,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements private final Handler mUpdateHandler; private final PipBoundsHandler mPipBoundsHandler; private final PipAnimationController mPipAnimationController; + private final PipUiEventLogger mPipUiEventLoggerLogger; private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>(); private final Rect mLastReportedBounds = new Rect(); private final int mEnterExitAnimationDuration; @@ -209,7 +210,8 @@ public class PipTaskOrganizer extends TaskOrganizer implements @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper, @Nullable Divider divider, @NonNull DisplayController displayController, - @NonNull PipAnimationController pipAnimationController) { + @NonNull PipAnimationController pipAnimationController, + @NonNull PipUiEventLogger pipUiEventLogger) { mMainHandler = new Handler(Looper.getMainLooper()); mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks); mPipBoundsHandler = boundsHandler; @@ -217,6 +219,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements .getInteger(R.integer.config_pipResizeAnimationDuration); mSurfaceTransactionHelper = surfaceTransactionHelper; mPipAnimationController = pipAnimationController; + mPipUiEventLoggerLogger = pipUiEventLogger; mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new; mSplitDivider = divider; displayController.addDisplayWindowListener(this); @@ -279,6 +282,8 @@ public class PipTaskOrganizer extends TaskOrganizer implements return; } + mPipUiEventLoggerLogger.log( + PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN); final Configuration initialConfig = mInitialState.remove(mToken.asBinder()); final boolean orientationDiffers = initialConfig.windowConfiguration.getRotation() != mPipBoundsHandler.getDisplayRotation(); @@ -381,6 +386,9 @@ public class PipTaskOrganizer extends TaskOrganizer implements mInitialState.put(mToken.asBinder(), new Configuration(mTaskInfo.configuration)); mPictureInPictureParams = mTaskInfo.pictureInPictureParams; + mPipUiEventLoggerLogger.setTaskInfo(mTaskInfo); + mPipUiEventLoggerLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_ENTER); + if (mShouldDeferEnteringPip) { if (DEBUG) Log.d(TAG, "Defer entering PiP animation, fixed rotation is ongoing"); // if deferred, hide the surface till fixed rotation is completed @@ -514,6 +522,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements mPictureInPictureParams = null; mInPip = false; mExitingPip = false; + mPipUiEventLoggerLogger.setTaskInfo(null); } @Override @@ -629,7 +638,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements * {@link PictureInPictureParams} would affect the bounds. */ private boolean applyPictureInPictureParams(@NonNull PictureInPictureParams params) { - final boolean changed = (mPictureInPictureParams == null) ? true : !Objects.equals( + final boolean changed = (mPictureInPictureParams == null) || !Objects.equals( mPictureInPictureParams.getAspectRatioRational(), params.getAspectRatioRational()); if (changed) { mPictureInPictureParams = params; diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java b/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java new file mode 100644 index 000000000000..5e2cd9c111b2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java @@ -0,0 +1,97 @@ +/* + * 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.pip; + +import android.app.TaskInfo; + +import com.android.internal.logging.UiEvent; +import com.android.internal.logging.UiEventLogger; + +import javax.inject.Inject; +import javax.inject.Singleton; + + +/** + * Helper class that ends PiP log to UiEvent, see also go/uievent + */ +@Singleton +public class PipUiEventLogger { + + private final UiEventLogger mUiEventLogger; + + private TaskInfo mTaskInfo; + + @Inject + public PipUiEventLogger(UiEventLogger uiEventLogger) { + mUiEventLogger = uiEventLogger; + } + + public void setTaskInfo(TaskInfo taskInfo) { + mTaskInfo = taskInfo; + } + + /** + * Sends log via UiEvent, reference go/uievent for how to debug locally + */ + public void log(PipUiEventEnum event) { + if (mTaskInfo == null) { + return; + } + mUiEventLogger.log(event, mTaskInfo.userId, mTaskInfo.topActivity.getPackageName()); + } + + /** + * Enums for logging the PiP events to UiEvent + */ + public enum PipUiEventEnum implements UiEventLogger.UiEventEnum { + @UiEvent(doc = "Activity enters picture-in-picture mode") + PICTURE_IN_PICTURE_ENTER(603), + + @UiEvent(doc = "Expands from picture-in-picture to fullscreen") + PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN(604), + + @UiEvent(doc = "Removes picture-in-picture by tap close button") + PICTURE_IN_PICTURE_TAP_TO_REMOVE(605), + + @UiEvent(doc = "Removes picture-in-picture by drag to dismiss area") + PICTURE_IN_PICTURE_DRAG_TO_REMOVE(606), + + @UiEvent(doc = "Shows picture-in-picture menu") + PICTURE_IN_PICTURE_SHOW_MENU(607), + + @UiEvent(doc = "Hides picture-in-picture menu") + PICTURE_IN_PICTURE_HIDE_MENU(608), + + @UiEvent(doc = "Changes the aspect ratio of picture-in-picture window. This is inherited" + + " from previous Tron-based logging and currently not in use.") + PICTURE_IN_PICTURE_CHANGE_ASPECT_RATIO(609), + + @UiEvent(doc = "User resize of the picture-in-picture window") + PICTURE_IN_PICTURE_RESIZE(610); + + private final int mId; + + PipUiEventEnum(int id) { + mId = id; + } + + @Override + public int getId() { + return mId; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java index 7c5054cb0544..9dfa864e2ee7 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java @@ -47,6 +47,7 @@ import com.android.systemui.pip.BasePipManager; import com.android.systemui.pip.PipBoundsHandler; import com.android.systemui.pip.PipSnapAlgorithm; import com.android.systemui.pip.PipTaskOrganizer; +import com.android.systemui.pip.PipUiEventLogger; import com.android.systemui.pip.phone.dagger.PipMenuActivityClass; import com.android.systemui.shared.recents.IPinnedStackAnimationListener; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -259,7 +260,8 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio PipSnapAlgorithm pipSnapAlgorithm, PipTaskOrganizer pipTaskOrganizer, SysUiState sysUiState, - ConfigurationController configController) { + ConfigurationController configController, + PipUiEventLogger pipUiEventLogger) { mContext = context; mActivityManager = ActivityManager.getService(); @@ -280,7 +282,8 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio mMediaController, mInputConsumerController); mTouchHandler = new PipTouchHandler(context, mActivityManager, mMenuController, mInputConsumerController, mPipBoundsHandler, mPipTaskOrganizer, - floatingContentCoordinator, deviceConfig, pipSnapAlgorithm, sysUiState); + floatingContentCoordinator, deviceConfig, pipSnapAlgorithm, sysUiState, + pipUiEventLogger); mAppOpsListener = new PipAppOpsListener(context, mActivityManager, mTouchHandler.getMotionHelper()); displayController.addDisplayChangingController(mRotationController); @@ -394,12 +397,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio * Update the bounds used to save the re-entry size and snap fraction when exiting PIP. */ public void updateReentryBounds(Rect bounds) { - // On phones, the expansion animation that happens on pip tap before restoring - // to fullscreen makes it so that the last reported bounds are the expanded - // bounds. We want to restore to the unexpanded bounds when re-entering pip, - // so we use the bounds before expansion (normal) instead of the reported - // bounds. - Rect reentryBounds = mTouchHandler.getNormalBounds(); + final Rect reentryBounds = mTouchHandler.getUserResizeBounds(); float snapFraction = mPipBoundsHandler.getSnapFraction(bounds); mPipBoundsHandler.applySnapFraction(reentryBounds, snapFraction); mReentryBounds.set(reentryBounds); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java index d884fa956edc..9c42f8bff378 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java @@ -52,6 +52,7 @@ import com.android.internal.policy.TaskResizingAlgorithm; import com.android.systemui.model.SysUiState; import com.android.systemui.pip.PipBoundsHandler; import com.android.systemui.pip.PipTaskOrganizer; +import com.android.systemui.pip.PipUiEventLogger; import com.android.systemui.util.DeviceConfigProxy; import com.android.wm.shell.R; @@ -88,6 +89,7 @@ public class PipResizeGestureHandler { private final Point mMaxSize = new Point(); private final Point mMinSize = new Point(); private final Rect mLastResizeBounds = new Rect(); + private final Rect mUserResizeBounds = new Rect(); private final Rect mLastDownBounds = new Rect(); private final Rect mDragCornerSize = new Rect(); private final Rect mTmpTopLeftCorner = new Rect(); @@ -109,13 +111,15 @@ public class PipResizeGestureHandler { private InputMonitor mInputMonitor; private InputEventReceiver mInputEventReceiver; private PipTaskOrganizer mPipTaskOrganizer; + private PipUiEventLogger mPipUiEventLogger; private int mCtrlType; public PipResizeGestureHandler(Context context, PipBoundsHandler pipBoundsHandler, PipMotionHelper motionHelper, DeviceConfigProxy deviceConfig, PipTaskOrganizer pipTaskOrganizer, Function<Rect, Rect> movementBoundsSupplier, - Runnable updateMovementBoundsRunnable, SysUiState sysUiState) { + Runnable updateMovementBoundsRunnable, SysUiState sysUiState, + PipUiEventLogger pipUiEventLogger) { mContext = context; mDisplayId = context.getDisplayId(); mMainExecutor = context.getMainExecutor(); @@ -125,6 +129,7 @@ public class PipResizeGestureHandler { mMovementBoundsSupplier = movementBoundsSupplier; mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable; mSysUiState = sysUiState; + mPipUiEventLogger = pipUiEventLogger; context.getDisplay().getRealSize(mMaxSize); reloadResources(); @@ -181,6 +186,7 @@ public class PipResizeGestureHandler { void onActivityUnpinned() { mIsAttached = false; + mUserResizeBounds.setEmpty(); updateIsEnabled(); } @@ -329,6 +335,7 @@ public class PipResizeGestureHandler { case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: if (!mLastResizeBounds.isEmpty()) { + mUserResizeBounds.set(mLastResizeBounds); mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds, (Rect bounds) -> { new Handler(Looper.getMainLooper()).post(() -> { @@ -337,6 +344,8 @@ public class PipResizeGestureHandler { resetState(); }); }); + mPipUiEventLogger.log( + PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_RESIZE); } else { resetState(); } @@ -351,6 +360,14 @@ public class PipResizeGestureHandler { mThresholdCrossed = false; } + void setUserResizeBounds(Rect bounds) { + mUserResizeBounds.set(bounds); + } + + Rect getUserResizeBounds() { + return mUserResizeBounds; + } + void updateMaxSize(int maxX, int maxY) { mMaxSize.set(maxX, maxY); } 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 7fc2823fc248..b20ea4e5c836 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -34,7 +34,6 @@ import android.graphics.drawable.TransitionDrawable; import android.os.Handler; import android.os.RemoteException; import android.util.Log; -import android.util.Pair; import android.util.Size; import android.view.Gravity; import android.view.IPinnedStackController; @@ -55,13 +54,13 @@ import androidx.dynamicanimation.animation.DynamicAnimation; import androidx.dynamicanimation.animation.SpringForce; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.os.logging.MetricsLoggerWrapper; import com.android.systemui.R; import com.android.systemui.model.SysUiState; import com.android.systemui.pip.PipAnimationController; import com.android.systemui.pip.PipBoundsHandler; import com.android.systemui.pip.PipSnapAlgorithm; import com.android.systemui.pip.PipTaskOrganizer; +import com.android.systemui.pip.PipUiEventLogger; import com.android.systemui.shared.system.InputConsumerController; import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.DismissCircleView; @@ -94,6 +93,8 @@ public class PipTouchHandler { private final WindowManager mWindowManager; private final IActivityManager mActivityManager; private final PipBoundsHandler mPipBoundsHandler; + private final PipUiEventLogger mPipUiEventLogger; + private PipResizeGestureHandler mPipResizeGestureHandler; private IPinnedStackController mPinnedStackController; @@ -132,9 +133,6 @@ public class PipTouchHandler { // The current movement bounds private Rect mMovementBounds = new Rect(); - // The current resized bounds, changed by user resize. - // This is used during expand/un-expand to save/restore the user's resized size. - @VisibleForTesting Rect mResizedBounds = new Rect(); // The reference inset bounds, used to determine the dismiss fraction private Rect mInsetBounds = new Rect(); @@ -198,11 +196,7 @@ public class PipTouchHandler { @Override public void onPipDismiss() { - Pair<ComponentName, Integer> topPipActivity = PipUtils.getTopPipActivity(mContext, - mActivityManager); - if (topPipActivity.first != null) { - MetricsLoggerWrapper.logPictureInPictureDismissByTap(mContext, topPipActivity); - } + mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_TAP_TO_REMOVE); mTouchState.removeDoubleTapTimeoutCallback(); mMotionHelper.dismissPip(); } @@ -223,7 +217,8 @@ public class PipTouchHandler { FloatingContentCoordinator floatingContentCoordinator, DeviceConfigProxy deviceConfig, PipSnapAlgorithm pipSnapAlgorithm, - SysUiState sysUiState) { + SysUiState sysUiState, + PipUiEventLogger pipUiEventLogger) { // Initialize the Pip input consumer mContext = context; mActivityManager = activityManager; @@ -238,7 +233,7 @@ public class PipTouchHandler { mPipResizeGestureHandler = new PipResizeGestureHandler(context, pipBoundsHandler, mMotionHelper, deviceConfig, pipTaskOrganizer, this::getMovementBounds, - this::updateMovementBounds, sysUiState); + this::updateMovementBounds, sysUiState, pipUiEventLogger); mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler, () -> mMenuController.showMenuWithDelay(MENU_STATE_FULL, mMotionHelper.getBounds(), true /* allowMenuTimeout */, willResizeMenu(), shouldShowResizeHandle()), @@ -259,6 +254,8 @@ public class PipTouchHandler { pipTaskOrganizer, pipSnapAlgorithm, this::onAccessibilityShowMenu, this::updateMovementBounds, mHandler); + mPipUiEventLogger = pipUiEventLogger; + mTargetView = new DismissCircleView(context); mTargetViewContainer = new FrameLayout(context); mTargetViewContainer.setBackgroundDrawable( @@ -307,11 +304,8 @@ public class PipTouchHandler { hideDismissTarget(); }); - Pair<ComponentName, Integer> topPipActivity = PipUtils.getTopPipActivity(mContext, - mActivityManager); - if (topPipActivity.first != null) { - MetricsLoggerWrapper.logPictureInPictureDismissByDrag(mContext, topPipActivity); - } + mPipUiEventLogger.log( + PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_DRAG_TO_REMOVE); } }); @@ -383,7 +377,6 @@ public class PipTouchHandler { mFloatingContentCoordinator.onContentRemoved(mMotionHelper); } - mResizedBounds.setEmpty(); mPipResizeGestureHandler.onActivityUnpinned(); } @@ -393,9 +386,8 @@ public class PipTouchHandler { mMotionHelper.synchronizePinnedStackBounds(); updateMovementBounds(); if (direction == TRANSITION_DIRECTION_TO_PIP) { - // updates mResizedBounds only if it's an entering PiP animation - // mResized should be otherwise updated in setMenuState. - mResizedBounds.set(mMotionHelper.getBounds()); + // Set the initial bounds as the user resize bounds. + mPipResizeGestureHandler.setUserResizeBounds(mMotionHelper.getBounds()); } if (mShowPipMenuOnAnimationEnd) { @@ -808,9 +800,7 @@ public class PipTouchHandler { // Save the current snap fraction and if we do not drag or move the PiP, then // we store back to this snap fraction. Otherwise, we'll reset the snap // fraction and snap to the closest edge. - // Also save the current resized bounds so when the menu disappears, we can restore it. if (resize) { - mResizedBounds.set(mMotionHelper.getBounds()); Rect expandedBounds = new Rect(mExpandedBounds); mSavedSnapFraction = mMotionHelper.animateToExpandedState(expandedBounds, mMovementBounds, mExpandedMovementBounds, callback); @@ -839,7 +829,7 @@ public class PipTouchHandler { } if (mDeferResizeToNormalBoundsUntilRotation == -1) { - Rect restoreBounds = new Rect(mResizedBounds); + Rect restoreBounds = new Rect(getUserResizeBounds()); Rect restoredMovementBounds = new Rect(); mSnapAlgorithm.getMovementBounds(restoreBounds, mInsetBounds, restoredMovementBounds, mIsImeShowing ? mImeHeight : 0); @@ -856,8 +846,10 @@ public class PipTouchHandler { // If pip menu has dismissed, we should register the A11y ActionReplacingConnection for pip // as well, or it can't handle a11y focus and pip menu can't perform any action. onRegistrationChanged(menuState == MENU_STATE_NONE); - if (menuState != MENU_STATE_CLOSE) { - MetricsLoggerWrapper.logPictureInPictureMenuVisible(mContext, menuState == MENU_STATE_FULL); + if (menuState == MENU_STATE_NONE) { + mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_HIDE_MENU); + } else if (menuState == MENU_STATE_FULL) { + mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_SHOW_MENU); } } @@ -890,6 +882,10 @@ public class PipTouchHandler { return mNormalBounds; } + Rect getUserResizeBounds() { + return mPipResizeGestureHandler.getUserResizeBounds(); + } + /** * Gesture controlling normal movement of the PIP. */ diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt index d5a14f7bef2f..affc5ee1fdf0 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt @@ -45,7 +45,6 @@ import javax.inject.Singleton @Singleton class PrivacyItemController @Inject constructor( - context: Context, private val appOpsController: AppOpsController, @Main uiExecutor: DelayableExecutor, @Background private val bgExecutor: Executor, @@ -57,16 +56,21 @@ class PrivacyItemController @Inject constructor( @VisibleForTesting internal companion object { - val OPS = intArrayOf(AppOpsManager.OP_CAMERA, - AppOpsManager.OP_RECORD_AUDIO, + val OPS_MIC_CAMERA = intArrayOf(AppOpsManager.OP_CAMERA, + AppOpsManager.OP_RECORD_AUDIO) + val OPS_LOCATION = intArrayOf( AppOpsManager.OP_COARSE_LOCATION, AppOpsManager.OP_FINE_LOCATION) + val OPS = OPS_MIC_CAMERA + OPS_LOCATION val intentFilter = IntentFilter().apply { addAction(Intent.ACTION_USER_SWITCHED) addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE) addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE) } const val TAG = "PrivacyItemController" + private const val ALL_INDICATORS = + SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED + private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED } @VisibleForTesting @@ -74,9 +78,14 @@ class PrivacyItemController @Inject constructor( @Synchronized get() = field.toList() // Returns a shallow copy of the list @Synchronized set - private fun isPermissionsHubEnabled(): Boolean { + private fun isAllIndicatorsEnabled(): Boolean { return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, - SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false) + ALL_INDICATORS, false) + } + + private fun isMicCameraEnabled(): Boolean { + return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, + MIC_CAMERA, false) } private var currentUserIds = emptyList<Int>() @@ -94,23 +103,28 @@ class PrivacyItemController @Inject constructor( uiExecutor.execute(notifyChanges) } - var indicatorsAvailable = isPermissionsHubEnabled() + var allIndicatorsAvailable = isAllIndicatorsEnabled() private set - @VisibleForTesting - internal val devicePropertiesChangedListener = + var micCameraAvailable = isMicCameraEnabled() + private set + + private val devicePropertiesChangedListener = object : DeviceConfig.OnPropertiesChangedListener { override fun onPropertiesChanged(properties: DeviceConfig.Properties) { if (DeviceConfig.NAMESPACE_PRIVACY.equals(properties.getNamespace()) && - properties.getKeyset().contains( - SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED)) { - val flag = properties.getBoolean( - SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false) - if (indicatorsAvailable != flag) { - // This is happening already in the UI executor, so we can iterate in the - indicatorsAvailable = flag - callbacks.forEach { it.get()?.onFlagChanged(flag) } + (properties.keyset.contains(ALL_INDICATORS) || + properties.keyset.contains(MIC_CAMERA))) { + + // Running on the ui executor so can iterate on callbacks + if (properties.keyset.contains(ALL_INDICATORS)) { + allIndicatorsAvailable = properties.getBoolean(ALL_INDICATORS, false) + callbacks.forEach { it.get()?.onFlagAllChanged(allIndicatorsAvailable) } } + if (properties.keyset.contains(MIC_CAMERA)) { + micCameraAvailable = properties.getBoolean(MIC_CAMERA, false) + callbacks.forEach { it.get()?.onFlagMicCameraChanged(micCameraAvailable) } + } internalUiExecutor.updateListeningState() } } @@ -123,6 +137,10 @@ class PrivacyItemController @Inject constructor( packageName: String, active: Boolean ) { + // Check if we care about this code right now + if (!allIndicatorsAvailable && code in OPS_LOCATION) { + return + } val userId = UserHandle.getUserId(uid) if (userId in currentUserIds) { update(false) @@ -166,13 +184,16 @@ class PrivacyItemController @Inject constructor( } /** - * Updates listening status based on whether there are callbacks and the indicators are enabled + * Updates listening status based on whether there are callbacks and the indicators are enabled. + * + * Always listen to all OPS so we don't have to figure out what we should be listening to. We + * still have to filter anyway. Updates are filtered in the callback. * * This is only called from private (add/remove)Callback and from the config listener, all in * main thread. */ private fun setListeningState() { - val listen = !callbacks.isEmpty() and indicatorsAvailable + val listen = !callbacks.isEmpty() and (allIndicatorsAvailable || micCameraAvailable) if (listening == listen) return listening = listen if (listening) { @@ -233,14 +254,19 @@ class PrivacyItemController @Inject constructor( AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE else -> return null } + if (type == PrivacyType.TYPE_LOCATION && !allIndicatorsAvailable) return null val app = PrivacyApplication(appOpItem.packageName, appOpItem.uid) return PrivacyItem(type, app) } interface Callback { fun onPrivacyItemsChanged(privacyItems: List<PrivacyItem>) + + @JvmDefault + fun onFlagAllChanged(flag: Boolean) {} + @JvmDefault - fun onFlagChanged(flag: Boolean) {} + fun onFlagMicCameraChanged(flag: Boolean) {} } internal inner class Receiver : BroadcastReceiver() { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index 2dc82dd853d4..2e258d56ece0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -151,7 +151,8 @@ public class QuickStatusBarHeader extends RelativeLayout implements private Space mSpace; private BatteryMeterView mBatteryRemainingIcon; private RingerModeTracker mRingerModeTracker; - private boolean mPermissionsHubEnabled; + private boolean mAllIndicatorsEnabled; + private boolean mMicCameraIndicatorsEnabled; private PrivacyItemController mPrivacyItemController; private final UiEventLogger mUiEventLogger; @@ -178,13 +179,26 @@ public class QuickStatusBarHeader extends RelativeLayout implements } @Override - public void onFlagChanged(boolean flag) { - if (mPermissionsHubEnabled != flag) { - StatusIconContainer iconContainer = requireViewById(R.id.statusIcons); - iconContainer.setIgnoredSlots(getIgnoredIconSlots()); - setChipVisibility(!mPrivacyChip.getPrivacyList().isEmpty()); + public void onFlagAllChanged(boolean flag) { + if (mAllIndicatorsEnabled != flag) { + mAllIndicatorsEnabled = flag; + update(); } } + + @Override + public void onFlagMicCameraChanged(boolean flag) { + if (mMicCameraIndicatorsEnabled != flag) { + mMicCameraIndicatorsEnabled = flag; + update(); + } + } + + private void update() { + StatusIconContainer iconContainer = requireViewById(R.id.statusIcons); + iconContainer.setIgnoredSlots(getIgnoredIconSlots()); + setChipVisibility(!mPrivacyChip.getPrivacyList().isEmpty()); + } }; @Inject @@ -267,7 +281,8 @@ public class QuickStatusBarHeader extends RelativeLayout implements mRingerModeTextView.setSelected(true); mNextAlarmTextView.setSelected(true); - mPermissionsHubEnabled = mPrivacyItemController.getIndicatorsAvailable(); + mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable(); + mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable(); } public QuickQSPanel getHeaderQsPanel() { @@ -276,13 +291,15 @@ public class QuickStatusBarHeader extends RelativeLayout implements private List<String> getIgnoredIconSlots() { ArrayList<String> ignored = new ArrayList<>(); - ignored.add(mContext.getResources().getString( - com.android.internal.R.string.status_bar_camera)); - ignored.add(mContext.getResources().getString( - com.android.internal.R.string.status_bar_microphone)); - if (mPermissionsHubEnabled) { + if (getChipEnabled()) { + ignored.add(mContext.getResources().getString( + com.android.internal.R.string.status_bar_camera)); ignored.add(mContext.getResources().getString( - com.android.internal.R.string.status_bar_location)); + com.android.internal.R.string.status_bar_microphone)); + if (mAllIndicatorsEnabled) { + ignored.add(mContext.getResources().getString( + com.android.internal.R.string.status_bar_location)); + } } return ignored; @@ -300,7 +317,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements } private void setChipVisibility(boolean chipVisible) { - if (chipVisible && mPermissionsHubEnabled) { + if (chipVisible && getChipEnabled()) { mPrivacyChip.setVisibility(View.VISIBLE); // Makes sure that the chip is logged as viewed at most once each time QS is opened // mListening makes sure that the callback didn't return after the user closed QS @@ -607,7 +624,8 @@ public class QuickStatusBarHeader extends RelativeLayout implements mAlarmController.addCallback(this); mLifecycle.setCurrentState(Lifecycle.State.RESUMED); // Get the most up to date info - mPermissionsHubEnabled = mPrivacyItemController.getIndicatorsAvailable(); + mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable(); + mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable(); mPrivacyItemController.addCallback(mPICCallback); } else { mZenController.removeCallback(this); @@ -747,4 +765,8 @@ public class QuickStatusBarHeader extends RelativeLayout implements updateHeaderTextContainerAlphaAnimator(); } } + + private boolean getChipEnabled() { + return mMicCameraIndicatorsEnabled || mAllIndicatorsEnabled; + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java index 8c485a6a950f..255513a31c75 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java @@ -14,6 +14,7 @@ package com.android.systemui.qs.tileimpl; +import static androidx.lifecycle.Lifecycle.State.CREATED; import static androidx.lifecycle.Lifecycle.State.DESTROYED; import static androidx.lifecycle.Lifecycle.State.RESUMED; import static androidx.lifecycle.Lifecycle.State.STARTED; @@ -173,6 +174,7 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy mState = newTileState(); mTmpState = newTileState(); + mUiHandler.post(() -> mLifecycle.setCurrentState(CREATED)); } protected final void resetStates() { @@ -451,15 +453,24 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy if (listening) { if (mListeners.add(listener) && mListeners.size() == 1) { if (DEBUG) Log.d(TAG, "handleSetListening true"); - mLifecycle.setCurrentState(RESUMED); handleSetListening(listening); - refreshState(); // Ensure we get at least one refresh after listening. + mUiHandler.post(() -> { + // This tile has been destroyed, the state should not change anymore and we + // should not refresh it anymore. + if (mLifecycle.getCurrentState().equals(DESTROYED)) return; + mLifecycle.setCurrentState(RESUMED); + refreshState(); // Ensure we get at least one refresh after listening. + }); } } else { if (mListeners.remove(listener) && mListeners.size() == 0) { if (DEBUG) Log.d(TAG, "handleSetListening false"); - mLifecycle.setCurrentState(STARTED); handleSetListening(listening); + mUiHandler.post(() -> { + // This tile has been destroyed, the state should not change anymore. + if (mLifecycle.getCurrentState().equals(DESTROYED)) return; + mLifecycle.setCurrentState(STARTED); + }); } } updateIsFullQs(); @@ -486,11 +497,14 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy mQSLogger.logTileDestroyed(mTileSpec, "Handle destroy"); if (mListeners.size() != 0) { handleSetListening(false); + mListeners.clear(); } mCallbacks.clear(); mHandler.removeCallbacksAndMessages(null); // This will force it to be removed from all controllers that may have it registered. - mLifecycle.setCurrentState(DESTROYED); + mUiHandler.post(() -> { + mLifecycle.setCurrentState(DESTROYED); + }); } protected void checkIfRestrictionEnforcedByAdminOnly(State state, String userRestriction) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java index 3264429a1723..d8548decc5f4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java @@ -67,7 +67,7 @@ public class NightDisplayTile extends QSTileImpl<BooleanState> implements private static final String PATTERN_HOUR_MINUTE = "h:mm a"; private static final String PATTERN_HOUR_NINUTE_24 = "HH:mm"; - private final ColorDisplayManager mManager; + private ColorDisplayManager mManager; private final LocationController mLocationController; private final NightDisplayListenerModule.Builder mNightDisplayListenerBuilder; private NightDisplayListener mListener; @@ -126,6 +126,8 @@ public class NightDisplayTile extends QSTileImpl<BooleanState> implements mListener.setCallback(null); } + mManager = getHost().getUserContext().getSystemService(ColorDisplayManager.class); + // Make a new controller for the new user. mListener = mNightDisplayListenerBuilder.setUser(newUserId).build(); if (mIsListening) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java index 07b841ffb6f7..78975a4798ce 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java @@ -58,10 +58,9 @@ public class UiModeNightTile extends QSTileImpl<QSTile.BooleanState> implements public static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("hh:mm a"); private final Icon mIcon = ResourceIcon.get( com.android.internal.R.drawable.ic_qs_ui_mode_night); - private final UiModeManager mUiModeManager; + private UiModeManager mUiModeManager; private final BatteryController mBatteryController; private final LocationController mLocationController; - @Inject public UiModeNightTile( QSHost host, @@ -78,7 +77,7 @@ public class UiModeNightTile extends QSTileImpl<QSTile.BooleanState> implements super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController, activityStarter, qsLogger); mBatteryController = batteryController; - mUiModeManager = mContext.getSystemService(UiModeManager.class); + mUiModeManager = host.getUserContext().getSystemService(UiModeManager.class); mLocationController = locationController; configurationController.observe(getLifecycle(), this); batteryController.observe(getLifecycle(), this); diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java index 71e788375d5e..1bea72aea2ba 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java +++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java @@ -385,7 +385,7 @@ public class BrightnessController implements ToggleSlider.Listener { if (stopTracking) { // TODO(brightnessfloat): change to use float value instead. MetricsLogger.action(mContext, metric, - BrightnessSynchronizer.brightnessFloatToInt(mContext, valFloat)); + BrightnessSynchronizer.brightnessFloatToInt(valFloat)); } setBrightness(valFloat); diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java index 4007abb39903..d40b666860e9 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java @@ -78,6 +78,22 @@ public class Divider extends SystemUI { onUndockingTask(); } } + + @Override + public void onActivityForcedResizable(String packageName, int taskId, + int reason) { + mDividerController.onActivityForcedResizable(packageName, taskId, reason); + } + + @Override + public void onActivityDismissingDockedStack() { + mDividerController.onActivityDismissingSplitScreen(); + } + + @Override + public void onActivityLaunchOnSecondaryDisplayFailed() { + mDividerController.onActivityLaunchOnSecondaryDisplayFailed(); + } } ); } diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerController.java index 81649f608581..1ee8abb411b9 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerController.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerController.java @@ -50,8 +50,7 @@ import java.util.function.Consumer; /** * Controls the docked stack divider. */ -public class DividerController implements DividerView.DividerCallbacks, - DisplayController.OnDisplaysChangedListener { +public class DividerController implements DisplayController.OnDisplaysChangedListener { static final boolean DEBUG = false; private static final String TAG = "Divider"; @@ -257,8 +256,8 @@ public class DividerController implements DividerView.DividerCallbacks, mView = (DividerView) LayoutInflater.from(dctx).inflate(R.layout.docked_stack_divider, null); DisplayLayout displayLayout = mDisplayController.getDisplayLayout(mContext.getDisplayId()); - mView.injectDependencies(mWindowManager, mDividerState, this, mSplits, mSplitLayout, - mImePositionProcessor, mWindowManagerProxy); + mView.injectDependencies(mWindowManager, mDividerState, mForcedResizableController, mSplits, + mSplitLayout, mImePositionProcessor, mWindowManagerProxy); mView.setVisibility(mVisible ? View.VISIBLE : View.INVISIBLE); mView.setMinimizedDockStack(mMinimized, mHomeStackResizable, null /* transaction */); final int size = dctx.getResources().getDimensionPixelSize( @@ -397,7 +396,22 @@ public class DividerController implements DividerView.DividerCallbacks, } } - /** Called when there's a task undocking. */ + /** Called when there's an activity forced resizable. */ + public void onActivityForcedResizable(String packageName, int taskId, int reason) { + mForcedResizableController.activityForcedResizable(packageName, taskId, reason); + } + + /** Called when there's an activity dismissing split screen. */ + public void onActivityDismissingSplitScreen() { + mForcedResizableController.activityDismissingSplitScreen(); + } + + /** Called when there's an activity launch on secondary display failed. */ + public void onActivityLaunchOnSecondaryDisplayFailed() { + mForcedResizableController.activityLaunchOnSecondaryDisplayFailed(); + } + + /** Called when there's a task undocking. */ public void onUndockingTask() { if (mView != null) { mView.onUndockingTask(); @@ -426,17 +440,7 @@ public class DividerController implements DividerView.DividerCallbacks, mForcedResizableController.onAppTransitionFinished(); } - @Override - public void onDraggingStart() { - mForcedResizableController.onDraggingStart(); - } - - @Override - public void onDraggingEnd() { - mForcedResizableController.onDraggingEnd(); - } - - /** Dumps current status of Divider.*/ + /** Dumps current status of Split Screen. */ public void dump(PrintWriter pw) { pw.print(" mVisible="); pw.println(mVisible); pw.print(" mMinimized="); pw.println(mMinimized); diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java index f412cc00981b..ff8bab07db05 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java @@ -28,15 +28,13 @@ import android.util.ArraySet; import android.widget.Toast; import com.android.systemui.R; -import com.android.systemui.shared.system.ActivityManagerWrapper; -import com.android.systemui.shared.system.TaskStackChangeListener; import java.util.function.Consumer; /** * Controller that decides when to show the {@link ForcedResizableInfoActivity}. */ -public class ForcedResizableInfoActivityController { +final class ForcedResizableInfoActivityController implements DividerView.DividerCallbacks { private static final String SELF_PACKAGE_NAME = "com.android.systemui"; @@ -47,12 +45,7 @@ public class ForcedResizableInfoActivityController { private final ArraySet<String> mPackagesShownInSession = new ArraySet<>(); private boolean mDividerDragging; - private final Runnable mTimeoutRunnable = new Runnable() { - @Override - public void run() { - showPending(); - } - }; + private final Runnable mTimeoutRunnable = this::showPending; private final Consumer<Boolean> mDockedStackExistsListener = exists -> { if (!exists) { @@ -78,44 +71,28 @@ public class ForcedResizableInfoActivityController { public ForcedResizableInfoActivityController(Context context, DividerController dividerController) { mContext = context; - ActivityManagerWrapper.getInstance().registerTaskStackListener( - new TaskStackChangeListener() { - @Override - public void onActivityForcedResizable(String packageName, int taskId, - int reason) { - activityForcedResizable(packageName, taskId, reason); - } - - @Override - public void onActivityDismissingDockedStack() { - activityDismissingDockedStack(); - } - - @Override - public void onActivityLaunchOnSecondaryDisplayFailed() { - activityLaunchOnSecondaryDisplayFailed(); - } - }); dividerController.registerInSplitScreenListener(mDockedStackExistsListener); } - public void onAppTransitionFinished() { - if (!mDividerDragging) { - showPending(); - } - } - - void onDraggingStart() { + @Override + public void onDraggingStart() { mDividerDragging = true; mHandler.removeCallbacks(mTimeoutRunnable); } - void onDraggingEnd() { + @Override + public void onDraggingEnd() { mDividerDragging = false; showPending(); } - private void activityForcedResizable(String packageName, int taskId, int reason) { + void onAppTransitionFinished() { + if (!mDividerDragging) { + showPending(); + } + } + + void activityForcedResizable(String packageName, int taskId, int reason) { if (debounce(packageName)) { return; } @@ -123,12 +100,12 @@ public class ForcedResizableInfoActivityController { postTimeout(); } - private void activityDismissingDockedStack() { + void activityDismissingSplitScreen() { Toast.makeText(mContext, R.string.dock_non_resizeble_failed_to_dock_text, Toast.LENGTH_SHORT).show(); } - private void activityLaunchOnSecondaryDisplayFailed() { + void activityLaunchOnSecondaryDisplayFailed() { Toast.makeText(mContext, R.string.activity_launch_on_secondary_display_failed_text, Toast.LENGTH_SHORT).show(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java index 53b369c3543e..eb476457dcec 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java @@ -34,6 +34,7 @@ public class ContextualButton extends ButtonDispatcher { private ContextButtonListener mListener; private ContextualButtonGroup mGroup; + protected final Context mLightContext; protected final @DrawableRes int mIconResId; /** @@ -42,8 +43,10 @@ public class ContextualButton extends ButtonDispatcher { * @param buttonResId the button view from xml layout * @param iconResId icon resource to be used */ - public ContextualButton(@IdRes int buttonResId, @DrawableRes int iconResId) { + public ContextualButton(@IdRes int buttonResId, Context lightContext, + @DrawableRes int iconResId) { super(buttonResId); + mLightContext = lightContext; mIconResId = iconResId; } @@ -117,17 +120,8 @@ public class ContextualButton extends ButtonDispatcher { } protected KeyButtonDrawable getNewDrawable(int lightIconColor, int darkIconColor) { - return KeyButtonDrawable.create(getContext().getApplicationContext(), lightIconColor, - darkIconColor, mIconResId, false /* shadow */, null /* ovalBackground */); - } - - /** - * This context is from the view that could be stale after rotation or config change. To get - * correct resources use getApplicationContext() as well. - * @return current view context - */ - protected Context getContext() { - return getCurrentView().getContext(); + return KeyButtonDrawable.create(mLightContext, lightIconColor, darkIconColor, mIconResId, + false /* shadow */, null /* ovalBackground */); } public interface ContextButtonListener { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java index 6297052550c3..8c5e2ceaeadf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -375,6 +375,34 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback } }; + private static class NavBarViewAttachedListener implements View.OnAttachStateChangeListener { + private NavigationBarFragment mFragment; + private FragmentListener mListener; + + NavBarViewAttachedListener(NavigationBarFragment fragment, FragmentListener listener) { + mFragment = fragment; + mListener = listener; + } + + @Override + public void onViewAttachedToWindow(View v) { + final FragmentHostManager fragmentHost = FragmentHostManager.get(v); + fragmentHost.getFragmentManager().beginTransaction() + .replace(R.id.navigation_bar_frame, mFragment, TAG) + .commit(); + fragmentHost.addTagListener(TAG, mListener); + mFragment = null; + } + + @Override + public void onViewDetachedFromWindow(View v) { + final FragmentHostManager fragmentHost = FragmentHostManager.get(v); + fragmentHost.removeTagListener(TAG, mListener); + FragmentHostManager.removeAndDestroy(v); + v.removeOnAttachStateChangeListener(this); + } + } + private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener = new DeviceConfig.OnPropertiesChangedListener() { @Override @@ -1470,26 +1498,10 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + navigationBarView); if (navigationBarView == null) return null; - navigationBarView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { - @Override - public void onViewAttachedToWindow(View v) { - final NavigationBarFragment fragment = - FragmentHostManager.get(v).create(NavigationBarFragment.class); - final FragmentHostManager fragmentHost = FragmentHostManager.get(v); - fragmentHost.getFragmentManager().beginTransaction() - .replace(R.id.navigation_bar_frame, fragment, TAG) - .commit(); - fragmentHost.addTagListener(TAG, listener); - } - - @Override - public void onViewDetachedFromWindow(View v) { - final FragmentHostManager fragmentHost = FragmentHostManager.get(v); - fragmentHost.removeTagListener(TAG, listener); - FragmentHostManager.removeAndDestroy(v); - navigationBarView.removeOnAttachStateChangeListener(this); - } - }); + NavigationBarFragment fragment = FragmentHostManager.get(navigationBarView) + .create(NavigationBarFragment.class); + navigationBarView.addOnAttachStateChangeListener(new NavBarViewAttachedListener(fragment, + listener)); context.getSystemService(WindowManager.class).addView(navigationBarView, lp); return navigationBarView; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index 84512ac85fa9..0b4a2b69ffb3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -295,11 +295,12 @@ public class NavigationBarView extends FrameLayout implements // Set up the context group of buttons mContextualButtonGroup = new ContextualButtonGroup(R.id.menu_container); final ContextualButton imeSwitcherButton = new ContextualButton(R.id.ime_switcher, - R.drawable.ic_ime_switcher_default); + mLightContext, R.drawable.ic_ime_switcher_default); final RotationContextButton rotateSuggestionButton = new RotationContextButton( - R.id.rotate_suggestion, R.drawable.ic_sysbar_rotate_button_ccw_start_0); + R.id.rotate_suggestion, mLightContext, + R.drawable.ic_sysbar_rotate_button_ccw_start_0); final ContextualButton accessibilityButton = - new ContextualButton(R.id.accessibility_button, + new ContextualButton(R.id.accessibility_button, mLightContext, R.drawable.ic_sysbar_accessibility_button); mContextualButtonGroup.addButton(imeSwitcherButton); if (!isGesturalMode) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index b2cfceae2cf6..8cb54eef01b4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -662,16 +662,18 @@ public class PhoneStatusBarPolicy mIconController.setIconVisibility(mSlotCamera, showCamera); mIconController.setIconVisibility(mSlotMicrophone, showMicrophone); - mIconController.setIconVisibility(mSlotLocation, showLocation); + if (mPrivacyItemController.getAllIndicatorsAvailable()) { + mIconController.setIconVisibility(mSlotLocation, showLocation); + } } @Override public void onLocationActiveChanged(boolean active) { - if (!mPrivacyItemController.getIndicatorsAvailable()) updateLocation(); + if (!mPrivacyItemController.getAllIndicatorsAvailable()) updateLocationFromController(); } // Updates the status view based on the current state of location requests. - private void updateLocation() { + private void updateLocationFromController() { if (mLocationController.isLocationActive()) { mIconController.setIconVisibility(mSlotLocation, true); } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButtonController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButtonController.java index f83cdd488c04..b0630a649ffe 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButtonController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationButtonController.java @@ -283,7 +283,6 @@ public class RotationButtonController { return; } - // TODO: Remove styles? // Prepare to show the navbar icon by updating the icon style to change anim params mLastRotationSuggestion = rotation; // Remember rotation for click final boolean rotationCCW = isRotationAnimationCCW(windowRotation, rotation); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java index d7e95e43ea8f..08aeb0425f68 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RotationContextButton.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.phone; import android.annotation.DrawableRes; import android.annotation.IdRes; +import android.content.Context; import android.view.View; import com.android.systemui.statusbar.policy.KeyButtonDrawable; @@ -28,8 +29,12 @@ public class RotationContextButton extends ContextualButton implements RotationB private RotationButtonController mRotationButtonController; - public RotationContextButton(@IdRes int buttonResId, @DrawableRes int iconResId) { - super(buttonResId, iconResId); + /** + * @param lightContext the context to use to load the icon resource + */ + public RotationContextButton(@IdRes int buttonResId, Context lightContext, + @DrawableRes int iconResId) { + super(buttonResId, lightContext, iconResId); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java index b89cb210dea1..8ff7a41cb22f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java @@ -86,7 +86,7 @@ public interface StatusBarIconController { static ArraySet<String> getIconHideList(Context context, String hideListStr) { ArraySet<String> ret = new ArraySet<>(); String[] hideList = hideListStr == null - ? context.getResources().getStringArray(R.array.config_statusBarIconBlackList) + ? context.getResources().getStringArray(R.array.config_statusBarIconsToExclude) : hideListStr.split(","); for (String slot : hideList) { if (!TextUtils.isEmpty(slot)) { diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java index b5f98ad47c09..89297fd83bb0 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java +++ b/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java @@ -54,7 +54,7 @@ public class UsbAccessoryUriActivity extends AlertActivity String uriString = intent.getStringExtra("uri"); mUri = (uriString == null ? null : Uri.parse(uriString)); - // sanity check before displaying dialog + // Exception check before displaying dialog if (mUri == null) { Log.e(TAG, "could not parse Uri " + uriString); finish(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java index 694f51be4e30..f4c07004f632 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java @@ -24,14 +24,13 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import android.app.Instrumentation; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import androidx.test.filters.SmallTest; -import androidx.test.platform.app.InstrumentationRegistry; import com.android.systemui.model.SysUiState; +import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.phone.NavigationModeController; import com.android.wm.shell.common.DisplayController; @@ -46,12 +45,13 @@ import org.mockito.MockitoAnnotations; @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper public class OneHandedGestureHandlerTest extends OneHandedTestCase { - Instrumentation mInstrumentation; OneHandedTouchHandler mTouchHandler; OneHandedTutorialHandler mTutorialHandler; OneHandedGestureHandler mGestureHandler; OneHandedManagerImpl mOneHandedManagerImpl; @Mock + CommandQueue mCommandQueue; + @Mock DisplayController mMockDisplayController; @Mock OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer; @@ -62,12 +62,13 @@ public class OneHandedGestureHandlerTest extends OneHandedTestCase { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mInstrumentation = InstrumentationRegistry.getInstrumentation(); mTouchHandler = new OneHandedTouchHandler(); mTutorialHandler = new OneHandedTutorialHandler(mContext); mGestureHandler = Mockito.spy(new OneHandedGestureHandler( mContext, mMockDisplayController, mMockNavigationModeController)); - mOneHandedManagerImpl = new OneHandedManagerImpl(mInstrumentation.getContext(), + mOneHandedManagerImpl = new OneHandedManagerImpl( + getContext(), + mCommandQueue, mMockDisplayController, mMockDisplayAreaOrganizer, mTouchHandler, @@ -100,6 +101,7 @@ public class OneHandedGestureHandlerTest extends OneHandedTestCase { @Test public void testOneHandedDisabled_shouldDisposeInputChannel() { mOneHandedManagerImpl.setOneHandedEnabled(false); + mOneHandedManagerImpl.setSwipeToNotificationEnabled(false); assertThat(mGestureHandler.mInputMonitor).isNull(); assertThat(mGestureHandler.mInputEventReceiver).isNull(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java index 3418ebf75e0c..763f6e4fe94b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java @@ -33,6 +33,7 @@ import android.view.Display; import androidx.test.filters.SmallTest; import com.android.systemui.model.SysUiState; +import com.android.systemui.statusbar.CommandQueue; import com.android.wm.shell.common.DisplayController; import org.junit.Before; @@ -51,6 +52,8 @@ public class OneHandedManagerImplTest extends OneHandedTestCase { OneHandedTimeoutHandler mTimeoutHandler; @Mock + CommandQueue mCommandQueue; + @Mock DisplayController mMockDisplayController; @Mock OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer; @@ -67,7 +70,9 @@ public class OneHandedManagerImplTest extends OneHandedTestCase { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mDisplay = mContext.getDisplay(); - mOneHandedManagerImpl = new OneHandedManagerImpl(getContext(), + mOneHandedManagerImpl = new OneHandedManagerImpl( + getContext(), + mCommandQueue, mMockDisplayController, mMockDisplayAreaOrganizer, mMockTouchHandler, @@ -94,14 +99,14 @@ public class OneHandedManagerImplTest extends OneHandedTestCase { @Test public void testRegisterOrganizer() { - verify(mMockDisplayAreaOrganizer, times(1)).registerOrganizer(anyInt()); + verify(mMockDisplayAreaOrganizer).registerOrganizer(anyInt()); } @Test public void testStartOneHanded() { mOneHandedManagerImpl.startOneHanded(); - verify(mMockDisplayAreaOrganizer, times(1)).scheduleOffset(anyInt(), anyInt()); + verify(mMockDisplayAreaOrganizer).scheduleOffset(anyInt(), anyInt()); } @Test @@ -121,7 +126,7 @@ public class OneHandedManagerImplTest extends OneHandedTestCase { public void testStopOneHanded_shouldRemoveTimer() { mOneHandedManagerImpl.stopOneHanded(); - verify(mTimeoutHandler, times(1)).removeTimer(); + verify(mTimeoutHandler).removeTimer(); } @Test @@ -129,7 +134,14 @@ public class OneHandedManagerImplTest extends OneHandedTestCase { final boolean enabled = true; mOneHandedManagerImpl.setOneHandedEnabled(enabled); - verify(mMockTouchHandler, atLeastOnce()).onOneHandedEnabled(enabled); + verify(mMockTouchHandler, times(2)).onOneHandedEnabled(enabled); } + @Test + public void testUpdateSwipeToNotificationIsEnabled() { + final boolean enabled = true; + mOneHandedManagerImpl.setSwipeToNotificationEnabled(enabled); + + verify(mMockTouchHandler, times(2)).onOneHandedEnabled(enabled); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedSettingsUtilTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedSettingsUtilTest.java index c157ae651cd5..f81d047b0f0c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedSettingsUtilTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedSettingsUtilTest.java @@ -104,4 +104,10 @@ public class OneHandedSettingsUtilTest extends OneHandedTestCase { ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS, ONE_HANDED_TIMEOUT_LONG_IN_SECONDS); } + + @Test + public void testGetSettingsSwipeToNotificationEnabled() { + assertThat(mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( + mContentResolver)).isAnyOf(true, false); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTestCase.java index befa42a0acc9..04ebf25e1b49 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTestCase.java +++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTestCase.java @@ -32,6 +32,7 @@ public abstract class OneHandedTestCase extends SysuiTestCase { static boolean sOrigEnabled; static boolean sOrigTapsAppToExitEnabled; static int sOrigTimeout; + static boolean sOrigSwipeToNotification; @Before public void setupSettings() { @@ -41,12 +42,16 @@ public abstract class OneHandedTestCase extends SysuiTestCase { getContext().getContentResolver()); sOrigTapsAppToExitEnabled = OneHandedSettingsUtil.getSettingsTapsAppToExit( getContext().getContentResolver()); + sOrigSwipeToNotification = OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( + getContext().getContentResolver()); Settings.Secure.putInt(getContext().getContentResolver(), Settings.Secure.ONE_HANDED_MODE_ENABLED, 1); Settings.Secure.putInt(getContext().getContentResolver(), Settings.Secure.ONE_HANDED_MODE_TIMEOUT, ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS); Settings.Secure.putInt(getContext().getContentResolver(), Settings.Secure.TAPS_APP_TO_EXIT, 1); + Settings.Secure.putInt(getContext().getContentResolver(), + Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 1); } @After @@ -57,6 +62,9 @@ public abstract class OneHandedTestCase extends SysuiTestCase { Settings.Secure.ONE_HANDED_MODE_TIMEOUT, sOrigTimeout); Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.TAPS_APP_TO_EXIT, sOrigTapsAppToExitEnabled ? 1 : 0); + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, + sOrigSwipeToNotification ? 1 : 0); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java index fdb28d3d43b5..15881a2e7c18 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java @@ -22,14 +22,13 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import android.app.Instrumentation; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import androidx.test.filters.SmallTest; -import androidx.test.platform.app.InstrumentationRegistry; import com.android.systemui.model.SysUiState; +import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.phone.NavigationModeController; import com.android.wm.shell.common.DisplayController; @@ -44,12 +43,13 @@ import org.mockito.MockitoAnnotations; @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper public class OneHandedTouchHandlerTest extends OneHandedTestCase { - Instrumentation mInstrumentation; OneHandedTouchHandler mTouchHandler; OneHandedTutorialHandler mTutorialHandler; OneHandedGestureHandler mGestureHandler; OneHandedManagerImpl mOneHandedManagerImpl; @Mock + CommandQueue mCommandQueue; + @Mock DisplayController mMockDisplayController; @Mock NavigationModeController mMockNavigationModeController; @@ -61,11 +61,12 @@ public class OneHandedTouchHandlerTest extends OneHandedTestCase { @Before public void setUp() { MockitoAnnotations.initMocks(this); - mInstrumentation = InstrumentationRegistry.getInstrumentation(); mTouchHandler = Mockito.spy(new OneHandedTouchHandler()); mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController, mMockNavigationModeController); - mOneHandedManagerImpl = new OneHandedManagerImpl(mInstrumentation.getContext(), + mOneHandedManagerImpl = new OneHandedManagerImpl( + getContext(), + mCommandQueue, mMockDisplayController, mMockDisplayAreaOrganizer, mTouchHandler, diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java index f4aa00eaf02f..f2b77a0a936b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java @@ -19,14 +19,13 @@ package com.android.systemui.onehanded; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import android.app.Instrumentation; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import androidx.test.filters.SmallTest; -import androidx.test.platform.app.InstrumentationRegistry; import com.android.systemui.model.SysUiState; +import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.phone.NavigationModeController; import com.android.wm.shell.common.DisplayController; @@ -41,12 +40,13 @@ import org.mockito.MockitoAnnotations; @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper public class OneHandedTutorialHandlerTest extends OneHandedTestCase { - Instrumentation mInstrumentation; OneHandedTouchHandler mTouchHandler; OneHandedTutorialHandler mTutorialHandler; OneHandedGestureHandler mGestureHandler; OneHandedManagerImpl mOneHandedManagerImpl; @Mock + CommandQueue mCommandQueue; + @Mock DisplayController mMockDisplayController; @Mock NavigationModeController mMockNavigationModeController; @@ -58,12 +58,13 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase { @Before public void setUp() { MockitoAnnotations.initMocks(this); - mInstrumentation = InstrumentationRegistry.getInstrumentation(); mTouchHandler = new OneHandedTouchHandler(); mTutorialHandler = Mockito.spy(new OneHandedTutorialHandler(mContext)); mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController, mMockNavigationModeController); - mOneHandedManagerImpl = new OneHandedManagerImpl(mInstrumentation.getContext(), + mOneHandedManagerImpl = new OneHandedManagerImpl( + getContext(), + mCommandQueue, mMockDisplayController, mMockDisplayAreaOrganizer, mTouchHandler, diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java index ffedb07b8db4..6db2679ea116 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java @@ -28,7 +28,6 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.statusbar.CommandQueue; @@ -53,8 +52,6 @@ public class OneHandedUITest extends OneHandedTestCase { @Mock OneHandedManagerImpl mMockOneHandedManagerImpl; @Mock - DumpManager mMockDumpManager; - @Mock OneHandedSettingsUtil mMockSettingsUtil; @Mock OneHandedTimeoutHandler mMockTimeoutHandler; @@ -68,7 +65,6 @@ public class OneHandedUITest extends OneHandedTestCase { mOneHandedUI = new OneHandedUI(mContext, mCommandQueue, mMockOneHandedManagerImpl, - mMockDumpManager, mMockSettingsUtil, mScreenLifecycle); mOneHandedUI.start(); @@ -168,6 +164,18 @@ public class OneHandedUITest extends OneHandedTestCase { OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS); } + @Test + public void tesSettingsObserver_updateSwipeToNotification() { + // Bypass test if device not support one-handed mode + if (!mIsSupportOneHandedMode) { + return; + } + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 1); + + verify(mMockOneHandedManagerImpl).setSwipeToNotificationEnabled(true); + } + @Ignore("Clarifying do not receive callback") @Test public void testKeyguardBouncerShowing_shouldStopOneHanded() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java index 96bb521a5d5b..9f67722041aa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java @@ -37,6 +37,7 @@ import com.android.systemui.model.SysUiState; import com.android.systemui.pip.PipBoundsHandler; import com.android.systemui.pip.PipSnapAlgorithm; import com.android.systemui.pip.PipTaskOrganizer; +import com.android.systemui.pip.PipUiEventLogger; import com.android.systemui.shared.system.InputConsumerController; import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.FloatingContentCoordinator; @@ -85,6 +86,9 @@ public class PipTouchHandlerTest extends SysuiTestCase { @Mock private SysUiState mSysUiState; + @Mock + private PipUiEventLogger mPipUiEventLogger; + private PipSnapAlgorithm mPipSnapAlgorithm; private PipMotionHelper mMotionHelper; private PipResizeGestureHandler mPipResizeGestureHandler; @@ -104,7 +108,7 @@ public class PipTouchHandlerTest extends SysuiTestCase { mPipTouchHandler = new PipTouchHandler(mContext, mActivityManager, mPipMenuActivityController, mInputConsumerController, mPipBoundsHandler, mPipTaskOrganizer, mFloatingContentCoordinator, mDeviceConfigProxy, - mPipSnapAlgorithm, mSysUiState); + mPipSnapAlgorithm, mSysUiState, mPipUiEventLogger); mMotionHelper = Mockito.spy(mPipTouchHandler.getMotionHelper()); mPipResizeGestureHandler = Mockito.spy(mPipTouchHandler.getPipResizeGestureHandler()); mPipTouchHandler.setPipMotionHelper(mMotionHelper); diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java index 548da8e1f2aa..35620329467b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java @@ -384,7 +384,7 @@ public class PowerUITest extends SysuiTestCase { mPowerUI.mSevereWarningShownThisChargeCycle = false; BatteryStateSnapshotWrapper state = new BatteryStateSnapshotWrapper(); - // sanity check to make sure we can show for a valid config + // readiness check to make sure we can show for a valid config state.mBatteryLevel = 10; state.mTimeRemainingMillis = Duration.ofHours(2).toMillis(); boolean shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); @@ -449,7 +449,7 @@ public class PowerUITest extends SysuiTestCase { mPowerUI.mSevereWarningShownThisChargeCycle = false; BatteryStateSnapshotWrapper state = new BatteryStateSnapshotWrapper(); - // sanity check to make sure we can show for a valid config + // readiness check to make sure we can show for a valid config state.mBatteryLevel = 1; state.mTimeRemainingMillis = Duration.ofMinutes(1).toMillis(); boolean shouldShow = mPowerUI.shouldShowHybridWarning(state.get()); @@ -572,7 +572,7 @@ public class PowerUITest extends SysuiTestCase { state.mIsHybrid = false; BatteryStateSnapshot lastState = state.get(); - // sanity check to make sure we can show for a valid config + // readiness check to make sure we can show for a valid config state.mBatteryLevel = 10; state.mBucket = -1; boolean shouldShow = mPowerUI.shouldShowLowBatteryWarning(state.get(), lastState); diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt new file mode 100644 index 000000000000..4ba29e6e02a6 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt @@ -0,0 +1,219 @@ +/* + * 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.privacy + +import android.os.UserManager +import android.provider.DeviceConfig +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.internal.config.sysui.SystemUiDeviceConfigFlags +import com.android.systemui.SysuiTestCase +import com.android.systemui.appops.AppOpsController +import com.android.systemui.broadcast.BroadcastDispatcher +import com.android.systemui.dump.DumpManager +import com.android.systemui.util.DeviceConfigProxy +import com.android.systemui.util.DeviceConfigProxyFake +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.time.FakeSystemClock +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.anyBoolean +import org.mockito.Mockito.atLeastOnce +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class PrivacyItemControllerFlagsTest : SysuiTestCase() { + companion object { + fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture() + fun <T> eq(value: T): T = Mockito.eq(value) ?: value + fun <T> any(): T = Mockito.any<T>() + + private const val ALL_INDICATORS = + SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED + private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED + } + + @Mock + private lateinit var appOpsController: AppOpsController + @Mock + private lateinit var callback: PrivacyItemController.Callback + @Mock + private lateinit var userManager: UserManager + @Mock + private lateinit var broadcastDispatcher: BroadcastDispatcher + @Mock + private lateinit var dumpManager: DumpManager + + private lateinit var privacyItemController: PrivacyItemController + private lateinit var executor: FakeExecutor + private lateinit var deviceConfigProxy: DeviceConfigProxy + + fun PrivacyItemController(): PrivacyItemController { + return PrivacyItemController( + appOpsController, + executor, + executor, + broadcastDispatcher, + deviceConfigProxy, + userManager, + dumpManager + ) + } + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + executor = FakeExecutor(FakeSystemClock()) + deviceConfigProxy = DeviceConfigProxyFake() + + privacyItemController = PrivacyItemController() + privacyItemController.addCallback(callback) + + executor.runAllReady() + } + + @Test + fun testNotListeningByDefault() { + assertFalse(privacyItemController.allIndicatorsAvailable) + assertFalse(privacyItemController.micCameraAvailable) + + verify(appOpsController, never()).addCallback(any(), any()) + } + + @Test + fun testMicCameraChanged() { + changeMicCamera(true) + executor.runAllReady() + + verify(callback).onFlagMicCameraChanged(true) + verify(callback, never()).onFlagAllChanged(anyBoolean()) + + assertTrue(privacyItemController.micCameraAvailable) + assertFalse(privacyItemController.allIndicatorsAvailable) + } + + @Test + fun testAllChanged() { + changeAll(true) + executor.runAllReady() + + verify(callback).onFlagAllChanged(true) + verify(callback, never()).onFlagMicCameraChanged(anyBoolean()) + + assertTrue(privacyItemController.allIndicatorsAvailable) + assertFalse(privacyItemController.micCameraAvailable) + } + + @Test + fun testBothChanged() { + changeAll(true) + changeMicCamera(true) + executor.runAllReady() + + verify(callback, atLeastOnce()).onFlagAllChanged(true) + verify(callback, atLeastOnce()).onFlagMicCameraChanged(true) + + assertTrue(privacyItemController.allIndicatorsAvailable) + assertTrue(privacyItemController.micCameraAvailable) + } + + @Test + fun testAll_listeningToAll() { + changeAll(true) + executor.runAllReady() + + verify(appOpsController).addCallback(eq(PrivacyItemController.OPS), any()) + } + + @Test + fun testMicCamera_listening() { + changeMicCamera(true) + executor.runAllReady() + + verify(appOpsController).addCallback(eq(PrivacyItemController.OPS), any()) + } + + @Test + fun testAll_listening() { + changeAll(true) + executor.runAllReady() + + verify(appOpsController).addCallback(eq(PrivacyItemController.OPS), any()) + } + + @Test + fun testAllFalse_notListening() { + changeAll(true) + executor.runAllReady() + changeAll(false) + executor.runAllReady() + + verify(appOpsController).removeCallback(any(), any()) + } + + @Test + fun testSomeListening_stillListening() { + changeAll(true) + changeMicCamera(true) + executor.runAllReady() + changeAll(false) + executor.runAllReady() + + verify(appOpsController, never()).removeCallback(any(), any()) + } + + @Test + fun testAllDeleted_stopListening() { + changeAll(true) + executor.runAllReady() + changeAll(null) + executor.runAllReady() + + verify(appOpsController).removeCallback(any(), any()) + } + + @Test + fun testMicDeleted_stopListening() { + changeMicCamera(true) + executor.runAllReady() + changeMicCamera(null) + executor.runAllReady() + + verify(appOpsController).removeCallback(any(), any()) + } + + private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value) + private fun changeAll(value: Boolean?) = changeProperty(ALL_INDICATORS, value) + + private fun changeProperty(name: String, value: Boolean?) { + deviceConfigProxy.setProperty( + DeviceConfig.NAMESPACE_PRIVACY, + name, + value?.toString(), + false + ) + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt index dddc35072315..fb42baaa0cb5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.privacy import android.app.ActivityManager import android.app.AppOpsManager -import android.content.Context import android.content.Intent import android.content.pm.UserInfo import android.os.UserHandle @@ -69,10 +68,11 @@ class PrivacyItemControllerTest : SysuiTestCase() { companion object { val CURRENT_USER_ID = ActivityManager.getCurrentUser() val TEST_UID = CURRENT_USER_ID * UserHandle.PER_USER_RANGE - const val SYSTEM_UID = 1000 const val TEST_PACKAGE_NAME = "test" - const val DEVICE_SERVICES_STRING = "Device services" - const val TAG = "PrivacyItemControllerTest" + + private const val ALL_INDICATORS = + SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED + private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture() fun <T> eq(value: T): T = Mockito.eq(value) ?: value fun <T> any(): T = Mockito.any<T>() @@ -97,9 +97,8 @@ class PrivacyItemControllerTest : SysuiTestCase() { private lateinit var executor: FakeExecutor private lateinit var deviceConfigProxy: DeviceConfigProxy - fun PrivacyItemController(context: Context): PrivacyItemController { + fun PrivacyItemController(): PrivacyItemController { return PrivacyItemController( - context, appOpsController, executor, executor, @@ -116,11 +115,8 @@ class PrivacyItemControllerTest : SysuiTestCase() { executor = FakeExecutor(FakeSystemClock()) deviceConfigProxy = DeviceConfigProxyFake() - appOpsController = mDependency.injectMockDependency(AppOpsController::class.java) - - deviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_PRIVACY, - SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, - "true", false) + // Listen to everything by default + changeAll(true) doReturn(listOf(object : UserInfo() { init { @@ -128,7 +124,7 @@ class PrivacyItemControllerTest : SysuiTestCase() { } })).`when`(userManager).getProfiles(anyInt()) - privacyItemController = PrivacyItemController(mContext) + privacyItemController = PrivacyItemController() } @Test @@ -276,15 +272,59 @@ class PrivacyItemControllerTest : SysuiTestCase() { @Test fun testNotListeningWhenIndicatorsDisabled() { - deviceConfigProxy.setProperty( - DeviceConfig.NAMESPACE_PRIVACY, - SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, - "false", - false - ) + changeAll(false) privacyItemController.addCallback(callback) executor.runAllReady() verify(appOpsController, never()).addCallback(eq(PrivacyItemController.OPS), any()) } + + @Test + fun testNotSendingLocationWhenOnlyMicCamera() { + changeAll(false) + changeMicCamera(true) + executor.runAllReady() + + doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 0), + AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, "", 0))) + .`when`(appOpsController).getActiveAppOpsForUser(anyInt()) + + privacyItemController.addCallback(callback) + executor.runAllReady() + + verify(callback).onPrivacyItemsChanged(capture(argCaptor)) + + assertEquals(1, argCaptor.value.size) + assertEquals(PrivacyType.TYPE_CAMERA, argCaptor.value[0].privacyType) + } + + @Test + fun testNotUpdated_LocationChangeWhenOnlyMicCamera() { + doReturn(listOf(AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, "", 0))) + .`when`(appOpsController).getActiveAppOpsForUser(anyInt()) + + privacyItemController.addCallback(callback) + changeAll(false) + changeMicCamera(true) + executor.runAllReady() + reset(callback) // Clean callback + + verify(appOpsController).addCallback(any(), capture(argCaptorCallback)) + argCaptorCallback.value.onActiveStateChanged( + AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true) + + verify(callback, never()).onPrivacyItemsChanged(any()) + } + + private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value) + private fun changeAll(value: Boolean?) = changeProperty(ALL_INDICATORS, value) + + private fun changeProperty(name: String, value: Boolean?) { + deviceConfigProxy.setProperty( + DeviceConfig.NAMESPACE_PRIVACY, + name, + value?.toString(), + false + ) + } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java index cccb65d11228..61a0d6c17eed 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java @@ -244,6 +244,8 @@ public class QSTileImplTest extends SysuiTestCase { assertNotEquals(DESTROYED, mTile.getLifecycle().getCurrentState()); mTile.handleDestroy(); + mTestableLooper.processAllMessages(); + assertEquals(DESTROYED, mTile.getLifecycle().getCurrentState()); } @@ -298,6 +300,25 @@ public class QSTileImplTest extends SysuiTestCase { assertNotEquals(DESTROYED, mTile.getLifecycle().getCurrentState()); } + @Test + public void testRefreshStateAfterDestroyedDoesNotCrash() { + mTile.destroy(); + mTile.refreshState(); + + mTestableLooper.processAllMessages(); + } + + @Test + public void testSetListeningAfterDestroyedDoesNotCrash() { + Object o = new Object(); + mTile.destroy(); + + mTile.setListening(o, true); + mTile.setListening(o, false); + + mTestableLooper.processAllMessages(); + } + private void assertEvent(UiEventLogger.UiEventEnum eventType, UiEventLoggerFake.FakeUiEvent fakeEvent) { assertEquals(eventType.getId(), fakeEvent.eventId); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java index b5060ee416f7..1fb28f0878bd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarContextTest.java @@ -65,9 +65,9 @@ public class NavigationBarContextTest extends SysuiTestCase { mDependency.injectMockDependency(AssistManager.class); mGroup = new ContextualButtonGroup(GROUP_ID); - mBtn0 = new ContextualButton(BUTTON_0_ID, ICON_RES_ID); - mBtn1 = new ContextualButton(BUTTON_1_ID, ICON_RES_ID); - mBtn2 = new ContextualButton(BUTTON_2_ID, ICON_RES_ID); + mBtn0 = new ContextualButton(BUTTON_0_ID, mContext, ICON_RES_ID); + mBtn1 = new ContextualButton(BUTTON_1_ID, mContext, ICON_RES_ID); + mBtn2 = new ContextualButton(BUTTON_2_ID, mContext, ICON_RES_ID); // Order of adding buttons to group determines the priority, ascending priority order mGroup.addButton(mBtn0); diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto index b0e401bdda8a..3f712dd1492f 100644 --- a/proto/src/metrics_constants/metrics_constants.proto +++ b/proto/src/metrics_constants/metrics_constants.proto @@ -3706,33 +3706,40 @@ message MetricsEvent { // OS: O BACKUP_SETTINGS = 818; + // DEPRECATED: The metrics has been migrated to UiEvent per go/uievent. // ACTION: Picture-in-picture was explicitly entered for an activity // VALUE: true if it was entered while hiding as a result of moving to // another task, false otherwise - ACTION_PICTURE_IN_PICTURE_ENTERED = 819; + ACTION_PICTURE_IN_PICTURE_ENTERED = 819 [deprecated=true]; + // DEPRECATED: The metrics has been migrated to UiEvent per go/uievent. // ACTION: The activity currently in picture-in-picture was expanded back to fullscreen // PACKAGE: The package name of the activity that was expanded back to fullscreen - ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN = 820; + ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN = 820 [deprecated=true]; + // DEPRECATED: The metrics no longer used after migration to UiEvent per go/uievent. // ACTION: The activity currently in picture-in-picture was minimized // VALUE: True if the PiP was minimized, false otherwise - ACTION_PICTURE_IN_PICTURE_MINIMIZED = 821; + ACTION_PICTURE_IN_PICTURE_MINIMIZED = 821 [deprecated=true]; + // DEPRECATED: The metrics has been migrated to UiEvent per go/uievent. // ACTION: Picture-in-picture was dismissed via the dismiss button // VALUE: 0 if dismissed by tap, 1 if dismissed by drag - ACTION_PICTURE_IN_PICTURE_DISMISSED = 822; + ACTION_PICTURE_IN_PICTURE_DISMISSED = 822 [deprecated=true]; - // ACTION: The visibility of the picture-in-picture meny + // DEPRECATED: The metrics has been migrated to UiEvent per go/uievent. + // ACTION: The visibility of the picture-in-picture menu // VALUE: Whether or not the menu is visible - ACTION_PICTURE_IN_PICTURE_MENU = 823; + ACTION_PICTURE_IN_PICTURE_MENU = 823 [deprecated=true]; + // DEPRECATED: The metrics has been migrated to UiEvent per go/uievent. // Enclosing category for group of PICTURE_IN_PICTURE_ASPECT_RATIO_FOO events, // logged when the aspect ratio changes - ACTION_PICTURE_IN_PICTURE_ASPECT_RATIO_CHANGED = 824; + ACTION_PICTURE_IN_PICTURE_ASPECT_RATIO_CHANGED = 824 [deprecated=true]; + // DEPRECATED: The metrics no longer used after migration to UiEvent per go/uievent. // The current aspect ratio of the PiP, logged when it changes. - PICTURE_IN_PICTURE_ASPECT_RATIO = 825; + PICTURE_IN_PICTURE_ASPECT_RATIO = 825 [deprecated=true]; // FIELD - length in dp of ACTION_LS_* gestures, or zero if not applicable // CATEGORY: GLOBAL_SYSTEM_UI diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index 9ad808a685a6..a167ab16f944 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -148,6 +148,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ private boolean mRequestMultiFingerGestures; + private boolean mRequestTwoFingerPassthrough; + boolean mRequestFilterKeyEvents; boolean mRetrieveInteractiveWindows; @@ -325,8 +327,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ & AccessibilityServiceInfo.FLAG_SERVICE_HANDLES_DOUBLE_TAP) != 0; mRequestMultiFingerGestures = (info.flags & AccessibilityServiceInfo.FLAG_REQUEST_MULTI_FINGER_GESTURES) != 0; - mRequestFilterKeyEvents = (info.flags - & AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS) != 0; + mRequestTwoFingerPassthrough = + (info.flags & AccessibilityServiceInfo.FLAG_REQUEST_2_FINGER_PASSTHROUGH) != 0; + mRequestFilterKeyEvents = + (info.flags & AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS) != 0; mRetrieveInteractiveWindows = (info.flags & AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS) != 0; mCaptureFingerprintGestures = (info.flags @@ -1772,6 +1776,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ return mRequestMultiFingerGestures; } + public boolean isTwoFingerPassthroughEnabled() { + return mRequestTwoFingerPassthrough; + } + @Override public void setGestureDetectionPassthroughRegion(int displayId, Region region) { mSystemSupport.setGestureDetectionPassthroughRegion(displayId, region); diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java index 8b40f610b4b3..cd9ab8db0854 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java @@ -118,6 +118,13 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo */ static final int FLAG_REQUEST_MULTI_FINGER_GESTURES = 0x00000100; + /** + * Flag for enabling multi-finger gestures. + * + * @see #setUserAndEnabledFeatures(int, int) + */ + static final int FLAG_REQUEST_2_FINGER_PASSTHROUGH = 0x00000200; + static final int FEATURES_AFFECTING_MOTION_EVENTS = FLAG_FEATURE_INJECT_MOTION_EVENTS | FLAG_FEATURE_AUTOCLICK @@ -125,7 +132,8 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo | FLAG_FEATURE_SCREEN_MAGNIFIER | FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER | FLAG_SERVICE_HANDLES_DOUBLE_TAP - | FLAG_REQUEST_MULTI_FINGER_GESTURES; + | FLAG_REQUEST_MULTI_FINGER_GESTURES + | FLAG_REQUEST_2_FINGER_PASSTHROUGH; private final Context mContext; @@ -421,6 +429,9 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo if ((mEnabledFeatures & FLAG_REQUEST_MULTI_FINGER_GESTURES) != 0) { explorer.setMultiFingerGesturesEnabled(true); } + if ((mEnabledFeatures & FLAG_REQUEST_2_FINGER_PASSTHROUGH) != 0) { + explorer.setTwoFingerPassthroughEnabled(true); + } addFirstEventHandler(displayId, explorer); mTouchExplorer.put(displayId, explorer); } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index b1340228ffa0..833aeecc4c47 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -1837,6 +1837,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (userState.isMultiFingerGesturesEnabledLocked()) { flags |= AccessibilityInputFilter.FLAG_REQUEST_MULTI_FINGER_GESTURES; } + if (userState.isTwoFingerPassthroughEnabledLocked()) { + flags |= AccessibilityInputFilter.FLAG_REQUEST_2_FINGER_PASSTHROUGH; + } } if (userState.isFilterKeyEventsEnabledLocked()) { flags |= AccessibilityInputFilter.FLAG_FEATURE_FILTER_KEY_EVENTS; @@ -2120,6 +2123,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub boolean touchExplorationEnabled = mUiAutomationManager.isTouchExplorationEnabledLocked(); boolean serviceHandlesDoubleTapEnabled = false; boolean requestMultiFingerGestures = false; + boolean requestTwoFingerPassthrough = false; final int serviceCount = userState.mBoundServices.size(); for (int i = 0; i < serviceCount; i++) { AccessibilityServiceConnection service = userState.mBoundServices.get(i); @@ -2127,6 +2131,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub touchExplorationEnabled = true; serviceHandlesDoubleTapEnabled = service.isServiceHandlesDoubleTapEnabled(); requestMultiFingerGestures = service.isMultiFingerGesturesEnabled(); + requestTwoFingerPassthrough = service.isTwoFingerPassthroughEnabled(); break; } } @@ -2143,6 +2148,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } userState.setServiceHandlesDoubleTapLocked(serviceHandlesDoubleTapEnabled); userState.setMultiFingerGesturesLocked(requestMultiFingerGestures); + userState.setTwoFingerPassthroughLocked(requestTwoFingerPassthrough); } private boolean readAccessibilityShortcutKeySettingLocked(AccessibilityUserState userState) { diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java index f865aa7d6e37..4c9e44403026 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java @@ -110,6 +110,7 @@ class AccessibilityUserState { private boolean mIsTouchExplorationEnabled; private boolean mServiceHandlesDoubleTap; private boolean mRequestMultiFingerGestures; + private boolean mRequestTwoFingerPassthrough; private int mUserInteractiveUiTimeout; private int mUserNonInteractiveUiTimeout; private int mNonInteractiveUiTimeout = 0; @@ -169,6 +170,7 @@ class AccessibilityUserState { mIsTouchExplorationEnabled = false; mServiceHandlesDoubleTap = false; mRequestMultiFingerGestures = false; + mRequestTwoFingerPassthrough = false; mIsDisplayMagnificationEnabled = false; mIsAutoclickEnabled = false; mUserNonInteractiveUiTimeout = 0; @@ -456,6 +458,8 @@ class AccessibilityUserState { .append(String.valueOf(mServiceHandlesDoubleTap)); pw.append(", requestMultiFingerGestures=") .append(String.valueOf(mRequestMultiFingerGestures)); + pw.append(", requestTwoFingerPassthrough=") + .append(String.valueOf(mRequestTwoFingerPassthrough)); pw.append(", displayMagnificationEnabled=").append(String.valueOf( mIsDisplayMagnificationEnabled)); pw.append(", autoclickEnabled=").append(String.valueOf(mIsAutoclickEnabled)); @@ -790,6 +794,14 @@ class AccessibilityUserState { public void setMultiFingerGesturesLocked(boolean enabled) { mRequestMultiFingerGestures = enabled; } + public boolean isTwoFingerPassthroughEnabledLocked() { + return mRequestTwoFingerPassthrough; + } + + public void setTwoFingerPassthroughLocked(boolean enabled) { + mRequestTwoFingerPassthrough = enabled; + } + public int getUserInteractiveUiTimeoutLocked() { return mUserInteractiveUiTimeout; diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java index e9c70c60a322..8604fe7a7359 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java @@ -94,8 +94,13 @@ class GestureManifold implements GestureMatcher.StateChangeListener { private boolean mServiceHandlesDoubleTap = false; // Whether multi-finger gestures are enabled. boolean mMultiFingerGesturesEnabled; + // Whether the two-finger passthrough is enabled when multi-finger gestures are enabled. + private boolean mTwoFingerPassthroughEnabled; // A list of all the multi-finger gestures, for easy adding and removal. private final List<GestureMatcher> mMultiFingerGestures = new ArrayList<>(); + // A list of two-finger swipes, for easy adding and removal when turning on or off two-finger + // passthrough. + private final List<GestureMatcher> mTwoFingerSwipes = new ArrayList<>(); // Shared state information. private TouchState mState; @@ -105,6 +110,7 @@ class GestureManifold implements GestureMatcher.StateChangeListener { mListener = listener; mState = state; mMultiFingerGesturesEnabled = false; + mTwoFingerPassthroughEnabled = false; // Set up gestures. // Start with double tap. mGestures.add(new MultiTap(context, 2, GESTURE_DOUBLE_TAP, this)); @@ -161,14 +167,14 @@ class GestureManifold implements GestureMatcher.StateChangeListener { mMultiFingerGestures.add( new MultiFingerMultiTap(mContext, 4, 3, GESTURE_4_FINGER_TRIPLE_TAP, this)); // Two-finger swipes. - mMultiFingerGestures.add( + mTwoFingerSwipes.add( new MultiFingerSwipe(context, 2, DOWN, GESTURE_2_FINGER_SWIPE_DOWN, this)); - mMultiFingerGestures.add( + mTwoFingerSwipes.add( new MultiFingerSwipe(context, 2, LEFT, GESTURE_2_FINGER_SWIPE_LEFT, this)); - mMultiFingerGestures.add( + mTwoFingerSwipes.add( new MultiFingerSwipe(context, 2, RIGHT, GESTURE_2_FINGER_SWIPE_RIGHT, this)); - mMultiFingerGestures.add( - new MultiFingerSwipe(context, 2, UP, GESTURE_2_FINGER_SWIPE_UP, this)); + mTwoFingerSwipes.add(new MultiFingerSwipe(context, 2, UP, GESTURE_2_FINGER_SWIPE_UP, this)); + mMultiFingerGestures.addAll(mTwoFingerSwipes); // Three-finger swipes. mMultiFingerGestures.add( new MultiFingerSwipe(context, 3, DOWN, GESTURE_3_FINGER_SWIPE_DOWN, this)); @@ -360,6 +366,25 @@ class GestureManifold implements GestureMatcher.StateChangeListener { } } + public boolean isTwoFingerPassthroughEnabled() { + return mTwoFingerPassthroughEnabled; + } + + public void setTwoFingerPassthroughEnabled(boolean mode) { + if (mTwoFingerPassthroughEnabled != mode) { + mTwoFingerPassthroughEnabled = mode; + if (!mode) { + mMultiFingerGestures.addAll(mTwoFingerSwipes); + if (mMultiFingerGesturesEnabled) { + mGestures.addAll(mTwoFingerSwipes); + } + } else { + mMultiFingerGestures.removeAll(mTwoFingerSwipes); + mGestures.removeAll(mTwoFingerSwipes); + } + } + } + public void setServiceHandlesDoubleTap(boolean mode) { mServiceHandlesDoubleTap = mode; } diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java index 696702fad730..e1baefed4425 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java @@ -41,6 +41,7 @@ import android.annotation.NonNull; import android.content.Context; import android.graphics.Region; import android.os.Handler; +import android.util.DisplayMetrics; import android.util.Slog; import android.view.InputDevice; import android.view.MotionEvent; @@ -91,12 +92,21 @@ public class TouchExplorer extends BaseEventStreamTransformation // The timeout after which we are no longer trying to detect a gesture. private static final int EXIT_GESTURE_DETECTION_TIMEOUT = 2000; + // The height of the top and bottom edges for edge-swipes. + // For now this is only used to allow three-finger edge-swipes from the bottom. + private static final float EDGE_SWIPE_HEIGHT_CM = 0.25f; + + // The calculated edge height for the top and bottom edges. + private final float mEdgeSwipeHeightPixels; // Timeout before trying to decide what the user is trying to do. private final int mDetermineUserIntentTimeout; // Slop between the first and second tap to be a double tap. private final int mDoubleTapSlop; + // Slop to move before being considered a move rather than a tap. + private final int mTouchSlop; + // The current state of the touch explorer. private TouchState mState; @@ -174,6 +184,9 @@ public class TouchExplorer extends BaseEventStreamTransformation mDispatcher = new EventDispatcher(context, mAms, super.getNext(), mState); mDetermineUserIntentTimeout = ViewConfiguration.getDoubleTapTimeout(); mDoubleTapSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop(); + mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); + DisplayMetrics metrics = mContext.getResources().getDisplayMetrics(); + mEdgeSwipeHeightPixels = metrics.ydpi / GestureUtils.CM_PER_INCH * EDGE_SWIPE_HEIGHT_CM; mHandler = mainHandler; mExitGestureDetectionModeDelayed = new ExitGestureDetectionModeDelayed(); mSendHoverEnterAndMoveDelayed = new SendHoverEnterAndMoveDelayed(); @@ -219,16 +232,10 @@ public class TouchExplorer extends BaseEventStreamTransformation if (mState.isTouchExploring()) { // If a touch exploration gesture is in progress send events for its end. sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); - } else if (mState.isDragging()) { - mDraggingPointerId = INVALID_POINTER_ID; - // Send exit to all pointers that we have delivered. - mDispatcher.sendUpForInjectedDownPointers(event, policyFlags); - } else if (mState.isDelegating()) { - // Send exit to all pointers that we have delivered. - mDispatcher.sendUpForInjectedDownPointers(event, policyFlags); - } else if (mState.isGestureDetecting()) { - // No state specific cleanup required. } + mDraggingPointerId = INVALID_POINTER_ID; + // Send exit to any pointers that we have delivered as part of delegating or dragging. + mDispatcher.sendUpForInjectedDownPointers(event, policyFlags); // Remove all pending callbacks. mSendHoverEnterAndMoveDelayed.cancel(); mSendHoverExitDelayed.cancel(); @@ -554,7 +561,26 @@ public class TouchExplorer extends BaseEventStreamTransformation // stream consistent. sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); } + if (mGestureDetector.isMultiFingerGesturesEnabled() + && mGestureDetector.isTwoFingerPassthroughEnabled()) { + if (event.getPointerCount() == 3) { + boolean isOnBottomEdge = false; + // If three fingers go down on the bottom edge of the screen, delegate immediately. + final long screenHeight = mContext.getResources().getDisplayMetrics().heightPixels; + for (int i = 0; i < TouchState.MAX_POINTER_COUNT; ++i) { + if (mReceivedPointerTracker.getReceivedPointerDownY(i) + > (screenHeight - mEdgeSwipeHeightPixels)) { + isOnBottomEdge = true; + } + } + if (isOnBottomEdge) { + mState.startDelegating(); + mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags); + } + } + } } + /** * Handles ACTION_MOVE while in the touch interacting state. This is where transitions to * delegating and dragging states are handled. @@ -563,7 +589,7 @@ public class TouchExplorer extends BaseEventStreamTransformation MotionEvent event, MotionEvent rawEvent, int policyFlags) { final int pointerId = mReceivedPointerTracker.getPrimaryPointerId(); final int pointerIndex = event.findPointerIndex(pointerId); - final int pointerIdBits = (1 << pointerId); + int pointerIdBits = (1 << pointerId); switch (event.getPointerCount()) { case 1: // We have not started sending events since we try to @@ -574,12 +600,26 @@ public class TouchExplorer extends BaseEventStreamTransformation } break; case 2: - if (mGestureDetector.isMultiFingerGesturesEnabled()) { + if (mGestureDetector.isMultiFingerGesturesEnabled() + && !mGestureDetector.isTwoFingerPassthroughEnabled()) { return; } // Make sure we don't have any pending transitions to touch exploration mSendHoverEnterAndMoveDelayed.cancel(); mSendHoverExitDelayed.cancel(); + if (mGestureDetector.isMultiFingerGesturesEnabled() + && mGestureDetector.isTwoFingerPassthroughEnabled()) { + final float deltaX = + mReceivedPointerTracker.getReceivedPointerDownX(pointerId) + - rawEvent.getX(pointerIndex); + final float deltaY = + mReceivedPointerTracker.getReceivedPointerDownY(pointerId) + - rawEvent.getY(pointerIndex); + final double moveDelta = Math.hypot(deltaX, deltaY); + if (moveDelta < mTouchSlop) { + return; + } + } // More than one pointer so the user is not touch exploring // and now we have to decide whether to delegate or drag. // Remove move history before send injected non-move events @@ -588,8 +628,8 @@ public class TouchExplorer extends BaseEventStreamTransformation // Two pointers moving in the same direction within // a given distance perform a drag. mState.startDragging(); - mDraggingPointerId = pointerId; adjustEventLocationForDrag(event); + pointerIdBits = 1 << mDraggingPointerId; event.setEdgeFlags(mReceivedPointerTracker.getLastReceivedDownEdgeFlags()); mDispatcher.sendMotionEvent( event, ACTION_DOWN, rawEvent, pointerIdBits, policyFlags); @@ -648,7 +688,8 @@ public class TouchExplorer extends BaseEventStreamTransformation event, ACTION_HOVER_MOVE, rawEvent, pointerIdBits, policyFlags); break; case 2: - if (mGestureDetector.isMultiFingerGesturesEnabled()) { + if (mGestureDetector.isMultiFingerGesturesEnabled() + && !mGestureDetector.isTwoFingerPassthroughEnabled()) { return; } if (mSendHoverEnterAndMoveDelayed.isPending()) { @@ -703,7 +744,8 @@ public class TouchExplorer extends BaseEventStreamTransformation */ private void handleMotionEventStateDragging( MotionEvent event, MotionEvent rawEvent, int policyFlags) { - if (mGestureDetector.isMultiFingerGesturesEnabled()) { + if (mGestureDetector.isMultiFingerGesturesEnabled() + && !mGestureDetector.isTwoFingerPassthroughEnabled()) { // Multi-finger gestures conflict with this functionality. return; } @@ -756,6 +798,7 @@ public class TouchExplorer extends BaseEventStreamTransformation // The two pointers are moving either in different directions or // no close enough => delegate the gesture to the view hierarchy. mState.startDelegating(); + mDraggingPointerId = INVALID_POINTER_ID; // Remove move history before send injected non-move events event = MotionEvent.obtainNoHistory(event); // Send an event to the end of the drag gesture. @@ -767,6 +810,7 @@ public class TouchExplorer extends BaseEventStreamTransformation break; default: mState.startDelegating(); + mDraggingPointerId = INVALID_POINTER_ID; event = MotionEvent.obtainNoHistory(event); // Send an event to the end of the drag gesture. mDispatcher.sendMotionEvent( @@ -784,15 +828,16 @@ public class TouchExplorer extends BaseEventStreamTransformation } break; case ACTION_UP: - mAms.onTouchInteractionEnd(); - // Announce the end of a new touch interaction. - mDispatcher.sendAccessibilityEvent(TYPE_TOUCH_INTERACTION_END); if (event.getPointerId(GestureUtils.getActionIndex(event)) == mDraggingPointerId) { mDraggingPointerId = INVALID_POINTER_ID; // Send an event to the end of the drag gesture. mDispatcher.sendMotionEvent( event, ACTION_UP, rawEvent, pointerIdBits, policyFlags); } + mAms.onTouchInteractionEnd(); + // Announce the end of a new touch interaction. + mDispatcher.sendAccessibilityEvent(TYPE_TOUCH_INTERACTION_END); + break; } } @@ -911,21 +956,55 @@ public class TouchExplorer extends BaseEventStreamTransformation } /** - * Adjust the location of an injected event when performing a drag The new location will be in - * between the two fingers touching the screen. + * Adjust the location of an injected event when performing a drag. The location will be the + * location of the finger closest to an edge of the screen. */ private void adjustEventLocationForDrag(MotionEvent event) { - final float firstPtrX = event.getX(0); final float firstPtrY = event.getY(0); + final int firstPtrId = event.getPointerId(0); final float secondPtrX = event.getX(1); final float secondPtrY = event.getY(1); - final int pointerIndex = event.findPointerIndex(mDraggingPointerId); - final float deltaX = - (pointerIndex == 0) ? (secondPtrX - firstPtrX) : (firstPtrX - secondPtrX); - final float deltaY = - (pointerIndex == 0) ? (secondPtrY - firstPtrY) : (firstPtrY - secondPtrY); - event.offsetLocation(deltaX / 2, deltaY / 2); + final int secondPtrId = event.getPointerId(1); + float draggingX; + float draggingY; + if (mDraggingPointerId == INVALID_POINTER_ID) { + // The goal is to use the coordinates of the finger that is closest to its closest edge. + if (getDistanceToClosestEdge(firstPtrX, firstPtrY) + < getDistanceToClosestEdge(secondPtrX, secondPtrY)) { + draggingX = firstPtrX; + draggingY = firstPtrY; + mDraggingPointerId = firstPtrId; + } else { + draggingX = secondPtrX; + draggingY = secondPtrY; + mDraggingPointerId = secondPtrId; + } + } else { + // Just use the coordinates of the dragging pointer. + int pointerIndex = event.findPointerIndex(mDraggingPointerId); + draggingX = event.getX(pointerIndex); + draggingY = event.getY(pointerIndex); + } + event.setLocation(draggingX, draggingY); + } + + private float getDistanceToClosestEdge(float x, float y) { + final long width = mContext.getResources().getDisplayMetrics().widthPixels; + final long height = mContext.getResources().getDisplayMetrics().heightPixels; + float distance = Float.MAX_VALUE; + if (x < (width - x)) { + distance = x; + } else { + distance = width - x; + } + if (distance > y) { + distance = y; + } + if (distance > (height - y)) { + distance = (height - y); + } + return distance; } public TouchState getState() { @@ -954,6 +1033,13 @@ public class TouchExplorer extends BaseEventStreamTransformation mGestureDetector.setMultiFingerGesturesEnabled(enabled); } + /** + * This function turns on and off two-finger passthrough gestures such as drag and pinch when + * multi-finger gestures are enabled. + */ + public void setTwoFingerPassthroughEnabled(boolean enabled) { + mGestureDetector.setTwoFingerPassthroughEnabled(enabled); + } public void setGestureDetectionPassthroughRegion(Region region) { mGestureDetectionPassthroughRegion = region; } diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index 12e6e10d9047..186812bc15c7 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -1239,9 +1239,10 @@ public class BackupManagerService extends IBackupManager.Stub { @Override public IRestoreSession beginRestoreSessionForUser( - int userId, String packageName, String transportID) throws RemoteException { + int userId, String packageName, String transportID, + @OperationType int operationType) throws RemoteException { return isUserReadyForBackup(userId) - ? beginRestoreSession(userId, packageName, transportID) : null; + ? beginRestoreSession(userId, packageName, transportID, operationType) : null; } /** @@ -1250,13 +1251,15 @@ public class BackupManagerService extends IBackupManager.Stub { */ @Nullable public IRestoreSession beginRestoreSession( - @UserIdInt int userId, String packageName, String transportName) { + @UserIdInt int userId, String packageName, String transportName, + @OperationType int operationType) { UserBackupManagerService userBackupManagerService = getServiceForUserIfCallerHasPermission(userId, "beginRestoreSession()"); return userBackupManagerService == null ? null - : userBackupManagerService.beginRestoreSession(packageName, transportName); + : userBackupManagerService.beginRestoreSession(packageName, transportName, + operationType); } @Override diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java index c9b09e31f94b..0855b9d8f675 100644 --- a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java +++ b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java @@ -10,6 +10,7 @@ import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP_ import android.app.ApplicationThreadConstants; import android.app.IBackupAgent; +import android.app.backup.BackupManager; import android.app.backup.FullBackup; import android.app.backup.FullBackupDataOutput; import android.app.backup.IBackupCallback; @@ -146,7 +147,8 @@ public class KeyValueAdbBackupEngine { private IBackupAgent bindToAgent(ApplicationInfo targetApp) { try { return mBackupManagerService.bindToAgentSynchronous(targetApp, - ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL); + ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL, + BackupManager.OperationType.BACKUP); } catch (SecurityException e) { Slog.e(TAG, "error in binding to agent for package " + targetApp.packageName + ". " + e); diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java index 3ab81cbb313f..29235dd1ee72 100644 --- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java @@ -1651,13 +1651,15 @@ public class UserBackupManagerService { /** Fires off a backup agent, blocking until it attaches or times out. */ @Nullable - public IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode) { + public IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode, + @OperationType int operationType) { IBackupAgent agent = null; synchronized (mAgentConnectLock) { mConnecting = true; mConnectedAgent = null; try { - if (mActivityManager.bindBackupAgent(app.packageName, mode, mUserId)) { + if (mActivityManager.bindBackupAgent(app.packageName, mode, mUserId, + operationType)) { Slog.d(TAG, addUserIdToLogMessage(mUserId, "awaiting agent for " + app)); // success; wait for the agent to arrive @@ -3973,7 +3975,8 @@ public class UserBackupManagerService { restoreSet, packageName, token, - listener); + listener, + mScheduledBackupEligibility); mBackupHandler.sendMessage(msg); } catch (Exception e) { // Calling into the transport broke; back off and proceed with the installation. @@ -4002,13 +4005,15 @@ public class UserBackupManagerService { } /** Hand off a restore session. */ - public IRestoreSession beginRestoreSession(String packageName, String transport) { + public IRestoreSession beginRestoreSession(String packageName, String transport, + @OperationType int operationType) { if (DEBUG) { Slog.v( TAG, addUserIdToLogMessage( mUserId, - "beginRestoreSession: pkg=" + packageName + " transport=" + transport)); + "beginRestoreSession: pkg=" + packageName + " transport=" + transport + + "operationType=" + operationType)); } boolean needPermission = true; @@ -4065,7 +4070,8 @@ public class UserBackupManagerService { "Restore session requested but currently running backups")); return null; } - mActiveRestoreSession = new ActiveRestoreSession(this, packageName, transport); + mActiveRestoreSession = new ActiveRestoreSession(this, packageName, transport, + getEligibilityRulesForOperation(operationType)); mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT, mAgentTimeoutParameters.getRestoreAgentTimeoutMillis()); } diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java index 846c6a23d394..fe5497f3eb94 100644 --- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java +++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java @@ -41,6 +41,7 @@ import com.android.server.backup.BackupAgentTimeoutParameters; import com.android.server.backup.BackupRestoreTask; import com.android.server.backup.UserBackupManagerService; import com.android.server.backup.remote.RemoteCall; +import com.android.server.backup.utils.BackupEligibilityRules; import com.android.server.backup.utils.FullBackupUtils; import java.io.File; @@ -64,6 +65,7 @@ public class FullBackupEngine { private final int mOpToken; private final int mTransportFlags; private final BackupAgentTimeoutParameters mAgentTimeoutParameters; + private final BackupEligibilityRules mBackupEligibilityRules; class FullBackupRunner implements Runnable { private final @UserIdInt int mUserId; @@ -190,7 +192,8 @@ public class FullBackupEngine { BackupRestoreTask timeoutMonitor, long quota, int opToken, - int transportFlags) { + int transportFlags, + BackupEligibilityRules backupEligibilityRules) { this.backupManagerService = backupManagerService; mOutput = output; mPreflightHook = preflightHook; @@ -204,6 +207,7 @@ public class FullBackupEngine { Objects.requireNonNull( backupManagerService.getAgentTimeoutParameters(), "Timeout parameters cannot be null"); + mBackupEligibilityRules = backupEligibilityRules; } public int preflightCheck() throws RemoteException { @@ -302,7 +306,8 @@ public class FullBackupEngine { } mAgent = backupManagerService.bindToAgentSynchronous( - mPkg.applicationInfo, ApplicationThreadConstants.BACKUP_MODE_FULL); + mPkg.applicationInfo, ApplicationThreadConstants.BACKUP_MODE_FULL, + mBackupEligibilityRules.getOperationType()); } return mAgent != null; } diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java index 0a117746ea3f..448e0860b88d 100644 --- a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java +++ b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java @@ -421,7 +421,8 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor this, Long.MAX_VALUE, mCurrentOpToken, - /*transportFlags=*/ 0); + /*transportFlags=*/ 0, + mBackupEligibilityRules); sendOnBackupPackage(isSharedStorage ? "Shared storage" : pkg.packageName); // Don't need to check preflight result as there is no preflight hook. diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java index 1fa88920ca74..a4d47d492451 100644 --- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java +++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java @@ -860,7 +860,8 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba this, mQuota, mCurrentOpToken, - mTransportFlags); + mTransportFlags, + mBackupEligibilityRules); try { try { if (!mIsCancelled) { diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java index 1bb434950563..100dbae9f01d 100644 --- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java +++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java @@ -305,8 +305,7 @@ public class BackupHandler extends Handler { params.isSystemRestore, params.filterSet, params.listener, - backupManagerService.getEligibilityRulesForOperation( - OperationType.BACKUP)); + params.backupEligibilityRules); synchronized (backupManagerService.getPendingRestores()) { if (backupManagerService.isRestoreInProgress()) { diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java index 6124171c7a0e..7267cdf8539c 100644 --- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java +++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java @@ -731,7 +731,8 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { try { agent = mBackupManagerService.bindToAgentSynchronous( - packageInfo.applicationInfo, BACKUP_MODE_INCREMENTAL); + packageInfo.applicationInfo, BACKUP_MODE_INCREMENTAL, + mBackupEligibilityRules.getOperationType()); if (agent == null) { mReporter.onAgentError(packageName); throw AgentException.transitory(); diff --git a/services/backup/java/com/android/server/backup/params/RestoreParams.java b/services/backup/java/com/android/server/backup/params/RestoreParams.java index a6fea6cc75a0..a08a1f8d5387 100644 --- a/services/backup/java/com/android/server/backup/params/RestoreParams.java +++ b/services/backup/java/com/android/server/backup/params/RestoreParams.java @@ -23,6 +23,7 @@ import android.content.pm.PackageInfo; import com.android.server.backup.internal.OnTaskFinishedListener; import com.android.server.backup.transport.TransportClient; +import com.android.server.backup.utils.BackupEligibilityRules; import java.util.Map; import java.util.Set; @@ -37,6 +38,7 @@ public class RestoreParams { public final boolean isSystemRestore; @Nullable public final String[] filterSet; public final OnTaskFinishedListener listener; + public final BackupEligibilityRules backupEligibilityRules; /** * No kill after restore. @@ -47,7 +49,8 @@ public class RestoreParams { IBackupManagerMonitor monitor, long token, PackageInfo packageInfo, - OnTaskFinishedListener listener) { + OnTaskFinishedListener listener, + BackupEligibilityRules eligibilityRules) { return new RestoreParams( transportClient, observer, @@ -57,7 +60,8 @@ public class RestoreParams { /* pmToken */ 0, /* isSystemRestore */ false, /* filterSet */ null, - listener); + listener, + eligibilityRules); } /** @@ -70,7 +74,8 @@ public class RestoreParams { long token, String packageName, int pmToken, - OnTaskFinishedListener listener) { + OnTaskFinishedListener listener, + BackupEligibilityRules backupEligibilityRules) { String[] filterSet = {packageName}; return new RestoreParams( transportClient, @@ -81,7 +86,8 @@ public class RestoreParams { pmToken, /* isSystemRestore */ false, filterSet, - listener); + listener, + backupEligibilityRules); } /** @@ -92,7 +98,8 @@ public class RestoreParams { IRestoreObserver observer, IBackupManagerMonitor monitor, long token, - OnTaskFinishedListener listener) { + OnTaskFinishedListener listener, + BackupEligibilityRules backupEligibilityRules) { return new RestoreParams( transportClient, observer, @@ -102,7 +109,8 @@ public class RestoreParams { /* pmToken */ 0, /* isSystemRestore */ true, /* filterSet */ null, - listener); + listener, + backupEligibilityRules); } /** @@ -115,7 +123,8 @@ public class RestoreParams { long token, String[] filterSet, boolean isSystemRestore, - OnTaskFinishedListener listener) { + OnTaskFinishedListener listener, + BackupEligibilityRules backupEligibilityRules) { return new RestoreParams( transportClient, observer, @@ -125,7 +134,8 @@ public class RestoreParams { /* pmToken */ 0, isSystemRestore, filterSet, - listener); + listener, + backupEligibilityRules); } private RestoreParams( @@ -137,7 +147,8 @@ public class RestoreParams { int pmToken, boolean isSystemRestore, @Nullable String[] filterSet, - OnTaskFinishedListener listener) { + OnTaskFinishedListener listener, + BackupEligibilityRules backupEligibilityRules) { this.transportClient = transportClient; this.observer = observer; this.monitor = monitor; @@ -147,5 +158,6 @@ public class RestoreParams { this.isSystemRestore = isSystemRestore; this.filterSet = filterSet; this.listener = listener; + this.backupEligibilityRules = backupEligibilityRules; } } diff --git a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java index 5a57cdc39402..3102b5f4a04d 100644 --- a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java +++ b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java @@ -42,6 +42,7 @@ import com.android.server.backup.internal.OnTaskFinishedListener; import com.android.server.backup.params.RestoreGetSetsParams; import com.android.server.backup.params.RestoreParams; import com.android.server.backup.transport.TransportClient; +import com.android.server.backup.utils.BackupEligibilityRules; import java.util.function.BiFunction; @@ -55,6 +56,7 @@ public class ActiveRestoreSession extends IRestoreSession.Stub { private final String mTransportName; private final UserBackupManagerService mBackupManagerService; private final int mUserId; + private final BackupEligibilityRules mBackupEligibilityRules; @Nullable private final String mPackageName; public RestoreSet[] mRestoreSets = null; boolean mEnded = false; @@ -63,12 +65,14 @@ public class ActiveRestoreSession extends IRestoreSession.Stub { public ActiveRestoreSession( UserBackupManagerService backupManagerService, @Nullable String packageName, - String transportName) { + String transportName, + BackupEligibilityRules backupEligibilityRules) { mBackupManagerService = backupManagerService; mPackageName = packageName; mTransportManager = backupManagerService.getTransportManager(); mTransportName = transportName; mUserId = backupManagerService.getUserId(); + mBackupEligibilityRules = backupEligibilityRules; } public void markTimedOut() { @@ -178,7 +182,8 @@ public class ActiveRestoreSession extends IRestoreSession.Stub { observer, monitor, token, - listener), + listener, + mBackupEligibilityRules), "RestoreSession.restoreAll()"); } finally { Binder.restoreCallingIdentity(oldId); @@ -271,7 +276,8 @@ public class ActiveRestoreSession extends IRestoreSession.Stub { token, packages, /* isSystemRestore */ packages.length > 1, - listener), + listener, + mBackupEligibilityRules), "RestoreSession.restorePackages(" + packages.length + " packages)"); } finally { Binder.restoreCallingIdentity(oldId); @@ -363,7 +369,8 @@ public class ActiveRestoreSession extends IRestoreSession.Stub { monitor, token, app, - listener), + listener, + mBackupEligibilityRules), "RestoreSession.restorePackage(" + packageName + ")"); } finally { Binder.restoreCallingIdentity(oldId); diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java index e42d3bd0e352..622067999f27 100644 --- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java +++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java @@ -49,6 +49,7 @@ import com.android.server.backup.FileMetadata; import com.android.server.backup.KeyValueAdbRestoreEngine; import com.android.server.backup.UserBackupManagerService; import com.android.server.backup.fullbackup.FullBackupObbConnection; +import com.android.server.backup.utils.BackupEligibilityRules; import com.android.server.backup.utils.BytesReadListener; import com.android.server.backup.utils.FullBackupRestoreObserverUtils; import com.android.server.backup.utils.RestoreUtils; @@ -129,11 +130,13 @@ public class FullRestoreEngine extends RestoreEngine { private final boolean mIsAdbRestore; @GuardedBy("mPipesLock") private boolean mPipesClosed; + private final BackupEligibilityRules mBackupEligibilityRules; public FullRestoreEngine(UserBackupManagerService backupManagerService, BackupRestoreTask monitorTask, IFullBackupRestoreObserver observer, IBackupManagerMonitor monitor, PackageInfo onlyPackage, boolean allowApks, - int ephemeralOpToken, boolean isAdbRestore) { + int ephemeralOpToken, boolean isAdbRestore, + BackupEligibilityRules backupEligibilityRules) { mBackupManagerService = backupManagerService; mEphemeralOpToken = ephemeralOpToken; mMonitorTask = monitorTask; @@ -147,6 +150,7 @@ public class FullRestoreEngine extends RestoreEngine { "Timeout parameters cannot be null"); mIsAdbRestore = isAdbRestore; mUserId = backupManagerService.getUserId(); + mBackupEligibilityRules = backupEligibilityRules; } public IBackupAgent getAgent() { @@ -365,7 +369,8 @@ public class FullRestoreEngine extends RestoreEngine { mAgent = mBackupManagerService.bindToAgentSynchronous(mTargetApp, FullBackup.KEY_VALUE_DATA_TOKEN.equals(info.domain) ? ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL - : ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL); + : ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL, + mBackupEligibilityRules.getOperationType()); mAgentPackage = pkg; } catch (IOException | NameNotFoundException e) { // fall through to error handling diff --git a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java index 923bb086f914..c94286ffcffa 100644 --- a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java +++ b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java @@ -25,12 +25,15 @@ import static com.android.server.backup.UserBackupManagerService.BACKUP_FILE_HEA import static com.android.server.backup.UserBackupManagerService.BACKUP_FILE_VERSION; import android.app.backup.IFullBackupRestoreObserver; +import android.content.pm.PackageManagerInternal; import android.os.ParcelFileDescriptor; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.LocalServices; import com.android.server.backup.UserBackupManagerService; import com.android.server.backup.fullbackup.FullBackupObbConnection; +import com.android.server.backup.utils.BackupEligibilityRules; import com.android.server.backup.utils.FullBackupRestoreObserverUtils; import com.android.server.backup.utils.PasswordUtils; @@ -102,7 +105,10 @@ public class PerformAdbRestoreTask implements Runnable { } FullRestoreEngine mEngine = new FullRestoreEngine(mBackupManagerService, null, - mObserver, null, null, true, 0 /*unused*/, true); + mObserver, null, null, true, 0 /*unused*/, true, + BackupEligibilityRules.forBackup(mBackupManagerService.getPackageManager(), + LocalServices.getService(PackageManagerInternal.class), + mBackupManagerService.getUserId())); FullRestoreEngineThread mEngineThread = new FullRestoreEngineThread(mEngine, tarInputStream); mEngineThread.run(); diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java index a7e360403ccc..7baf55992770 100644 --- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java +++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java @@ -162,6 +162,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { private final int mEphemeralOpToken; private final BackupAgentTimeoutParameters mAgentTimeoutParameters; + private final BackupEligibilityRules mBackupEligibilityRules; @VisibleForTesting PerformUnifiedRestoreTask(UserBackupManagerService backupManagerService) { @@ -171,6 +172,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { mTransportManager = null; mEphemeralOpToken = 0; mUserId = 0; + mBackupEligibilityRules = null; this.backupManagerService = backupManagerService; } @@ -208,6 +210,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { mAgentTimeoutParameters = Objects.requireNonNull( backupManagerService.getAgentTimeoutParameters(), "Timeout parameters cannot be null"); + mBackupEligibilityRules = backupEligibilityRules; if (targetPackage != null) { // Single package restore @@ -656,7 +659,8 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { // Good to go! Set up and bind the agent... mAgent = backupManagerService.bindToAgentSynchronous( mCurrentPackage.applicationInfo, - ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL); + ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL, + mBackupEligibilityRules.getOperationType()); if (mAgent == null) { Slog.w(TAG, "Can't find backup agent for " + packageName); mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor, @@ -913,7 +917,8 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { mCurrentPackage.packageName); mEngine = new FullRestoreEngine(backupManagerService, this, null, - mMonitor, mCurrentPackage, false, mEphemeralOpToken, false); + mMonitor, mCurrentPackage, false, mEphemeralOpToken, false, + mBackupEligibilityRules); mEngineThread = new FullRestoreEngineThread(mEngine, mEnginePipes[0]); ParcelFileDescriptor eWriteEnd = mEnginePipes[1]; diff --git a/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java index ee05c2b9ea3b..d6598975a647 100644 --- a/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java +++ b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java @@ -318,4 +318,8 @@ public class BackupEligibilityRules { return true; } } + + public int getOperationType() { + return mOperationType; + } } diff --git a/services/core/java/android/os/BatteryStatsInternal.java b/services/core/java/android/os/BatteryStatsInternal.java index 7cf5fd621f18..b7fed87d570d 100644 --- a/services/core/java/android/os/BatteryStatsInternal.java +++ b/services/core/java/android/os/BatteryStatsInternal.java @@ -50,5 +50,5 @@ public abstract class BatteryStatsInternal { * Informs battery stats of binder stats for the given work source UID. */ public abstract void noteBinderCallStats(int workSourceUid, long incrementalBinderCallCount, - Collection<BinderCallsStats.CallStat> callStats); + Collection<BinderCallsStats.CallStat> callStats, int[] binderThreadNativeTids); } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 02c08cc4cd39..ef62a991f323 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -220,6 +220,8 @@ import com.android.server.utils.PriorityDump; import com.google.android.collect.Lists; +import libcore.io.IoUtils; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -7513,18 +7515,34 @@ public class ConnectivityService extends IConnectivityManager.Stub public void startNattKeepaliveWithFd(Network network, FileDescriptor fd, int resourceId, int intervalSeconds, ISocketKeepaliveCallback cb, String srcAddr, String dstAddr) { - mKeepaliveTracker.startNattKeepalive( - getNetworkAgentInfoForNetwork(network), fd, resourceId, - intervalSeconds, cb, - srcAddr, dstAddr, NattSocketKeepalive.NATT_PORT); + try { + mKeepaliveTracker.startNattKeepalive( + getNetworkAgentInfoForNetwork(network), fd, resourceId, + intervalSeconds, cb, + srcAddr, dstAddr, NattSocketKeepalive.NATT_PORT); + } finally { + // FileDescriptors coming from AIDL calls must be manually closed to prevent leaks. + // startNattKeepalive calls Os.dup(fd) before returning, so we can close immediately. + if (fd != null && Binder.getCallingPid() != Process.myPid()) { + IoUtils.closeQuietly(fd); + } + } } @Override public void startTcpKeepalive(Network network, FileDescriptor fd, int intervalSeconds, ISocketKeepaliveCallback cb) { - enforceKeepalivePermission(); - mKeepaliveTracker.startTcpKeepalive( - getNetworkAgentInfoForNetwork(network), fd, intervalSeconds, cb); + try { + enforceKeepalivePermission(); + mKeepaliveTracker.startTcpKeepalive( + getNetworkAgentInfoForNetwork(network), fd, intervalSeconds, cb); + } finally { + // FileDescriptors coming from AIDL calls must be manually closed to prevent leaks. + // startTcpKeepalive calls Os.dup(fd) before returning, so we can close immediately. + if (fd != null && Binder.getCallingPid() != Process.myPid()) { + IoUtils.closeQuietly(fd); + } + } } @Override diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java index df9dee89f5a2..6dbb1e922f60 100644 --- a/services/core/java/com/android/server/UiModeManagerService.java +++ b/services/core/java/com/android/server/UiModeManagerService.java @@ -137,6 +137,7 @@ final class UiModeManagerService extends SystemService { int mCurUiMode = 0; private int mSetUiMode = 0; private boolean mHoldingConfiguration = false; + private int mCurrentUser; private Configuration mConfiguration = new Configuration(); boolean mSystemReady; @@ -325,6 +326,7 @@ final class UiModeManagerService extends SystemService { @Override public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) { + mCurrentUser = to.getUserIdentifier(); getContext().getContentResolver().unregisterContentObserver(mSetupWizardObserver); verifySetupWizardCompleted(); } @@ -727,16 +729,30 @@ final class UiModeManagerService extends SystemService { @Override public boolean setNightModeActivated(boolean active) { + if (isNightModeLocked() && (getContext().checkCallingOrSelfPermission( + android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) + != PackageManager.PERMISSION_GRANTED)) { + Slog.e(TAG, "Night mode locked, requires MODIFY_DAY_NIGHT_MODE permission"); + return false; + } + final int user = Binder.getCallingUserHandle().getIdentifier(); + if (user != mCurrentUser && getContext().checkCallingOrSelfPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS) + != PackageManager.PERMISSION_GRANTED) { + Slog.e(TAG, "Target user is not current user," + + " INTERACT_ACROSS_USERS permission is required"); + return false; + + } synchronized (mLock) { - final int user = UserHandle.getCallingUserId(); final long ident = Binder.clearCallingIdentity(); try { if (mNightMode == MODE_NIGHT_AUTO || mNightMode == MODE_NIGHT_CUSTOM) { unregisterScreenOffEventLocked(); mOverrideNightModeOff = !active; mOverrideNightModeOn = active; - mOverrideNightModeUser = user; - persistNightModeOverrides(user); + mOverrideNightModeUser = mCurrentUser; + persistNightModeOverrides(mCurrentUser); } else if (mNightMode == UiModeManager.MODE_NIGHT_NO && active) { mNightMode = UiModeManager.MODE_NIGHT_YES; @@ -746,7 +762,7 @@ final class UiModeManagerService extends SystemService { } updateConfigurationLocked(); applyConfigurationExternallyLocked(); - persistNightMode(user); + persistNightMode(mCurrentUser); return true; } finally { Binder.restoreCallingIdentity(ident); diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java index 2534a535079e..59ac09ca2f3d 100644 --- a/services/core/java/com/android/server/VibratorService.java +++ b/services/core/java/com/android/server/VibratorService.java @@ -167,7 +167,7 @@ public class VibratorService extends IVibratorService.Stub private boolean mIsVibrating; @GuardedBy("mLock") private final RemoteCallbackList<IVibratorStateListener> mVibratorStateListeners = - new RemoteCallbackList<>(); + new RemoteCallbackList<>(); private int mHapticFeedbackIntensity; private int mNotificationIntensity; private int mRingIntensity; @@ -176,16 +176,25 @@ public class VibratorService extends IVibratorService.Stub static native long vibratorInit(); static native long vibratorGetFinalizer(); + static native boolean vibratorExists(long controllerPtr); - static native void vibratorOn(long milliseconds); + + static native void vibratorOn(long controllerPtr, long milliseconds, Vibration vibration); + static native void vibratorOff(long controllerPtr); + static native void vibratorSetAmplitude(long controllerPtr, int amplitude); + static native int[] vibratorGetSupportedEffects(long controllerPtr); + static native long vibratorPerformEffect(long effect, long strength, Vibration vibration, boolean withCallback); - static native void vibratorPerformComposedEffect( + + static native void vibratorPerformComposedEffect(long controllerPtr, VibrationEffect.Composition.PrimitiveEffect[] effect, Vibration vibration); + static native void vibratorSetExternalControl(long controllerPtr, boolean enabled); + static native long vibratorGetCapabilities(long controllerPtr); static native void vibratorAlwaysOnEnable(long controllerPtr, long id, long effect, long strength); @@ -231,8 +240,7 @@ public class VibratorService extends IVibratorService.Stub // The actual effect to be played. public VibrationEffect effect; - // The original effect that was requested. This is non-null only when the original effect - // differs from the effect that's being played. Typically these two things differ because + // The original effect that was requested. Typically these two things differ because // the effect was scaled based on the users vibration intensity settings. public VibrationEffect originalEffect; @@ -248,9 +256,13 @@ public class VibratorService extends IVibratorService.Stub this.reason = reason; } + @Override public void binderDied() { synchronized (mLock) { if (this == mCurrentVibration) { + if (DEBUG) { + Slog.d(TAG, "Vibration finished because binder died, cleaning up"); + } doCancelVibrateLocked(); } } @@ -261,6 +273,9 @@ public class VibratorService extends IVibratorService.Stub public void onComplete() { synchronized (mLock) { if (this == mCurrentVibration) { + if (DEBUG) { + Slog.d(TAG, "Vibration finished by callback, cleaning up"); + } doCancelVibrateLocked(); } } @@ -915,7 +930,7 @@ public class VibratorService extends IVibratorService.Stub // Callback for whenever the current vibration has finished played out public void onVibrationFinished() { if (DEBUG) { - Slog.e(TAG, "Vibration finished, cleaning up"); + Slog.d(TAG, "Vibration finished, cleaning up"); } synchronized (mLock) { // Make sure the vibration is really done. This also reports that the vibration is @@ -943,25 +958,22 @@ public class VibratorService extends IVibratorService.Stub private void startVibrationInnerLocked(Vibration vib) { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationInnerLocked"); try { + long timeout = 0; mCurrentVibration = vib; if (vib.effect instanceof VibrationEffect.OneShot) { Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.effect; - doVibratorOn(oneShot.getDuration(), oneShot.getAmplitude(), vib.uid, vib.attrs); - mH.postDelayed(mVibrationEndRunnable, oneShot.getDuration()); + doVibratorOn(oneShot.getDuration(), oneShot.getAmplitude(), vib); + timeout = oneShot.getDuration() * ASYNC_TIMEOUT_MULTIPLIER; } else if (vib.effect instanceof VibrationEffect.Waveform) { // mThread better be null here. doCancelVibrate should always be // called before startNextVibrationLocked or startVibrationLocked. - Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) vib.effect; mThread = new VibrateThread(waveform, vib.uid, vib.attrs); mThread.start(); } else if (vib.effect instanceof VibrationEffect.Prebaked) { Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); - long timeout = doVibratorPrebakedEffectLocked(vib); - if (timeout > 0) { - mH.postDelayed(mVibrationEndRunnable, timeout); - } + timeout = doVibratorPrebakedEffectLocked(vib); } else if (vib.effect instanceof VibrationEffect.Composed) { Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); doVibratorComposedEffectLocked(vib); @@ -969,10 +981,16 @@ public class VibratorService extends IVibratorService.Stub // devices which support composition also support the completion callback. If we // ever get a device that supports the former but not the latter, then we have no // real way of knowing how long a given effect should last. - mH.postDelayed(mVibrationEndRunnable, 10000); + timeout = 10_000; } else { Slog.e(TAG, "Unknown vibration type, ignoring"); } + // Post extra runnable to ensure vibration will end even if the HAL or native controller + // never triggers the callback. + // TODO: Move ASYNC_TIMEOUT_MULTIPLIER here once native controller is fully integrated. + if (timeout > 0) { + mH.postDelayed(mVibrationEndRunnable, timeout); + } } finally { Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); } @@ -1062,18 +1080,18 @@ public class VibratorService extends IVibratorService.Stub return attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY); } - private int getAppOpMode(Vibration vib) { + private int getAppOpMode(int uid, String packageName, VibrationAttributes attrs) { int mode = mAppOps.checkAudioOpNoThrow(AppOpsManager.OP_VIBRATE, - vib.attrs.getAudioAttributes().getUsage(), vib.uid, vib.opPkg); + attrs.getAudioAttributes().getUsage(), uid, packageName); if (mode == AppOpsManager.MODE_ALLOWED) { - mode = mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, vib.uid, vib.opPkg); + mode = mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, uid, packageName); } - if (mode == AppOpsManager.MODE_IGNORED && shouldBypassDnd(vib.attrs)) { + if (mode == AppOpsManager.MODE_IGNORED && shouldBypassDnd(attrs)) { // If we're just ignoring the vibration op then this is set by DND and we should ignore // if we're asked to bypass. AppOps won't be able to record this operation, so make // sure we at least note it in the logs for debugging. - Slog.d(TAG, "Bypassing DND for vibration: " + vib); + Slog.d(TAG, "Bypassing DND for vibrate from uid " + uid); mode = AppOpsManager.MODE_ALLOWED; } return mode; @@ -1095,7 +1113,7 @@ public class VibratorService extends IVibratorService.Stub return false; } - final int mode = getAppOpMode(vib); + final int mode = getAppOpMode(vib.uid, vib.opPkg, vib.attrs); if (mode != AppOpsManager.MODE_ALLOWED) { if (mode == AppOpsManager.MODE_ERRORED) { // We might be getting calls from within system_server, so we don't actually @@ -1265,7 +1283,18 @@ public class VibratorService extends IVibratorService.Stub return mNativeWrapper.vibratorExists(); } + /** Vibrates with native callback trigger for {@link Vibration#onComplete()}. */ + private void doVibratorOn(long millis, int amplitude, Vibration vib) { + doVibratorOn(millis, amplitude, vib.uid, vib.attrs, vib); + } + + /** Vibrates without native callback. */ private void doVibratorOn(long millis, int amplitude, int uid, VibrationAttributes attrs) { + doVibratorOn(millis, amplitude, uid, attrs, /* vib= */ null); + } + + private void doVibratorOn(long millis, int amplitude, int uid, VibrationAttributes attrs, + @Nullable Vibration vib) { Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOn"); try { synchronized (mInputDeviceVibrators) { @@ -1280,13 +1309,14 @@ public class VibratorService extends IVibratorService.Stub final int vibratorCount = mInputDeviceVibrators.size(); if (vibratorCount != 0) { for (int i = 0; i < vibratorCount; i++) { - mInputDeviceVibrators.get(i).vibrate(millis, attrs.getAudioAttributes()); + Vibrator inputDeviceVibrator = mInputDeviceVibrators.get(i); + inputDeviceVibrator.vibrate(millis, attrs.getAudioAttributes()); } } else { // Note: ordering is important here! Many haptic drivers will reset their // amplitude when enabled, so we always have to enable first, then set the // amplitude. - mNativeWrapper.vibratorOn(millis); + mNativeWrapper.vibratorOn(millis, vib); doVibratorSetAmplitude(amplitude); } } @@ -1570,6 +1600,7 @@ public class VibratorService extends IVibratorService.Stub proto.flush(); } + /** Thread that plays a single {@link VibrationEffect.Waveform}. */ private class VibrateThread extends Thread { private final VibrationEffect.Waveform mWaveform; private final int mUid; @@ -1738,8 +1769,8 @@ public class VibratorService extends IVibratorService.Stub } /** Turns vibrator on for given time. */ - public void vibratorOn(long milliseconds) { - VibratorService.vibratorOn(milliseconds); + public void vibratorOn(long milliseconds, @Nullable Vibration vibration) { + VibratorService.vibratorOn(mNativeControllerPtr, milliseconds, vibration); } /** Turns vibrator off. */ @@ -1766,7 +1797,7 @@ public class VibratorService extends IVibratorService.Stub /** Turns vibrator on to perform one of the supported composed effects. */ public void vibratorPerformComposedEffect( VibrationEffect.Composition.PrimitiveEffect[] effect, Vibration vibration) { - VibratorService.vibratorPerformComposedEffect(effect, vibration); + VibratorService.vibratorPerformComposedEffect(mNativeControllerPtr, effect, vibration); } /** Enabled the device vibrator to be controlled by another service. */ @@ -1865,7 +1896,7 @@ public class VibratorService extends IVibratorService.Stub return SCALE_MUTE; } if (ActivityManager.checkComponentPermission(android.Manifest.permission.VIBRATE, - vib.getUid(), -1 /*owningUid*/, true /*exported*/) + vib.getUid(), -1 /*owningUid*/, true /*exported*/) != PackageManager.PERMISSION_GRANTED) { Slog.w(TAG, "pkg=" + vib.getPackage() + ", uid=" + vib.getUid() + " tried to play externally controlled vibration" @@ -1873,6 +1904,14 @@ public class VibratorService extends IVibratorService.Stub return SCALE_MUTE; } + int mode = getAppOpMode(vib.getUid(), vib.getPackage(), vib.getVibrationAttributes()); + if (mode != AppOpsManager.MODE_ALLOWED) { + if (mode == AppOpsManager.MODE_ERRORED) { + Slog.w(TAG, "Would be an error: external vibrate from uid " + vib.getUid()); + } + return SCALE_MUTE; + } + final int scaleLevel; synchronized (mLock) { if (!vib.equals(mCurrentExternalVibration)) { diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 33a92e6ad0ac..3eb26de3a675 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -1836,11 +1836,13 @@ public final class ActiveServices { if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "bindService: " + service + " type=" + resolvedType + " conn=" + connection.asBinder() + " flags=0x" + Integer.toHexString(flags)); + final int callingPid = Binder.getCallingPid(); + final int callingUid = Binder.getCallingUid(); final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller); if (callerApp == null) { throw new SecurityException( "Unable to find app for caller " + caller - + " (pid=" + Binder.getCallingPid() + + " (pid=" + callingPid + ") when binding service " + service); } @@ -1880,19 +1882,19 @@ public final class ActiveServices { } if ((flags & Context.BIND_SCHEDULE_LIKE_TOP_APP) != 0 && !isCallerSystem) { - throw new SecurityException("Non-system caller (pid=" + Binder.getCallingPid() + throw new SecurityException("Non-system caller (pid=" + callingPid + ") set BIND_SCHEDULE_LIKE_TOP_APP when binding service " + service); } if ((flags & Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0 && !isCallerSystem) { throw new SecurityException( - "Non-system caller " + caller + " (pid=" + Binder.getCallingPid() + "Non-system caller " + caller + " (pid=" + callingPid + ") set BIND_ALLOW_WHITELIST_MANAGEMENT when binding service " + service); } if ((flags & Context.BIND_ALLOW_INSTANT) != 0 && !isCallerSystem) { throw new SecurityException( - "Non-system caller " + caller + " (pid=" + Binder.getCallingPid() + "Non-system caller " + caller + " (pid=" + callingPid + ") set BIND_ALLOW_INSTANT when binding service " + service); } @@ -1908,7 +1910,7 @@ public final class ActiveServices { ServiceLookupResult res = retrieveServiceLocked(service, instanceName, resolvedType, callingPackage, - Binder.getCallingPid(), Binder.getCallingUid(), userId, true, + callingPid, callingUid, userId, true, callerFg, isBindExternal, allowInstant); if (res == null) { return 0; @@ -2068,7 +2070,7 @@ public final class ActiveServices { if (!s.mAllowWhileInUsePermissionInFgs) { s.mAllowWhileInUsePermissionInFgs = shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, - Binder.getCallingPid(), Binder.getCallingUid(), + callingPid, callingUid, service, s, false); } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index e546a2812caa..71d12eed5412 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -176,6 +176,8 @@ import android.app.PendingIntent; import android.app.ProcessMemoryState; import android.app.ProfilerInfo; import android.app.WaitResult; +import android.app.backup.BackupManager; +import android.app.backup.BackupManager.OperationType; import android.app.backup.IBackupManager; import android.app.usage.UsageEvents; import android.app.usage.UsageEvents.Event; @@ -1956,7 +1958,7 @@ public class ActivityManagerService extends IActivityManager.Stub nativeTotalPss += Debug.getPss(stats.get(j).pid, null, null); } memInfo.readMemInfo(); - synchronized (ActivityManagerService.this) { + synchronized (mProcessStats.mLock) { if (DEBUG_PSS) Slog.d(TAG_PSS, "Collected native and kernel memory in " + (SystemClock.uptimeMillis()-start) + "ms"); final long cachedKb = memInfo.getCachedSizeKb(); @@ -5374,7 +5376,7 @@ public class ActivityManagerService extends IActivityManager.Stub try { thread.scheduleCreateBackupAgent(backupTarget.appInfo, compatibilityInfoForPackage(backupTarget.appInfo), - backupTarget.backupMode, backupTarget.userId); + backupTarget.backupMode, backupTarget.userId, backupTarget.operationType); } catch (Exception e) { Slog.wtf(TAG, "Exception thrown creating backup agent in " + app, e); badApp = true; @@ -7048,9 +7050,7 @@ public class ActivityManagerService extends IActivityManager.Stub mUsageStatsService.prepareShutdown(); } mBatteryStatsService.shutdown(); - synchronized (this) { - mProcessStats.shutdownLocked(); - } + mProcessStats.shutdown(); return timedout; } @@ -12352,7 +12352,7 @@ public class ActivityManagerService extends IActivityManager.Stub MemInfoReader memInfo = new MemInfoReader(); memInfo.readMemInfo(); if (nativeProcTotalPss > 0) { - synchronized (this) { + synchronized (mProcessStats.mLock) { final long cachedKb = memInfo.getCachedSizeKb(); final long freeKb = memInfo.getFreeSizeKb(); final long zramKb = memInfo.getZramTotalSizeKb(); @@ -12934,7 +12934,7 @@ public class ActivityManagerService extends IActivityManager.Stub MemInfoReader memInfo = new MemInfoReader(); memInfo.readMemInfo(); if (nativeProcTotalPss > 0) { - synchronized (this) { + synchronized (mProcessStats.mLock) { final long cachedKb = memInfo.getCachedSizeKb(); final long freeKb = memInfo.getFreeSizeKb(); final long zramKb = memInfo.getZramTotalSizeKb(); @@ -13778,7 +13778,8 @@ public class ActivityManagerService extends IActivityManager.Stub // Cause the target app to be launched if necessary and its backup agent // instantiated. The backup agent will invoke backupAgentCreated() on the // activity manager to announce its creation. - public boolean bindBackupAgent(String packageName, int backupMode, int targetUserId) { + public boolean bindBackupAgent(String packageName, int backupMode, int targetUserId, + @OperationType int operationType) { if (DEBUG_BACKUP) { Slog.v(TAG, "bindBackupAgent: app=" + packageName + " mode=" + backupMode + " targetUserId=" + targetUserId + " callingUid = " + Binder.getCallingUid() @@ -13821,7 +13822,7 @@ public class ActivityManagerService extends IActivityManager.Stub + app.packageName + ": " + e); } - BackupRecord r = new BackupRecord(app, backupMode, targetUserId); + BackupRecord r = new BackupRecord(app, backupMode, targetUserId, operationType); ComponentName hostingName = (backupMode == ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL) ? new ComponentName(app.packageName, app.backupAgentName) @@ -13860,7 +13861,8 @@ public class ActivityManagerService extends IActivityManager.Stub if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "Agent proc already running: " + proc); try { proc.thread.scheduleCreateBackupAgent(app, - compatibilityInfoForPackage(app), backupMode, targetUserId); + compatibilityInfoForPackage(app), backupMode, targetUserId, + operationType); } catch (RemoteException e) { // Will time out on the backup manager side } @@ -16505,9 +16507,7 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override public void run() { - synchronized (mService) { - mProcessStats.writeStateAsyncLocked(); - } + mProcessStats.writeStateAsync(); } } @@ -16557,9 +16557,13 @@ public class ActivityManagerService extends IActivityManager.Stub } mLastMemoryLevel = memFactor; mLastNumProcesses = mProcessList.getLruSizeLocked(); - boolean allChanged = mProcessStats.setMemFactorLocked( - memFactor, mAtmInternal != null ? !mAtmInternal.isSleeping() : true, now); - final int trackerMemFactor = mProcessStats.getMemFactorLocked(); + boolean allChanged; + int trackerMemFactor; + synchronized (mProcessStats.mLock) { + allChanged = mProcessStats.setMemFactorLocked( + memFactor, mAtmInternal != null ? !mAtmInternal.isSleeping() : true, now); + trackerMemFactor = mProcessStats.getMemFactorLocked(); + } if (memFactor != ProcessStats.ADJ_MEM_FACTOR_NORMAL) { if (mLowRamStartTime == 0) { mLowRamStartTime = now; diff --git a/services/core/java/com/android/server/am/BackupRecord.java b/services/core/java/com/android/server/am/BackupRecord.java index 37b4be4ea0ec..d4198562c1dd 100644 --- a/services/core/java/com/android/server/am/BackupRecord.java +++ b/services/core/java/com/android/server/am/BackupRecord.java @@ -16,6 +16,8 @@ package com.android.server.am; +import android.app.backup.BackupManager; +import android.app.backup.BackupManager.OperationType; import android.content.pm.ApplicationInfo; /** @hide */ @@ -30,14 +32,16 @@ final class BackupRecord { final ApplicationInfo appInfo; // information about BackupAgent's app final int userId; // user for which backup is performed final int backupMode; // full backup / incremental / restore + @OperationType final int operationType; // see BackupManager#OperationType ProcessRecord app; // where this agent is running or null // ----- Implementation ----- - BackupRecord(ApplicationInfo _appInfo, int _backupMode, int _userId) { + BackupRecord(ApplicationInfo _appInfo, int _backupMode, int _userId, int _operationType) { appInfo = _appInfo; backupMode = _backupMode; userId = _userId; + operationType = _operationType; } public String toString() { diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 5081b360b4fe..d72998ba95f1 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -227,8 +227,9 @@ public final class BatteryStatsService extends IBatteryStats.Stub @Override public void noteBinderCallStats(int workSourceUid, long incrementatCallCount, - Collection<BinderCallsStats.CallStat> callStats) { - mStats.noteBinderCallStats(workSourceUid, incrementatCallCount, callStats); + Collection<BinderCallsStats.CallStat> callStats, int[] binderThreadNativeTids) { + mStats.noteBinderCallStats(workSourceUid, incrementatCallCount, callStats, + binderThreadNativeTids); } } diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java index 8970ec4c7bb7..0f2dfcc699e2 100644 --- a/services/core/java/com/android/server/am/CoreSettingsObserver.java +++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java @@ -116,6 +116,10 @@ final class CoreSettingsObserver extends ContentObserver { WidgetFlags.KEY_ENABLE_CURSOR_DRAG_FROM_ANYWHERE, boolean.class, WidgetFlags.ENABLE_CURSOR_DRAG_FROM_ANYWHERE_DEFAULT)); sDeviceConfigEntries.add(new DeviceConfigEntry<Integer>( + DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL, + WidgetFlags.KEY_CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL, int.class, + WidgetFlags.CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL_DEFAULT)); + sDeviceConfigEntries.add(new DeviceConfigEntry<Integer>( DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.FINGER_TO_CURSOR_DISTANCE, WidgetFlags.KEY_FINGER_TO_CURSOR_DISTANCE, int.class, WidgetFlags.FINGER_TO_CURSOR_DISTANCE_DEFAULT)); diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index f0343e1d807c..bf15f1737cfc 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -659,13 +659,15 @@ public final class OomAdjuster { updateUidsLocked(activeUids, nowElapsed); - if (mService.mProcessStats.shouldWriteNowLocked(now)) { - mService.mHandler.post(new ActivityManagerService.ProcStatsRunnable(mService, - mService.mProcessStats)); - } + synchronized (mService.mProcessStats.mLock) { + if (mService.mProcessStats.shouldWriteNowLocked(now)) { + mService.mHandler.post(new ActivityManagerService.ProcStatsRunnable(mService, + mService.mProcessStats)); + } - // Run this after making sure all procstates are updated. - mService.mProcessStats.updateTrackingAssociationsLocked(mAdjSeq, now); + // Run this after making sure all procstates are updated. + mService.mProcessStats.updateTrackingAssociationsLocked(mAdjSeq, now); + } if (DEBUG_OOM_ADJ) { final long duration = SystemClock.uptimeMillis() - now; diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 1647fdaa57b6..e3e13391a8b0 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -673,30 +673,33 @@ class ProcessRecord implements WindowProcessListener { public void makeActive(IApplicationThread _thread, ProcessStatsService tracker) { if (thread == null) { - final ProcessState origBase = baseProcessTracker; - if (origBase != null) { - origBase.setState(ProcessStats.STATE_NOTHING, - tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), pkgList.mPkgList); - for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) { - FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED, - uid, processName, pkgList.keyAt(ipkg), - ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING), - pkgList.valueAt(ipkg).appVersion); - } - origBase.makeInactive(); - } - baseProcessTracker = tracker.getProcessStateLocked(info.packageName, info.uid, - info.longVersionCode, processName); - baseProcessTracker.makeActive(); - for (int i=0; i<pkgList.size(); i++) { - ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i); - if (holder.state != null && holder.state != origBase) { - holder.state.makeInactive(); + synchronized (tracker.mLock) { + final ProcessState origBase = baseProcessTracker; + if (origBase != null) { + origBase.setState(ProcessStats.STATE_NOTHING, + tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), + pkgList.mPkgList); + for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) { + FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED, + uid, processName, pkgList.keyAt(ipkg), + ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING), + pkgList.valueAt(ipkg).appVersion); + } + origBase.makeInactive(); } - tracker.updateProcessStateHolderLocked(holder, pkgList.keyAt(i), info.uid, + baseProcessTracker = tracker.getProcessStateLocked(info.packageName, info.uid, info.longVersionCode, processName); - if (holder.state != baseProcessTracker) { - holder.state.makeActive(); + baseProcessTracker.makeActive(); + for (int i = 0, ipkg = pkgList.size(); i < ipkg; i++) { + ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i); + if (holder.state != null && holder.state != origBase) { + holder.state.makeInactive(); + } + tracker.updateProcessStateHolderLocked(holder, pkgList.keyAt(i), info.uid, + info.longVersionCode, processName); + if (holder.state != baseProcessTracker) { + holder.state.makeActive(); + } } } } @@ -707,27 +710,30 @@ class ProcessRecord implements WindowProcessListener { public void makeInactive(ProcessStatsService tracker) { thread = null; mWindowProcessController.setThread(null); - final ProcessState origBase = baseProcessTracker; - if (origBase != null) { + synchronized (tracker.mLock) { + final ProcessState origBase = baseProcessTracker; if (origBase != null) { - origBase.setState(ProcessStats.STATE_NOTHING, - tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), pkgList.mPkgList); - for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) { - FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED, - uid, processName, pkgList.keyAt(ipkg), - ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING), - pkgList.valueAt(ipkg).appVersion); + if (origBase != null) { + origBase.setState(ProcessStats.STATE_NOTHING, + tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), + pkgList.mPkgList); + for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) { + FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED, + uid, processName, pkgList.keyAt(ipkg), + ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING), + pkgList.valueAt(ipkg).appVersion); + } + origBase.makeInactive(); } - origBase.makeInactive(); - } - baseProcessTracker = null; - for (int i=0; i<pkgList.size(); i++) { - ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i); - if (holder.state != null && holder.state != origBase) { - holder.state.makeInactive(); + baseProcessTracker = null; + for (int i = 0, ipkg = pkgList.size(); i < ipkg; i++) { + ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i); + if (holder.state != null && holder.state != origBase) { + holder.state.makeInactive(); + } + holder.pkg = null; + holder.state = null; } - holder.pkg = null; - holder.state = null; } } } @@ -1026,17 +1032,19 @@ class ProcessRecord implements WindowProcessListener { */ public boolean addPackage(String pkg, long versionCode, ProcessStatsService tracker) { if (!pkgList.containsKey(pkg)) { - ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder( - versionCode); - if (baseProcessTracker != null) { - tracker.updateProcessStateHolderLocked(holder, pkg, info.uid, versionCode, - processName); - pkgList.put(pkg, holder); - if (holder.state != baseProcessTracker) { - holder.state.makeActive(); + synchronized (tracker.mLock) { + ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder( + versionCode); + if (baseProcessTracker != null) { + tracker.updateProcessStateHolderLocked(holder, pkg, info.uid, versionCode, + processName); + pkgList.put(pkg, holder); + if (holder.state != baseProcessTracker) { + holder.state.makeActive(); + } + } else { + pkgList.put(pkg, holder); } - } else { - pkgList.put(pkg, holder); } return true; } @@ -1072,31 +1080,33 @@ class ProcessRecord implements WindowProcessListener { public void resetPackageList(ProcessStatsService tracker) { final int N = pkgList.size(); if (baseProcessTracker != null) { - long now = SystemClock.uptimeMillis(); - baseProcessTracker.setState(ProcessStats.STATE_NOTHING, - tracker.getMemFactorLocked(), now, pkgList.mPkgList); - for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) { - FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED, - uid, processName, pkgList.keyAt(ipkg), - ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING), - pkgList.valueAt(ipkg).appVersion); - } - if (N != 1) { - for (int i=0; i<N; i++) { - ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i); - if (holder.state != null && holder.state != baseProcessTracker) { - holder.state.makeInactive(); - } - + synchronized (tracker.mLock) { + long now = SystemClock.uptimeMillis(); + baseProcessTracker.setState(ProcessStats.STATE_NOTHING, + tracker.getMemFactorLocked(), now, pkgList.mPkgList); + for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) { + FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED, + uid, processName, pkgList.keyAt(ipkg), + ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING), + pkgList.valueAt(ipkg).appVersion); } - pkgList.clear(); - ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder( - info.longVersionCode); - tracker.updateProcessStateHolderLocked(holder, info.packageName, info.uid, - info.longVersionCode, processName); - pkgList.put(info.packageName, holder); - if (holder.state != baseProcessTracker) { - holder.state.makeActive(); + if (N != 1) { + for (int i = 0; i < N; i++) { + ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i); + if (holder.state != null && holder.state != baseProcessTracker) { + holder.state.makeInactive(); + } + + } + pkgList.clear(); + ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder( + info.longVersionCode); + tracker.updateProcessStateHolderLocked(holder, info.packageName, info.uid, + info.longVersionCode, processName); + pkgList.put(info.packageName, holder); + if (holder.state != baseProcessTracker) { + holder.state.makeActive(); + } } } } else if (N != 1) { diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java index a168af5ad842..4e8c386a3c66 100644 --- a/services/core/java/com/android/server/am/ProcessStatsService.java +++ b/services/core/java/com/android/server/am/ProcessStatsService.java @@ -71,33 +71,62 @@ public final class ProcessStatsService extends IProcessStats.Stub { final ActivityManagerService mAm; final File mBaseDir; - ProcessStats mProcessStats; + + // Note: The locking order of the below 3 locks should be: + // mLock, mPendingWriteLock, mFileLock + + // The lock object to protect the internal state/structures + final Object mLock = new Object(); + + // The lock object to protect the access to pending writes + final Object mPendingWriteLock = new Object(); + + // The lock object to protect the access to all of the file read/write + final ReentrantLock mFileLock = new ReentrantLock(); + + @GuardedBy("mLock") + final ProcessStats mProcessStats; + + @GuardedBy("mFileLock") AtomicFile mFile; + + @GuardedBy("mLock") boolean mCommitPending; + + @GuardedBy("mLock") boolean mShuttingDown; + + @GuardedBy("mLock") int mLastMemOnlyState = -1; boolean mMemFactorLowered; - final ReentrantLock mWriteLock = new ReentrantLock(); - final Object mPendingWriteLock = new Object(); + @GuardedBy("mPendingWriteLock") AtomicFile mPendingWriteFile; + + @GuardedBy("mPendingWriteLock") Parcel mPendingWrite; + + @GuardedBy("mPendingWriteLock") boolean mPendingWriteCommitted; + + @GuardedBy("mLock") long mLastWriteTime; /** For CTS to inject the screen state. */ - @GuardedBy("mAm") + @GuardedBy("mLock") Boolean mInjectedScreenState; public ProcessStatsService(ActivityManagerService am, File file) { mAm = am; mBaseDir = file; mBaseDir.mkdirs(); - mProcessStats = new ProcessStats(true); - updateFile(); + synchronized (mLock) { + mProcessStats = new ProcessStats(true); + updateFileLocked(); + } SystemProperties.addChangeCallback(new Runnable() { @Override public void run() { - synchronized (mAm) { + synchronized (mLock) { if (mProcessStats.evaluateSystemProperties(false)) { mProcessStats.mFlags |= ProcessStats.FLAG_SYSPROPS; writeStateLocked(true, true); @@ -121,32 +150,33 @@ public final class ProcessStatsService extends IProcessStats.Stub { } } - @GuardedBy("mAm") - public void updateProcessStateHolderLocked(ProcessStats.ProcessStateHolder holder, + @GuardedBy("mLock") + void updateProcessStateHolderLocked(ProcessStats.ProcessStateHolder holder, String packageName, int uid, long versionCode, String processName) { holder.pkg = mProcessStats.getPackageStateLocked(packageName, uid, versionCode); holder.state = mProcessStats.getProcessStateLocked(holder.pkg, processName); } - @GuardedBy("mAm") - public ProcessState getProcessStateLocked(String packageName, + @GuardedBy("mLock") + ProcessState getProcessStateLocked(String packageName, int uid, long versionCode, String processName) { return mProcessStats.getProcessStateLocked(packageName, uid, versionCode, processName); } - @GuardedBy("mAm") - public ServiceState getServiceStateLocked(String packageName, int uid, + ServiceState getServiceState(String packageName, int uid, long versionCode, String processName, String className) { - return mProcessStats.getServiceStateLocked(packageName, uid, versionCode, processName, - className); + synchronized (mLock) { + return mProcessStats.getServiceStateLocked(packageName, uid, versionCode, processName, + className); + } } - public boolean isMemFactorLowered() { + boolean isMemFactorLowered() { return mMemFactorLowered; } - @GuardedBy("mAm") - public boolean setMemFactorLocked(int memFactor, boolean screenOn, long now) { + @GuardedBy("mLock") + boolean setMemFactorLocked(int memFactor, boolean screenOn, long now) { mMemFactorLowered = memFactor < mLastMemOnlyState; mLastMemOnlyState = memFactor; if (mInjectedScreenState != null) { @@ -184,24 +214,24 @@ public final class ProcessStatsService extends IProcessStats.Stub { return false; } - @GuardedBy("mAm") - public int getMemFactorLocked() { + @GuardedBy("mLock") + int getMemFactorLocked() { return mProcessStats.mMemFactor != ProcessStats.STATE_NOTHING ? mProcessStats.mMemFactor : 0; } - @GuardedBy("mAm") - public void addSysMemUsageLocked(long cachedMem, long freeMem, long zramMem, long kernelMem, + @GuardedBy("mLock") + void addSysMemUsageLocked(long cachedMem, long freeMem, long zramMem, long kernelMem, long nativeMem) { mProcessStats.addSysMemUsage(cachedMem, freeMem, zramMem, kernelMem, nativeMem); } - @GuardedBy("mAm") - public void updateTrackingAssociationsLocked(int curSeq, long now) { + @GuardedBy("mLock") + void updateTrackingAssociationsLocked(int curSeq, long now) { mProcessStats.updateTrackingAssociationsLocked(curSeq, now); } - @GuardedBy("mAm") - public boolean shouldWriteNowLocked(long now) { + @GuardedBy("mLock") + boolean shouldWriteNowLocked(long now) { if (now > (mLastWriteTime+WRITE_PERIOD)) { if (SystemClock.elapsedRealtime() > (mProcessStats.mTimePeriodStartRealtime+ProcessStats.COMMIT_PERIOD) && @@ -214,25 +244,27 @@ public final class ProcessStatsService extends IProcessStats.Stub { return false; } - @GuardedBy("mAm") - public void shutdownLocked() { + void shutdown() { Slog.w(TAG, "Writing process stats before shutdown..."); - mProcessStats.mFlags |= ProcessStats.FLAG_SHUTDOWN; - writeStateSyncLocked(); - mShuttingDown = true; + synchronized (mLock) { + mProcessStats.mFlags |= ProcessStats.FLAG_SHUTDOWN; + writeStateSyncLocked(); + mShuttingDown = true; + } } - @GuardedBy("mAm") - public void writeStateAsyncLocked() { - writeStateLocked(false); + void writeStateAsync() { + synchronized (mLock) { + writeStateLocked(false); + } } - @GuardedBy("mAm") - public void writeStateSyncLocked() { + @GuardedBy("mLock") + private void writeStateSyncLocked() { writeStateLocked(true); } - @GuardedBy("mAm") + @GuardedBy("mLock") private void writeStateLocked(boolean sync) { if (mShuttingDown) { return; @@ -242,8 +274,8 @@ public final class ProcessStatsService extends IProcessStats.Stub { writeStateLocked(sync, commitPending); } - @GuardedBy("mAm") - public void writeStateLocked(boolean sync, final boolean commit) { + @GuardedBy("mLock") + private void writeStateLocked(boolean sync, final boolean commit) { final long totalTime; synchronized (mPendingWriteLock) { final long now = SystemClock.uptimeMillis(); @@ -255,13 +287,13 @@ public final class ProcessStatsService extends IProcessStats.Stub { mProcessStats.mFlags |= ProcessStats.FLAG_COMPLETE; } mProcessStats.writeToParcel(mPendingWrite, 0); - mPendingWriteFile = new AtomicFile(mFile.getBaseFile()); + mPendingWriteFile = new AtomicFile(getCurrentFile()); mPendingWriteCommitted = commit; } if (commit) { mProcessStats.resetSafely(); - updateFile(); - mAm.requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false); + updateFileLocked(); + scheduleRequestPssAllProcs(true, false); } mLastWriteTime = SystemClock.uptimeMillis(); totalTime = SystemClock.uptimeMillis() - now; @@ -279,14 +311,37 @@ public final class ProcessStatsService extends IProcessStats.Stub { performWriteState(totalTime); } - private void updateFile() { - mFile = new AtomicFile(new File(mBaseDir, STATE_FILE_PREFIX - + mProcessStats.mTimePeriodStartClockStr + STATE_FILE_SUFFIX)); + private void scheduleRequestPssAllProcs(boolean always, boolean memLowered) { + mAm.mHandler.post(() -> { + synchronized (mAm) { + mAm.requestPssAllProcsLocked(SystemClock.uptimeMillis(), always, memLowered); + } + }); + } + + @GuardedBy("mLock") + private void updateFileLocked() { + mFileLock.lock(); + try { + mFile = new AtomicFile(new File(mBaseDir, STATE_FILE_PREFIX + + mProcessStats.mTimePeriodStartClockStr + STATE_FILE_SUFFIX)); + } finally { + mFileLock.unlock(); + } mLastWriteTime = SystemClock.uptimeMillis(); } - void performWriteState(long initialTime) { - if (DEBUG) Slog.d(TAG, "Performing write to " + mFile.getBaseFile()); + private File getCurrentFile() { + mFileLock.lock(); + try { + return mFile.getBaseFile(); + } finally { + mFileLock.unlock(); + } + } + + private void performWriteState(long initialTime) { + if (DEBUG) Slog.d(TAG, "Performing write to " + getCurrentFile()); Parcel data; AtomicFile file; synchronized (mPendingWriteLock) { @@ -298,7 +353,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { } mPendingWrite = null; mPendingWriteFile = null; - mWriteLock.lock(); + mFileLock.lock(); } final long startTime = SystemClock.uptimeMillis(); @@ -316,13 +371,13 @@ public final class ProcessStatsService extends IProcessStats.Stub { file.failWrite(stream); } finally { data.recycle(); - trimHistoricStatesWriteLocked(); - mWriteLock.unlock(); + trimHistoricStatesWriteLF(); + mFileLock.unlock(); } } - @GuardedBy("mAm") - boolean readLocked(ProcessStats stats, AtomicFile file) { + @GuardedBy("mFileLock") + private boolean readLF(ProcessStats stats, AtomicFile file) { try { FileInputStream stream = file.openRead(); stats.read(stream); @@ -387,7 +442,8 @@ public final class ProcessStatsService extends IProcessStats.Stub { return true; } - private ArrayList<String> getCommittedFiles(int minNum, boolean inclCurrent, + @GuardedBy("mFileLock") + private ArrayList<String> getCommittedFilesLF(int minNum, boolean inclCurrent, boolean inclCheckedIn) { File[] files = mBaseDir.listFiles(); if (files == null || files.length <= minNum) { @@ -414,9 +470,9 @@ public final class ProcessStatsService extends IProcessStats.Stub { return filesArray; } - @GuardedBy("mAm") - public void trimHistoricStatesWriteLocked() { - ArrayList<String> filesArray = getCommittedFiles(MAX_HISTORIC_STATES, false, true); + @GuardedBy("mFileLock") + private void trimHistoricStatesWriteLF() { + ArrayList<String> filesArray = getCommittedFilesLF(MAX_HISTORIC_STATES, false, true); if (filesArray == null) { return; } @@ -427,8 +483,8 @@ public final class ProcessStatsService extends IProcessStats.Stub { } } - @GuardedBy("mAm") - boolean dumpFilteredProcessesCsvLocked(PrintWriter pw, String header, + @GuardedBy("mLock") + private boolean dumpFilteredProcessesCsvLocked(PrintWriter pw, String header, boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates, boolean sepProcStates, int[] procStates, long now, String reqPackage) { ArrayList<ProcessState> procs = mProcessStats.collectProcessesLocked( @@ -502,20 +558,21 @@ public final class ProcessStatsService extends IProcessStats.Stub { return res; } + @Override public byte[] getCurrentStats(List<ParcelFileDescriptor> historic) { mAm.mContext.enforceCallingOrSelfPermission( android.Manifest.permission.PACKAGE_USAGE_STATS, null); Parcel current = Parcel.obtain(); - synchronized (mAm) { + synchronized (mLock) { long now = SystemClock.uptimeMillis(); mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime(); mProcessStats.mTimePeriodEndUptime = now; mProcessStats.writeToParcel(current, now, 0); } - mWriteLock.lock(); + mFileLock.lock(); try { if (historic != null) { - ArrayList<String> files = getCommittedFiles(0, false, true); + ArrayList<String> files = getCommittedFilesLF(0, false, true); if (files != null) { for (int i=files.size()-1; i>=0; i--) { try { @@ -529,7 +586,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { } } } finally { - mWriteLock.unlock(); + mFileLock.unlock(); } return current.marshall(); } @@ -563,9 +620,9 @@ public final class ProcessStatsService extends IProcessStats.Stub { android.Manifest.permission.PACKAGE_USAGE_STATS, null); long newHighWaterMark = highWaterMarkMs; - mWriteLock.lock(); + mFileLock.lock(); try { - ArrayList<String> files = getCommittedFiles(0, false, true); + ArrayList<String> files = getCommittedFilesLF(0, false, true); if (files != null) { String highWaterMarkStr = DateFormat.format("yyyy-MM-dd-HH-mm-ss", highWaterMarkMs).toString(); @@ -612,7 +669,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { } catch (IOException e) { Slog.w(TAG, "Failure opening procstat file", e); } finally { - mWriteLock.unlock(); + mFileLock.unlock(); } return newHighWaterMark; } @@ -625,7 +682,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { return mAm.mConstants.MIN_ASSOC_LOG_DURATION; } - private ParcelFileDescriptor protoToParcelFileDescriptor(ProcessStats stats, int section) + private static ParcelFileDescriptor protoToParcelFileDescriptor(ProcessStats stats, int section) throws IOException { final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe(); Thread thr = new Thread("ProcessStats pipe output") { @@ -645,12 +702,13 @@ public final class ProcessStatsService extends IProcessStats.Stub { return fds[0]; } + @Override public ParcelFileDescriptor getStatsOverTime(long minTime) { mAm.mContext.enforceCallingOrSelfPermission( android.Manifest.permission.PACKAGE_USAGE_STATS, null); Parcel current = Parcel.obtain(); long curTime; - synchronized (mAm) { + synchronized (mLock) { long now = SystemClock.uptimeMillis(); mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime(); mProcessStats.mTimePeriodEndUptime = now; @@ -658,11 +716,11 @@ public final class ProcessStatsService extends IProcessStats.Stub { curTime = mProcessStats.mTimePeriodEndRealtime - mProcessStats.mTimePeriodStartRealtime; } - mWriteLock.lock(); + mFileLock.lock(); try { if (curTime < minTime) { // Need to add in older stats to reach desired time. - ArrayList<String> files = getCommittedFiles(0, false, true); + ArrayList<String> files = getCommittedFilesLF(0, false, true); if (files != null && files.size() > 0) { current.setDataPosition(0); ProcessStats stats = ProcessStats.CREATOR.createFromParcel(current); @@ -673,7 +731,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { AtomicFile file = new AtomicFile(new File(files.get(i))); i--; ProcessStats moreStats = new ProcessStats(false); - readLocked(moreStats, file); + readLF(moreStats, file); if (moreStats.mReadError == null) { stats.add(moreStats); StringBuilder sb = new StringBuilder(); @@ -712,13 +770,14 @@ public final class ProcessStatsService extends IProcessStats.Stub { } catch (IOException e) { Slog.w(TAG, "Failed building output pipe", e); } finally { - mWriteLock.unlock(); + mFileLock.unlock(); } return null; } + @Override public int getCurrentMemoryState() { - synchronized (mAm) { + synchronized (mLock) { return mLastMemOnlyState; } } @@ -947,7 +1006,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { } else if ("--current".equals(arg)) { currentOnly = true; } else if ("--commit".equals(arg)) { - synchronized (mAm) { + synchronized (mLock) { mProcessStats.mFlags |= ProcessStats.FLAG_COMPLETE; writeStateLocked(true, true); pw.println("Process stats committed."); @@ -962,29 +1021,39 @@ public final class ProcessStatsService extends IProcessStats.Stub { } section = parseSectionOptions(args[i]); } else if ("--clear".equals(arg)) { - synchronized (mAm) { + synchronized (mLock) { mProcessStats.resetSafely(); - mAm.requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false); - ArrayList<String> files = getCommittedFiles(0, true, true); - if (files != null) { - for (int fi=0; fi<files.size(); fi++) { - (new File(files.get(fi))).delete(); + scheduleRequestPssAllProcs(true, false); + mFileLock.lock(); + try { + ArrayList<String> files = getCommittedFilesLF(0, true, true); + if (files != null) { + for (int fi = files.size() - 1; fi >= 0; fi--) { + (new File(files.get(fi))).delete(); + } } + } finally { + mFileLock.unlock(); } pw.println("All process stats cleared."); quit = true; } } else if ("--write".equals(arg)) { - synchronized (mAm) { + synchronized (mLock) { writeStateSyncLocked(); pw.println("Process stats written."); quit = true; } } else if ("--read".equals(arg)) { - synchronized (mAm) { - readLocked(mProcessStats, mFile); - pw.println("Process stats read."); - quit = true; + synchronized (mLock) { + mFileLock.lock(); + try { + readLF(mProcessStats, mFile); + pw.println("Process stats read."); + quit = true; + } finally { + mFileLock.unlock(); + } } } else if ("--start-testing".equals(arg)) { synchronized (mAm) { @@ -999,17 +1068,17 @@ public final class ProcessStatsService extends IProcessStats.Stub { quit = true; } } else if ("--pretend-screen-on".equals(arg)) { - synchronized (mAm) { + synchronized (mLock) { mInjectedScreenState = true; } quit = true; } else if ("--pretend-screen-off".equals(arg)) { - synchronized (mAm) { + synchronized (mLock) { mInjectedScreenState = false; } quit = true; } else if ("--stop-pretend-screen".equals(arg)) { - synchronized (mAm) { + synchronized (mLock) { mInjectedScreenState = null; } quit = true; @@ -1060,7 +1129,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { } } pw.println(); - synchronized (mAm) { + synchronized (mLock) { dumpFilteredProcessesCsvLocked(pw, null, csvSepScreenStats, csvScreenStats, csvSepMemStats, csvMemStats, csvSepProcStats, csvProcStats, now, reqPackage); @@ -1090,14 +1159,26 @@ public final class ProcessStatsService extends IProcessStats.Stub { return; } else if (lastIndex > 0) { pw.print("LAST STATS AT INDEX "); pw.print(lastIndex); pw.println(":"); - ArrayList<String> files = getCommittedFiles(0, false, true); - if (lastIndex >= files.size()) { - pw.print("Only have "); pw.print(files.size()); pw.println(" data sets"); - return; + + ArrayList<String> files; + AtomicFile file; + ProcessStats processStats; + + mFileLock.lock(); + try { + files = getCommittedFilesLF(0, false, true); + if (lastIndex >= files.size()) { + pw.print("Only have "); pw.print(files.size()); pw.println(" data sets"); + return; + } + file = new AtomicFile(new File(files.get(lastIndex))); + processStats = new ProcessStats(false); + readLF(processStats, file); + } finally { + mFileLock.unlock(); } - AtomicFile file = new AtomicFile(new File(files.get(lastIndex))); - ProcessStats processStats = new ProcessStats(false); - readLocked(processStats, file); + + // No lock is needed now, since only us have the access to the 'processStats'. if (processStats.mReadError != null) { if (isCheckin || isCompact) pw.print("err,"); pw.print("Failure reading "); pw.print(files.get(lastIndex)); @@ -1118,7 +1199,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { processStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpDetails, dumpAll, activeOnly, section); if (dumpAll) { - pw.print(" mFile="); pw.println(mFile.getBaseFile()); + pw.print(" mFile="); pw.println(getCurrentFile()); } } else { processStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly); @@ -1129,9 +1210,9 @@ public final class ProcessStatsService extends IProcessStats.Stub { boolean sepNeeded = false; if ((dumpAll || isCheckin) && !currentOnly) { - mWriteLock.lock(); + mFileLock.lock(); try { - ArrayList<String> files = getCommittedFiles(0, false, !isCheckin); + ArrayList<String> files = getCommittedFilesLF(0, false, !isCheckin); if (files != null) { int start = isCheckin ? 0 : (files.size() - maxNum); if (start < 0) { @@ -1142,7 +1223,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { try { AtomicFile file = new AtomicFile(new File(files.get(i))); ProcessStats processStats = new ProcessStats(false); - readLocked(processStats, file); + readLF(processStats, file); if (processStats.mReadError != null) { if (isCheckin || isCompact) pw.print("err,"); pw.print("Failure reading "); pw.print(files.get(i)); @@ -1188,11 +1269,11 @@ public final class ProcessStatsService extends IProcessStats.Stub { } } } finally { - mWriteLock.unlock(); + mFileLock.unlock(); } } if (!isCheckin) { - synchronized (mAm) { + synchronized (mLock) { if (isCompact) { mProcessStats.dumpCheckinLocked(pw, reqPackage, section); } else { @@ -1204,7 +1285,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { mProcessStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpDetails, dumpAll, activeOnly, section); if (dumpAll) { - pw.print(" mFile="); pw.println(mFile.getBaseFile()); + pw.print(" mFile="); pw.println(getCurrentFile()); } } else { mProcessStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly); @@ -1249,7 +1330,7 @@ public final class ProcessStatsService extends IProcessStats.Stub { // dump current procstats long now; - synchronized (mAm) { + synchronized (mLock) { now = SystemClock.uptimeMillis(); final long token = proto.start(ProcessStatsServiceDumpProto.PROCSTATS_NOW); mProcessStats.dumpDebug(proto, now, ProcessStats.REPORT_ALL); diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 022b04d89774..4a2703056871 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -528,8 +528,9 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN return tracker; } if ((serviceInfo.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) == 0) { - tracker = ams.mProcessStats.getServiceStateLocked(serviceInfo.packageName, - serviceInfo.applicationInfo.uid, serviceInfo.applicationInfo.longVersionCode, + tracker = ams.mProcessStats.getServiceState(serviceInfo.packageName, + serviceInfo.applicationInfo.uid, + serviceInfo.applicationInfo.longVersionCode, serviceInfo.processName, serviceInfo.name); tracker.applyNewOwner(this); } @@ -546,7 +547,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN public void makeRestarting(int memFactor, long now) { if (restartTracker == null) { if ((serviceInfo.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) == 0) { - restartTracker = ams.mProcessStats.getServiceStateLocked(serviceInfo.packageName, + restartTracker = ams.mProcessStats.getServiceState( + serviceInfo.packageName, serviceInfo.applicationInfo.uid, serviceInfo.applicationInfo.longVersionCode, serviceInfo.processName, serviceInfo.name); diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 45f95fd3f663..2bbbbf1664d1 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -224,12 +224,35 @@ import java.util.concurrent.atomic.AtomicBoolean; if (!addSpeakerphoneClient(cb, pid, on)) { return false; } + if (on) { + // Cancel BT SCO ON request by this same client: speakerphone and BT SCO routes + // are mutually exclusive. + // See symmetrical operation for startBluetoothScoForClient_Sync(). + mBtHelper.stopBluetoothScoForPid(pid); + } final boolean wasOn = isSpeakerphoneOn(); updateSpeakerphoneOn(eventSource); return (wasOn != isSpeakerphoneOn()); } } + /** + * Turns speakerphone off for a given pid and update speakerphone state. + * @param pid + */ + @GuardedBy("mDeviceStateLock") + private void setSpeakerphoneOffForPid(int pid) { + SpeakerphoneClient client = getSpeakerphoneClientForPid(pid); + if (client == null) { + return; + } + client.unregisterDeathRecipient(); + mSpeakerphoneClients.remove(client); + final String eventSource = new StringBuilder("setSpeakerphoneOffForPid(") + .append(pid).append(")").toString(); + updateSpeakerphoneOn(eventSource); + } + @GuardedBy("mDeviceStateLock") private void updateSpeakerphoneOn(String eventSource) { if (isSpeakerphoneOnRequested()) { @@ -488,6 +511,10 @@ import java.util.concurrent.atomic.AtomicBoolean; /*package*/ void startBluetoothScoForClient_Sync(IBinder cb, int scoAudioMode, @NonNull String eventSource) { synchronized (mDeviceStateLock) { + // Cancel speakerphone ON request by this same client: speakerphone and BT SCO routes + // are mutually exclusive. + // See symmetrical operation for setSpeakerphoneOn(true). + setSpeakerphoneOffForPid(Binder.getCallingPid()); mBtHelper.startBluetoothScoForClient(cb, scoAudioMode, eventSource); } } @@ -1379,6 +1406,16 @@ import java.util.concurrent.atomic.AtomicBoolean; return false; } + @GuardedBy("mDeviceStateLock") + private SpeakerphoneClient getSpeakerphoneClientForPid(int pid) { + for (SpeakerphoneClient cl : mSpeakerphoneClients) { + if (cl.getPid() == pid) { + return cl; + } + } + return null; + } + // List of clients requesting speakerPhone ON @GuardedBy("mDeviceStateLock") private final @NonNull ArrayList<SpeakerphoneClient> mSpeakerphoneClients = diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 366f30318cd5..23b09294260c 100755 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -7903,9 +7903,6 @@ public class AudioService extends IAudioService.Stub return null; } - mDynPolicyLogger.log((new AudioEventLogger.StringEvent("registerAudioPolicy for " - + pcb.asBinder() + " with config:" + policyConfig)).printLog(TAG)); - String regId = null; synchronized (mAudioPolicies) { if (mAudioPolicies.containsKey(pcb.asBinder())) { @@ -7916,6 +7913,13 @@ public class AudioService extends IAudioService.Stub AudioPolicyProxy app = new AudioPolicyProxy(policyConfig, pcb, hasFocusListener, isFocusPolicy, isTestFocusPolicy, isVolumeController, projection); pcb.asBinder().linkToDeath(app, 0/*flags*/); + + // logging after registration so we have the registration id + mDynPolicyLogger.log((new AudioEventLogger.StringEvent("registerAudioPolicy for " + + pcb.asBinder() + " u/pid:" + Binder.getCallingUid() + "/" + + Binder.getCallingPid() + " with config:" + app.toCompactLogString())) + .printLog(TAG)); + regId = app.getRegistrationId(); mAudioPolicies.put(pcb.asBinder(), app); } catch (RemoteException e) { @@ -8079,7 +8083,10 @@ public class AudioService extends IAudioService.Stub * @param pcb nullable because on service interface */ public void unregisterAudioPolicyAsync(@Nullable IAudioPolicyCallback pcb) { - unregisterAudioPolicy(pcb); + if (pcb == null) { + return; + } + unregisterAudioPolicyInt(pcb, "unregisterAudioPolicyAsync"); } /** @@ -8090,12 +8097,12 @@ public class AudioService extends IAudioService.Stub if (pcb == null) { return; } - unregisterAudioPolicyInt(pcb); + unregisterAudioPolicyInt(pcb, "unregisterAudioPolicy"); } - private void unregisterAudioPolicyInt(@NonNull IAudioPolicyCallback pcb) { - mDynPolicyLogger.log((new AudioEventLogger.StringEvent("unregisterAudioPolicyAsync for " + private void unregisterAudioPolicyInt(@NonNull IAudioPolicyCallback pcb, String operationName) { + mDynPolicyLogger.log((new AudioEventLogger.StringEvent(operationName + " for " + pcb.asBinder()).printLog(TAG))); synchronized (mAudioPolicies) { AudioPolicyProxy app = mAudioPolicies.remove(pcb.asBinder()); @@ -8570,7 +8577,8 @@ public class AudioService extends IAudioService.Stub } public void binderDied() { - Log.i(TAG, "audio policy " + mPolicyCallback + " died"); + mDynPolicyLogger.log((new AudioEventLogger.StringEvent("AudioPolicy " + + mPolicyCallback.asBinder() + " died").printLog(TAG))); release(); } diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index 5e8f1ef0db85..ded0f9a3dca7 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -432,19 +432,35 @@ public class BtHelper { // and this must be done on behalf of system server to make sure permissions are granted. final long ident = Binder.clearCallingIdentity(); if (client != null) { - AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource)); - client.requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, - SCO_MODE_VIRTUAL_CALL); - // If a disconnection is pending, the client will be removed whne clearAllScoClients() - // is called form receiveBtEvent() - if (mScoAudioState != SCO_STATE_DEACTIVATE_REQ - && mScoAudioState != SCO_STATE_DEACTIVATING) { - client.remove(false /*stop */, true /*unregister*/); - } + stopAndRemoveClient(client, eventSource); } Binder.restoreCallingIdentity(ident); } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") + /*package*/ synchronized void stopBluetoothScoForPid(int pid) { + ScoClient client = getScoClientForPid(pid); + if (client == null) { + return; + } + final String eventSource = new StringBuilder("stopBluetoothScoForPid(") + .append(pid).append(")").toString(); + stopAndRemoveClient(client, eventSource); + } + + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") + private void stopAndRemoveClient(ScoClient client, @NonNull String eventSource) { + AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource)); + client.requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, + SCO_MODE_VIRTUAL_CALL); + // If a disconnection is pending, the client will be removed when clearAllScoClients() + // is called form receiveBtEvent() + if (mScoAudioState != SCO_STATE_DEACTIVATE_REQ + && mScoAudioState != SCO_STATE_DEACTIVATING) { + client.remove(false /*stop */, true /*unregister*/); + } + } /*package*/ synchronized void setHearingAidVolume(int index, int streamType) { if (mHearingAid == null) { @@ -982,6 +998,16 @@ public class BtHelper { return null; } + @GuardedBy("BtHelper.this") + private ScoClient getScoClientForPid(int pid) { + for (ScoClient cl : mScoClients) { + if (cl.getPid() == pid) { + return cl; + } + } + return null; + } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") //@GuardedBy("AudioDeviceBroker.mDeviceStateLock") @GuardedBy("BtHelper.this") diff --git a/services/core/java/com/android/server/backup/SystemBackupAgent.java b/services/core/java/com/android/server/backup/SystemBackupAgent.java index 0a30b763b8f4..d98298cbef5a 100644 --- a/services/core/java/com/android/server/backup/SystemBackupAgent.java +++ b/services/core/java/com/android/server/backup/SystemBackupAgent.java @@ -20,6 +20,7 @@ import android.app.IWallpaperManager; import android.app.backup.BackupAgentHelper; import android.app.backup.BackupDataInput; import android.app.backup.BackupHelper; +import android.app.backup.BackupManager; import android.app.backup.FullBackup; import android.app.backup.FullBackupDataOutput; import android.app.backup.WallpaperBackupHelper; @@ -87,8 +88,8 @@ public class SystemBackupAgent extends BackupAgentHelper { private int mUserId = UserHandle.USER_SYSTEM; @Override - public void onCreate(UserHandle user) { - super.onCreate(user); + public void onCreate(UserHandle user, @BackupManager.OperationType int operationType) { + super.onCreate(user, operationType); mUserId = user.getIdentifier(); diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 1f85d1046523..1c93d4eb599b 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -48,6 +48,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.net.ConnectivityManager; +import android.net.DnsResolver; import android.net.INetworkManagementEventObserver; import android.net.Ikev2VpnProfile; import android.net.IpPrefix; @@ -79,6 +80,7 @@ import android.net.ipsec.ike.IkeSessionParams; import android.os.Binder; import android.os.Build.VERSION_CODES; import android.os.Bundle; +import android.os.CancellationSignal; import android.os.FileUtils; import android.os.IBinder; import android.os.INetworkManagementService; @@ -123,6 +125,7 @@ import java.math.BigInteger; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; +import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.util.ArrayList; @@ -134,6 +137,8 @@ import java.util.Objects; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -190,6 +195,7 @@ public class Vpn { // automated reconnection private final Context mContext; + @VisibleForTesting final Dependencies mDeps; private final NetworkInfo mNetworkInfo; @VisibleForTesting protected String mPackage; private int mOwnerUID; @@ -252,17 +258,143 @@ public class Vpn { // Handle of the user initiating VPN. private final int mUserHandle; + interface RetryScheduler { + void checkInterruptAndDelay(boolean sleepLonger) throws InterruptedException; + } + + static class Dependencies { + public void startService(final String serviceName) { + SystemService.start(serviceName); + } + + public void stopService(final String serviceName) { + SystemService.stop(serviceName); + } + + public boolean isServiceRunning(final String serviceName) { + return SystemService.isRunning(serviceName); + } + + public boolean isServiceStopped(final String serviceName) { + return SystemService.isStopped(serviceName); + } + + public File getStateFile() { + return new File("/data/misc/vpn/state"); + } + + public void sendArgumentsToDaemon( + final String daemon, final LocalSocket socket, final String[] arguments, + final RetryScheduler retryScheduler) throws IOException, InterruptedException { + final LocalSocketAddress address = new LocalSocketAddress( + daemon, LocalSocketAddress.Namespace.RESERVED); + + // Wait for the socket to connect. + while (true) { + try { + socket.connect(address); + break; + } catch (Exception e) { + // ignore + } + retryScheduler.checkInterruptAndDelay(true /* sleepLonger */); + } + socket.setSoTimeout(500); + + final OutputStream out = socket.getOutputStream(); + for (String argument : arguments) { + byte[] bytes = argument.getBytes(StandardCharsets.UTF_8); + if (bytes.length >= 0xFFFF) { + throw new IllegalArgumentException("Argument is too large"); + } + out.write(bytes.length >> 8); + out.write(bytes.length); + out.write(bytes); + retryScheduler.checkInterruptAndDelay(false /* sleepLonger */); + } + out.write(0xFF); + out.write(0xFF); + + // Wait for End-of-File. + final InputStream in = socket.getInputStream(); + while (true) { + try { + if (in.read() == -1) { + break; + } + } catch (Exception e) { + // ignore + } + retryScheduler.checkInterruptAndDelay(true /* sleepLonger */); + } + } + + @NonNull + public InetAddress resolve(final String endpoint) + throws ExecutionException, InterruptedException { + try { + return InetAddress.parseNumericAddress(endpoint); + } catch (IllegalArgumentException e) { + // Endpoint is not numeric : fall through and resolve + } + + final CancellationSignal cancellationSignal = new CancellationSignal(); + try { + final DnsResolver resolver = DnsResolver.getInstance(); + final CompletableFuture<InetAddress> result = new CompletableFuture(); + final DnsResolver.Callback<List<InetAddress>> cb = + new DnsResolver.Callback<List<InetAddress>>() { + @Override + public void onAnswer(@NonNull final List<InetAddress> answer, + final int rcode) { + if (answer.size() > 0) { + result.complete(answer.get(0)); + } else { + result.completeExceptionally( + new UnknownHostException(endpoint)); + } + } + + @Override + public void onError(@Nullable final DnsResolver.DnsException error) { + // Unfortunately UnknownHostException doesn't accept a cause, so + // print a message here instead. Only show the summary, not the + // full stack trace. + Log.e(TAG, "Async dns resolver error : " + error); + result.completeExceptionally(new UnknownHostException(endpoint)); + } + }; + resolver.query(null /* network, null for default */, endpoint, + DnsResolver.FLAG_EMPTY, r -> r.run(), cancellationSignal, cb); + return result.get(); + } catch (final ExecutionException e) { + Log.e(TAG, "Cannot resolve VPN endpoint : " + endpoint + ".", e); + throw e; + } catch (final InterruptedException e) { + Log.e(TAG, "Legacy VPN was interrupted while resolving the endpoint", e); + cancellationSignal.cancel(); + throw e; + } + } + + public boolean checkInterfacePresent(final Vpn vpn, final String iface) { + return vpn.jniCheck(iface) == 0; + } + } + public Vpn(Looper looper, Context context, INetworkManagementService netService, @UserIdInt int userHandle, @NonNull KeyStore keyStore) { - this(looper, context, netService, userHandle, keyStore, + this(looper, context, new Dependencies(), netService, userHandle, keyStore, new SystemServices(context), new Ikev2SessionCreator()); } @VisibleForTesting - protected Vpn(Looper looper, Context context, INetworkManagementService netService, + protected Vpn(Looper looper, Context context, Dependencies deps, + INetworkManagementService netService, int userHandle, @NonNull KeyStore keyStore, SystemServices systemServices, Ikev2SessionCreator ikev2SessionCreator) { mContext = context; + mDeps = deps; mNetd = netService; mUserHandle = userHandle; mLooper = looper; @@ -2129,7 +2261,8 @@ public class Vpn { } /** This class represents the common interface for all VPN runners. */ - private abstract class VpnRunner extends Thread { + @VisibleForTesting + abstract class VpnRunner extends Thread { protected VpnRunner(String name) { super(name); @@ -2638,7 +2771,7 @@ public class Vpn { } catch (InterruptedException e) { } for (String daemon : mDaemons) { - SystemService.stop(daemon); + mDeps.stopService(daemon); } } agentDisconnect(); @@ -2655,21 +2788,55 @@ public class Vpn { } } + private void checkAndFixupArguments(@NonNull final InetAddress endpointAddress) { + final String endpointAddressString = endpointAddress.getHostAddress(); + // Perform some safety checks before inserting the address in place. + // Position 0 in mDaemons and mArguments must be racoon, and position 1 must be mtpd. + if (!"racoon".equals(mDaemons[0]) || !"mtpd".equals(mDaemons[1])) { + throw new IllegalStateException("Unexpected daemons order"); + } + + // Respectively, the positions at which racoon and mtpd take the server address + // argument are 1 and 2. Not all types of VPN require both daemons however, and + // in that case the corresponding argument array is null. + if (mArguments[0] != null) { + if (!mProfile.server.equals(mArguments[0][1])) { + throw new IllegalStateException("Invalid server argument for racoon"); + } + mArguments[0][1] = endpointAddressString; + } + + if (mArguments[1] != null) { + if (!mProfile.server.equals(mArguments[1][2])) { + throw new IllegalStateException("Invalid server argument for mtpd"); + } + mArguments[1][2] = endpointAddressString; + } + } + private void bringup() { // Catch all exceptions so we can clean up a few things. try { + // resolve never returns null. If it does because of some bug, it will be + // caught by the catch() block below and cleanup gracefully. + final InetAddress endpointAddress = mDeps.resolve(mProfile.server); + + // Big hack : dynamically replace the address of the server in the arguments + // with the resolved address. + checkAndFixupArguments(endpointAddress); + // Initialize the timer. mBringupStartTime = SystemClock.elapsedRealtime(); // Wait for the daemons to stop. for (String daemon : mDaemons) { - while (!SystemService.isStopped(daemon)) { + while (!mDeps.isServiceStopped(daemon)) { checkInterruptAndDelay(true); } } // Clear the previous state. - File state = new File("/data/misc/vpn/state"); + final File state = mDeps.getStateFile(); state.delete(); if (state.exists()) { throw new IllegalStateException("Cannot delete the state"); @@ -2696,57 +2863,19 @@ public class Vpn { // Start the daemon. String daemon = mDaemons[i]; - SystemService.start(daemon); + mDeps.startService(daemon); // Wait for the daemon to start. - while (!SystemService.isRunning(daemon)) { + while (!mDeps.isServiceRunning(daemon)) { checkInterruptAndDelay(true); } // Create the control socket. mSockets[i] = new LocalSocket(); - LocalSocketAddress address = new LocalSocketAddress( - daemon, LocalSocketAddress.Namespace.RESERVED); - - // Wait for the socket to connect. - while (true) { - try { - mSockets[i].connect(address); - break; - } catch (Exception e) { - // ignore - } - checkInterruptAndDelay(true); - } - mSockets[i].setSoTimeout(500); - - // Send over the arguments. - OutputStream out = mSockets[i].getOutputStream(); - for (String argument : arguments) { - byte[] bytes = argument.getBytes(StandardCharsets.UTF_8); - if (bytes.length >= 0xFFFF) { - throw new IllegalArgumentException("Argument is too large"); - } - out.write(bytes.length >> 8); - out.write(bytes.length); - out.write(bytes); - checkInterruptAndDelay(false); - } - out.write(0xFF); - out.write(0xFF); - - // Wait for End-of-File. - InputStream in = mSockets[i].getInputStream(); - while (true) { - try { - if (in.read() == -1) { - break; - } - } catch (Exception e) { - // ignore - } - checkInterruptAndDelay(true); - } + + // Wait for the socket to connect and send over the arguments. + mDeps.sendArgumentsToDaemon(daemon, mSockets[i], arguments, + this::checkInterruptAndDelay); } // Wait for the daemons to create the new state. @@ -2754,7 +2883,7 @@ public class Vpn { // Check if a running daemon is dead. for (int i = 0; i < mDaemons.length; ++i) { String daemon = mDaemons[i]; - if (mArguments[i] != null && !SystemService.isRunning(daemon)) { + if (mArguments[i] != null && !mDeps.isServiceRunning(daemon)) { throw new IllegalStateException(daemon + " is dead"); } } @@ -2764,7 +2893,8 @@ public class Vpn { // Now we are connected. Read and parse the new state. String[] parameters = FileUtils.readTextFile(state, 0, null).split("\n", -1); if (parameters.length != 7) { - throw new IllegalStateException("Cannot parse the state"); + throw new IllegalStateException("Cannot parse the state: '" + + String.join("', '", parameters) + "'"); } // Set the interface and the addresses in the config. @@ -2793,20 +2923,15 @@ public class Vpn { } // Add a throw route for the VPN server endpoint, if one was specified. - String endpoint = parameters[5].isEmpty() ? mProfile.server : parameters[5]; - if (!endpoint.isEmpty()) { - try { - InetAddress addr = InetAddress.parseNumericAddress(endpoint); - if (addr instanceof Inet4Address) { - mConfig.routes.add(new RouteInfo(new IpPrefix(addr, 32), RTN_THROW)); - } else if (addr instanceof Inet6Address) { - mConfig.routes.add(new RouteInfo(new IpPrefix(addr, 128), RTN_THROW)); - } else { - Log.e(TAG, "Unknown IP address family for VPN endpoint: " + endpoint); - } - } catch (IllegalArgumentException e) { - Log.e(TAG, "Exception constructing throw route to " + endpoint + ": " + e); - } + if (endpointAddress instanceof Inet4Address) { + mConfig.routes.add(new RouteInfo( + new IpPrefix(endpointAddress, 32), RTN_THROW)); + } else if (endpointAddress instanceof Inet6Address) { + mConfig.routes.add(new RouteInfo( + new IpPrefix(endpointAddress, 128), RTN_THROW)); + } else { + Log.e(TAG, "Unknown IP address family for VPN endpoint: " + + endpointAddress); } // Here is the last step and it must be done synchronously. @@ -2818,7 +2943,7 @@ public class Vpn { checkInterruptAndDelay(false); // Check if the interface is gone while we are waiting. - if (jniCheck(mConfig.interfaze) == 0) { + if (mDeps.checkInterfacePresent(Vpn.this, mConfig.interfaze)) { throw new IllegalStateException(mConfig.interfaze + " is gone"); } @@ -2849,7 +2974,7 @@ public class Vpn { while (true) { Thread.sleep(2000); for (int i = 0; i < mDaemons.length; i++) { - if (mArguments[i] != null && SystemService.isStopped(mDaemons[i])) { + if (mArguments[i] != null && mDeps.isServiceStopped(mDaemons[i])) { return; } } diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java index 6f12155c5ec6..b8e579d58794 100644 --- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java +++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java @@ -349,9 +349,7 @@ public abstract class BrightnessMappingStrategy { // Normalize entire brightness range to 0 - 1. protected static float normalizeAbsoluteBrightness(int brightness) { - return BrightnessSynchronizer.brightnessIntToFloat(brightness, - PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON, - PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX); + return BrightnessSynchronizer.brightnessIntToFloat(brightness); } private Pair<float[], float[]> insertControlPoint( diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 0979ad67a8cd..397358b44362 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -2550,8 +2550,7 @@ public final class DisplayManagerService extends SystemService { public boolean requestPowerState(DisplayPowerRequest request, boolean waitForNegativeProximity) { synchronized (mSyncRoot) { - return mDisplayPowerController.requestPowerState(request, - waitForNegativeProximity); + return mDisplayPowerController.requestPowerState(request, waitForNegativeProximity); } } @@ -2679,6 +2678,10 @@ public final class DisplayManagerService extends SystemService { return getDisplayedContentSampleInternal(displayId, maxFrames, timestamp); } + @Override + public void ignoreProximitySensorUntilChanged() { + mDisplayPowerController.ignoreProximitySensorUntilChanged(); + } } class DesiredDisplayModeSpecsObserver diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 9411c5629457..58ef9d11ef97 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -117,6 +117,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private static final int MSG_CONFIGURE_BRIGHTNESS = 5; private static final int MSG_SET_TEMPORARY_BRIGHTNESS = 6; private static final int MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT = 7; + private static final int MSG_IGNORE_PROXIMITY = 8; private static final int PROXIMITY_UNKNOWN = -1; private static final int PROXIMITY_NEGATIVE = 0; @@ -263,6 +264,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // go to sleep by the user. While true, the screen remains off. private boolean mWaitingForNegativeProximity; + // True if the device should not take into account the proximity sensor + // until either the proximity sensor state changes, or there is no longer a + // request to listen to proximity sensor. + private boolean mIgnoreProximityUntilChanged; + // The actual proximity sensor threshold value. private float mProximityThreshold; @@ -699,13 +705,13 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Initialize screen state for battery stats. try { mBatteryStats.noteScreenState(mPowerState.getScreenState()); - mBatteryStats.noteScreenBrightness(BrightnessSynchronizer.brightnessFloatToInt(mContext, + mBatteryStats.noteScreenBrightness(BrightnessSynchronizer.brightnessFloatToInt( mPowerState.getScreenBrightness())); } catch (RemoteException ex) { // same process } // Initialize all of the brightness tracking state - final float brightness = convertToNits(BrightnessSynchronizer.brightnessFloatToInt(mContext, + final float brightness = convertToNits(BrightnessSynchronizer.brightnessFloatToInt( mPowerState.getScreenBrightness())); if (brightness >= 0.0f) { mBrightnessTracker.start(brightness); @@ -760,8 +766,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (mPowerRequest == null) { mPowerRequest = new DisplayPowerRequest(mPendingRequestLocked); - mWaitingForNegativeProximity = mPendingWaitForNegativeProximityLocked; - mPendingWaitForNegativeProximityLocked = false; + updatePendingProximityRequestsLocked(); mPendingRequestChangedLocked = false; mustInitialize = true; // Assume we're on and bright until told otherwise, since that's the state we turn @@ -770,8 +775,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } else if (mPendingRequestChangedLocked) { previousPolicy = mPowerRequest.policy; mPowerRequest.copyFrom(mPendingRequestLocked); - mWaitingForNegativeProximity |= mPendingWaitForNegativeProximityLocked; - mPendingWaitForNegativeProximityLocked = false; + updatePendingProximityRequestsLocked(); mPendingRequestChangedLocked = false; mDisplayReadyLocked = false; } else { @@ -822,9 +826,16 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Apply the proximity sensor. if (mProximitySensor != null) { if (mPowerRequest.useProximitySensor && state != Display.STATE_OFF) { + // At this point the policy says that the screen should be on, but we've been + // asked to listen to the prox sensor to adjust the display state, so lets make + // sure the sensor is on. setProximitySensorEnabled(true); if (!mScreenOffBecauseOfProximity - && mProximity == PROXIMITY_POSITIVE) { + && mProximity == PROXIMITY_POSITIVE + && !mIgnoreProximityUntilChanged) { + // Prox sensor already reporting "near" so we should turn off the screen. + // Also checked that we aren't currently set to ignore the proximity sensor + // temporarily. mScreenOffBecauseOfProximity = true; sendOnProximityPositiveWithWakelock(); } @@ -832,18 +843,28 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call && mScreenOffBecauseOfProximity && mProximity == PROXIMITY_POSITIVE && state != Display.STATE_OFF) { + // The policy says that we should have the screen on, but it's off due to the prox + // and we've been asked to wait until the screen is far from the user to turn it + // back on. Let keep the prox sensor on so we can tell when it's far again. setProximitySensorEnabled(true); } else { + // We haven't been asked to use the prox sensor and we're not waiting on the screen + // to turn back on...so lets shut down the prox sensor. setProximitySensorEnabled(false); mWaitingForNegativeProximity = false; } + if (mScreenOffBecauseOfProximity - && mProximity != PROXIMITY_POSITIVE) { + && (mProximity != PROXIMITY_POSITIVE || mIgnoreProximityUntilChanged)) { + // The screen *was* off due to prox being near, but now it's "far" so lets turn + // the screen back on. Also turn it back on if we've been asked to ignore the + // prox sensor temporarily. mScreenOffBecauseOfProximity = false; sendOnProximityNegativeWithWakelock(); } } else { mWaitingForNegativeProximity = false; + mIgnoreProximityUntilChanged = false; } if (mScreenOffBecauseOfProximity) { state = Display.STATE_OFF; @@ -1093,7 +1114,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call userInitiatedChange = false; } notifyBrightnessChanged( - BrightnessSynchronizer.brightnessFloatToInt(mContext, brightnessState), + BrightnessSynchronizer.brightnessFloatToInt(brightnessState), userInitiatedChange, hadUserBrightnessPoint); } @@ -1181,6 +1202,14 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call sendUpdatePowerState(); } + /** + * Ignores the proximity sensor until the sensor state changes, but only if the sensor is + * currently enabled and forcing the screen to be dark. + */ + public void ignoreProximitySensorUntilChanged() { + mHandler.sendEmptyMessage(MSG_IGNORE_PROXIMITY); + } + public void setBrightnessConfiguration(BrightnessConfiguration c) { Message msg = mHandler.obtainMessage(MSG_CONFIGURE_BRIGHTNESS, c); msg.sendToTarget(); @@ -1341,8 +1370,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call try { // TODO(brightnessfloat): change BatteryStats to use float mBatteryStats.noteScreenBrightness( - BrightnessSynchronizer.brightnessFloatToInt( - mContext, target)); + BrightnessSynchronizer.brightnessFloatToInt(target)); } catch (RemoteException ex) { // same process } @@ -1529,6 +1557,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Register the listener. // Proximity sensor state already cleared initially. mProximitySensorEnabled = true; + mIgnoreProximityUntilChanged = false; mSensorManager.registerListener(mProximitySensorListener, mProximitySensor, SensorManager.SENSOR_DELAY_NORMAL, mHandler); } @@ -1538,6 +1567,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Clear the proximity sensor state for next time. mProximitySensorEnabled = false; mProximity = PROXIMITY_UNKNOWN; + mIgnoreProximityUntilChanged = false; mPendingProximity = PROXIMITY_UNKNOWN; mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED); mSensorManager.unregisterListener(mProximitySensorListener); @@ -1580,6 +1610,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call && mPendingProximityDebounceTime >= 0) { final long now = SystemClock.uptimeMillis(); if (mPendingProximityDebounceTime <= now) { + if (mProximity != mPendingProximity) { + // if the status of the sensor changed, stop ignoring. + mIgnoreProximityUntilChanged = false; + Slog.i(TAG, "No longer ignoring proximity [" + mPendingProximity + "]"); + } // Sensor reading accepted. Apply the change then release the wake lock. mProximity = mPendingProximity; updatePowerState(); @@ -1723,6 +1758,27 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } } + private void updatePendingProximityRequestsLocked() { + mWaitingForNegativeProximity |= mPendingWaitForNegativeProximityLocked; + mPendingWaitForNegativeProximityLocked = false; + + if (mIgnoreProximityUntilChanged) { + // Also, lets stop waiting for negative proximity if we're ignoring it. + mWaitingForNegativeProximity = false; + } + } + + private void ignoreProximitySensorUntilChangedInternal() { + if (!mIgnoreProximityUntilChanged + && mPowerRequest.useProximitySensor + && mProximity == PROXIMITY_POSITIVE) { + // Only ignore if it is still reporting positive (near) + mIgnoreProximityUntilChanged = true; + Slog.i(TAG, "Ignoring proximity"); + updatePowerState(); + } + } + private final Runnable mOnStateChangedRunnable = new Runnable() { @Override public void run() { @@ -1961,6 +2017,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mTemporaryAutoBrightnessAdjustment = Float.intBitsToFloat(msg.arg1); updatePowerState(); break; + + case MSG_IGNORE_PROXIMITY: + ignoreProximitySensorUntilChangedInternal(); + break; } } } diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 48fa1bf9f246..0be428bdba47 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -728,16 +728,14 @@ final class LocalDisplayAdapter extends DisplayAdapter { try { if (isHalBrightnessRangeSpecified()) { brightness = displayBrightnessToHalBrightness( - BrightnessSynchronizer.brightnessFloatToIntRange( - getContext(), brightness)); + BrightnessSynchronizer.brightnessFloatToIntRange(brightness)); } if (mBacklight != null) { mBacklight.setBrightness(brightness); } Trace.traceCounter(Trace.TRACE_TAG_POWER, "ScreenBrightness", - BrightnessSynchronizer.brightnessFloatToInt( - getContext(), brightness)); + BrightnessSynchronizer.brightnessFloatToInt(brightness)); } finally { Trace.traceEnd(Trace.TRACE_TAG_POWER); } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java index 596c1eccfabe..d675b81629a4 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java @@ -232,11 +232,12 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource { mAutoTvOff = enabled; } + @Override @ServiceThreadOnly @VisibleForTesting void setIsActiveSource(boolean on) { assertRunOnServiceThread(); - mIsActiveSource = on; + super.setIsActiveSource(on); if (on) { getWakeLock().acquire(); } else { @@ -274,19 +275,15 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource { @Override @ServiceThreadOnly - protected boolean handleActiveSource(HdmiCecMessage message) { - super.handleActiveSource(message); - if (mIsActiveSource) { - return true; - } + protected void onActiveSourceLost() { + assertRunOnServiceThread(); switch (mPowerStateChangeOnActiveSourceLost) { case STANDBY_NOW: mService.standby(); - return true; + return; case NONE: - return true; + return; } - return true; } @ServiceThreadOnly @@ -398,9 +395,12 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource { } @Override + @ServiceThreadOnly protected void handleRoutingChangeAndInformation(int physicalAddress, HdmiCecMessage message) { + assertRunOnServiceThread(); if (physicalAddress != mService.getPhysicalAddress()) { - return; // Do nothing. + setActiveSource(physicalAddress); + return; } switch (mPlaybackDeviceActionOnRoutingControl) { case WAKE_UP_AND_SEND_ACTIVE_SOURCE: diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java index 1c677184b9d2..44ad8eea65ca 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java @@ -114,6 +114,19 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { } @ServiceThreadOnly + protected void onActiveSourceLost() { + // Nothing to do. + } + + @ServiceThreadOnly + protected void setActiveSource(int physicalAddress) { + assertRunOnServiceThread(); + // Invalidate the internal active source record. This will also update mIsActiveSource. + ActiveSource activeSource = ActiveSource.of(Constants.ADDR_INVALID, physicalAddress); + setActiveSource(activeSource); + } + + @ServiceThreadOnly protected boolean handleActiveSource(HdmiCecMessage message) { assertRunOnServiceThread(); int logicalAddress = message.getSource(); @@ -148,6 +161,9 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { if (physicalAddress == mService.getPhysicalAddress() && mService.isPlaybackDevice()) { setAndBroadcastActiveSource(message, physicalAddress); } + if (physicalAddress != mService.getPhysicalAddress()) { + setActiveSource(physicalAddress); + } switchInputOnReceivingNewActivePath(physicalAddress); return true; } @@ -156,18 +172,21 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { @ServiceThreadOnly protected boolean handleRoutingChange(HdmiCecMessage message) { assertRunOnServiceThread(); + int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams(), 2); + if (physicalAddress != mService.getPhysicalAddress()) { + setActiveSource(physicalAddress); + } if (!isRoutingControlFeatureEnabled()) { mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED); return true; } - int newPath = HdmiUtils.twoBytesToInt(message.getParams(), 2); // if the current device is a pure playback device if (!mIsSwitchDevice - && newPath == mService.getPhysicalAddress() + && physicalAddress == mService.getPhysicalAddress() && mService.isPlaybackDevice()) { - setAndBroadcastActiveSource(message, newPath); + setAndBroadcastActiveSource(message, physicalAddress); } - handleRoutingChangeAndInformation(newPath, message); + handleRoutingChangeAndInformation(physicalAddress, message); return true; } @@ -175,11 +194,14 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { @ServiceThreadOnly protected boolean handleRoutingInformation(HdmiCecMessage message) { assertRunOnServiceThread(); + int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams()); + if (physicalAddress != mService.getPhysicalAddress()) { + setActiveSource(physicalAddress); + } if (!isRoutingControlFeatureEnabled()) { mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED); return true; } - int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams()); // if the current device is a pure playback device if (!mIsSwitchDevice && physicalAddress == mService.getPhysicalAddress() @@ -222,7 +244,11 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { @ServiceThreadOnly void setIsActiveSource(boolean on) { assertRunOnServiceThread(); + boolean wasActiveSource = mIsActiveSource; mIsActiveSource = on; + if (wasActiveSource && !mIsActiveSource) { + onActiveSourceLost(); + } } protected void wakeUpIfActiveSource() { diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 5a4237938086..813def409c28 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -63,6 +63,7 @@ import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; import android.os.UserHandle; +import android.os.VibrationEffect; import android.provider.DeviceConfig; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; @@ -141,6 +142,8 @@ public class InputManagerService extends IInputManager.Stub private static final int MSG_RELOAD_DEVICE_ALIASES = 5; private static final int MSG_DELIVER_TABLET_MODE_CHANGED = 6; + private static final int DEFAULT_VIBRATION_MAGNITUDE = 192; + // Pointer to native input manager service object. private final long mPtr; @@ -1728,7 +1731,37 @@ public class InputManagerService extends IInputManager.Stub // Binder call @Override - public void vibrate(int deviceId, long[] pattern, int[] amplitudes, int repeat, IBinder token) { + public void vibrate(int deviceId, VibrationEffect effect, IBinder token) { + long[] pattern; + int[] amplitudes; + int repeat; + if (effect instanceof VibrationEffect.OneShot) { + VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) effect; + pattern = new long[] { 0, oneShot.getDuration() }; + int amplitude = oneShot.getAmplitude(); + // android framework uses DEFAULT_AMPLITUDE to signal that the vibration + // should use some built-in default value, denoted here as DEFAULT_VIBRATION_MAGNITUDE + if (amplitude == VibrationEffect.DEFAULT_AMPLITUDE) { + amplitude = DEFAULT_VIBRATION_MAGNITUDE; + } + amplitudes = new int[] { 0, amplitude }; + repeat = -1; + } else if (effect instanceof VibrationEffect.Waveform) { + VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) effect; + pattern = waveform.getTimings(); + amplitudes = waveform.getAmplitudes(); + for (int i = 0; i < amplitudes.length; i++) { + if (amplitudes[i] == VibrationEffect.DEFAULT_AMPLITUDE) { + amplitudes[i] = DEFAULT_VIBRATION_MAGNITUDE; + } + } + repeat = waveform.getRepeatIndex(); + } else { + // TODO: Add support for prebaked effects + Log.w(TAG, "Pre-baked effects aren't supported on input devices"); + return; + } + if (repeat >= pattern.length) { throw new ArrayIndexOutOfBoundsException(); } @@ -1747,7 +1780,6 @@ public class InputManagerService extends IInputManager.Stub mVibratorTokens.put(token, v); } } - synchronized (v) { v.mVibrating = true; nativeVibrate(mPtr, deviceId, pattern, amplitudes, repeat, v.mTokenValue); diff --git a/services/core/java/com/android/server/lights/LightsService.java b/services/core/java/com/android/server/lights/LightsService.java index c4f8441a995b..ac9b7ea0d808 100644 --- a/services/core/java/com/android/server/lights/LightsService.java +++ b/services/core/java/com/android/server/lights/LightsService.java @@ -318,8 +318,7 @@ public class LightsService extends SystemService { SurfaceControl.setDisplayBrightness(mDisplayToken, brightness); } else { // Old system - int brightnessInt = BrightnessSynchronizer.brightnessFloatToInt( - getContext(), brightness); + int brightnessInt = BrightnessSynchronizer.brightnessFloatToInt(brightness); int color = brightnessInt & 0x000000ff; color = 0xff000000 | (color << 16) | (color << 8) | color; setLightLocked(color, LIGHT_FLASH_NONE, 0, 0, brightnessMode); diff --git a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java index e17cca423822..cea5a69526c6 100644 --- a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java +++ b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java @@ -88,7 +88,7 @@ class GnssNetworkConnectivityHandler { // Default time limit in milliseconds for the ConnectivityManager to find a suitable // network with SUPL connectivity or report an error. - private static final int SUPL_NETWORK_REQUEST_TIMEOUT_MILLIS = 10 * 1000; + private static final int SUPL_NETWORK_REQUEST_TIMEOUT_MILLIS = 20 * 1000; private static final int HASH_MAP_INITIAL_CAPACITY_TO_TRACK_CONNECTED_NETWORKS = 5; diff --git a/services/core/java/com/android/server/media/HandlerExecutor.java b/services/core/java/com/android/server/media/HandlerExecutor.java new file mode 100644 index 000000000000..7c9e72bcf384 --- /dev/null +++ b/services/core/java/com/android/server/media/HandlerExecutor.java @@ -0,0 +1,45 @@ +/* + * 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. + */ + +package com.android.server.media; + +import android.annotation.NonNull; +import android.os.Handler; + +import java.util.Objects; +import java.util.concurrent.Executor; +import java.util.concurrent.RejectedExecutionException; + +/** + * An adapter {@link Executor} that posts all executed tasks onto the given + * {@link Handler}. + * + * @hide + */ +public class HandlerExecutor implements Executor { + private final Handler mHandler; + + public HandlerExecutor(@NonNull Handler handler) { + mHandler = Objects.requireNonNull(handler); + } + + @Override + public void execute(Runnable command) { + if (!mHandler.post(command)) { + throw new RejectedExecutionException(mHandler + " is shutting down"); + } + } +} diff --git a/services/core/java/com/android/server/media/MediaSession2Record.java b/services/core/java/com/android/server/media/MediaSession2Record.java index 5d1b74912546..162c388dadd1 100644 --- a/services/core/java/com/android/server/media/MediaSession2Record.java +++ b/services/core/java/com/android/server/media/MediaSession2Record.java @@ -20,7 +20,6 @@ import android.media.MediaController2; import android.media.Session2CommandGroup; import android.media.Session2Token; import android.os.Handler; -import android.os.HandlerExecutor; import android.os.Looper; import android.os.ResultReceiver; import android.os.UserHandle; diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index d71c33e6e6f5..2d052da22714 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -7288,12 +7288,12 @@ public class NotificationManagerService extends SystemService { @GuardedBy("mToastQueue") private void keepProcessAliveForToastIfNeededLocked(int pid) { - int toastCount = 0; // toasts from this pid + int toastCount = 0; // toasts from this pid, rendered by the app ArrayList<ToastRecord> list = mToastQueue; int n = list.size(); for (int i = 0; i < n; i++) { ToastRecord r = list.get(i); - if (r.pid == pid) { + if (r.pid == pid && r.keepProcessAlive()) { toastCount++; } } diff --git a/services/core/java/com/android/server/notification/toast/CustomToastRecord.java b/services/core/java/com/android/server/notification/toast/CustomToastRecord.java index 2b91a00f9da5..17e0b39ea890 100644 --- a/services/core/java/com/android/server/notification/toast/CustomToastRecord.java +++ b/services/core/java/com/android/server/notification/toast/CustomToastRecord.java @@ -71,6 +71,13 @@ public class CustomToastRecord extends ToastRecord { } @Override + public boolean keepProcessAlive() { + // As custom toasts are rendered by the app, we need to keep the app alive for it to show + // the toast. + return true; + } + + @Override public String toString() { return "CustomToastRecord{" + Integer.toHexString(System.identityHashCode(this)) diff --git a/services/core/java/com/android/server/notification/toast/ToastRecord.java b/services/core/java/com/android/server/notification/toast/ToastRecord.java index 7915f7013227..33906ccd3cd1 100644 --- a/services/core/java/com/android/server/notification/toast/ToastRecord.java +++ b/services/core/java/com/android/server/notification/toast/ToastRecord.java @@ -85,4 +85,14 @@ public abstract class ToastRecord { } pw.println(prefix + this); } + + /** + * Returns whether it's necessary to bump the process state to keep it alive in order to show + * the toast. + */ + public boolean keepProcessAlive() { + // By default we assume the toast is rendered by the systemUI. Any toast rendered by the app + // should override this method. + return false; + } } diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java index 249b6801758b..07527c2a15d8 100644 --- a/services/core/java/com/android/server/pm/ApexManager.java +++ b/services/core/java/com/android/server/pm/ApexManager.java @@ -270,11 +270,12 @@ public abstract class ApexManager { abstract boolean revertActiveSessions(); /** - * Abandons the staged session with the given sessionId. + * Abandons the staged session with the given sessionId. Client should handle {@code false} + * return value carefully as failure here can leave device in inconsistent state. * - * @return {@code true} upon success, {@code false} if any remote exception occurs + * @return {@code true} upon success, {@code false} if any exception occurs */ - abstract boolean abortStagedSession(int sessionId) throws PackageManagerException; + abstract boolean abortStagedSession(int sessionId); /** * Uninstalls given {@code apexPackage}. @@ -753,17 +754,13 @@ public abstract class ApexManager { } @Override - boolean abortStagedSession(int sessionId) throws PackageManagerException { + boolean abortStagedSession(int sessionId) { try { waitForApexService().abortStagedSession(sessionId); return true; - } catch (RemoteException re) { - Slog.e(TAG, "Unable to contact apexservice", re); - return false; } catch (Exception e) { - throw new PackageManagerException( - PackageInstaller.SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, - "Failed to abort staged session : " + e.getMessage()); + Slog.e(TAG, e.getMessage(), e); + return false; } } @@ -1122,7 +1119,7 @@ public abstract class ApexManager { } @Override - boolean abortStagedSession(int sessionId) throws PackageManagerException { + boolean abortStagedSession(int sessionId) { throw new UnsupportedOperationException(); } diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java index 92c0c6af17d4..def9c78f98c7 100644 --- a/services/core/java/com/android/server/pm/AppsFilter.java +++ b/services/core/java/com/android/server/pm/AppsFilter.java @@ -35,6 +35,9 @@ import android.content.pm.parsing.component.ParsedInstrumentation; import android.content.pm.parsing.component.ParsedIntentInfo; import android.content.pm.parsing.component.ParsedMainComponent; import android.content.pm.parsing.component.ParsedProvider; +import android.os.Handler; +import android.os.HandlerExecutor; +import android.os.HandlerThread; import android.os.Process; import android.os.Trace; import android.os.UserHandle; @@ -48,6 +51,7 @@ import android.util.SparseBooleanArray; import android.util.SparseSetArray; import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.server.FgThread; @@ -61,6 +65,7 @@ import java.util.List; import java.util.Objects; import java.util.Set; import java.util.StringTokenizer; +import java.util.concurrent.Executor; /** * The entity responsible for filtering visibility between apps based on declarations in their @@ -96,6 +101,12 @@ public class AppsFilter { private final SparseSetArray<Integer> mQueriesViaComponent = new SparseSetArray<>(); /** + * Executor for running reasonably short background tasks such as building the initial + * visibility cache. + */ + private final Executor mBackgroundExecutor; + + /** * Pending full recompute of mQueriesViaComponent. Occurs when a package adds a new set of * protected broadcast. This in turn invalidates all prior additions and require a very * computationally expensive recomputing. @@ -125,6 +136,8 @@ public class AppsFilter { private PackageParser.SigningDetails mSystemSigningDetails; private Set<String> mProtectedBroadcasts = new ArraySet<>(); + private final Object mCacheLock = new Object(); + /** * This structure maps uid -> uid and indicates whether access from the first should be * filtered to the second. It's essentially a cache of the @@ -132,6 +145,7 @@ public class AppsFilter { * NOTE: It can only be relied upon after the system is ready to avoid unnecessary update on * initial scam and is null until {@link #onSystemReady()} is called. */ + @GuardedBy("mCacheLock") private volatile SparseArray<SparseBooleanArray> mShouldFilterCache; @VisibleForTesting(visibility = PRIVATE) @@ -139,13 +153,15 @@ public class AppsFilter { FeatureConfig featureConfig, String[] forceQueryableList, boolean systemAppsQueryable, - @Nullable OverlayReferenceMapper.Provider overlayProvider) { + @Nullable OverlayReferenceMapper.Provider overlayProvider, + Executor backgroundExecutor) { mFeatureConfig = featureConfig; mForceQueryableByDevicePackageNames = forceQueryableList; mSystemAppsQueryable = systemAppsQueryable; mOverlayReferenceMapper = new OverlayReferenceMapper(true /*deferRebuild*/, overlayProvider); mStateProvider = stateProvider; + mBackgroundExecutor = backgroundExecutor; } /** @@ -337,8 +353,13 @@ public class AppsFilter { injector.getUserManagerInternal().getUserInfos()); } }; + HandlerThread appsFilterThread = new HandlerThread("appsFilter"); + appsFilterThread.start(); + Handler appsFilterHandler = new Handler(appsFilterThread.getLooper()); + Executor executor = new HandlerExecutor(appsFilterHandler); + AppsFilter appsFilter = new AppsFilter(stateProvider, featureConfig, - forcedQueryablePackageNames, forceSystemAppsQueryable, null); + forcedQueryablePackageNames, forceSystemAppsQueryable, null, executor); featureConfig.setAppsFilter(appsFilter); return appsFilter; } @@ -470,29 +491,26 @@ public class AppsFilter { if (mImplicitlyQueryable.add(recipientUid, visibleUid) && DEBUG_LOGGING) { Slog.i(TAG, "implicit access granted: " + recipientUid + " -> " + visibleUid); } - if (mShouldFilterCache != null) { - // update the cache in a one-off manner since we've got all the information we need. - SparseBooleanArray visibleUids = mShouldFilterCache.get(recipientUid); - if (visibleUids == null) { - visibleUids = new SparseBooleanArray(); - mShouldFilterCache.put(recipientUid, visibleUids); + synchronized (mCacheLock) { + if (mShouldFilterCache != null) { + // update the cache in a one-off manner since we've got all the information we + // need. + SparseBooleanArray visibleUids = mShouldFilterCache.get(recipientUid); + if (visibleUids == null) { + visibleUids = new SparseBooleanArray(); + mShouldFilterCache.put(recipientUid, visibleUids); + } + visibleUids.put(visibleUid, false); } - visibleUids.put(visibleUid, false); } } } public void onSystemReady() { - mStateProvider.runWithState(new StateProvider.CurrentStateCallback() { - @Override - public void currentState(ArrayMap<String, PackageSetting> settings, - UserInfo[] users) { - mShouldFilterCache = new SparseArray<>(users.length * settings.size()); - } - }); - mFeatureConfig.onSystemReady(); mOverlayReferenceMapper.rebuildIfDeferred(); - updateEntireShouldFilterCache(); + mFeatureConfig.onSystemReady(); + + updateEntireShouldFilterCacheAsync(); } /** @@ -510,10 +528,12 @@ public class AppsFilter { } mStateProvider.runWithState((settings, users) -> { addPackageInternal(newPkgSetting, settings); - if (mShouldFilterCache != null) { - updateShouldFilterCacheForPackage( - null, newPkgSetting, settings, users, settings.size()); - } // else, rebuild entire cache when system is ready + synchronized (mCacheLock) { + if (mShouldFilterCache != null) { + updateShouldFilterCacheForPackage(mShouldFilterCache, null, newPkgSetting, + settings, users, settings.size()); + } // else, rebuild entire cache when system is ready + } }); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); @@ -607,6 +627,7 @@ public class AppsFilter { mFeatureConfig.updatePackageState(newPkgSetting, false /*removed*/); } + @GuardedBy("mCacheLock") private void removeAppIdFromVisibilityCache(int appId) { if (mShouldFilterCache == null) { return; @@ -625,33 +646,47 @@ public class AppsFilter { } } + private void updateEntireShouldFilterCacheAsync() { + mBackgroundExecutor.execute(this::updateEntireShouldFilterCache); + } + private void updateEntireShouldFilterCache() { mStateProvider.runWithState((settings, users) -> { - mShouldFilterCache.clear(); + SparseArray<SparseBooleanArray> cache = + new SparseArray<>(users.length * settings.size()); for (int i = settings.size() - 1; i >= 0; i--) { - updateShouldFilterCacheForPackage( + updateShouldFilterCacheForPackage(cache, null /*skipPackage*/, settings.valueAt(i), settings, users, i); } + synchronized (mCacheLock) { + mShouldFilterCache = cache; + } }); } public void onUsersChanged() { - if (mShouldFilterCache != null) { - updateEntireShouldFilterCache(); + synchronized (mCacheLock) { + if (mShouldFilterCache != null) { + updateEntireShouldFilterCache(); + } } } private void updateShouldFilterCacheForPackage(String packageName) { - mStateProvider.runWithState((settings, users) -> { - updateShouldFilterCacheForPackage(null /* skipPackage */, settings.get(packageName), - settings, users, settings.size() /*maxIndex*/); - }); - + synchronized (mCacheLock) { + if (mShouldFilterCache != null) { + mStateProvider.runWithState((settings, users) -> { + updateShouldFilterCacheForPackage(mShouldFilterCache, null /* skipPackage */, + settings.get(packageName), settings, users, + settings.size() /*maxIndex*/); + }); + } + } } - private void updateShouldFilterCacheForPackage(@Nullable String skipPackageName, - PackageSetting subjectSetting, ArrayMap<String, PackageSetting> allSettings, - UserInfo[] allUsers, int maxIndex) { + private void updateShouldFilterCacheForPackage(SparseArray<SparseBooleanArray> cache, + @Nullable String skipPackageName, PackageSetting subjectSetting, ArrayMap<String, + PackageSetting> allSettings, UserInfo[] allUsers, int maxIndex) { for (int i = Math.min(maxIndex, allSettings.size() - 1); i >= 0; i--) { PackageSetting otherSetting = allSettings.valueAt(i); if (subjectSetting.appId == otherSetting.appId) { @@ -668,17 +703,17 @@ public class AppsFilter { for (int ou = 0; ou < userCount; ou++) { int otherUser = allUsers[ou].id; int subjectUid = UserHandle.getUid(subjectUser, subjectSetting.appId); - if (!mShouldFilterCache.contains(subjectUid)) { - mShouldFilterCache.put(subjectUid, new SparseBooleanArray(appxUidCount)); + if (!cache.contains(subjectUid)) { + cache.put(subjectUid, new SparseBooleanArray(appxUidCount)); } int otherUid = UserHandle.getUid(otherUser, otherSetting.appId); - if (!mShouldFilterCache.contains(otherUid)) { - mShouldFilterCache.put(otherUid, new SparseBooleanArray(appxUidCount)); + if (!cache.contains(otherUid)) { + cache.put(otherUid, new SparseBooleanArray(appxUidCount)); } - mShouldFilterCache.get(subjectUid).put(otherUid, + cache.get(subjectUid).put(otherUid, shouldFilterApplicationInternal( subjectUid, subjectSetting, otherSetting, otherUser)); - mShouldFilterCache.get(otherUid).put(subjectUid, + cache.get(otherUid).put(subjectUid, shouldFilterApplicationInternal( otherUid, otherSetting, subjectSetting, subjectUser)); } @@ -712,7 +747,8 @@ public class AppsFilter { * This method recomputes all component / intent-based visibility and is intended to match the * relevant logic of {@link #addPackageInternal(PackageSetting, ArrayMap)} */ - private void recomputeComponentVisibility(ArrayMap<String, PackageSetting> existingSettings) { + private void recomputeComponentVisibility( + ArrayMap<String, PackageSetting> existingSettings) { mQueriesViaComponent.clear(); for (int i = existingSettings.size() - 1; i >= 0; i--) { PackageSetting setting = existingSettings.valueAt(i); @@ -854,15 +890,17 @@ public class AppsFilter { } } - removeAppIdFromVisibilityCache(setting.appId); - if (mShouldFilterCache != null && setting.sharedUser != null) { - for (int i = setting.sharedUser.packages.size() - 1; i >= 0; i--) { - PackageSetting siblingSetting = setting.sharedUser.packages.valueAt(i); - if (siblingSetting == setting) { - continue; + synchronized (mCacheLock) { + removeAppIdFromVisibilityCache(setting.appId); + if (mShouldFilterCache != null && setting.sharedUser != null) { + for (int i = setting.sharedUser.packages.size() - 1; i >= 0; i--) { + PackageSetting siblingSetting = setting.sharedUser.packages.valueAt(i); + if (siblingSetting == setting) { + continue; + } + updateShouldFilterCacheForPackage(mShouldFilterCache, setting.name, + siblingSetting, settings, users, settings.size()); } - updateShouldFilterCacheForPackage( - setting.name, siblingSetting, settings, users, settings.size()); } } }); @@ -888,26 +926,29 @@ public class AppsFilter { || callingAppId == targetPkgSetting.appId) { return false; } - if (mShouldFilterCache != null) { // use cache - SparseBooleanArray shouldFilterTargets = mShouldFilterCache.get(callingUid); - final int targetUid = UserHandle.getUid(userId, targetPkgSetting.appId); - if (shouldFilterTargets == null) { - Slog.wtf(TAG, "Encountered calling uid with no cached rules: " + callingUid); - return true; - } - int indexOfTargetUid = shouldFilterTargets.indexOfKey(targetUid); - if (indexOfTargetUid < 0) { - Slog.w(TAG, "Encountered calling -> target with no cached rules: " - + callingUid + " -> " + targetUid); - return true; - } - if (!shouldFilterTargets.valueAt(indexOfTargetUid)) { - return false; - } - } else { - if (!shouldFilterApplicationInternal( - callingUid, callingSetting, targetPkgSetting, userId)) { - return false; + synchronized (mCacheLock) { + if (mShouldFilterCache != null) { // use cache + SparseBooleanArray shouldFilterTargets = mShouldFilterCache.get(callingUid); + final int targetUid = UserHandle.getUid(userId, targetPkgSetting.appId); + if (shouldFilterTargets == null) { + Slog.wtf(TAG, "Encountered calling uid with no cached rules: " + + callingUid); + return true; + } + int indexOfTargetUid = shouldFilterTargets.indexOfKey(targetUid); + if (indexOfTargetUid < 0) { + Slog.w(TAG, "Encountered calling -> target with no cached rules: " + + callingUid + " -> " + targetUid); + return true; + } + if (!shouldFilterTargets.valueAt(indexOfTargetUid)) { + return false; + } + } else { + if (!shouldFilterApplicationInternal( + callingUid, callingSetting, targetPkgSetting, userId)) { + return false; + } } } if (DEBUG_LOGGING || mFeatureConfig.isLoggingEnabled(callingAppId)) { diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java index 0eaac4140c14..9646b9ce8edf 100644 --- a/services/core/java/com/android/server/pm/InstantAppRegistry.java +++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java @@ -52,6 +52,7 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.XmlUtils; import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.parsing.pkg.AndroidPackage; +import com.android.server.pm.permission.PermissionManagerServiceInternal; import libcore.io.IoUtils; import libcore.util.HexEncoding; @@ -112,6 +113,7 @@ class InstantAppRegistry { private static final String ATTR_GRANTED = "granted"; private final PackageManagerService mService; + private final PermissionManagerServiceInternal mPermissionManager; private final CookiePersistence mCookiePersistence; /** State for uninstalled instant apps */ @@ -131,8 +133,10 @@ class InstantAppRegistry { @GuardedBy("mService.mLock") private SparseArray<SparseBooleanArray> mInstalledInstantAppUids; - public InstantAppRegistry(PackageManagerService service) { + public InstantAppRegistry(PackageManagerService service, + PermissionManagerServiceInternal permissionManager) { mService = service; + mPermissionManager = permissionManager; mCookiePersistence = new CookiePersistence(BackgroundThread.getHandler().getLooper()); } @@ -861,7 +865,8 @@ class InstantAppRegistry { String[] requestedPermissions = new String[pkg.getRequestedPermissions().size()]; pkg.getRequestedPermissions().toArray(requestedPermissions); - Set<String> permissions = ps.getPermissionsState().getPermissions(userId); + Set<String> permissions = mPermissionManager.getGrantedPermissions( + pkg.getPackageName(), userId); String[] grantedPermissions = new String[permissions.size()]; permissions.toArray(grantedPermissions); diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 55e7ca8ca838..840645edcb82 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -442,7 +442,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements // After reboot housekeeping. for (int i = 0; i < mSessions.size(); ++i) { PackageInstallerSession session = mSessions.valueAt(i); - session.onAfterSessionRead(); + session.onAfterSessionRead(mSessions); } } diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 7765f18300fe..ff9edd511e84 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -122,7 +122,7 @@ import android.util.ArraySet; import android.util.ExceptionUtils; import android.util.MathUtils; import android.util.Slog; -import android.util.SparseIntArray; +import android.util.SparseArray; import android.util.apk.ApkSignatureVerifier; import com.android.internal.R; @@ -169,7 +169,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private static final int MSG_STREAM_VALIDATE_AND_COMMIT = 2; private static final int MSG_INSTALL = 3; private static final int MSG_ON_PACKAGE_INSTALLED = 4; - private static final int MSG_SESSION_VERIFICATION_FAILURE = 5; + private static final int MSG_SESSION_VALIDATION_FAILURE = 5; /** XML constants used for persisting a session */ static final String TAG_SESSION = "session"; @@ -336,7 +336,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @GuardedBy("mLock") private PackageParser.SigningDetails mSigningDetails; @GuardedBy("mLock") - private SparseIntArray mChildSessionIds = new SparseIntArray(); + private SparseArray<PackageInstallerSession> mChildSessions = new SparseArray<>(); @GuardedBy("mLock") private int mParentSessionId; @@ -475,10 +475,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { packageName, returnCode, message, extras); break; - case MSG_SESSION_VERIFICATION_FAILURE: + case MSG_SESSION_VALIDATION_FAILURE: final int error = msg.arg1; final String detailMessage = (String) msg.obj; - onSessionVerificationFailure(error, detailMessage); + onSessionValidationFailure(error, detailMessage); break; } @@ -589,7 +589,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { this.mShouldBeSealed = sealed; if (childSessionIds != null) { for (int childSessionId : childSessionIds) { - mChildSessionIds.put(childSessionId, 0); + // Null values will be resolved to actual object references in + // #onAfterSessionRead later. + mChildSessions.put(childSessionId, null); } } this.mParentSessionId = parentSessionId; @@ -708,10 +710,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { info.isStaged = params.isStaged; info.rollbackDataPolicy = params.rollbackDataPolicy; info.parentSessionId = mParentSessionId; - info.childSessionIds = mChildSessionIds.copyKeys(); - if (info.childSessionIds == null) { - info.childSessionIds = EMPTY_CHILD_SESSION_ARRAY; - } + info.childSessionIds = getChildSessionIdsLocked(); info.isStagedSessionApplied = mStagedSessionApplied; info.isStagedSessionReady = mStagedSessionReady; info.isStagedSessionFailed = mStagedSessionFailed; @@ -1159,27 +1158,22 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { return; } if (isMultiPackage()) { - final SparseIntArray remainingSessions; - final int[] childSessionIds; synchronized (mLock) { - remainingSessions = mChildSessionIds.clone(); - childSessionIds = mChildSessionIds.copyKeys(); - } - final IntentSender childIntentSender = - new ChildStatusIntentReceiver(remainingSessions, statusReceiver) - .getIntentSender(); - boolean sealFailed = false; - for (int i = childSessionIds.length - 1; i >= 0; --i) { - final int childSessionId = childSessionIds[i]; - // seal all children, regardless if any of them fail; we'll throw/return - // as appropriate once all children have been processed - if (!mSessionProvider.getSession(childSessionId) - .markAsSealed(childIntentSender, forTransfer)) { - sealFailed = true; + final IntentSender childIntentSender = + new ChildStatusIntentReceiver(mChildSessions.clone(), statusReceiver) + .getIntentSender(); + boolean sealFailed = false; + for (int i = mChildSessions.size() - 1; i >= 0; --i) { + // seal all children, regardless if any of them fail; we'll throw/return + // as appropriate once all children have been processed + if (!mChildSessions.valueAt(i) + .markAsSealed(childIntentSender, forTransfer)) { + sealFailed = true; + } + } + if (sealFailed) { + return; } - } - if (sealFailed) { - return; } } @@ -1218,21 +1212,20 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } if (isMultiPackage()) { - final int[] childSessionIds; + final List<PackageInstallerSession> childSessions; synchronized (mLock) { - childSessionIds = mChildSessionIds.copyKeys(); + childSessions = getChildSessionsLocked(); } - int childCount = childSessionIds.length; + int childCount = childSessions.size(); // This will contain all child sessions that do not encounter an unrecoverable failure ArrayList<PackageInstallerSession> nonFailingSessions = new ArrayList<>(childCount); for (int i = childCount - 1; i >= 0; --i) { - final int childSessionId = childSessionIds[i]; // commit all children, regardless if any of them fail; we'll throw/return // as appropriate once all children have been processed try { - PackageInstallerSession session = mSessionProvider.getSession(childSessionId); + PackageInstallerSession session = childSessions.get(i); allSessionsReady &= session.streamValidateAndCommit(); nonFailingSessions.add(session); } catch (PackageManagerException e) { @@ -1246,14 +1239,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // the parent if (unrecoverableFailure != null) { // {@link #streamValidateAndCommit()} calls - // {@link #onSessionVerificationFailure(PackageManagerException)}, but we don't + // {@link #onSessionValidationFailure(PackageManagerException)}, but we don't // expect it to ever do so for parent sessions. Call that on this parent to clean // it up and notify listeners of the error. - onSessionVerificationFailure(unrecoverableFailure); + onSessionValidationFailure(unrecoverableFailure); // fail other child sessions that did not already fail for (int i = nonFailingSessions.size() - 1; i >= 0; --i) { PackageInstallerSession session = nonFailingSessions.get(i); - session.onSessionVerificationFailure(unrecoverableFailure); + session.onSessionValidationFailure(unrecoverableFailure); } } } @@ -1293,7 +1286,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } private class ChildStatusIntentReceiver { - private final SparseIntArray mChildSessionsRemaining; + private final SparseArray<PackageInstallerSession> mChildSessionsRemaining; private final IntentSender mStatusReceiver; private final IIntentSender.Stub mLocalSender = new IIntentSender.Stub() { @Override @@ -1303,7 +1296,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } }; - private ChildStatusIntentReceiver(SparseIntArray remainingSessions, + private ChildStatusIntentReceiver(SparseArray<PackageInstallerSession> remainingSessions, IntentSender statusReceiver) { this.mChildSessionsRemaining = remainingSessions; this.mStatusReceiver = statusReceiver; @@ -1413,8 +1406,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private boolean markAsSealed(@NonNull IntentSender statusReceiver, boolean forTransfer) { Objects.requireNonNull(statusReceiver); - List<PackageInstallerSession> childSessions = getChildSessionsNotLocked(); - synchronized (mLock) { assertCallerIsOwnerOrRootLocked(); assertPreparedAndNotDestroyedLocked("commit"); @@ -1446,7 +1437,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } try { - sealLocked(childSessions); + sealLocked(); } catch (PackageManagerException e) { return false; } @@ -1487,21 +1478,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { return true; } - /** Return a list of child sessions or null if the session is not multipackage - * - * <p> This method is handy to prevent potential deadlocks (b/123391593) - */ - private @Nullable List<PackageInstallerSession> getChildSessionsNotLocked() { - if (Thread.holdsLock(mLock)) { - Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName() - + " is holding mLock", new Throwable()); - } + @GuardedBy("mLock") + private @Nullable List<PackageInstallerSession> getChildSessionsLocked() { List<PackageInstallerSession> childSessions = null; if (isMultiPackage()) { - final int[] childSessionIds = getChildSessionIds(); - childSessions = new ArrayList<>(childSessionIds.length); - for (int childSessionId : childSessionIds) { - childSessions.add(mSessionProvider.getSession(childSessionId)); + int size = mChildSessions.size(); + childSessions = new ArrayList<>(size); + for (int i = 0; i < size; ++i) { + childSessions.add(mChildSessions.valueAt(i)); } } return childSessions; @@ -1563,23 +1547,23 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { * session was sealed this is the only possible exception. */ @GuardedBy("mLock") - private void sealLocked(List<PackageInstallerSession> childSessions) + private void sealLocked() throws PackageManagerException { try { assertNoWriteFileTransfersOpenLocked(); assertPreparedAndNotDestroyedLocked("sealing of session"); mSealed = true; - + List<PackageInstallerSession> childSessions = getChildSessionsLocked(); if (childSessions != null) { assertMultiPackageConsistencyLocked(childSessions); } } catch (PackageManagerException e) { - throw onSessionVerificationFailure(e); + throw onSessionValidationFailure(e); } catch (Throwable e) { // Convert all exceptions into package manager exceptions as only those are handled // in the code above. - throw onSessionVerificationFailure(new PackageManagerException(e)); + throw onSessionValidationFailure(new PackageManagerException(e)); } } @@ -1613,20 +1597,20 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } return true; } catch (PackageManagerException e) { - throw onSessionVerificationFailure(e); + throw onSessionValidationFailure(e); } catch (Throwable e) { // Convert all exceptions into package manager exceptions as only those are handled // in the code above. - throw onSessionVerificationFailure(new PackageManagerException(e)); + throw onSessionValidationFailure(new PackageManagerException(e)); } } - private PackageManagerException onSessionVerificationFailure(PackageManagerException e) { - onSessionVerificationFailure(e.error, ExceptionUtils.getCompleteMessage(e)); + private PackageManagerException onSessionValidationFailure(PackageManagerException e) { + onSessionValidationFailure(e.error, ExceptionUtils.getCompleteMessage(e)); return e; } - private void onSessionVerificationFailure(int error, String detailMessage) { + private void onSessionValidationFailure(int error, String detailMessage) { // Session is sealed but could not be verified, we need to destroy it. destroyInternal(); // Dispatch message to remove session from PackageInstallerService. @@ -1657,17 +1641,30 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { * * <p> This is meant to be called after all of the sessions are loaded and added to * PackageInstallerService + * + * @param allSessions All sessions loaded by PackageInstallerService, guaranteed to be + * immutable by the caller during the method call. Used to resolve child + * sessions Ids to actual object reference. */ - void onAfterSessionRead() { + void onAfterSessionRead(SparseArray<PackageInstallerSession> allSessions) { synchronized (mLock) { + // Resolve null values to actual object references + for (int i = mChildSessions.size() - 1; i >= 0; --i) { + int childSessionId = mChildSessions.keyAt(i); + PackageInstallerSession childSession = allSessions.get(childSessionId); + if (childSession != null) { + mChildSessions.setValueAt(i, childSession); + } else { + Slog.e(TAG, "Child session not existed: " + childSessionId); + mChildSessions.removeAt(i); + } + } + if (!mShouldBeSealed || isStagedAndInTerminalState()) { return; } - } - List<PackageInstallerSession> childSessions = getChildSessionsNotLocked(); - synchronized (mLock) { try { - sealLocked(childSessions); + sealLocked(); if (isApexInstallation()) { // APEX installations rely on certain fields to be populated after reboot. @@ -1708,14 +1705,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { throw new SecurityException("Can only transfer sessions that use public options"); } - List<PackageInstallerSession> childSessions = getChildSessionsNotLocked(); - synchronized (mLock) { assertCallerIsOwnerOrRootLocked(); assertPreparedAndNotSealedLocked("transfer"); try { - sealLocked(childSessions); + sealLocked(); } catch (PackageManagerException e) { throw new IllegalArgumentException("Package is not valid", e); } @@ -1746,14 +1741,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { return; } - // For a multiPackage session, read the child sessions - // outside of the lock, because reading the child - // sessions with the lock held could lead to deadlock - // (b/123391593). - List<PackageInstallerSession> childSessions = getChildSessionsNotLocked(); - try { - verifyNonStaged(childSessions); + verifyNonStaged(); } catch (PackageManagerException e) { final String completeMsg = ExceptionUtils.getCompleteMessage(e); Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg); @@ -1762,7 +1751,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } - private void verifyNonStaged(List<PackageInstallerSession> childSessions) + private void verifyNonStaged() throws PackageManagerException { final PackageManagerService.VerificationParams verifyingSession = makeVerificationParams(); @@ -1770,6 +1759,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { return; } if (isMultiPackage()) { + final List<PackageInstallerSession> childSessions; + synchronized (mLock) { + childSessions = getChildSessionsLocked(); + } List<PackageManagerService.VerificationParams> verifyingChildSessions = new ArrayList<>(childSessions.size()); boolean success = true; @@ -1803,7 +1796,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } - private void installNonStaged(List<PackageInstallerSession> childSessions) + private void installNonStaged() throws PackageManagerException { final PackageManagerService.InstallParams installingSession = makeInstallParams(); @@ -1811,6 +1804,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { return; } if (isMultiPackage()) { + final List<PackageInstallerSession> childSessions; + synchronized (mLock) { + childSessions = getChildSessionsLocked(); + } List<PackageManagerService.InstallParams> installingChildSessions = new ArrayList<>(childSessions.size()); boolean success = true; @@ -2004,9 +2001,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { return; } - List<PackageInstallerSession> childSessions = getChildSessionsNotLocked(); try { - installNonStaged(childSessions); + installNonStaged(); } catch (PackageManagerException e) { final String completeMsg = ExceptionUtils.getCompleteMessage(e); Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg); @@ -2091,7 +2087,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { if (ps == null) { return 0; } - final File apkDirOrPath = ps.codePath; + final File apkDirOrPath = ps.getCodePath(); if (apkDirOrPath == null) { return 0; } @@ -2741,15 +2737,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } - /** - * Adds a child session ID without any safety / sanity checks. This should only be used to - * build a session from XML or similar. - */ - @GuardedBy("mLock") - void addChildSessionIdLocked(int sessionId) { - mChildSessionIds.put(sessionId, 0); - } - public void open() throws IOException { if (mActiveCount.getAndIncrement() == 0) { mCallback.onSessionActiveChanged(this, true); @@ -2804,7 +2791,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { + getParentSessionId() + " and may not be abandoned directly."); } - List<PackageInstallerSession> childSessions = getChildSessionsNotLocked(); synchronized (mLock) { if (params.isStaged && mDestroyed) { // If a user abandons staged session in an unsafe state, then system will try to @@ -2828,7 +2814,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mCallback.onStagedSessionChanged(this); return; } - cleanStageDir(childSessions); + cleanStageDir(getChildSessionsLocked()); } if (mRelinquished) { @@ -2990,7 +2976,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { synchronized (mLock) { mDataLoaderFinished = true; } - dispatchSessionVerificationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE, + dispatchSessionValidationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE, "Failure to obtain data loader"); return; } @@ -3040,7 +3026,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { synchronized (mLock) { mDataLoaderFinished = true; } - dispatchSessionVerificationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE, + dispatchSessionValidationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE, "Failed to prepare image."); if (manualStartAndDestroy) { dataLoader.destroy(dataLoaderId); @@ -3061,7 +3047,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { synchronized (mLock) { mDataLoaderFinished = true; } - dispatchSessionVerificationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE, + dispatchSessionValidationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE, "DataLoader reported unrecoverable failure."); break; } @@ -3117,7 +3103,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { synchronized (mLock) { mDataLoaderFinished = true; } - dispatchSessionVerificationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE, + dispatchSessionValidationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE, "Image is missing pages required for installation."); break; } @@ -3142,15 +3128,22 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { return false; } - private void dispatchSessionVerificationFailure(int error, String detailMessage) { - mHandler.obtainMessage(MSG_SESSION_VERIFICATION_FAILURE, error, -1, + private void dispatchSessionValidationFailure(int error, String detailMessage) { + mHandler.obtainMessage(MSG_SESSION_VALIDATION_FAILURE, error, -1, detailMessage).sendToTarget(); } @GuardedBy("mLock") private int[] getChildSessionIdsLocked() { - final int[] childSessionIds = mChildSessionIds.copyKeys(); - return childSessionIds != null ? childSessionIds : EMPTY_CHILD_SESSION_ARRAY; + int size = mChildSessions.size(); + if (size == 0) { + return EMPTY_CHILD_SESSION_ARRAY; + } + final int[] childSessionIds = new int[size]; + for (int i = 0; i < size; ++i) { + childSessionIds[i] = mChildSessions.keyAt(i); + } + return childSessionIds; } @Override @@ -3205,12 +3198,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { assertCallerIsOwnerOrRootLocked(); assertPreparedAndNotSealedLocked("addChildSessionId"); - final int indexOfSession = mChildSessionIds.indexOfKey(childSessionId); + final int indexOfSession = mChildSessions.indexOfKey(childSessionId); if (indexOfSession >= 0) { return; } childSession.setParentSessionId(this.sessionId); - addChildSessionIdLocked(childSessionId); + mChildSessions.put(childSessionId, childSession); } } finally { releaseTransactionLock(); @@ -3220,30 +3213,23 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @Override public void removeChildSessionId(int sessionId) { - final PackageInstallerSession session = mSessionProvider.getSession(sessionId); - try { - acquireTransactionLock(); - if (session != null) { - session.acquireTransactionLock(); - } - - synchronized (mLock) { - assertCallerIsOwnerOrRootLocked(); - assertPreparedAndNotSealedLocked("removeChildSessionId"); + synchronized (mLock) { + assertCallerIsOwnerOrRootLocked(); + assertPreparedAndNotSealedLocked("removeChildSessionId"); - final int indexOfSession = mChildSessionIds.indexOfKey(sessionId); - if (indexOfSession < 0) { - // not added in the first place; no-op - return; - } - if (session != null) { - session.setParentSessionId(SessionInfo.INVALID_ID); - } - mChildSessionIds.removeAt(indexOfSession); + final int indexOfSession = mChildSessions.indexOfKey(sessionId); + if (indexOfSession < 0) { + // not added in the first place; no-op + return; } - } finally { - releaseTransactionLock(); - if (session != null) { + PackageInstallerSession session = mChildSessions.valueAt(indexOfSession); + try { + acquireTransactionLock(); + session.acquireTransactionLock(); + session.setParentSessionId(SessionInfo.INVALID_ID); + mChildSessions.removeAt(indexOfSession); + } finally { + releaseTransactionLock(); session.releaseTransactionLock(); } } @@ -3321,7 +3307,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { /** {@hide} */ void setStagedSessionReady() { synchronized (mLock) { - if (mDestroyed) return; // Do not allow destroyed staged session to change state + // Do not allow destroyed/failed staged session to change state + if (mDestroyed || mStagedSessionFailed) return; mStagedSessionReady = true; mStagedSessionApplied = false; mStagedSessionFailed = false; @@ -3332,33 +3319,38 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } /** {@hide} */ - void setStagedSessionFailed(@StagedSessionErrorCode int errorCode, - String errorMessage) { + void setStagedSessionFailed(@StagedSessionErrorCode int errorCode, String errorMessage) { + List<PackageInstallerSession> childSessions; synchronized (mLock) { - if (mDestroyed) return; // Do not allow destroyed staged session to change state + // Do not allow destroyed/failed staged session to change state + if (mDestroyed || mStagedSessionFailed) return; mStagedSessionReady = false; mStagedSessionApplied = false; mStagedSessionFailed = true; mStagedSessionErrorCode = errorCode; mStagedSessionErrorMessage = errorMessage; Slog.d(TAG, "Marking session " + sessionId + " as failed: " + errorMessage); + childSessions = getChildSessionsLocked(); } - cleanStageDirNotLocked(); + cleanStageDir(childSessions); mCallback.onStagedSessionChanged(this); } /** {@hide} */ void setStagedSessionApplied() { + List<PackageInstallerSession> childSessions; synchronized (mLock) { - if (mDestroyed) return; // Do not allow destroyed staged session to change state + // Do not allow destroyed/failed staged session to change state + if (mDestroyed || mStagedSessionFailed) return; mStagedSessionReady = false; mStagedSessionApplied = true; mStagedSessionFailed = false; mStagedSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR; mStagedSessionErrorMessage = ""; Slog.d(TAG, "Marking session " + sessionId + " as applied"); + childSessions = getChildSessionsLocked(); } - cleanStageDirNotLocked(); + cleanStageDir(childSessions); mCallback.onStagedSessionChanged(this); } @@ -3426,23 +3418,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } - /** - * <b>must not hold {@link #mLock}</b> - */ - private void cleanStageDirNotLocked() { - if (Thread.holdsLock(mLock)) { - Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName() - + " is holding mLock", new Throwable()); - } - cleanStageDir(getChildSessionsNotLocked()); - } - private void cleanStageDir(List<PackageInstallerSession> childSessions) { if (childSessions != null) { for (PackageInstallerSession childSession : childSessions) { - if (childSession != null) { - childSession.cleanStageDir(); - } + childSession.cleanStageDir(); } } else { cleanStageDir(); @@ -3502,7 +3481,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { pw.printPair("params.isMultiPackage", params.isMultiPackage); pw.printPair("params.isStaged", params.isStaged); pw.printPair("mParentSessionId", mParentSessionId); - pw.printPair("mChildSessionIds", mChildSessionIds); + pw.printPair("mChildSessionIds", getChildSessionIdsLocked()); pw.printPair("mStagedSessionApplied", mStagedSessionApplied); pw.printPair("mStagedSessionFailed", mStagedSessionFailed); pw.printPair("mStagedSessionReady", mStagedSessionReady); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index a726c8d26b98..a874f612e106 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -2970,7 +2970,7 @@ public class PackageManagerService extends IPackageManager.Stub mHandler = new PackageHandler(mHandlerThread.getLooper()); mProcessLoggingHandler = new ProcessLoggingHandler(); Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT); - mInstantAppRegistry = new InstantAppRegistry(this); + mInstantAppRegistry = new InstantAppRegistry(this, mPermissionManager); ArrayMap<String, SystemConfig.SharedLibraryEntry> libConfig = systemConfig.getSharedLibraries(); @@ -3010,7 +3010,7 @@ public class PackageManagerService extends IPackageManager.Stub final int packageSettingCount = mSettings.mPackages.size(); for (int i = packageSettingCount - 1; i >= 0; i--) { PackageSetting ps = mSettings.mPackages.valueAt(i); - if (!isExternal(ps) && (ps.codePath == null || !ps.codePath.exists()) + if (!isExternal(ps) && (ps.getCodePath() == null || !ps.getCodePath().exists()) && mSettings.getDisabledSystemPkgLPr(ps.name) != null) { mSettings.mPackages.removeAt(i); mSettings.enableSystemPackageLPw(ps.name); @@ -3175,11 +3175,11 @@ public class PackageManagerService extends IPackageManager.Stub logCriticalInfo(Log.WARN, "Expecting better updated system app for " + ps.name + "; removing system app. Last known" - + " codePath=" + ps.codePathString + + " codePath=" + ps.getCodePathString() + ", versionCode=" + ps.versionCode + "; scanned versionCode=" + scannedPkg.getLongVersionCode()); removePackageLI(scannedPkg, true); - mExpectingBetter.put(ps.name, ps.codePath); + mExpectingBetter.put(ps.name, ps.getCodePath()); } continue; @@ -3202,14 +3202,14 @@ public class PackageManagerService extends IPackageManager.Stub // code path, but, changes the package name. final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps.name); - if (disabledPs.codePath == null || !disabledPs.codePath.exists() + if (disabledPs.getCodePath() == null || !disabledPs.getCodePath().exists() || disabledPs.pkg == null) { possiblyDeletedUpdatedSystemApps.add(ps.name); } else { // We're expecting that the system app should remain disabled, but add // it to expecting better to recover in case the data version cannot // be scanned. - mExpectingBetter.put(disabledPs.name, disabledPs.codePath); + mExpectingBetter.put(disabledPs.name, disabledPs.getCodePath()); } } } @@ -4385,14 +4385,13 @@ public class PackageManagerService extends IPackageManager.Stub final PackageUserState state = ps.readUserState(userId); AndroidPackage p = ps.pkg; if (p != null) { - final PermissionsState permissionsState = ps.getPermissionsState(); - // Compute GIDs only if requested final int[] gids = (flags & PackageManager.GET_GIDS) == 0 - ? EMPTY_INT_ARRAY : permissionsState.computeGids(userId); + ? EMPTY_INT_ARRAY : mPermissionManager.getPackageGids(ps.name, userId); // Compute granted permissions only if package has requested permissions final Set<String> permissions = ArrayUtils.isEmpty(p.getRequestedPermissions()) - ? Collections.emptySet() : permissionsState.getPermissions(userId); + ? Collections.emptySet() + : mPermissionManager.getGrantedPermissions(ps.name, userId); PackageInfo packageInfo = PackageInfoUtils.generate(p, gids, flags, ps.firstInstallTime, ps.lastUpdateTime, permissions, state, userId, ps); @@ -4863,13 +4862,13 @@ public class PackageManagerService extends IPackageManager.Stub } // TODO: Shouldn't this be checking for package installed state for userId and // return null? - return ps.getPermissionsState().computeGids(userId); + return mPermissionManager.getPackageGids(packageName, userId); } if ((flags & MATCH_KNOWN_PACKAGES) != 0) { final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps != null && ps.isMatch(flags) && !shouldFilterApplicationLocked(ps, callingUid, userId)) { - return ps.getPermissionsState().computeGids(userId); + return mPermissionManager.getPackageGids(packageName, userId); } } } @@ -8551,6 +8550,15 @@ public class PackageManagerService extends IPackageManager.Stub if (listUninstalled) { list = new ArrayList<>(mSettings.mPackages.size()); for (PackageSetting ps : mSettings.mPackages.values()) { + if (listFactory) { + if (!ps.isSystem()) { + continue; + } + PackageSetting psDisabled = mSettings.getDisabledSystemPkgLPr(ps); + if (psDisabled != null) { + ps = psDisabled; + } + } if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) { continue; } @@ -8565,7 +8573,16 @@ public class PackageManagerService extends IPackageManager.Stub } else { list = new ArrayList<>(mPackages.size()); for (AndroidPackage p : mPackages.values()) { - final PackageSetting ps = getPackageSetting(p.getPackageName()); + PackageSetting ps = getPackageSetting(p.getPackageName()); + if (listFactory) { + if (!p.isSystem()) { + continue; + } + PackageSetting psDisabled = mSettings.getDisabledSystemPkgLPr(ps); + if (psDisabled != null) { + ps = psDisabled; + } + } if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) { continue; } @@ -9150,7 +9167,7 @@ public class PackageManagerService extends IPackageManager.Stub : getLastModifiedTime(parsedPackage); final VersionInfo settingsVersionForPackage = getSettingsVersionForPackage(parsedPackage); if (ps != null && !forceCollect - && ps.codePathString.equals(parsedPackage.getCodePath()) + && ps.getCodePathString().equals(parsedPackage.getCodePath()) && ps.timeStamp == lastModifiedTime && !isCompatSignatureUpdateNeeded(settingsVersionForPackage) && !isRecoverSignatureUpdateNeeded(settingsVersionForPackage)) { @@ -9383,8 +9400,8 @@ public class PackageManagerService extends IPackageManager.Stub } } - final boolean newPkgChangedPaths = - pkgAlreadyExists && !pkgSetting.codePathString.equals(parsedPackage.getCodePath()); + final boolean newPkgChangedPaths = pkgAlreadyExists + && !pkgSetting.getCodePathString().equals(parsedPackage.getCodePath()); final boolean newPkgVersionGreater = pkgAlreadyExists && parsedPackage.getLongVersionCode() > pkgSetting.versionCode; final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated @@ -9403,11 +9420,11 @@ public class PackageManagerService extends IPackageManager.Stub "System package updated;" + " name: " + pkgSetting.name + "; " + pkgSetting.versionCode + " --> " + parsedPackage.getLongVersionCode() - + "; " + pkgSetting.codePathString + " --> " + parsedPackage.getCodePath()); + + "; " + pkgSetting.getCodePathString() + + " --> " + parsedPackage.getCodePath()); final InstallArgs args = createInstallArgsForExisting( - pkgSetting.codePathString, - pkgSetting.resourcePathString, getAppDexInstructionSets( + pkgSetting.getCodePathString(), getAppDexInstructionSets( pkgSetting.primaryCpuAbiString, pkgSetting.secondaryCpuAbiString)); args.cleanUpResourcesLI(); synchronized (mLock) { @@ -9482,11 +9499,10 @@ public class PackageManagerService extends IPackageManager.Stub + " name: " + pkgSetting.name + "; " + pkgSetting.versionCode + " --> " + parsedPackage.getLongVersionCode() - + "; " + pkgSetting.codePathString + " --> " + + "; " + pkgSetting.getCodePathString() + " --> " + parsedPackage.getCodePath()); InstallArgs args = createInstallArgsForExisting( - pkgSetting.codePathString, - pkgSetting.resourcePathString, getAppDexInstructionSets( + pkgSetting.getCodePathString(), getAppDexInstructionSets( pkgSetting.primaryCpuAbiString, pkgSetting.secondaryCpuAbiString)); synchronized (mInstallLock) { args.cleanUpResourcesLI(); @@ -9499,7 +9515,7 @@ public class PackageManagerService extends IPackageManager.Stub logCriticalInfo(Log.INFO, "System package disabled;" + " name: " + pkgSetting.name - + "; old: " + pkgSetting.codePathString + " @ " + + "; old: " + pkgSetting.getCodePathString() + " @ " + pkgSetting.versionCode + "; new: " + parsedPackage.getCodePath() + " @ " + parsedPackage.getCodePath()); @@ -10335,6 +10351,7 @@ public class PackageManagerService extends IPackageManager.Stub mInstaller.rmPackageDir(codePath.getAbsolutePath()); if (codePathParent.getName().startsWith(RANDOM_DIR_PREFIX)) { mInstaller.rmPackageDir(codePathParent.getAbsolutePath()); + removeCachedResult(codePathParent); } } catch (InstallerException e) { Slog.w(TAG, "Failed to remove code path", e); @@ -10344,6 +10361,16 @@ public class PackageManagerService extends IPackageManager.Stub } } + private void removeCachedResult(@NonNull File codePath) { + if (mCacheDir == null) { + return; + } + + final PackageCacher cacher = new PackageCacher(mCacheDir); + // Find and delete the cached result belong to the given codePath. + cacher.cleanCachedResult(codePath); + } + private int[] resolveUserIds(int userId) { return (userId == UserHandle.USER_ALL) ? mUserManager.getUserIds() : new int[] { userId }; } @@ -11325,7 +11352,7 @@ public class PackageManagerService extends IPackageManager.Stub if (changedAbiCodePath == null) { changedAbiCodePath = new ArrayList<>(); } - changedAbiCodePath.add(ps.codePathString); + changedAbiCodePath.add(ps.getCodePathString()); } } } @@ -11421,7 +11448,6 @@ public class PackageManagerService extends IPackageManager.Stub // Initialize package source and resource directories final File destCodeFile = new File(parsedPackage.getCodePath()); - final File destResourceFile = new File(parsedPackage.getCodePath()); // We keep references to the derived CPU Abis from settings in oder to reuse // them in the case where we're not upgrading or booting for the first time. @@ -11479,7 +11505,7 @@ public class PackageManagerService extends IPackageManager.Stub // REMOVE SharedUserSetting from method; update in a separate call pkgSetting = Settings.createNewSetting(parsedPackage.getPackageName(), originalPkgSetting, disabledPkgSetting, realPkgName, sharedUserSetting, - destCodeFile, destResourceFile, parsedPackage.getNativeLibraryRootDir(), + destCodeFile, parsedPackage.getNativeLibraryRootDir(), AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage), AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage), parsedPackage.getVersionCode(), pkgFlags, pkgPrivateFlags, user, @@ -11497,7 +11523,7 @@ public class PackageManagerService extends IPackageManager.Stub // secondaryCpuAbi are not known at this point so we always update them // to null here, only to reset them at a later point. Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, sharedUserSetting, - destCodeFile, destResourceFile, parsedPackage.getNativeLibraryDir(), + destCodeFile, parsedPackage.getNativeLibraryDir(), AndroidPackageUtils.getPrimaryCpuAbi(parsedPackage, pkgSetting), AndroidPackageUtils.getSecondaryCpuAbi(parsedPackage, pkgSetting), PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting), @@ -12066,15 +12092,13 @@ public class PackageManagerService extends IPackageManager.Stub if (known != null) { if (DEBUG_PACKAGE_SCANNING) { Log.d(TAG, "Examining " + pkg.getCodePath() - + " and requiring known paths " + known.codePathString - + " & " + known.resourcePathString); + + " and requiring known path " + known.getCodePathString()); } - if (!pkg.getCodePath().equals(known.codePathString) - || !pkg.getCodePath().equals(known.resourcePathString)) { + if (!pkg.getCodePath().equals(known.getCodePathString())) { throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED, "Application package " + pkg.getPackageName() + " found at " + pkg.getCodePath() - + " but expected at " + known.codePathString + + " but expected at " + known.getCodePathString() + "; ignoring."); } } else { @@ -15586,9 +15610,8 @@ public class PackageManagerService extends IPackageManager.Stub * Create args that describe an existing installed package. Typically used * when cleaning up old installs, or used as a move source. */ - private InstallArgs createInstallArgsForExisting(String codePath, - String resourcePath, String[] instructionSets) { - return new FileInstallArgs(codePath, resourcePath, instructionSets); + private InstallArgs createInstallArgsForExisting(String codePath, String[] instructionSets) { + return new FileInstallArgs(codePath, instructionSets); } static abstract class InstallArgs { @@ -15669,10 +15692,8 @@ public class PackageManagerService extends IPackageManager.Stub abstract boolean doRename(int status, ParsedPackage parsedPackage); abstract int doPostInstall(int status, int uid); - /** @see PackageSettingBase#codePathString */ + /** @see PackageSettingBase#getCodePath() */ abstract String getCodePath(); - /** @see PackageSettingBase#resourcePathString */ - abstract String getResourcePath(); // Need installer lock especially for dex file removal. abstract void cleanUpResourcesLI(); @@ -15743,14 +15764,13 @@ public class PackageManagerService extends IPackageManager.Stub } /** Existing install */ - FileInstallArgs(String codePath, String resourcePath, String[] instructionSets) { + FileInstallArgs(String codePath, String[] instructionSets) { super(OriginInfo.fromNothing(), null, null, 0, InstallSource.EMPTY, null, null, instructionSets, null, null, null, MODE_DEFAULT, null, 0, PackageParser.SigningDetails.UNKNOWN, PackageManager.INSTALL_REASON_UNKNOWN, false, DataLoaderType.NONE); this.codeFile = (codePath != null) ? new File(codePath) : null; - this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null; } int copyApk() { @@ -15766,7 +15786,6 @@ public class PackageManagerService extends IPackageManager.Stub if (origin.staged) { if (DEBUG_INSTALL) Slog.d(TAG, origin.file + " already staged; skipping copy"); codeFile = origin.file; - resourceFile = origin.file; return PackageManager.INSTALL_SUCCEEDED; } @@ -15775,7 +15794,6 @@ public class PackageManagerService extends IPackageManager.Stub final File tempDir = mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral); codeFile = tempDir; - resourceFile = tempDir; } catch (IOException e) { Slog.w(TAG, "Failed to create copy file: " + e); return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; @@ -15846,7 +15864,6 @@ public class PackageManagerService extends IPackageManager.Stub // Reflect the rename internally codeFile = afterCodeFile; - resourceFile = afterCodeFile; // Reflect the rename in scanned details try { @@ -15875,11 +15892,6 @@ public class PackageManagerService extends IPackageManager.Stub return (codeFile != null) ? codeFile.getAbsolutePath() : null; } - @Override - String getResourcePath() { - return (resourceFile != null) ? resourceFile.getAbsolutePath() : null; - } - private boolean cleanUp() { if (codeFile == null || !codeFile.exists()) { return false; @@ -15892,10 +15904,6 @@ public class PackageManagerService extends IPackageManager.Stub removeCodePathLI(codeFile); - if (resourceFile != null && !FileUtils.contains(codeFile, resourceFile)) { - resourceFile.delete(); - } - return true; } @@ -15927,7 +15935,6 @@ public class PackageManagerService extends IPackageManager.Stub */ class MoveInstallArgs extends InstallArgs { private File codeFile; - private File resourceFile; /** New install */ MoveInstallArgs(InstallParams params) { @@ -15950,7 +15957,6 @@ public class PackageManagerService extends IPackageManager.Stub final String toPathName = new File(move.fromCodePath).getName(); codeFile = new File(Environment.getDataAppDirectory(move.toUuid), toPathName); - resourceFile = codeFile; if (DEBUG_INSTALL) Slog.d(TAG, "codeFile after move is " + codeFile); return PackageManager.INSTALL_SUCCEEDED; @@ -15987,11 +15993,6 @@ public class PackageManagerService extends IPackageManager.Stub return (codeFile != null) ? codeFile.getAbsolutePath() : null; } - @Override - String getResourcePath() { - return (resourceFile != null) ? resourceFile.getAbsolutePath() : null; - } - private boolean cleanUp(String volumeUuid) { final String toPathName = new File(move.fromCodePath).getName(); final File codeFile = new File(Environment.getDataAppDirectory(volumeUuid), @@ -16777,7 +16778,6 @@ public class PackageManagerService extends IPackageManager.Stub // installed. We need to make sure to delete the older one's .apk. res.removedInfo.args = createInstallArgsForExisting( oldPackage.getCodePath(), - oldPackage.getCodePath(), getAppDexInstructionSets( AndroidPackageUtils.getPrimaryCpuAbi(oldPackage, deletedPkgSetting), @@ -18558,7 +18558,6 @@ public class PackageManagerService extends IPackageManager.Stub // user handle installed state int[] allUsers; /** enabled state of the uninstalled application */ - final int origEnabledState; synchronized (mLock) { uninstalledPs = mSettings.mPackages.get(packageName); if (uninstalledPs == null) { @@ -18574,10 +18573,6 @@ public class PackageManagerService extends IPackageManager.Stub } disabledSystemPs = mSettings.getDisabledSystemPkgLPr(packageName); - // Save the enabled state before we delete the package. When deleting a stub - // application we always set the enabled state to 'disabled'. - origEnabledState = uninstalledPs == null - ? COMPONENT_ENABLED_STATE_DEFAULT : uninstalledPs.getEnabled(userId); // Static shared libs can be declared by any package, so let us not // allow removing a package if it provides a lib others depend on. pkg = mPackages.get(packageName); @@ -18656,20 +18651,32 @@ public class PackageManagerService extends IPackageManager.Stub if (stubPkg != null && stubPkg.isStub()) { final PackageSetting stubPs; synchronized (mLock) { - // restore the enabled state of the stub; the state is overwritten when - // the stub is uninstalled stubPs = mSettings.getPackageLPr(stubPkg.getPackageName()); - if (stubPs != null) { - stubPs.setEnabled(origEnabledState, userId, "android"); - } } - if (origEnabledState == COMPONENT_ENABLED_STATE_DEFAULT - || origEnabledState == COMPONENT_ENABLED_STATE_ENABLED) { - if (DEBUG_COMPRESSION) { - Slog.i(TAG, "Enabling system stub after removal; pkg: " - + stubPkg.getPackageName()); + + if (stubPs != null) { + boolean enable = false; + for (int aUserId : allUsers) { + if (stubPs.getInstalled(aUserId)) { + int enabled = stubPs.getEnabled(aUserId); + if (enabled == COMPONENT_ENABLED_STATE_DEFAULT + || enabled == COMPONENT_ENABLED_STATE_ENABLED) { + enable = true; + break; + } + } + } + + if (enable) { + if (DEBUG_COMPRESSION) { + Slog.i(TAG, "Enabling system stub after removal; pkg: " + + stubPkg.getPackageName()); + } + enableCompressedPackage(stubPkg, stubPs); + } else if (DEBUG_COMPRESSION) { + Slog.i(TAG, "System stub disabled for all users, leaving uncompressed " + + "after removal; pkg: " + stubPkg.getPackageName()); } - enableCompressedPackage(stubPkg, stubPs); } } } @@ -18998,7 +19005,7 @@ public class PackageManagerService extends IPackageManager.Stub // Install the system package if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs); try { - installPackageFromSystemLIF(disabledPs.codePathString, allUserHandles, + installPackageFromSystemLIF(disabledPs.getCodePathString(), allUserHandles, outInfo == null ? null : outInfo.origUsers, deletedPs.getPermissionsState(), writeSettings); } catch (PackageManagerException e) { @@ -19013,8 +19020,15 @@ public class PackageManagerService extends IPackageManager.Stub // and re-enable it afterward. final PackageSetting stubPs = mSettings.mPackages.get(deletedPkg.getPackageName()); if (stubPs != null) { - stubPs.setEnabled( - COMPONENT_ENABLED_STATE_DISABLED, UserHandle.USER_SYSTEM, "android"); + int userId = action.user == null + ? UserHandle.USER_ALL : action.user.getIdentifier(); + if (userId == UserHandle.USER_ALL) { + for (int aUserId : allUserHandles) { + stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, aUserId, "android"); + } + } else if (userId >= UserHandle.USER_SYSTEM) { + stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, userId, "android"); + } } } } @@ -19123,7 +19137,7 @@ public class PackageManagerService extends IPackageManager.Stub // Delete application code and resources only for parent packages if (deleteCodeAndResources && (outInfo != null)) { outInfo.args = createInstallArgsForExisting( - ps.codePathString, ps.resourcePathString, getAppDexInstructionSets( + ps.getCodePathString(), getAppDexInstructionSets( ps.primaryCpuAbiString, ps.secondaryCpuAbiString)); if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.args); } @@ -19660,7 +19674,7 @@ public class PackageManagerService extends IPackageManager.Stub final String[] packageNames = { packageName }; final long[] ceDataInodes = { ps.getCeDataInode(userId) }; - final String[] codePaths = { ps.codePathString }; + final String[] codePaths = { ps.getCodePathString() }; try { mInstaller.getAppSize(ps.volumeUuid, packageNames, userId, 0, @@ -22582,11 +22596,11 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mInstallLock) { final AndroidPackage pkg; try { - pkg = scanPackageTracedLI(ps.codePath, parseFlags, SCAN_INITIAL, 0, null); + pkg = scanPackageTracedLI(ps.getCodePath(), parseFlags, SCAN_INITIAL, 0, null); loaded.add(pkg); } catch (PackageManagerException e) { - Slog.w(TAG, "Failed to scan " + ps.codePath + ": " + e.getMessage()); + Slog.w(TAG, "Failed to scan " + ps.getCodePath() + ": " + e.getMessage()); } if (!Build.FINGERPRINT.equals(ver.fingerprint)) { @@ -22657,33 +22671,33 @@ public class PackageManagerService extends IPackageManager.Stub final ArrayList<AndroidPackage> unloaded = new ArrayList<>(); synchronized (mInstallLock) { - synchronized (mLock) { - final List<PackageSetting> packages = mSettings.getVolumePackagesLPr(volumeUuid); - for (PackageSetting ps : packages) { - if (ps.pkg == null) continue; - - final AndroidPackage pkg = ps.pkg; - final int deleteFlags = PackageManager.DELETE_KEEP_DATA; - final PackageRemovedInfo outInfo = new PackageRemovedInfo(this); - - try (PackageFreezer freezer = freezePackageForDelete(ps.name, deleteFlags, - "unloadPrivatePackagesInner")) { - if (deletePackageLIF(ps.name, null, false, null, deleteFlags, outInfo, - false, null)) { - unloaded.add(pkg); - } else { - Slog.w(TAG, "Failed to unload " + ps.codePath); + synchronized (mLock) { + final List<PackageSetting> packages = mSettings.getVolumePackagesLPr(volumeUuid); + for (PackageSetting ps : packages) { + if (ps.pkg == null) continue; + + final AndroidPackage pkg = ps.pkg; + final int deleteFlags = PackageManager.DELETE_KEEP_DATA; + final PackageRemovedInfo outInfo = new PackageRemovedInfo(this); + + try (PackageFreezer freezer = freezePackageForDelete(ps.name, deleteFlags, + "unloadPrivatePackagesInner")) { + if (deletePackageLIF(ps.name, null, false, null, deleteFlags, outInfo, + false, null)) { + unloaded.add(pkg); + } else { + Slog.w(TAG, "Failed to unload " + ps.getCodePath()); + } } + + // Try very hard to release any references to this package + // so we don't risk the system server being killed due to + // open FDs + AttributeCache.instance().removePackage(ps.name); } - // Try very hard to release any references to this package - // so we don't risk the system server being killed due to - // open FDs - AttributeCache.instance().removePackage(ps.name); + mSettings.writeLPr(); } - - mSettings.writeLPr(); - } } if (DEBUG_INSTALL) Slog.d(TAG, "Unloaded packages " + unloaded); @@ -22726,7 +22740,7 @@ public class PackageManagerService extends IPackageManager.Stub final int packageCount = mSettings.mPackages.size(); for (int i = 0; i < packageCount; i++) { final PackageSetting ps = mSettings.mPackages.valueAt(i); - codePaths.add(ps.codePath.getAbsolutePath()); + codePaths.add(ps.getCodePath().getAbsolutePath()); } return codePaths; } @@ -23643,8 +23657,20 @@ public class PackageManagerService extends IPackageManager.Stub } } - void onNewUserCreated(final int userId) { - mPermissionManager.onNewUserCreated(userId); + void onNewUserCreated(@UserIdInt int userId, boolean convertedFromPreCreated) { + if (DEBUG_PERMISSIONS) { + Slog.d(TAG, "onNewUserCreated(id=" + userId + + ", convertedFromPreCreated=" + convertedFromPreCreated + ")"); + } + if (!convertedFromPreCreated) { + mPermissionManager.onNewUserCreated(userId); + return; + } + if (!readPermissionStateForUser(userId)) { + // Could not read the existing permissions, re-grant them. + Slog.i(TAG, "re-granting permissions for pre-created user " + userId); + mPermissionManager.onNewUserCreated(userId); + } } boolean readPermissionStateForUser(@UserIdInt int userId) { @@ -24932,9 +24958,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public int[] getPermissionGids(String permissionName, int userId) { - synchronized (mLock) { - return getPermissionGidsLocked(permissionName, userId); - } + return mPermissionManager.getPermissionGids(permissionName, userId); } @Override @@ -25255,16 +25279,6 @@ public class PackageManagerService extends IPackageManager.Stub return null; } - @GuardedBy("mLock") - public int[] getPermissionGidsLocked(String permissionName, int userId) { - BasePermission perm - = mPermissionManager.getPermissionSettings().getPermission(permissionName); - if (perm != null) { - return perm.computeGids(userId); - } - return null; - } - @Override public int getRuntimePermissionsVersion(@UserIdInt int userId) { Preconditions.checkArgumentNonnegative(userId); @@ -25398,18 +25412,7 @@ public class PackageManagerService extends IPackageManager.Stub int mode = mInjector.getAppOpsManager().checkOpNoThrow( AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, Binder.getCallingUid(), packageName); - if (mode == MODE_ALLOWED) { - return false; - } else if (mode == MODE_IGNORED) { - return true; - } else { - synchronized (mLock) { - boolean manifestWhitelisted = - mPackages.get(packageName).getAutoRevokePermissions() - == ApplicationInfo.AUTO_REVOKE_DISALLOWED; - return manifestWhitelisted; - } - } + return mode == MODE_IGNORED; } @Override diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 668f375e2e9b..7aeec6d68d26 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -423,13 +423,15 @@ class PackageManagerShellCommand extends ShellCommand { final List<ApplicationInfo> list; if (packageName == null) { final ParceledListSlice<ApplicationInfo> packages = - mInterface.getInstalledApplications( - PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM); + mInterface.getInstalledApplications(PackageManager.MATCH_SYSTEM_ONLY + | PackageManager.MATCH_UNINSTALLED_PACKAGES, + UserHandle.USER_SYSTEM); list = packages.getList(); } else { list = new ArrayList<>(1); - list.add(mInterface.getApplicationInfo(packageName, - PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM)); + list.add(mInterface.getApplicationInfo(packageName, PackageManager.MATCH_SYSTEM_ONLY + | PackageManager.MATCH_UNINSTALLED_PACKAGES, + UserHandle.USER_SYSTEM)); } for (ApplicationInfo info : list) { if (info.isUpdatedSystemApp()) { diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java index 432d7f335ebc..a3a727367c56 100644 --- a/services/core/java/com/android/server/pm/PackageSetting.java +++ b/services/core/java/com/android/server/pm/PackageSetting.java @@ -71,13 +71,13 @@ public class PackageSetting extends PackageSettingBase { private PackageStateUnserialized pkgState = new PackageStateUnserialized(); @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) - public PackageSetting(String name, String realName, File codePath, File resourcePath, + public PackageSetting(String name, String realName, @NonNull File codePath, String legacyNativeLibraryPathString, String primaryCpuAbiString, String secondaryCpuAbiString, String cpuAbiOverrideString, long pVersionCode, int pkgFlags, int privateFlags, int sharedUserId, String[] usesStaticLibraries, long[] usesStaticLibrariesVersions, Map<String, ArraySet<String>> mimeGroups) { - super(name, realName, codePath, resourcePath, legacyNativeLibraryPathString, + super(name, realName, codePath, legacyNativeLibraryPathString, primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString, pVersionCode, pkgFlags, privateFlags, usesStaticLibraries, usesStaticLibrariesVersions); diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index 834303cc14c6..6010344b8c65 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -64,10 +64,8 @@ public abstract class PackageSettingBase extends SettingBase { * this is path to single base APK file; for cluster packages this is * path to the cluster directory. */ - File codePath; - String codePathString; - File resourcePath; - String resourcePathString; + private File mCodePath; + private String mCodePathString; String[] usesStaticLibraries; long[] usesStaticLibrariesVersions; @@ -138,7 +136,7 @@ public abstract class PackageSettingBase extends SettingBase { boolean forceQueryableOverride; - PackageSettingBase(String name, String realName, File codePath, File resourcePath, + PackageSettingBase(String name, String realName, @NonNull File codePath, String legacyNativeLibraryPathString, String primaryCpuAbiString, String secondaryCpuAbiString, String cpuAbiOverrideString, long pVersionCode, int pkgFlags, int pkgPrivateFlags, @@ -148,10 +146,7 @@ public abstract class PackageSettingBase extends SettingBase { this.realName = realName; this.usesStaticLibraries = usesStaticLibraries; this.usesStaticLibrariesVersions = usesStaticLibrariesVersions; - this.codePath = codePath; - this.codePathString = codePath.toString(); - this.resourcePath = resourcePath; - this.resourcePathString = resourcePath.toString(); + setCodePath(codePath); this.legacyNativeLibraryPathString = legacyNativeLibraryPathString; this.primaryCpuAbiString = primaryCpuAbiString; this.secondaryCpuAbiString = secondaryCpuAbiString; @@ -235,8 +230,7 @@ public abstract class PackageSettingBase extends SettingBase { } private void doCopy(PackageSettingBase orig) { - codePath = orig.codePath; - codePathString = orig.codePathString; + setCodePath(orig.getCodePath()); cpuAbiOverrideString = orig.cpuAbiOverrideString; firstInstallTime = orig.firstInstallTime; installPermissionsFixed = orig.installPermissionsFixed; @@ -246,8 +240,6 @@ public abstract class PackageSettingBase extends SettingBase { legacyNativeLibraryPathString = orig.legacyNativeLibraryPathString; // Intentionally skip mOldCodePaths; it's not relevant for copies primaryCpuAbiString = orig.primaryCpuAbiString; - resourcePath = orig.resourcePath; - resourcePathString = orig.resourcePathString; secondaryCpuAbiString = orig.secondaryCpuAbiString; signatures = orig.signatures; timeStamp = orig.timeStamp; @@ -705,6 +697,20 @@ public abstract class PackageSettingBase extends SettingBase { return userState.harmfulAppWarning; } + PackageSettingBase setCodePath(@NonNull File codePath) { + this.mCodePath = codePath; + this.mCodePathString = codePath.toString(); + return this; + } + + File getCodePath() { + return mCodePath; + } + + String getCodePathString() { + return mCodePathString; + } + /** * @see PackageUserState#overrideLabelAndIcon(ComponentName, String, Integer) * @@ -727,10 +733,7 @@ public abstract class PackageSettingBase extends SettingBase { protected PackageSettingBase updateFrom(PackageSettingBase other) { super.copyFrom(other); - this.codePath = other.codePath; - this.codePathString = other.codePathString; - this.resourcePath = other.resourcePath; - this.resourcePathString = other.resourcePathString; + setCodePath(other.getCodePath()); this.usesStaticLibraries = other.usesStaticLibraries; this.usesStaticLibrariesVersions = other.usesStaticLibrariesVersions; this.legacyNativeLibraryPathString = other.legacyNativeLibraryPathString; diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 1805713387ae..3e3e3c590491 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -538,7 +538,7 @@ public final class Settings { return null; } p.getPkgState().setUpdatedSystemApp(false); - PackageSetting ret = addPackageLPw(name, p.realName, p.codePath, p.resourcePath, + PackageSetting ret = addPackageLPw(name, p.realName, p.getCodePath(), p.legacyNativeLibraryPathString, p.primaryCpuAbiString, p.secondaryCpuAbiString, p.cpuAbiOverrideString, p.appId, p.versionCode, p.pkgFlags, p.pkgPrivateFlags, @@ -558,7 +558,7 @@ public final class Settings { mDisabledSysPackages.remove(name); } - PackageSetting addPackageLPw(String name, String realName, File codePath, File resourcePath, + PackageSetting addPackageLPw(String name, String realName, File codePath, String legacyNativeLibraryPathString, String primaryCpuAbiString, String secondaryCpuAbiString, String cpuAbiOverrideString, int uid, long vc, int pkgFlags, int pkgPrivateFlags, String[] usesStaticLibraries, @@ -572,10 +572,9 @@ public final class Settings { "Adding duplicate package, keeping first: " + name); return null; } - p = new PackageSetting(name, realName, codePath, resourcePath, - legacyNativeLibraryPathString, primaryCpuAbiString, secondaryCpuAbiString, - cpuAbiOverrideString, vc, pkgFlags, pkgPrivateFlags, - 0 /*userId*/, usesStaticLibraries, usesStaticLibraryNames, + p = new PackageSetting(name, realName, codePath, legacyNativeLibraryPathString, + primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString, vc, pkgFlags, + pkgPrivateFlags, 0 /*userId*/, usesStaticLibraries, usesStaticLibraryNames, mimeGroups); p.appId = uid; if (registerExistingAppIdLPw(uid, p, name)) { @@ -635,7 +634,7 @@ public final class Settings { */ static @NonNull PackageSetting createNewSetting(String pkgName, PackageSetting originalPkg, PackageSetting disabledPkg, String realPkgName, SharedUserSetting sharedUser, - File codePath, File resourcePath, String legacyNativeLibraryPath, String primaryCpuAbi, + File codePath, String legacyNativeLibraryPath, String primaryCpuAbi, String secondaryCpuAbi, long versionCode, int pkgFlags, int pkgPrivateFlags, UserHandle installUser, boolean allowInstall, boolean instantApp, boolean virtualPreload, UserManagerService userManager, @@ -646,12 +645,11 @@ public final class Settings { if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, "Package " + pkgName + " is adopting original package " + originalPkg.name); pkgSetting = new PackageSetting(originalPkg, pkgName /*realPkgName*/); - pkgSetting.codePath = codePath; + pkgSetting.setCodePath(codePath); pkgSetting.legacyNativeLibraryPathString = legacyNativeLibraryPath; pkgSetting.pkgFlags = pkgFlags; pkgSetting.pkgPrivateFlags = pkgPrivateFlags; pkgSetting.primaryCpuAbiString = primaryCpuAbi; - pkgSetting.resourcePath = resourcePath; pkgSetting.secondaryCpuAbiString = secondaryCpuAbi; // NOTE: Create a deeper copy of the package signatures so we don't // overwrite the signatures in the original package setting. @@ -662,7 +660,7 @@ public final class Settings { // Update new package state. pkgSetting.setTimeStamp(codePath.lastModified()); } else { - pkgSetting = new PackageSetting(pkgName, realPkgName, codePath, resourcePath, + pkgSetting = new PackageSetting(pkgName, realPkgName, codePath, legacyNativeLibraryPath, primaryCpuAbi, secondaryCpuAbi, null /*cpuAbiOverrideString*/, versionCode, pkgFlags, pkgPrivateFlags, 0 /*sharedUserId*/, usesStaticLibraries, @@ -756,10 +754,9 @@ public final class Settings { */ static void updatePackageSetting(@NonNull PackageSetting pkgSetting, @Nullable PackageSetting disabledPkg, @Nullable SharedUserSetting sharedUser, - @NonNull File codePath, File resourcePath, - @Nullable String legacyNativeLibraryPath, @Nullable String primaryCpuAbi, - @Nullable String secondaryCpuAbi, int pkgFlags, int pkgPrivateFlags, - @NonNull UserManagerService userManager, + @NonNull File codePath, @Nullable String legacyNativeLibraryPath, + @Nullable String primaryCpuAbi, @Nullable String secondaryCpuAbi, int pkgFlags, + int pkgPrivateFlags, @NonNull UserManagerService userManager, @Nullable String[] usesStaticLibraries, @Nullable long[] usesStaticLibrariesVersions, @Nullable Set<String> mimeGroupNames) throws PackageManagerException { @@ -773,12 +770,12 @@ public final class Settings { "Updating application package " + pkgName + " failed"); } - if (!pkgSetting.codePath.equals(codePath)) { + if (!pkgSetting.getCodePath().equals(codePath)) { final boolean isSystem = pkgSetting.isSystem(); Slog.i(PackageManagerService.TAG, "Update" + (isSystem ? " system" : "") + " package " + pkgName - + " code path from " + pkgSetting.codePathString + + " code path from " + pkgSetting.getCodePathString() + " to " + codePath.toString() + "; Retain data and using new"); if (!isSystem) { @@ -800,19 +797,7 @@ public final class Settings { // internal to external storage or vice versa. pkgSetting.legacyNativeLibraryPathString = legacyNativeLibraryPath; } - pkgSetting.codePath = codePath; - pkgSetting.codePathString = codePath.toString(); - } - if (!pkgSetting.resourcePath.equals(resourcePath)) { - final boolean isSystem = pkgSetting.isSystem(); - Slog.i(PackageManagerService.TAG, - "Update" + (isSystem ? " system" : "") - + " package " + pkgName - + " resource path from " + pkgSetting.resourcePathString - + " to " + resourcePath.toString() - + "; Retain data and using new"); - pkgSetting.resourcePath = resourcePath; - pkgSetting.resourcePathString = resourcePath.toString(); + pkgSetting.setCodePath(codePath); } // If what we are scanning is a system (and possibly privileged) package, // then make it so, regardless of whether it was previously installed only @@ -2710,7 +2695,7 @@ public final class Settings { private void writePackageListLPrInternal(int creatingUserId) { // Only derive GIDs for active users (not dying) - final List<UserInfo> users = getUsers(UserManagerService.getInstance(), true); + final List<UserInfo> users = getActiveUsers(UserManagerService.getInstance(), true); int[] userIds = new int[users.size()]; for (int i = 0; i < userIds.length; i++) { userIds[i] = users.get(i).id; @@ -2812,14 +2797,11 @@ public final class Settings { if (pkg.realName != null) { serializer.attribute(null, "realName", pkg.realName); } - serializer.attribute(null, "codePath", pkg.codePathString); + serializer.attribute(null, "codePath", pkg.getCodePathString()); serializer.attribute(null, "ft", Long.toHexString(pkg.timeStamp)); serializer.attribute(null, "it", Long.toHexString(pkg.firstInstallTime)); serializer.attribute(null, "ut", Long.toHexString(pkg.lastUpdateTime)); serializer.attribute(null, "version", String.valueOf(pkg.versionCode)); - if (!pkg.resourcePathString.equals(pkg.codePathString)) { - serializer.attribute(null, "resourcePath", pkg.resourcePathString); - } if (pkg.legacyNativeLibraryPathString != null) { serializer.attribute(null, "nativeLibraryPath", pkg.legacyNativeLibraryPathString); } @@ -2857,10 +2839,7 @@ public final class Settings { if (pkg.realName != null) { serializer.attribute(null, "realName", pkg.realName); } - serializer.attribute(null, "codePath", pkg.codePathString); - if (!pkg.resourcePathString.equals(pkg.codePathString)) { - serializer.attribute(null, "resourcePath", pkg.resourcePathString); - } + serializer.attribute(null, "codePath", pkg.getCodePathString()); if (pkg.legacyNativeLibraryPathString != null) { serializer.attribute(null, "nativeLibraryPath", pkg.legacyNativeLibraryPathString); @@ -3559,13 +3538,10 @@ public final class Settings { String name = parser.getAttributeValue(null, ATTR_NAME); String realName = parser.getAttributeValue(null, "realName"); String codePathStr = parser.getAttributeValue(null, "codePath"); - String resourcePathStr = parser.getAttributeValue(null, "resourcePath"); String legacyCpuAbiStr = parser.getAttributeValue(null, "requiredCpuAbi"); String legacyNativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath"); - String parentPackageName = parser.getAttributeValue(null, "parentPackageName"); - String primaryCpuAbiStr = parser.getAttributeValue(null, "primaryCpuAbi"); String secondaryCpuAbiStr = parser.getAttributeValue(null, "secondaryCpuAbi"); String cpuAbiOverrideStr = parser.getAttributeValue(null, "cpuAbiOverride"); @@ -3574,9 +3550,6 @@ public final class Settings { primaryCpuAbiStr = legacyCpuAbiStr; } - if (resourcePathStr == null) { - resourcePathStr = codePathStr; - } String version = parser.getAttributeValue(null, "version"); long versionCode = 0; if (version != null) { @@ -3593,9 +3566,8 @@ public final class Settings { pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED; } PackageSetting ps = new PackageSetting(name, realName, new File(codePathStr), - new File(resourcePathStr), legacyNativeLibraryPathStr, primaryCpuAbiStr, - secondaryCpuAbiStr, cpuAbiOverrideStr, versionCode, pkgFlags, pkgPrivateFlags, - 0 /*sharedUserId*/, null, null, null); + legacyNativeLibraryPathStr, primaryCpuAbiStr, secondaryCpuAbiStr, cpuAbiOverrideStr, + versionCode, pkgFlags, pkgPrivateFlags, 0 /*sharedUserId*/, null, null, null); String timeStampStr = parser.getAttributeValue(null, "ft"); if (timeStampStr != null) { try { @@ -3666,7 +3638,6 @@ public final class Settings { String idStr = null; String sharedIdStr = null; String codePathStr = null; - String resourcePathStr = null; String legacyCpuAbiString = null; String legacyNativeLibraryPathStr = null; String primaryCpuAbiString = null; @@ -3700,7 +3671,6 @@ public final class Settings { uidError = parser.getAttributeValue(null, "uidError"); sharedIdStr = parser.getAttributeValue(null, "sharedUserId"); codePathStr = parser.getAttributeValue(null, "codePath"); - resourcePathStr = parser.getAttributeValue(null, "resourcePath"); legacyCpuAbiString = parser.getAttributeValue(null, "requiredCpuAbi"); @@ -3818,9 +3788,6 @@ public final class Settings { + " sharedUserId=" + sharedIdStr); final int userId = idStr != null ? Integer.parseInt(idStr) : 0; final int sharedUserId = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0; - if (resourcePathStr == null) { - resourcePathStr = codePathStr; - } if (realName != null) { realName = realName.intern(); } @@ -3834,10 +3801,10 @@ public final class Settings { + parser.getPositionDescription()); } else if (userId > 0) { packageSetting = addPackageLPw(name.intern(), realName, new File(codePathStr), - new File(resourcePathStr), legacyNativeLibraryPathStr, primaryCpuAbiString, - secondaryCpuAbiString, cpuAbiOverrideString, userId, versionCode, pkgFlags, - pkgPrivateFlags, null /*usesStaticLibraries*/, - null /*usesStaticLibraryVersions*/, null /*mimeGroups*/); + legacyNativeLibraryPathStr, primaryCpuAbiString, secondaryCpuAbiString, + cpuAbiOverrideString, userId, versionCode, pkgFlags, pkgPrivateFlags, + null /*usesStaticLibraries*/, null /*usesStaticLibraryVersions*/, + null /*mimeGroups*/); if (PackageManagerService.DEBUG_SETTINGS) Log.i(PackageManagerService.TAG, "Reading package " + name + ": userId=" + userId + " pkg=" + packageSetting); @@ -3852,8 +3819,8 @@ public final class Settings { } } else if (sharedIdStr != null) { if (sharedUserId > 0) { - packageSetting = new PackageSetting(name.intern(), realName, new File( - codePathStr), new File(resourcePathStr), legacyNativeLibraryPathStr, + packageSetting = new PackageSetting(name.intern(), realName, + new File(codePathStr), legacyNativeLibraryPathStr, primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString, versionCode, pkgFlags, pkgPrivateFlags, sharedUserId, null /*usesStaticLibraries*/, @@ -4456,25 +4423,43 @@ public final class Settings { } /** - * Return all users on the device, including partial or dying users. + * Returns all users on the device, including pre-created and dying users. + * * @param userManager UserManagerService instance * @return the list of users */ private static List<UserInfo> getAllUsers(UserManagerService userManager) { - return getUsers(userManager, false); + return getUsers(userManager, /* excludeDying= */ false, /* excludePreCreated= */ false); } /** - * Return the list of users on the device. Clear the calling identity before calling into - * UserManagerService. + * Returns the list of users on the device, excluding pre-created ones. + * * @param userManager UserManagerService instance * @param excludeDying Indicates whether to exclude any users marked for deletion. + * + * @return the list of users + */ + private static List<UserInfo> getActiveUsers(UserManagerService userManager, + boolean excludeDying) { + return getUsers(userManager, excludeDying, /* excludePreCreated= */ true); + } + + /** + * Returns the list of users on the device. + * + * @param userManager UserManagerService instance + * @param excludeDying Indicates whether to exclude any users marked for deletion. + * @param excludePreCreated Indicates whether to exclude any pre-created users. + * * @return the list of users */ - private static List<UserInfo> getUsers(UserManagerService userManager, boolean excludeDying) { + private static List<UserInfo> getUsers(UserManagerService userManager, boolean excludeDying, + boolean excludePreCreated) { long id = Binder.clearCallingIdentity(); try { - return userManager.getUsers(excludeDying); + return userManager.getUsers(/* excludePartial= */ true, excludeDying, + excludePreCreated); } catch (NullPointerException npe) { // packagemanager not yet initialized } finally { @@ -4657,9 +4642,9 @@ public final class Settings { pw.print(prefix); pw.print(" sharedUser="); pw.println(ps.sharedUser); } pw.print(prefix); pw.print(" pkg="); pw.println(pkg); - pw.print(prefix); pw.print(" codePath="); pw.println(ps.codePathString); + pw.print(prefix); pw.print(" codePath="); pw.println(ps.getCodePathString()); if (permissionNames == null) { - pw.print(prefix); pw.print(" resourcePath="); pw.println(ps.resourcePathString); + pw.print(prefix); pw.print(" resourcePath="); pw.println(ps.getCodePathString()); pw.print(prefix); pw.print(" legacyNativeLibraryDir="); pw.println(ps.legacyNativeLibraryPathString); pw.print(prefix); pw.print(" primaryCpuAbi="); pw.println(ps.primaryCpuAbiString); diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 89bdb3ecbff9..f9bf54a11df0 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -322,9 +322,6 @@ public class StagingManager { } final long activeVersion = activePackage.applicationInfo.longVersionCode; if (activeVersion != session.params.requiredInstalledVersionCode) { - if (!mApexManager.abortStagedSession(session.sessionId)) { - Slog.e(TAG, "Failed to abort apex session " + session.sessionId); - } throw new PackageManagerException( SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, "Installed version of APEX package " + activePackage.packageName @@ -338,14 +335,11 @@ public class StagingManager { throws PackageManagerException { final long activeVersion = activePackage.applicationInfo.longVersionCode; final long newVersionCode = newPackage.applicationInfo.longVersionCode; - boolean isAppDebuggable = (activePackage.applicationInfo.flags + final boolean isAppDebuggable = (activePackage.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; final boolean allowsDowngrade = PackageManagerServiceUtils.isDowngradePermitted( session.params.installFlags, isAppDebuggable); if (activeVersion > newVersionCode && !allowsDowngrade) { - if (!mApexManager.abortStagedSession(session.sessionId)) { - Slog.e(TAG, "Failed to abort apex session " + session.sessionId); - } throw new PackageManagerException( SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, "Downgrade of APEX package " + newPackage.packageName @@ -835,37 +829,6 @@ public class StagingManager { return null; } - private void verifyApksInSession(PackageInstallerSession session) - throws PackageManagerException { - - final PackageInstallerSession apksToVerify = extractApksInSession( - session, /* preReboot */ true); - if (apksToVerify == null) { - return; - } - - final LocalIntentReceiverAsync receiver = new LocalIntentReceiverAsync( - (Intent result) -> { - int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, - PackageInstaller.STATUS_FAILURE); - if (status != PackageInstaller.STATUS_SUCCESS) { - final String errorMessage = result.getStringExtra( - PackageInstaller.EXTRA_STATUS_MESSAGE); - Slog.e(TAG, "Failure to verify APK staged session " - + session.sessionId + " [" + errorMessage + "]"); - session.setStagedSessionFailed( - SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, errorMessage); - mPreRebootVerificationHandler.onPreRebootVerificationComplete( - session.sessionId); - return; - } - mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete( - session.sessionId); - }); - - apksToVerify.commit(receiver.getIntentSender(), false); - } - private void installApksInSession(@NonNull PackageInstallerSession session) throws PackageManagerException { @@ -908,10 +871,21 @@ public class StagingManager { mPreRebootVerificationHandler.startPreRebootVerification(session.sessionId); } - private int parentOrOwnSessionId(PackageInstallerSession session) { + private int getSessionIdForParentOrSelf(PackageInstallerSession session) { return session.hasParentSessionId() ? session.getParentSessionId() : session.sessionId; } + private PackageInstallerSession getParentSessionOrSelf(PackageInstallerSession session) { + return session.hasParentSessionId() + ? getStagedSession(session.getParentSessionId()) + : session; + } + + private boolean isRollback(PackageInstallerSession session) { + final PackageInstallerSession root = getParentSessionOrSelf(session); + return root.params.installReason == PackageManager.INSTALL_REASON_ROLLBACK; + } + /** * <p> Check if the session provided is non-overlapping with the active staged sessions. * @@ -937,6 +911,8 @@ public class StagingManager { boolean supportsCheckpoint = ((StorageManager) mContext.getSystemService( Context.STORAGE_SERVICE)).isCheckpointSupported(); + final boolean isRollback = isRollback(session); + synchronized (mStagedSessions) { for (int i = 0; i < mStagedSessions.size(); i++) { final PackageInstallerSession stagedSession = mStagedSessions.valueAt(i); @@ -951,8 +927,8 @@ public class StagingManager { } // Check if stagedSession has an active parent session or not if (stagedSession.hasParentSessionId()) { - int parentId = stagedSession.getParentSessionId(); - PackageInstallerSession parentSession = mStagedSessions.get(parentId); + final int parentId = stagedSession.getParentSessionId(); + final PackageInstallerSession parentSession = mStagedSessions.get(parentId); if (parentSession == null || parentSession.isStagedAndInTerminalState() || parentSession.isDestroyed()) { // Parent session has been abandoned or terminated already @@ -968,21 +944,37 @@ public class StagingManager { continue; } - // If session is not among the active sessions, then it cannot have same package - // name as any of the active sessions. + // New session cannot have same package name as one of the active sessions if (session.getPackageName().equals(stagedSession.getPackageName())) { - throw new PackageManagerException( - PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS, - "Package: " + session.getPackageName() + " in session: " - + session.sessionId + " has been staged already by session: " - + stagedSession.sessionId, null); + if (isRollback) { + // If the new session is a rollback, then it gets priority. The existing + // session is failed to unblock rollback. + final PackageInstallerSession root = getParentSessionOrSelf(stagedSession); + if (!ensureActiveApexSessionIsAborted(root)) { + Slog.e(TAG, "Failed to abort apex session " + root.sessionId); + // Safe to ignore active apex session abort failure since session + // will be marked failed on next step and staging directory for session + // will be deleted. + } + root.setStagedSessionFailed( + SessionInfo.STAGED_SESSION_OTHER_ERROR, + "Session was blocking rollback session: " + session.sessionId); + Slog.i(TAG, "Session " + root.sessionId + " is marked failed due to " + + "blocking rollback session: " + session.sessionId); + } else { + throw new PackageManagerException( + PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS, + "Package: " + session.getPackageName() + " in session: " + + session.sessionId + " has been staged already by session:" + + " " + stagedSession.sessionId, null); + } } // Staging multiple root sessions is not allowed if device doesn't support // checkpoint. If session and stagedSession do not have common ancestor, they are // from two different root sessions. - if (!supportsCheckpoint - && parentOrOwnSessionId(session) != parentOrOwnSessionId(stagedSession)) { + if (!supportsCheckpoint && getSessionIdForParentOrSelf(session) + != getSessionIdForParentOrSelf(stagedSession)) { throw new PackageManagerException( PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS, "Cannot stage multiple sessions without checkpoint support", null); @@ -1042,23 +1034,11 @@ public class StagingManager { // 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); - } + if (!ensureActiveApexSessionIsAborted(session)) { + // Failed to ensure apex session is aborted, so it can 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.e(TAG, "Failed to abort apex session " + session.sessionId); } } @@ -1068,6 +1048,22 @@ public class StagingManager { return true; } + /** + * Ensure that there is no active apex session staged in apexd for the given session. + * + * @return returns true if it is ensured that there is no active apex session, otherwise false + */ + private boolean ensureActiveApexSessionIsAborted(PackageInstallerSession session) { + if (!sessionContainsApex(session)) { + return true; + } + final ApexSessionInfo apexSession = mApexManager.getStagedSessionInfo(session.sessionId); + if (apexSession == null || isApexSessionFinalized(apexSession)) { + return true; + } + return mApexManager.abortStagedSession(session.sessionId); + } + private boolean isApexSessionFinalized(ApexSessionInfo session) { /* checking if the session is in a final state, i.e., not active anymore */ return session.isUnknown || session.isActivationFailed || session.isSuccess @@ -1294,8 +1290,8 @@ public class StagingManager { + sessionId); return; } - if (session.isDestroyed()) { - // No point in running verification on a destroyed session + if (session.isDestroyed() || session.isStagedSessionFailed()) { + // No point in running verification on a destroyed/failed session onPreRebootVerificationComplete(sessionId); return; } @@ -1348,6 +1344,17 @@ public class StagingManager { obtainMessage(MSG_PRE_REBOOT_VERIFICATION_START, sessionId, 0).sendToTarget(); } + private void onPreRebootVerificationFailure(PackageInstallerSession session, + @SessionInfo.StagedSessionErrorCode int errorCode, String errorMessage) { + if (!ensureActiveApexSessionIsAborted(session)) { + Slog.e(TAG, "Failed to abort apex session " + session.sessionId); + // Safe to ignore active apex session abortion failure since session will be marked + // failed on next step and staging directory for session will be deleted. + } + session.setStagedSessionFailed(errorCode, errorMessage); + onPreRebootVerificationComplete(session.sessionId); + } + // 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 @@ -1432,8 +1439,7 @@ public class StagingManager { validateApexSignature(apexPackages.get(i)); } } catch (PackageManagerException e) { - session.setStagedSessionFailed(e.error, e.getMessage()); - onPreRebootVerificationComplete(session.sessionId); + onPreRebootVerificationFailure(session, e.error, e.getMessage()); return; } @@ -1460,16 +1466,42 @@ public class StagingManager { try { Slog.d(TAG, "Running a pre-reboot verification for APKs in session " + session.sessionId + " by performing a dry-run install"); - // verifyApksInSession will notify the handler when APK verification is complete verifyApksInSession(session); - // TODO(b/118865310): abort the session on apexd. } catch (PackageManagerException e) { - session.setStagedSessionFailed(e.error, e.getMessage()); - onPreRebootVerificationComplete(session.sessionId); + onPreRebootVerificationFailure(session, e.error, e.getMessage()); } } + private void verifyApksInSession(PackageInstallerSession session) + throws PackageManagerException { + + final PackageInstallerSession apksToVerify = extractApksInSession( + session, /* preReboot */ true); + if (apksToVerify == null) { + return; + } + + final LocalIntentReceiverAsync receiver = new LocalIntentReceiverAsync( + (Intent result) -> { + final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, + PackageInstaller.STATUS_FAILURE); + if (status != PackageInstaller.STATUS_SUCCESS) { + final String errorMessage = result.getStringExtra( + PackageInstaller.EXTRA_STATUS_MESSAGE); + Slog.e(TAG, "Failure to verify APK staged session " + + session.sessionId + " [" + errorMessage + "]"); + onPreRebootVerificationFailure(session, + SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMessage); + return; + } + mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete( + session.sessionId); + }); + + apksToVerify.commit(receiver.getIntentSender(), false); + } + /** * Pre-reboot verification state for wrapping up: * <p><ul> @@ -1487,9 +1519,8 @@ public class StagingManager { } catch (Exception e) { // Failed to get hold of StorageManager Slog.e(TAG, "Failed to get hold of StorageManager", e); - session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN, + onPreRebootVerificationFailure(session, SessionInfo.STAGED_SESSION_UNKNOWN, "Failed to get hold of StorageManager"); - onPreRebootVerificationComplete(session.sessionId); return; } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 8f11fd529e60..bf9506ad51f1 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -134,6 +134,7 @@ import java.io.OutputStream; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -3497,7 +3498,7 @@ public class UserManagerService extends IUserManager.Stub { } t.traceBegin("PM.onNewUserCreated-" + userId); - mPm.onNewUserCreated(userId); + mPm.onNewUserCreated(userId, /* convertedFromPreCreated= */ false); t.traceEnd(); if (preCreate) { // Must start user (which will be stopped right away, through @@ -3570,10 +3571,7 @@ public class UserManagerService extends IUserManager.Stub { writeUserListLP(); } updateUserIds(); - if (!mPm.readPermissionStateForUser(preCreatedUser.id)) { - // Could not read the existing permissions, re-grant them. - mPm.onNewUserCreated(preCreatedUser.id); - } + mPm.onNewUserCreated(preCreatedUser.id, /* convertedFromPreCreated= */ true); dispatchUserAdded(preCreatedUser); return preCreatedUser; } @@ -4787,7 +4785,7 @@ public class UserManagerService extends IUserManager.Stub { } } } - pw.println(); + pw.println("Device properties:"); pw.println(" Device owner id:" + mDeviceOwnerUserId); pw.println(); @@ -4804,8 +4802,26 @@ public class UserManagerService extends IUserManager.Stub { } } synchronized (mUserStates) { - pw.println(" Started users state: " + mUserStates); + pw.print(" Started users state: ["); + final int size = mUserStates.states.size(); + for (int i = 0; i < size; i++) { + final int userId = mUserStates.states.keyAt(i); + final int state = mUserStates.states.valueAt(i); + pw.print(userId); + pw.print('='); + pw.print(UserState.stateToString(state)); + if (i != size - 1) pw.print(", "); + } + pw.println(']'); + } + + synchronized (mUsersLock) { + pw.println(); + pw.print("Cached user IDs: "); + pw.println(Arrays.toString(mUserIds)); + pw.println(); } + } // synchronized (mPackagesLock) // Dump some capabilities 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 be93b8f95b79..03771be0ce7c 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -2454,6 +2454,36 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } + @NonNull + private Set<String> getGrantedPermissions(@NonNull String packageName, + @UserIdInt int userId) { + final PackageSetting ps = mPackageManagerInt.getPackageSetting(packageName); + if (ps == null) { + return null; + } + final PermissionsState permissionsState = ps.getPermissionsState(); + return permissionsState.getPermissions(userId); + } + + @Nullable + private int[] getPermissionGids(@NonNull String permissionName, @UserIdInt int userId) { + BasePermission permission = mSettings.getPermission(permissionName); + if (permission == null) { + return null; + } + return permission.computeGids(userId); + } + + @Nullable + private int[] getPackageGids(@NonNull String packageName, @UserIdInt int userId) { + final PackageSetting ps = mPackageManagerInt.getPackageSetting(packageName); + if (ps == null) { + return null; + } + final PermissionsState permissionsState = ps.getPermissionsState(); + return permissionsState.computeGids(userId); + } + /** * Restore the permission state for a package. * @@ -4650,6 +4680,22 @@ public class PermissionManagerService extends IPermissionManager.Stub { public void removeAllPermissions(AndroidPackage pkg, boolean chatty) { PermissionManagerService.this.removeAllPermissions(pkg, chatty); } + @NonNull + @Override + public Set<String> getGrantedPermissions(@NonNull String packageName, + @UserIdInt int userId) { + return PermissionManagerService.this.getGrantedPermissions(packageName, userId); + } + @Nullable + @Override + public int[] getPermissionGids(@NonNull String permissionName, @UserIdInt int userId) { + return PermissionManagerService.this.getPermissionGids(permissionName, userId); + } + @Nullable + @Override + public int[] getPackageGids(@NonNull String packageName, @UserIdInt int userId) { + return PermissionManagerService.this.getPackageGids(packageName, userId); + } @Override public void grantRequestedRuntimePermissions(AndroidPackage pkg, int[] userIds, String[] grantedPermissions, int callingUid) { diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java index 2e83b23f57d8..cfa371ddbad3 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java @@ -28,6 +28,7 @@ import com.android.server.pm.parsing.pkg.AndroidPackage; import java.util.ArrayList; import java.util.List; +import java.util.Set; import java.util.function.Consumer; /** @@ -263,6 +264,25 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager public abstract void addAllPermissionGroups(@NonNull AndroidPackage pkg, boolean chatty); public abstract void removeAllPermissions(@NonNull AndroidPackage pkg, boolean chatty); + /** + * Get all the permissions granted to a package. + */ + @NonNull + public abstract Set<String> getGrantedPermissions(@NonNull String packageName, + @UserIdInt int userId); + + /** + * Get the GIDs of a permission. + */ + @Nullable + public abstract int[] getPermissionGids(@NonNull String permissionName, @UserIdInt int userId); + + /** + * Get the GIDs computed from the permission state of a package. + */ + @Nullable + public abstract int[] getPackageGids(@NonNull String packageName, @UserIdInt int userId); + /** Retrieve the packages that have requested the given app op permission */ public abstract @Nullable String[] getAppOpPermissionPackages( @NonNull String permName, int callingUid); diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 03868e922bdd..fa3bcb6b98eb 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -933,6 +933,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + final boolean handledByPowerManager = mPowerManagerInternal.interceptPowerKeyDown(event); + GestureLauncherService gestureService = LocalServices.getService( GestureLauncherService.class); boolean gesturedServiceIntercepted = false; @@ -952,7 +954,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { // If the power key has still not yet been handled, then detect short // press, long press, or multi press and decide what to do. mPowerKeyHandled = hungUp || mScreenshotChordVolumeDownKeyTriggered - || mA11yShortcutChordVolumeUpKeyTriggered || gesturedServiceIntercepted; + || mA11yShortcutChordVolumeUpKeyTriggered || gesturedServiceIntercepted + || handledByPowerManager; if (!mPowerKeyHandled) { if (interactive) { // When interactive, we're already awake. diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index f9a49c9d8244..882ed1b7960b 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -86,6 +86,7 @@ import android.util.SparseArray; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; import android.view.Display; +import android.view.KeyEvent; import com.android.internal.BrightnessSynchronizer; import com.android.internal.annotations.VisibleForTesting; @@ -892,19 +893,13 @@ public final class PowerManagerService extends SystemService || def == INVALID_BRIGHTNESS_IN_CONFIG) { mScreenBrightnessMinimum = BrightnessSynchronizer.brightnessIntToFloat( mContext.getResources().getInteger(com.android.internal.R.integer - .config_screenBrightnessSettingMinimum), - PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON, - PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX); + .config_screenBrightnessSettingMinimum)); mScreenBrightnessMaximum = BrightnessSynchronizer.brightnessIntToFloat( mContext.getResources().getInteger(com.android.internal.R.integer - .config_screenBrightnessSettingMaximum), - PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON, - PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX); + .config_screenBrightnessSettingMaximum)); mScreenBrightnessDefault = BrightnessSynchronizer.brightnessIntToFloat( mContext.getResources().getInteger(com.android.internal.R.integer - .config_screenBrightnessSettingDefault), - PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON, - PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX); + .config_screenBrightnessSettingDefault)); } else { mScreenBrightnessMinimum = min; mScreenBrightnessMaximum = max; @@ -913,18 +908,14 @@ public final class PowerManagerService extends SystemService if (doze == INVALID_BRIGHTNESS_IN_CONFIG) { mScreenBrightnessDoze = BrightnessSynchronizer.brightnessIntToFloat( mContext.getResources().getInteger(com.android.internal.R.integer - .config_screenBrightnessDoze), PowerManager.BRIGHTNESS_OFF + 1, - PowerManager.BRIGHTNESS_ON, PowerManager.BRIGHTNESS_MIN, - PowerManager.BRIGHTNESS_MAX); + .config_screenBrightnessDoze)); } else { mScreenBrightnessDoze = doze; } if (dim == INVALID_BRIGHTNESS_IN_CONFIG) { mScreenBrightnessDim = BrightnessSynchronizer.brightnessIntToFloat( mContext.getResources().getInteger(com.android.internal.R.integer - .config_screenBrightnessDim), PowerManager.BRIGHTNESS_OFF + 1, - PowerManager.BRIGHTNESS_ON, PowerManager.BRIGHTNESS_MIN, - PowerManager.BRIGHTNESS_MAX); + .config_screenBrightnessDim)); } else { mScreenBrightnessDim = dim; } @@ -939,19 +930,13 @@ public final class PowerManagerService extends SystemService || vrDef == INVALID_BRIGHTNESS_IN_CONFIG) { mScreenBrightnessMinimumVr = BrightnessSynchronizer.brightnessIntToFloat( mContext.getResources().getInteger(com.android.internal.R.integer - .config_screenBrightnessForVrSettingMinimum), - PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON, - PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX); + .config_screenBrightnessForVrSettingMinimum)); mScreenBrightnessMaximumVr = BrightnessSynchronizer.brightnessIntToFloat( mContext.getResources().getInteger(com.android.internal.R.integer - .config_screenBrightnessForVrSettingMaximum), - PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON, - PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX); + .config_screenBrightnessForVrSettingMaximum)); mScreenBrightnessDefaultVr = BrightnessSynchronizer.brightnessIntToFloat( mContext.getResources().getInteger(com.android.internal.R.integer - .config_screenBrightnessForVrSettingDefault), - PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON, - PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX); + .config_screenBrightnessForVrSettingDefault)); } else { mScreenBrightnessMinimumVr = vrMin; mScreenBrightnessMaximumVr = vrMax; @@ -3590,8 +3575,7 @@ public final class PowerManagerService extends SystemService mDozeScreenStateOverrideFromDreamManager = screenState; mDozeScreenBrightnessOverrideFromDreamManager = screenBrightness; mDozeScreenBrightnessOverrideFromDreamManagerFloat = - BrightnessSynchronizer.brightnessIntToFloat(mContext, - mDozeScreenBrightnessOverrideFromDreamManager); + BrightnessSynchronizer.brightnessIntToFloat(mDozeScreenBrightnessOverrideFromDreamManager); mDirty |= DIRTY_SETTINGS; updatePowerStateLocked(); } @@ -5393,6 +5377,29 @@ public final class PowerManagerService extends SystemService } } + /** + * If the user presses power while the proximity sensor is enabled and keeping + * the screen off, then turn the screen back on by telling display manager to + * ignore the proximity sensor. We don't turn off the proximity sensor because + * we still want it to be reenabled if it's state changes. + * + * @return True if the proximity sensor was successfully ignored and we should + * consume the key event. + */ + private boolean interceptPowerKeyDownInternal(KeyEvent event) { + synchronized (mLock) { + // DisplayPowerController only reports proximity positive (near) if it's + // positive and the proximity wasn't already being ignored. So it reliably + // also tells us that we're not already ignoring the proximity sensor. + if (mDisplayPowerRequest.useProximitySensor && mProximityPositive) { + mDisplayManagerInternal.ignoreProximitySensorUntilChanged(); + return true; + } + } + + return false; + } + @VisibleForTesting final class LocalService extends PowerManagerInternal { @Override @@ -5525,5 +5532,10 @@ public final class PowerManagerService extends SystemService public WakeData getLastWakeup() { return getLastWakeupInternal(); } + + @Override + public boolean interceptPowerKeyDown(KeyEvent event) { + return interceptPowerKeyDownInternal(event); + } } } diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java index 4349ca451c36..2a74b3d23829 100644 --- a/services/core/java/com/android/server/slice/SliceManagerService.java +++ b/services/core/java/com/android/server/slice/SliceManagerService.java @@ -28,6 +28,8 @@ import static android.os.Process.SYSTEM_UID; import android.Manifest.permission; import android.annotation.NonNull; import android.app.AppOpsManager; +import android.app.role.OnRoleHoldersChangedListener; +import android.app.role.RoleManager; import android.app.slice.ISliceManager; import android.app.slice.SliceSpec; import android.app.usage.UsageStatsManagerInternal; @@ -77,6 +79,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.concurrent.Executor; import java.util.function.Supplier; public class SliceManagerService extends ISliceManager.Stub { @@ -121,6 +124,7 @@ public class SliceManagerService extends ISliceManager.Stub { filter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addDataScheme("package"); + mRoleObserver = new RoleObserver(); mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler); } @@ -478,10 +482,26 @@ public class SliceManagerService extends ISliceManager.Stub { return cn.getPackageName(); } + /** + * A cached value of the default home app + */ + private String mCachedDefaultHome = null; + // Based on getDefaultHome in ShortcutService. // TODO: Unify if possible @VisibleForTesting protected String getDefaultHome(int userId) { + + // Set VERIFY to true to run the cache in "shadow" mode for cache + // testing. Do not commit set to true; + final boolean VERIFY = false; + + if (mCachedDefaultHome != null) { + if (!VERIFY) { + return mCachedDefaultHome; + } + } + final long token = Binder.clearCallingIdentity(); try { final List<ResolveInfo> allHomeCandidates = new ArrayList<>(); @@ -490,10 +510,12 @@ public class SliceManagerService extends ISliceManager.Stub { final ComponentName defaultLauncher = mPackageManagerInternal .getHomeActivitiesAsUser(allHomeCandidates, userId); - ComponentName detected = null; - if (defaultLauncher != null) { - detected = defaultLauncher; - } + ComponentName detected = defaultLauncher; + + // Cache the default launcher. It is not a problem if the + // launcher is null - eventually, the default launcher will be + // set to something non-null. + mCachedDefaultHome = ((detected != null) ? detected.getPackageName() : null); if (detected == null) { // If we reach here, that means it's the first check since the user was created, @@ -517,12 +539,54 @@ public class SliceManagerService extends ISliceManager.Stub { lastPriority = ri.priority; } } - return detected != null ? detected.getPackageName() : null; + final String ret = ((detected != null) ? detected.getPackageName() : null); + if (VERIFY) { + if (mCachedDefaultHome != null && !mCachedDefaultHome.equals(ret)) { + Slog.e(TAG, "getDefaultHome() cache failure, is " + + mCachedDefaultHome + " should be " + ret); + } + } + return ret; } finally { Binder.restoreCallingIdentity(token); } } + public void invalidateCachedDefaultHome() { + mCachedDefaultHome = null; + } + + /** + * Listen for changes in the roles, and invalidate the cached default + * home as necessary. + */ + private RoleObserver mRoleObserver; + + class RoleObserver implements OnRoleHoldersChangedListener { + private RoleManager mRm; + private final Executor mExecutor; + + RoleObserver() { + mExecutor = mContext.getMainExecutor(); + register(); + } + + public void register() { + mRm = mContext.getSystemService(RoleManager.class); + if (mRm != null) { + mRm.addOnRoleHoldersChangedListenerAsUser(mExecutor, this, UserHandle.ALL); + invalidateCachedDefaultHome(); + } + } + + @Override + public void onRoleHoldersChanged(@NonNull String roleName, @NonNull UserHandle user) { + if (RoleManager.ROLE_HOME.equals(roleName)) { + invalidateCachedDefaultHome(); + } + } + } + private boolean isGrantedFullAccess(String pkg, int userId) { return mPermissions.hasFullAccess(pkg, userId); } diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java index 3ec61fdda917..7467439905eb 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java @@ -18,7 +18,6 @@ package com.android.server.timezonedetector; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UserIdInt; import android.app.timezonedetector.ITimeZoneConfigurationListener; import android.app.timezonedetector.ITimeZoneDetectorService; import android.app.timezonedetector.ManualTimeZoneSuggestion; @@ -38,6 +37,7 @@ import android.os.UserHandle; import android.provider.Settings; import android.util.IndentingPrintWriter; import android.util.Slog; +import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -60,7 +60,8 @@ import java.util.Objects; * and making calls async, leaving the (consequently more testable) {@link TimeZoneDetectorStrategy} * implementation to deal with the logic around time zone detection. */ -public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub { +public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub + implements IBinder.DeathRecipient { private static final String TAG = "TimeZoneDetectorService"; @@ -104,9 +105,15 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub @NonNull private final TimeZoneDetectorStrategy mTimeZoneDetectorStrategy; + /** + * This sparse array acts as a map from userId to listeners running as that userId. User scoped + * as time zone detection configuration is partially user-specific, so different users can + * get different configuration. + */ @GuardedBy("mConfigurationListeners") @NonNull - private final ArrayList<ConfigListenerInfo> mConfigurationListeners = new ArrayList<>(); + private final SparseArray<ArrayList<ITimeZoneConfigurationListener>> mConfigurationListeners = + new SparseArray<>(); private static TimeZoneDetectorService create( @NonNull Context context, @NonNull Handler handler, @@ -188,18 +195,23 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub Objects.requireNonNull(listener); int userId = UserHandle.getCallingUserId(); - ConfigListenerInfo listenerInfo = new ConfigListenerInfo(userId, listener); - synchronized (mConfigurationListeners) { - if (mConfigurationListeners.contains(listenerInfo)) { + ArrayList<ITimeZoneConfigurationListener> listeners = + mConfigurationListeners.get(userId); + if (listeners != null && listeners.contains(listener)) { return; } try { - // Ensure the reference to the listener is removed if the client process dies. - listenerInfo.linkToDeath(); + if (listeners == null) { + listeners = new ArrayList<>(1); + mConfigurationListeners.put(userId, listeners); + } + + // Ensure the reference to the listener will be removed if the client process dies. + listener.asBinder().linkToDeath(this, 0 /* flags */); // Only add the listener if we can linkToDeath(). - mConfigurationListeners.add(listenerInfo); + listeners.add(listener); } catch (RemoteException e) { Slog.e(TAG, "Unable to linkToDeath() for listener=" + listener, e); } @@ -213,21 +225,56 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub int userId = UserHandle.getCallingUserId(); synchronized (mConfigurationListeners) { - ConfigListenerInfo toRemove = new ConfigListenerInfo(userId, listener); - Iterator<ConfigListenerInfo> listenerIterator = mConfigurationListeners.iterator(); - while (listenerIterator.hasNext()) { - ConfigListenerInfo currentListenerInfo = listenerIterator.next(); - if (currentListenerInfo.equals(toRemove)) { - listenerIterator.remove(); - - // Stop listening for the client process to die. - try { - currentListenerInfo.unlinkToDeath(); - } catch (RemoteException e) { - Slog.e(TAG, "Unable to unlinkToDeath() for listener=" + listener, e); + boolean removedListener = false; + ArrayList<ITimeZoneConfigurationListener> userListeners = + mConfigurationListeners.get(userId); + if (userListeners.remove(listener)) { + // Stop listening for the client process to die. + listener.asBinder().unlinkToDeath(this, 0 /* flags */); + removedListener = true; + } + if (!removedListener) { + Slog.w(TAG, "Client asked to remove listenener=" + listener + + ", but no listeners were removed." + + " mConfigurationListeners=" + mConfigurationListeners); + } + } + } + + @Override + public void binderDied() { + // Should not be used as binderDied(IBinder who) is overridden. + Slog.wtf(TAG, "binderDied() called unexpectedly."); + } + + /** + * Called when one of the ITimeZoneConfigurationListener processes dies before calling + * {@link #removeConfigurationListener(ITimeZoneConfigurationListener)}. + */ + @Override + public void binderDied(IBinder who) { + synchronized (mConfigurationListeners) { + boolean removedListener = false; + final int userCount = mConfigurationListeners.size(); + for (int i = 0; i < userCount; i++) { + ArrayList<ITimeZoneConfigurationListener> userListeners = + mConfigurationListeners.valueAt(i); + Iterator<ITimeZoneConfigurationListener> userListenerIterator = + userListeners.iterator(); + while (userListenerIterator.hasNext()) { + ITimeZoneConfigurationListener userListener = userListenerIterator.next(); + if (userListener.asBinder().equals(who)) { + userListenerIterator.remove(); + removedListener = true; + break; } } } + if (!removedListener) { + Slog.w(TAG, "Notified of binder death for who=" + who + + ", but did not remove any listeners." + + " mConfigurationListeners=" + mConfigurationListeners); + } } } @@ -243,14 +290,24 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub // problem. synchronized (mConfigurationListeners) { - for (ConfigListenerInfo listenerInfo : mConfigurationListeners) { + final int userCount = mConfigurationListeners.size(); + for (int userIndex = 0; userIndex < userCount; userIndex++) { + int userId = mConfigurationListeners.keyAt(userIndex); TimeZoneConfiguration configuration = - mTimeZoneDetectorStrategy.getConfiguration(listenerInfo.getUserId()); - try { - listenerInfo.getListener().onChange(configuration); - } catch (RemoteException e) { - Slog.w(TAG, "Unable to notify listener=" - + listenerInfo + " of updated configuration=" + configuration, e); + mTimeZoneDetectorStrategy.getConfiguration(userId); + + ArrayList<ITimeZoneConfigurationListener> listeners = + mConfigurationListeners.valueAt(userIndex); + final int listenerCount = listeners.size(); + for (int listenerIndex = 0; listenerIndex < listenerCount; listenerIndex++) { + ITimeZoneConfigurationListener listener = listeners.get(listenerIndex); + try { + listener.onChange(configuration); + } catch (RemoteException e) { + Slog.w(TAG, "Unable to notify listener=" + listener + + " for userId=" + userId + + " of updated configuration=" + configuration, e); + } } } } @@ -338,66 +395,5 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub (new TimeZoneDetectorShellCommand(this)).exec( this, in, out, err, args, callback, resultReceiver); } - - private class ConfigListenerInfo implements IBinder.DeathRecipient { - private final @UserIdInt int mUserId; - private final ITimeZoneConfigurationListener mListener; - - ConfigListenerInfo( - @UserIdInt int userId, @NonNull ITimeZoneConfigurationListener listener) { - this.mUserId = userId; - this.mListener = Objects.requireNonNull(listener); - } - - @UserIdInt int getUserId() { - return mUserId; - } - - ITimeZoneConfigurationListener getListener() { - return mListener; - } - - void linkToDeath() throws RemoteException { - mListener.asBinder().linkToDeath(this, 0 /* flags */); - } - - void unlinkToDeath() throws RemoteException { - mListener.asBinder().unlinkToDeath(this, 0 /* flags */); - } - - @Override - public void binderDied() { - synchronized (mConfigurationListeners) { - Slog.i(TAG, "Configuration listener client died: " + this); - mConfigurationListeners.remove(this); - } - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - ConfigListenerInfo that = (ConfigListenerInfo) o; - return mUserId == that.mUserId - && mListener.equals(that.mListener); - } - - @Override - public int hashCode() { - return Objects.hash(mUserId, mListener); - } - - @Override - public String toString() { - return "ConfigListenerInfo{" - + "mUserId=" + mUserId - + ", mListener=" + mListener - + '}'; - } - } } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 1b4fac6f407a..e573d362fef4 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -1675,7 +1675,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A static int getLockTaskLaunchMode(ActivityInfo aInfo, @Nullable ActivityOptions options) { int lockTaskLaunchMode = aInfo.lockTaskLaunchMode; - if (aInfo.applicationInfo.isPrivilegedApp() + // Non-priv apps are not allowed to use always or never, fall back to default + if (!aInfo.applicationInfo.isPrivilegedApp() && (lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_ALWAYS || lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_NEVER)) { lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT; diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index a05289f9f651..2c475e0b9bcb 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -130,6 +130,7 @@ import android.util.MergedConfiguration; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; +import android.view.Display; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; @@ -1090,9 +1091,9 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { // Check if caller is already present on display final boolean uidPresentOnDisplay = displayContent.isUidPresent(callingUid); - final int displayOwnerUid = displayContent.mDisplay.getOwnerUid(); - if (displayContent.mDisplay.getType() == TYPE_VIRTUAL && displayOwnerUid != SYSTEM_UID) { - // Limit launching on virtual displays, because their contents can be read from Surface + final Display display = displayContent.mDisplay; + if (!display.isTrusted()) { + // Limit launching on untrusted displays because their contents can be read from Surface // by apps that created them. if ((aInfo.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) == 0) { if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:" @@ -1116,7 +1117,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { } // Check if the caller is the owner of the display. - if (displayOwnerUid == callingUid) { + if (display.getOwnerUid() == callingUid) { if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:" + " allow launch for owner of the display"); return true; diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 8da16b502c20..31a9c5d4242c 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -183,7 +183,6 @@ import android.database.ContentObserver; import android.graphics.Bitmap; import android.graphics.Point; import android.graphics.Rect; -import android.metrics.LogMaker; import android.net.Uri; import android.os.Binder; import android.os.Build; @@ -238,12 +237,9 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.AssistUtils; import com.android.internal.app.IVoiceInteractor; import com.android.internal.app.ProcessMap; -import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.TransferPipe; -import com.android.internal.os.logging.MetricsLoggerWrapper; import com.android.internal.policy.IKeyguardDismissCallback; import com.android.internal.policy.KeyguardDismissCallback; import com.android.internal.util.ArrayUtils; @@ -3003,13 +2999,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public void stopLockTaskModeByToken(IBinder token) { - synchronized (mGlobalLock) { - final ActivityRecord r = ActivityRecord.forTokenLocked(token); - if (r == null) { - return; - } - stopLockTaskModeInternal(r.getTask(), false /* isSystemCaller */); - } + stopLockTaskModeInternal(token, false /* isSystemCaller */); } /** @@ -3051,11 +3041,19 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } - private void stopLockTaskModeInternal(@Nullable Task task, boolean isSystemCaller) { + private void stopLockTaskModeInternal(@Nullable IBinder token, boolean isSystemCaller) { final int callingUid = Binder.getCallingUid(); long ident = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { + Task task = null; + if (token != null) { + final ActivityRecord r = ActivityRecord.forTokenLocked(token); + if (r == null) { + return; + } + task = r.getTask(); + } getLockTaskController().stopLockTaskMode(task, isSystemCaller, callingUid); } // Launch in-call UI if a call is ongoing. This is necessary to allow stopping the lock @@ -4095,10 +4093,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final Task stack = r.getRootTask(); stack.setPictureInPictureAspectRatio(aspectRatio); stack.setPictureInPictureActions(actions); - MetricsLoggerWrapper.logPictureInPictureEnter(mContext, - r.info.applicationInfo.uid, r.shortComponentName, - r.supportsEnterPipOnTaskSwitch); - logPictureInPictureArgs(params); } }; @@ -4142,7 +4136,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { r.pictureInPictureArgs.getAspectRatio()); stack.setPictureInPictureActions(r.pictureInPictureArgs.getActions()); } - logPictureInPictureArgs(params); } } finally { Binder.restoreCallingIdentity(origId); @@ -4156,18 +4149,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return 3; } - private void logPictureInPictureArgs(PictureInPictureParams params) { - if (params.hasSetActions()) { - MetricsLogger.histogram(mContext, "tron_varz_picture_in_picture_actions_count", - params.getActions().size()); - } - if (params.hasSetAspectRatio()) { - LogMaker lm = new LogMaker(MetricsEvent.ACTION_PICTURE_IN_PICTURE_ASPECT_RATIO_CHANGED); - lm.addTaggedData(MetricsEvent.PICTURE_IN_PICTURE_ASPECT_RATIO, params.getAspectRatio()); - MetricsLogger.action(lm); - } - } - /** * Checks the state of the system and the activity associated with the given {@param token} to * verify that picture-in-picture is supported for that activity. diff --git a/services/core/java/com/android/server/wm/SurfaceFreezer.java b/services/core/java/com/android/server/wm/SurfaceFreezer.java index 5cea786c3367..1c9743041923 100644 --- a/services/core/java/com/android/server/wm/SurfaceFreezer.java +++ b/services/core/java/com/android/server/wm/SurfaceFreezer.java @@ -22,7 +22,6 @@ import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_SCREEN_ROTATI import android.annotation.NonNull; import android.annotation.Nullable; -import android.graphics.ColorSpace; import android.graphics.PixelFormat; import android.graphics.Rect; import android.hardware.HardwareBuffer; @@ -89,8 +88,7 @@ class SurfaceFreezer { if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) { return; } - mSnapshot = new Snapshot(mWmService.mSurfaceFactory, t, buffer, - screenshotBuffer.getColorSpace(), mLeash); + mSnapshot = new Snapshot(mWmService.mSurfaceFactory, t, screenshotBuffer, mLeash); } } @@ -135,8 +133,12 @@ class SurfaceFreezer { cropBounds = new Rect(bounds); cropBounds.offsetTo(0, 0); } - return SurfaceControl.captureLayers(target, cropBounds, 1.f /* frameScale */, - PixelFormat.RGBA_8888); + SurfaceControl.LayerCaptureArgs captureArgs = + new SurfaceControl.LayerCaptureArgs.Builder(target) + .setSourceCrop(cropBounds) + .setCaptureSecureLayers(true) + .build(); + return SurfaceControl.captureLayers(captureArgs); } class Snapshot { @@ -146,21 +148,23 @@ class SurfaceFreezer { /** * @param t Transaction to create the thumbnail in. - * @param thumbnailHeader A thumbnail or placeholder for thumbnail to initialize with. + * @param screenshotBuffer A thumbnail or placeholder for thumbnail to initialize with. */ Snapshot(Supplier<Surface> surfaceFactory, SurfaceControl.Transaction t, - HardwareBuffer thumbnailHeader, ColorSpace colorSpace, SurfaceControl parent) { + SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer, SurfaceControl parent) { Surface drawSurface = surfaceFactory.get(); // We can't use a delegating constructor since we need to // reference this::onAnimationFinished - final int width = thumbnailHeader.getWidth(); - final int height = thumbnailHeader.getHeight(); + HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer(); + final int width = hardwareBuffer.getWidth(); + final int height = hardwareBuffer.getHeight(); mSurfaceControl = mAnimatable.makeAnimationLeash() .setName("snapshot anim: " + mAnimatable.toString()) .setBufferSize(width, height) .setFormat(PixelFormat.TRANSLUCENT) .setParent(parent) + .setSecure(screenshotBuffer.containsSecureLayers()) .setCallsite("SurfaceFreezer.Snapshot") .build(); @@ -168,7 +172,8 @@ class SurfaceFreezer { // Transfer the thumbnail to the surface drawSurface.copyFrom(mSurfaceControl); - drawSurface.attachAndQueueBufferWithColorSpace(thumbnailHeader, colorSpace); + drawSurface.attachAndQueueBufferWithColorSpace(hardwareBuffer, + screenshotBuffer.getColorSpace()); drawSurface.release(); t.show(mSurfaceControl); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index e76b5bc5eddc..be0815b06051 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -210,7 +210,6 @@ import android.window.ITaskOrganizer; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IVoiceInteractor; -import com.android.internal.os.logging.MetricsLoggerWrapper; import com.android.internal.util.XmlUtils; import com.android.internal.util.function.pooled.PooledConsumer; import com.android.internal.util.function.pooled.PooledFunction; @@ -2366,7 +2365,6 @@ class Task extends WindowContainer<WindowContainer> { private boolean shouldStartChangeTransition(int prevWinMode, int newWinMode) { if (mWmService.mDisableTransitionAnimation || !isVisible() - || getDisplayContent().mAppTransition.isTransitionSet() || getSurfaceControl() == null || !isLeafTask()) { return false; @@ -7372,8 +7370,6 @@ class Task extends WindowContainer<WindowContainer> { getDisplayArea().positionChildAt(POSITION_TOP, this, false /* includingParents */); mStackSupervisor.scheduleUpdatePictureInPictureModeIfNeeded(task, this); - MetricsLoggerWrapper.logPictureInPictureFullScreen(mAtmService.mContext, - task.effectiveUid, task.realActivity.flattenToString()); }); } diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java index f3c7a5dcb6d5..9b18ac8f7702 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.graphics.Color.WHITE; import static android.graphics.Color.alpha; import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; @@ -142,6 +143,7 @@ class TaskSnapshotSurface implements StartingSurface { private final Handler mHandler; private boolean mSizeMismatch; private final Paint mBackgroundPaint = new Paint(); + private final int mActivityType; private final int mStatusBarColor; @VisibleForTesting final SystemBarBackgroundPainter mSystemBarBackgroundPainter; private final int mOrientationOnCreation; @@ -173,6 +175,7 @@ class TaskSnapshotSurface implements StartingSurface { final int windowFlags; final int windowPrivateFlags; final int currentOrientation; + final int activityType; final InsetsState insetsState; synchronized (service.mGlobalLock) { final WindowState mainWindow = activity.findMainWindow(); @@ -241,6 +244,7 @@ class TaskSnapshotSurface implements StartingSurface { taskBounds = new Rect(); task.getBounds(taskBounds); currentOrientation = topFullscreenOpaqueWindow.getConfiguration().orientation; + activityType = activity.getActivityType(); final InsetsPolicy insetsPolicy = topFullscreenOpaqueWindow.getDisplayContent() .getInsetsPolicy(); @@ -261,7 +265,8 @@ class TaskSnapshotSurface implements StartingSurface { } final TaskSnapshotSurface snapshotSurface = new TaskSnapshotSurface(service, window, surfaceControl, snapshot, layoutParams.getTitle(), taskDescription, sysUiVis, - windowFlags, windowPrivateFlags, taskBounds, currentOrientation, insetsState); + windowFlags, windowPrivateFlags, taskBounds, currentOrientation, activityType, + insetsState); window.setOuter(snapshotSurface); try { session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, -1, @@ -282,7 +287,7 @@ class TaskSnapshotSurface implements StartingSurface { TaskSnapshotSurface(WindowManagerService service, Window window, SurfaceControl surfaceControl, TaskSnapshot snapshot, CharSequence title, TaskDescription taskDescription, int sysUiVis, int windowFlags, int windowPrivateFlags, Rect taskBounds, - int currentOrientation, InsetsState insetsState) { + int currentOrientation, int activityType, InsetsState insetsState) { mService = service; mSurface = service.mSurfaceFactory.get(); mHandler = new Handler(mService.mH.getLooper()); @@ -298,6 +303,7 @@ class TaskSnapshotSurface implements StartingSurface { windowPrivateFlags, sysUiVis, taskDescription, 1f, insetsState); mStatusBarColor = taskDescription.getStatusBarColor(); mOrientationOnCreation = currentOrientation; + mActivityType = activityType; mTransaction = mService.mTransactionFactory.get(); } @@ -305,7 +311,9 @@ class TaskSnapshotSurface implements StartingSurface { public void remove() { synchronized (mService.mGlobalLock) { final long now = SystemClock.uptimeMillis(); - if (mSizeMismatch && now - mShownTime < SIZE_MISMATCH_MINIMUM_TIME_MS) { + if (mSizeMismatch && now - mShownTime < SIZE_MISMATCH_MINIMUM_TIME_MS + // Show the latest content as soon as possible for unlocking to home. + && mActivityType != ACTIVITY_TYPE_HOME) { mHandler.postAtTime(this::remove, mShownTime + SIZE_MISMATCH_MINIMUM_TIME_MS); ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Defer removing snapshot surface in %dms", (now - mShownTime)); diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index a58c5646858b..70c30c9180d7 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -1177,12 +1177,19 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio boolean handleAppDied() { mAtm.mStackSupervisor.removeHistoryRecords(this); - final boolean isRemoved = isRemoved(); boolean hasVisibleActivities = false; if (mInactiveActivities != null && !mInactiveActivities.isEmpty()) { // Make sure that all activities in this process are handled. mActivities.addAll(mInactiveActivities); } + if (isRemoved()) { + // The package of the died process should be force-stopped, so make its activities as + // finishing to prevent the process from being started again if the next top (or being + // visible) activity also resides in the same process. This must be done before removal. + for (int i = mActivities.size() - 1; i >= 0; i--) { + mActivities.get(i).makeFinishingLocked(); + } + } for (int i = mActivities.size() - 1; i >= 0; i--) { final ActivityRecord r = mActivities.get(i); if (r.mVisibleRequested || r.isVisible()) { @@ -1191,12 +1198,6 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio // is not yet committed, so isVisible()=true. hasVisibleActivities = true; } - if (isRemoved) { - // The package of the died process should be force-stopped, so make its activities - // as finishing to prevent the process from being started again if the next top (or - // being visible) activity also resides in the same process. - r.makeFinishingLocked(); - } final Task rootTask = r.getRootTask(); if (rootTask != null) { diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp index 74e2328105dc..b6633ced771b 100644 --- a/services/core/jni/com_android_server_VibratorService.cpp +++ b/services/core/jni/com_android_server_VibratorService.cpp @@ -49,6 +49,8 @@ namespace aidl = android::hardware::vibrator; namespace android { +static JavaVM* sJvm = nullptr; + static jmethodID sMethodIdOnComplete; static struct { @@ -228,6 +230,15 @@ bool isValidEffect(jlong effect) { return val >= *iter.begin() && val <= *std::prev(iter.end()); } +static void callVibrationOnComplete(jobject vibration) { + if (vibration == nullptr) { + return; + } + auto jniEnv = GetOrAttachJNIEnvironment(sJvm); + jniEnv->CallVoidMethod(vibration, sMethodIdOnComplete); + jniEnv->DeleteGlobalRef(vibration); +} + static aidl::CompositeEffect effectFromJavaPrimitive(JNIEnv* env, jobject primitive) { aidl::CompositeEffect effect; effect.primitive = static_cast<aidl::CompositePrimitive>( @@ -273,18 +284,16 @@ static jboolean vibratorExists(JNIEnv* env, jclass /* clazz */, jlong controller return controller->ping().isOk() ? JNI_TRUE : JNI_FALSE; } -static void vibratorOn(JNIEnv* /* env */, jclass /* clazz */, jlong timeout_ms) { - if (auto hal = getHal<aidl::IVibrator>()) { - auto status = hal->call(&aidl::IVibrator::on, timeout_ms, nullptr); - if (!status.isOk()) { - ALOGE("vibratorOn command failed: %s", status.toString8().string()); - } - } else { - Status retStatus = halCall(&V1_0::IVibrator::on, timeout_ms).withDefault(Status::UNKNOWN_ERROR); - if (retStatus != Status::OK) { - ALOGE("vibratorOn command failed (%" PRIu32 ").", static_cast<uint32_t>(retStatus)); - } +static void vibratorOn(JNIEnv* env, jclass /* clazz */, jlong controllerPtr, jlong timeoutMs, + jobject vibration) { + vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr); + if (controller == nullptr) { + ALOGE("vibratorOn failed because controller was not initialized"); + return; } + jobject vibrationRef = vibration == nullptr ? vibration : MakeGlobalRefOrDie(env, vibration); + auto callback = [vibrationRef]() { callVibrationOnComplete(vibrationRef); }; + controller->on(std::chrono::milliseconds(timeoutMs), callback); } static void vibratorOff(JNIEnv* env, jclass /* clazz */, jlong controllerPtr) { @@ -399,10 +408,11 @@ static jlong vibratorPerformEffect(JNIEnv* env, jclass /* clazz */, jlong effect return -1; } -static void vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jobjectArray composition, - jobject vibration) { - auto hal = getHal<aidl::IVibrator>(); - if (!hal) { +static void vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlong controllerPtr, + jobjectArray composition, jobject vibration) { + vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr); + if (controller == nullptr) { + ALOGE("vibratorPerformComposedEffect failed because controller was not initialized"); return; } size_t size = env->GetArrayLength(composition); @@ -411,14 +421,9 @@ static void vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jobje jobject element = env->GetObjectArrayElement(composition, i); effects.push_back(effectFromJavaPrimitive(env, element)); } - sp<AidlVibratorCallback> effectCallback = new AidlVibratorCallback(env, vibration); - - auto status = hal->call(&aidl::IVibrator::compose, effects, effectCallback); - if (!status.isOk()) { - if (status.exceptionCode() != binder::Status::EX_UNSUPPORTED_OPERATION) { - ALOGE("Failed to play haptic effect composition"); - } - } + jobject vibrationRef = vibration == nullptr ? vibration : MakeGlobalRefOrDie(env, vibration); + auto callback = [vibrationRef]() { callVibrationOnComplete(vibrationRef); }; + controller->performComposedEffect(effects, callback); } static jlong vibratorGetCapabilities(JNIEnv* env, jclass /* clazz */, jlong controllerPtr) { @@ -456,13 +461,13 @@ static const JNINativeMethod method_table[] = { {"vibratorInit", "()J", (void*)vibratorInit}, {"vibratorGetFinalizer", "()J", (void*)vibratorGetFinalizer}, {"vibratorExists", "(J)Z", (void*)vibratorExists}, - {"vibratorOn", "(J)V", (void*)vibratorOn}, + {"vibratorOn", "(JJLcom/android/server/VibratorService$Vibration;)V", (void*)vibratorOn}, {"vibratorOff", "(J)V", (void*)vibratorOff}, {"vibratorSetAmplitude", "(JI)V", (void*)vibratorSetAmplitude}, {"vibratorPerformEffect", "(JJLcom/android/server/VibratorService$Vibration;Z)J", (void*)vibratorPerformEffect}, {"vibratorPerformComposedEffect", - "([Landroid/os/VibrationEffect$Composition$PrimitiveEffect;Lcom/android/server/" + "(J[Landroid/os/VibrationEffect$Composition$PrimitiveEffect;Lcom/android/server/" "VibratorService$Vibration;)V", (void*)vibratorPerformComposedEffect}, {"vibratorGetSupportedEffects", "(J)[I", (void*)vibratorGetSupportedEffects}, @@ -472,7 +477,8 @@ static const JNINativeMethod method_table[] = { {"vibratorAlwaysOnDisable", "(JJ)V", (void*)vibratorAlwaysOnDisable}, }; -int register_android_server_VibratorService(JNIEnv *env) { +int register_android_server_VibratorService(JavaVM* vm, JNIEnv* env) { + sJvm = vm; sMethodIdOnComplete = GetMethodIDOrDie(env, FindClassOrDie(env, "com/android/server/VibratorService$Vibration"), diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 0202c88009fd..9751c46f93c9 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -75,6 +75,12 @@ using android::base::ParseUint; using android::base::StringPrintf; +// Maximum allowable delay value in a vibration pattern before +// which the delay will be truncated. +static constexpr std::chrono::duration MAX_VIBRATE_PATTERN_DELAY = 100s; +static constexpr std::chrono::milliseconds MAX_VIBRATE_PATTERN_DELAY_MILLIS = + std::chrono::duration_cast<std::chrono::milliseconds>(MAX_VIBRATE_PATTERN_DELAY); + namespace android { // The exponent used to calculate the pointer speed scaling factor. @@ -415,6 +421,10 @@ void NativeInputManager::setDisplayViewports(JNIEnv* env, jobjectArray viewportO AutoMutex _l(mLock); mLocked.viewports = viewports; mLocked.pointerDisplayId = pointerDisplayId; + std::shared_ptr<PointerController> controller = mLocked.pointerController.lock(); + if (controller != nullptr) { + controller->onDisplayViewportsUpdated(mLocked.viewports); + } } // release lock mInputManager->getReader()->requestRefreshConfiguration( @@ -812,8 +822,8 @@ void NativeInputManager::updateInactivityTimeoutLocked() REQUIRES(mLock) { } bool lightsOut = mLocked.systemUiVisibility & ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN; - controller->setInactivityTimeout(lightsOut ? PointerController::InactivityTimeout::SHORT - : PointerController::InactivityTimeout::NORMAL); + controller->setInactivityTimeout(lightsOut ? InactivityTimeout::SHORT + : InactivityTimeout::NORMAL); } void NativeInputManager::setPointerSpeed(int32_t speed) { @@ -1636,17 +1646,19 @@ static void nativeVibrate(JNIEnv* env, jclass /* clazz */, jlong ptr, jint devic patternObj, nullptr)); jint* amplitudes = static_cast<jint*>(env->GetPrimitiveArrayCritical(amplitudesObj, nullptr)); - std::vector<VibrationElement> pattern(patternSize); + std::vector<VibrationElement> elements(patternSize); for (size_t i = 0; i < patternSize; i++) { - jlong duration = - max(min(patternMillis[i], (jlong)MAX_VIBRATE_PATTERN_DELAY_MSECS), (jlong)0); - pattern[i].duration = std::chrono::milliseconds(duration); - pattern[i].channels = {amplitudes[i]}; + // VibrationEffect.validate guarantees duration > 0. + std::chrono::milliseconds duration(patternMillis[i]); + elements[i].duration = std::min(duration, MAX_VIBRATE_PATTERN_DELAY_MILLIS); + // TODO: (b/161629089) apply channel specific amplitudes from development API. + elements[i].channels = {static_cast<uint8_t>(amplitudes[i]), + static_cast<uint8_t>(amplitudes[i])}; } env->ReleasePrimitiveArrayCritical(patternObj, patternMillis, JNI_ABORT); env->ReleasePrimitiveArrayCritical(amplitudesObj, amplitudes, JNI_ABORT); - im->getInputManager()->getReader()->vibrate(deviceId, pattern, repeat, token); + im->getInputManager()->getReader()->vibrate(deviceId, elements, repeat, token); } static void nativeCancelVibrate(JNIEnv* /* env */, diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp index e904645bda8f..9e2bb45ab341 100644 --- a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp +++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp @@ -314,31 +314,6 @@ static inline InputDescs openInputs(JNIEnv* env, const JniIds& jni, jobject shel return result; } -static inline JNIEnv* GetJNIEnvironment(JavaVM* vm) { - JNIEnv* env; - if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { - return 0; - } - return env; -} - -static inline JNIEnv* GetOrAttachJNIEnvironment(JavaVM* jvm) { - JNIEnv* env = GetJNIEnvironment(jvm); - if (!env) { - int result = jvm->AttachCurrentThread(&env, nullptr); - CHECK_EQ(result, JNI_OK) << "thread attach failed"; - struct VmDetacher { - VmDetacher(JavaVM* vm) : mVm(vm) {} - ~VmDetacher() { mVm->DetachCurrentThread(); } - - private: - JavaVM* const mVm; - }; - static thread_local VmDetacher detacher(jvm); - } - return env; -} - class PMSCDataLoader; struct OnTraceChanged { @@ -415,7 +390,7 @@ private: bool onPrepareImage(dataloader::DataLoaderInstallationFiles addedFiles) final { ALOGE("onPrepareImage: start."); - JNIEnv* env = GetOrAttachJNIEnvironment(mJvm); + JNIEnv* env = GetOrAttachJNIEnvironment(mJvm, JNI_VERSION_1_6); const auto& jni = jniIds(env); jobject shellCommand = env->CallStaticObjectMethod(jni.packageManagerShellCommandDataLoader, diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index 6f24e3b580b7..5df1adafed5e 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -37,7 +37,7 @@ int register_android_server_UsbDeviceManager(JNIEnv* env); int register_android_server_UsbMidiDevice(JNIEnv* env); int register_android_server_UsbHostManager(JNIEnv* env); int register_android_server_vr_VrManagerService(JNIEnv* env); -int register_android_server_VibratorService(JNIEnv* env); +int register_android_server_VibratorService(JavaVM* vm, JNIEnv* env); int register_android_server_location_GnssLocationProvider(JNIEnv* env); int register_android_server_connectivity_Vpn(JNIEnv* env); int register_android_server_TestNetworkService(JNIEnv* env); @@ -90,7 +90,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) register_android_server_UsbAlsaJackDetector(env); register_android_server_UsbHostManager(env); register_android_server_vr_VrManagerService(env); - register_android_server_VibratorService(env); + register_android_server_VibratorService(vm, env); register_android_server_SystemServer(env); register_android_server_location_GnssLocationProvider(env); register_android_server_connectivity_Vpn(env); diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java index 37bf66491882..33317a38853e 100644 --- a/services/people/java/com/android/server/people/PeopleService.java +++ b/services/people/java/com/android/server/people/PeopleService.java @@ -19,6 +19,8 @@ package com.android.server.people; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.app.people.ConversationChannel; +import android.app.people.IPeopleManager; import android.app.prediction.AppPredictionContext; import android.app.prediction.AppPredictionSessionId; import android.app.prediction.AppTarget; @@ -26,8 +28,11 @@ import android.app.prediction.AppTargetEvent; import android.app.prediction.IPredictionCallback; import android.content.Context; import android.content.pm.ParceledListSlice; +import android.os.Binder; import android.os.CancellationSignal; +import android.os.Process; import android.os.RemoteException; +import android.os.UserHandle; import android.util.ArrayMap; import android.util.Slog; @@ -35,6 +40,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.server.SystemService; import com.android.server.people.data.DataManager; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.function.Consumer; @@ -68,6 +74,7 @@ public class PeopleService extends SystemService { @Override public void onStart() { + publishBinderService(Context.PEOPLE_SERVICE, new BinderService()); publishLocalService(PeopleServiceInternal.class, new LocalService()); } @@ -81,6 +88,38 @@ public class PeopleService extends SystemService { mDataManager.onUserStopping(user.getUserIdentifier()); } + /** + * Enforces that only the system or root UID can make certain calls. + * + * @param message used as message if SecurityException is thrown + * @throws SecurityException if the caller is not system or root + */ + private static void enforceSystemOrRoot(String message) { + int uid = Binder.getCallingUid(); + if (!UserHandle.isSameApp(uid, Process.SYSTEM_UID) && uid != Process.ROOT_UID) { + throw new SecurityException("Only system may " + message); + } + } + + private final class BinderService extends IPeopleManager.Stub { + + @Override + public ParceledListSlice<ConversationChannel> getRecentConversations() { + enforceSystemOrRoot("get recent conversations"); + return new ParceledListSlice<>(new ArrayList<>()); + } + + @Override + public void removeRecentConversation(String packageName, int userId, String shortcutId) { + enforceSystemOrRoot("remove a recent conversation"); + } + + @Override + public void removeAllRecentConversations() { + enforceSystemOrRoot("remove all recent conversations"); + } + } + @VisibleForTesting final class LocalService extends PeopleServiceInternal { diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java index cd9b6aca72e0..cbebe6984794 100644 --- a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java +++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java @@ -1092,9 +1092,11 @@ public class BackupManagerServiceRoboTest { registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false); - backupManagerService.beginRestoreSession(mUserOneId, TEST_PACKAGE, TEST_TRANSPORT); + backupManagerService.beginRestoreSession(mUserOneId, TEST_PACKAGE, TEST_TRANSPORT, + OperationType.BACKUP); - verify(mUserOneService).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT); + verify(mUserOneService).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT, + OperationType.BACKUP); } /** Test that the backup service does not route methods for non-registered users. */ @@ -1104,9 +1106,11 @@ public class BackupManagerServiceRoboTest { registerUser(backupManagerService, mUserOneId, mUserOneService); setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false); - backupManagerService.beginRestoreSession(mUserTwoId, TEST_PACKAGE, TEST_TRANSPORT); + backupManagerService.beginRestoreSession(mUserTwoId, TEST_PACKAGE, TEST_TRANSPORT, + OperationType.BACKUP); - verify(mUserOneService, never()).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT); + verify(mUserOneService, never()).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT, + OperationType.BACKUP); } /** Test that the backup service routes methods correctly to the user that requests it. */ diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java index 6184c4ed7f1a..09e3bfe9cf20 100644 --- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java +++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java @@ -794,7 +794,7 @@ public class KeyValueBackupTaskTest { setUpAgent(PACKAGE_1); doThrow(SecurityException.class) .when(mBackupManagerService) - .bindToAgentSynchronous(argThat(applicationInfo(PACKAGE_1)), anyInt()); + .bindToAgentSynchronous(argThat(applicationInfo(PACKAGE_1)), anyInt(), anyInt()); KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1); runTask(task); @@ -812,7 +812,7 @@ public class KeyValueBackupTaskTest { setUpAgent(PACKAGE_1); doThrow(SecurityException.class) .when(mBackupManagerService) - .bindToAgentSynchronous(argThat(applicationInfo(PACKAGE_1)), anyInt()); + .bindToAgentSynchronous(argThat(applicationInfo(PACKAGE_1)), anyInt(), anyInt()); KeyValueBackupTask task = createKeyValueBackupTask(transportMock, true, PACKAGE_1); runTask(task); @@ -2593,11 +2593,13 @@ public class KeyValueBackupTaskTest { if (packageData.available) { doReturn(backupAgentBinder) .when(mBackupManagerService) - .bindToAgentSynchronous(argThat(applicationInfo(packageData)), anyInt()); + .bindToAgentSynchronous(argThat(applicationInfo(packageData)), anyInt(), + anyInt()); } else { doReturn(null) .when(mBackupManagerService) - .bindToAgentSynchronous(argThat(applicationInfo(packageData)), anyInt()); + .bindToAgentSynchronous(argThat(applicationInfo(packageData)), anyInt(), + anyInt()); } return new AgentMock(backupAgentBinder, backupAgent); } catch (RemoteException e) { diff --git a/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java b/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java index 3fc421dfb6e9..5883c1cb5995 100644 --- a/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java +++ b/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java @@ -57,6 +57,7 @@ import com.android.server.backup.internal.BackupHandler; import com.android.server.backup.testing.TransportData; import com.android.server.backup.testing.TransportTestUtils; import com.android.server.backup.testing.TransportTestUtils.TransportMock; +import com.android.server.backup.utils.BackupEligibilityRules; import com.android.server.testing.shadows.ShadowApplicationPackageManager; import com.android.server.testing.shadows.ShadowEventLog; import com.android.server.testing.shadows.ShadowPerformUnifiedRestoreTask; @@ -96,6 +97,7 @@ public class ActiveRestoreSessionTest { @Mock private TransportManager mTransportManager; @Mock private IRestoreObserver mObserver; @Mock private IBackupManagerMonitor mMonitor; + @Mock private BackupEligibilityRules mBackupEligibilityRules; private ShadowLooper mShadowBackupLooper; private ShadowApplication mShadowApplication; private UserBackupManagerService.BackupWakeLock mWakeLock; @@ -576,7 +578,8 @@ public class ActiveRestoreSessionTest { private IRestoreSession createActiveRestoreSession( String packageName, TransportData transport) { return new ActiveRestoreSession( - mBackupManagerService, packageName, transport.transportName); + mBackupManagerService, packageName, transport.transportName, + mBackupEligibilityRules); } private IRestoreSession createActiveRestoreSessionWithRestoreSets( @@ -584,7 +587,8 @@ public class ActiveRestoreSessionTest { throws RemoteException { ActiveRestoreSession restoreSession = new ActiveRestoreSession( - mBackupManagerService, packageName, transport.transportName); + mBackupManagerService, packageName, transport.transportName, + mBackupEligibilityRules); restoreSession.setRestoreSets(restoreSets); return restoreSession; } diff --git a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt index ecdb30f5e84b..09552082e4af 100644 --- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt +++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt @@ -33,7 +33,6 @@ import com.android.server.pm.parsing.pkg.PackageImpl import com.android.server.pm.parsing.pkg.ParsedPackage import com.android.server.pm.permission.PermissionManagerServiceInternal import com.android.server.pm.test.override.PackageManagerComponentLabelIconOverrideTest.Companion.Params.AppType -import com.android.server.pm.test.override.R import com.android.server.testutils.TestHandler import com.android.server.testutils.mock import com.android.server.testutils.mockThrowOnUnmocked @@ -266,7 +265,7 @@ class PackageManagerComponentLabelIconOverrideTest { .hideAsFinal() private fun makePkgSetting(pkgName: String) = spy(PackageSetting(pkgName, null, File("/test"), - File("/test"), null, null, null, null, 0, 0, 0, 0, null, null, null)) { + null, null, null, null, 0, 0, 0, 0, null, null, null)) { this.pkgState.isUpdatedSystemApp = params.isUpdatedSystemApp } diff --git a/services/tests/PackageManagerServiceTests/host/Android.bp b/services/tests/PackageManagerServiceTests/host/Android.bp index 41dfade1a09a..cffcdd8f94bd 100644 --- a/services/tests/PackageManagerServiceTests/host/Android.bp +++ b/services/tests/PackageManagerServiceTests/host/Android.bp @@ -25,17 +25,28 @@ java_test_host { ], test_suites: ["general-tests"], java_resources: [ - ":PackageManagerDummyAppVersion1", - ":PackageManagerDummyAppVersion2", - ":PackageManagerDummyAppVersion3", - ":PackageManagerDummyAppVersion4", - ":PackageManagerDummyAppOriginalOverride", - ":PackageManagerServiceHostTestsResources", - ] + ":PackageManagerTestAppStub", + ":PackageManagerTestAppVersion1", + ":PackageManagerTestAppVersion2", + ":PackageManagerTestAppVersion3", + ":PackageManagerTestAppVersion3Invalid", + ":PackageManagerTestAppVersion4", + ":PackageManagerTestAppOriginalOverride", + ], } -filegroup { - name: "PackageManagerServiceHostTestsResources", - srcs: [ "resources/*" ], - path: "resources/" +genrule { + name: "PackageManagerTestAppVersion3Invalid", + tools: [ + "soong_zip", + "zipalign", + ], + srcs: [ + ":PackageManagerTestAppVersion3", + ], + out: ["PackageManagerTestAppVersion3Invalid.apk"], + cmd: "mkdir -p $(genDir)/apk && unzip $(in) -d $(genDir)/apk" + + " && truncate -s 800 $(genDir)/apk/META-INF/CERT.RSA" + + " && $(location soong_zip) -o $(genDir)/temp.apk -L 0 -C $(genDir)/apk -D $(genDir)/apk" + + " && $(location zipalign) -f 4 $(genDir)/temp.apk $(out)", } diff --git a/services/tests/PackageManagerServiceTests/host/resources/PackageManagerDummyAppVersion3Invalid.apk b/services/tests/PackageManagerServiceTests/host/resources/PackageManagerDummyAppVersion3Invalid.apk Binary files differdeleted file mode 100644 index 127886cf8e9e..000000000000 --- a/services/tests/PackageManagerServiceTests/host/resources/PackageManagerDummyAppVersion3Invalid.apk +++ /dev/null diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt index 234fcf19062d..8dfefaf9750f 100644 --- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt +++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt @@ -21,8 +21,9 @@ import com.android.tradefed.device.ITestDevice import java.io.File import java.io.FileOutputStream -internal fun SystemPreparer.pushApk(file: String, partition: Partition) = - pushResourceFile(file, HostUtils.makePathForApk(file, partition).toString()) +internal fun SystemPreparer.pushApk(javaResourceName: String, partition: Partition) = + pushResourceFile(javaResourceName, HostUtils.makePathForApk(javaResourceName, partition) + .toString()) internal fun SystemPreparer.deleteApkFolders( partition: Partition, @@ -58,4 +59,55 @@ internal object HostUtils { } return file } + + /** + * dumpsys package and therefore device.getAppPackageInfo doesn't work immediately after reboot, + * so the following methods parse the package dump directly to see if the path matches. + */ + fun getCodePaths(device: ITestDevice, pkgName: String) = + device.executeShellCommand("pm dump $pkgName") + .lineSequence() + .map(String::trim) + .filter { it.startsWith("codePath=") } + .map { it.removePrefix("codePath=") } + .toList() + + private fun userIdLineSequence(device: ITestDevice, pkgName: String) = + device.executeShellCommand("pm dump $pkgName") + .lineSequence() + .dropWhile { !it.startsWith("Packages:") } + .takeWhile { + !it.startsWith("Hidden system packages:") && + !it.startsWith("Queries:") + } + .map(String::trim) + .filter { it.startsWith("User ") } + + fun getUserIdToPkgEnabledState(device: ITestDevice, pkgName: String) = + userIdLineSequence(device, pkgName).associate { + val userId = it.removePrefix("User ") + .takeWhile(Char::isDigit) + .toInt() + val enabled = it.substringAfter("enabled=") + .takeWhile(Char::isDigit) + .toInt() + .let { + when (it) { + 0, 1 -> true + else -> false + } + } + userId to enabled + } + + fun getUserIdToPkgInstalledState(device: ITestDevice, pkgName: String) = + userIdLineSequence(device, pkgName).associate { + val userId = it.removePrefix("User ") + .takeWhile(Char::isDigit) + .toInt() + val installed = it.substringAfter("installed=") + .takeWhile { !it.isWhitespace() } + .toBoolean() + userId to installed + } } diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt index 39b40d8d3195..b7d135991ccd 100644 --- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt +++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt @@ -33,11 +33,11 @@ import org.junit.runner.RunWith class InvalidNewSystemAppTest : BaseHostJUnit4Test() { companion object { - private const val TEST_PKG_NAME = "com.android.server.pm.test.dummy_app" - private const val VERSION_ONE = "PackageManagerDummyAppVersion1.apk" - private const val VERSION_TWO = "PackageManagerDummyAppVersion2.apk" - private const val VERSION_THREE_INVALID = "PackageManagerDummyAppVersion3Invalid.apk" - private const val VERSION_FOUR = "PackageManagerDummyAppVersion4.apk" + private const val TEST_PKG_NAME = "com.android.server.pm.test.test_app" + private const val VERSION_ONE = "PackageManagerTestAppVersion1.apk" + private const val VERSION_TWO = "PackageManagerTestAppVersion2.apk" + private const val VERSION_THREE_INVALID = "PackageManagerTestAppVersion3Invalid.apk" + private const val VERSION_FOUR = "PackageManagerTestAppVersion4.apk" @get:ClassRule val deviceRebootRule = SystemPreparer.TestRuleDelegate(true) @@ -49,14 +49,14 @@ class InvalidNewSystemAppTest : BaseHostJUnit4Test() { @get:Rule val rules = RuleChain.outerRule(tempFolder).around(preparer)!! - private val filePath = HostUtils.makePathForApk("PackageManagerDummyApp.apk", Partition.PRODUCT) + private val filePath = HostUtils.makePathForApk("PackageManagerTestApp.apk", Partition.PRODUCT) @Before @After fun removeApk() { device.uninstallPackage(TEST_PKG_NAME) - device.deleteFile(filePath.parent.toString()) - device.reboot() + preparer.deleteFile(filePath.parent.toString()) + .reboot() } @Test diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt index fb0348c3146b..4ae3ca5f7263 100644 --- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt +++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt @@ -33,11 +33,11 @@ import org.junit.runner.RunWith class OriginalPackageMigrationTest : BaseHostJUnit4Test() { companion object { - private const val TEST_PKG_NAME = "com.android.server.pm.test.dummy_app" - private const val VERSION_ONE = "PackageManagerDummyAppVersion1.apk" - private const val VERSION_TWO = "PackageManagerDummyAppVersion2.apk" - private const val VERSION_THREE = "PackageManagerDummyAppVersion3.apk" - private const val NEW_PKG = "PackageManagerDummyAppOriginalOverride.apk" + private const val TEST_PKG_NAME = "com.android.server.pm.test.test_app" + private const val VERSION_ONE = "PackageManagerTestAppVersion1.apk" + private const val VERSION_TWO = "PackageManagerTestAppVersion2.apk" + private const val VERSION_THREE = "PackageManagerTestAppVersion3.apk" + private const val NEW_PKG = "PackageManagerTestAppOriginalOverride.apk" @get:ClassRule val deviceRebootRule = SystemPreparer.TestRuleDelegate(true) @@ -55,6 +55,7 @@ class OriginalPackageMigrationTest : BaseHostJUnit4Test() { fun deleteApkFolders() { preparer.deleteApkFolders(Partition.SYSTEM, VERSION_ONE, VERSION_TWO, VERSION_THREE, NEW_PKG) + .reboot() } @Test @@ -99,9 +100,7 @@ class OriginalPackageMigrationTest : BaseHostJUnit4Test() { } private fun assertCodePath(apk: String) { - // dumpsys package and therefore device.getAppPackageInfo doesn't work here for some reason, - // so parse the package dump directly to see if the path matches. - assertThat(device.executeShellCommand("pm dump $TEST_PKG_NAME")) - .contains(HostUtils.makePathForApk(apk, Partition.SYSTEM).parent.toString()) + assertThat(HostUtils.getCodePaths(device, TEST_PKG_NAME)) + .containsExactly(HostUtils.makePathForApk(apk, Partition.SYSTEM).parent.toString()) } } diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt new file mode 100644 index 000000000000..207f10a3027b --- /dev/null +++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt @@ -0,0 +1,653 @@ +/* + * 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.pm.test + +import com.android.internal.util.test.SystemPreparer +import com.android.tradefed.device.ITestDevice +import com.android.tradefed.device.UserInfo +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner +import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test +import com.google.common.truth.Truth.assertThat +import org.junit.AfterClass +import org.junit.Before +import org.junit.ClassRule +import org.junit.Rule +import org.junit.Test +import org.junit.rules.RuleChain +import org.junit.rules.TemporaryFolder +import org.junit.runner.RunWith +import java.io.File +import java.util.zip.GZIPOutputStream + +@RunWith(DeviceJUnit4ClassRunner::class) +class SystemStubMultiUserDisableUninstallTest : BaseHostJUnit4Test() { + + companion object { + private const val TEST_PKG_NAME = "com.android.server.pm.test.test_app" + private const val VERSION_STUB = "PackageManagerTestAppStub.apk" + private const val VERSION_ONE = "PackageManagerTestAppVersion1.apk" + + /** + * How many total users on device to test, including primary. This will clean up any + * users created specifically for this test. + */ + private const val USER_COUNT = 3 + + /** + * Whether to manually reset state at each test method without rebooting + * for faster iterative development. + */ + private const val DEBUG_NO_REBOOT = false + + @get:ClassRule + val deviceRebootRule = SystemPreparer.TestRuleDelegate(true) + + private val parentClassName = SystemStubMultiUserDisableUninstallTest::class.java.simpleName + + private val deviceCompressedFile = + HostUtils.makePathForApk("$parentClassName.apk", Partition.PRODUCT).parent + .resolve("$parentClassName.apk.gz") + + private val stubFile = + HostUtils.makePathForApk("$parentClassName-Stub.apk", Partition.PRODUCT) + + private val secondaryUsers = mutableListOf<Int>() + private val usersToRemove = mutableListOf<Int>() + private var savedDevice: ITestDevice? = null + private var savedPreparer: SystemPreparer? = null + + private fun setUpUsers(device: ITestDevice) { + if (this.savedDevice != null) return + this.savedDevice = device + secondaryUsers.clear() + secondaryUsers += device.userInfos.values.map(UserInfo::userId).filterNot { it == 0 } + while (secondaryUsers.size < USER_COUNT) { + secondaryUsers += device.createUser(parentClassName + secondaryUsers.size) + .also { usersToRemove += it } + } + } + + @JvmStatic + @AfterClass + fun cleanUp() { + savedDevice ?: return + + usersToRemove.forEach { + savedDevice?.removeUser(it) + } + + savedDevice?.uninstallPackage(TEST_PKG_NAME) + savedDevice?.deleteFile(stubFile.parent.toString()) + savedDevice?.deleteFile(deviceCompressedFile.parent.toString()) + savedDevice?.reboot() + savedDevice = null + + if (DEBUG_NO_REBOOT) { + savedPreparer?.after() + savedPreparer = null + } + } + } + + private val tempFolder = TemporaryFolder() + + // TODO(b/160159215): Use START_STOP rather than FULL once it's fixed. This will drastically + // improve pre/post-submit times. + private val preparer: SystemPreparer = SystemPreparer(tempFolder, + SystemPreparer.RebootStrategy.FULL, deviceRebootRule) { this.device } + + @get:Rule + val rules = RuleChain.outerRule(tempFolder).let { + if (DEBUG_NO_REBOOT) { + it!! + } else { + it.around(preparer)!! + } + } + + private var hostCompressedFile: File? = null + + private val previousCodePaths = mutableListOf<String>() + + @Before + fun ensureUserAndCompressStubAndInstall() { + setUpUsers(device) + + val initialized = hostCompressedFile != null + if (!initialized) { + hostCompressedFile = tempFolder.newFile() + hostCompressedFile!!.outputStream().use { + javaClass.classLoader + .getResource(VERSION_ONE)!! + .openStream() + .use { input -> + GZIPOutputStream(it).use { output -> + input.copyTo(output) + } + } + } + } + + device.uninstallPackage(TEST_PKG_NAME) + + if (!initialized || !DEBUG_NO_REBOOT) { + savedPreparer = preparer + preparer.pushResourceFile(VERSION_STUB, stubFile.toString()) + .pushFile(hostCompressedFile, deviceCompressedFile.toString()) + .reboot() + } + + // This test forces the state to installed/enabled for all users, + // since it only tests the uninstall/disable side. + installExisting(User.PRIMARY) + installExisting(User.SECONDARY) + + ensureEnabled() + + // Ensure data app isn't re-installed multiple times by comparing against the original path + val codePath = HostUtils.getCodePaths(device, TEST_PKG_NAME).first() + assertThat(codePath).contains("/data/app") + assertThat(codePath).contains(TEST_PKG_NAME) + + previousCodePaths.clear() + previousCodePaths += codePath + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + } + + @Test + fun disablePrimaryFirstAndUninstall() { + toggleEnabled(false, User.PRIMARY) + + assertState( + primaryInstalled = true, primaryEnabled = false, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + + toggleEnabled(false, User.SECONDARY) + + assertState( + primaryInstalled = true, primaryEnabled = false, + secondaryInstalled = true, secondaryEnabled = false, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + + device.uninstallPackage(TEST_PKG_NAME) + + assertState( + primaryInstalled = true, primaryEnabled = false, + secondaryInstalled = true, secondaryEnabled = false, + codePaths = listOf(CodePath.SYSTEM) + ) + } + + @Test + fun disableSecondaryFirstAndUninstall() { + toggleEnabled(false, User.SECONDARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = false, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + + toggleEnabled(false, User.PRIMARY) + + assertState( + primaryInstalled = true, primaryEnabled = false, + secondaryInstalled = true, secondaryEnabled = false, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + + device.uninstallPackage(TEST_PKG_NAME) + + assertState( + primaryInstalled = true, primaryEnabled = false, + secondaryInstalled = true, secondaryEnabled = false, + codePaths = listOf(CodePath.SYSTEM) + ) + } + + @Test + fun disabledUninstalledEnablePrimaryFirst() { + toggleEnabled(false, User.PRIMARY) + toggleEnabled(false, User.SECONDARY) + device.uninstallPackage(TEST_PKG_NAME) + + toggleEnabled(true, User.PRIMARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = false, + codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) + ) + + toggleEnabled(true, User.SECONDARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + } + + @Test + fun disabledUninstalledEnableSecondaryFirst() { + toggleEnabled(false, User.PRIMARY) + toggleEnabled(false, User.SECONDARY) + device.uninstallPackage(TEST_PKG_NAME) + + toggleEnabled(true, User.SECONDARY) + + assertState( + primaryInstalled = true, primaryEnabled = false, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) + ) + + toggleEnabled(true, User.PRIMARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + } + + @Test + fun uninstallPrimaryFirstByUserAndInstallExistingPrimaryFirst() { + uninstall(User.PRIMARY) + + assertState( + primaryInstalled = false, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) + ) + + uninstall(User.SECONDARY) + + assertState( + primaryInstalled = false, primaryEnabled = true, + secondaryInstalled = false, secondaryEnabled = true, + codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) + ) + + installExisting(User.PRIMARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = false, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + + installExisting(User.SECONDARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + } + + @Test + fun uninstallSecondaryFirstByUserAndInstallExistingSecondaryFirst() { + uninstall(User.PRIMARY) + + assertState( + primaryInstalled = false, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) + ) + + uninstall(User.SECONDARY) + + assertState( + primaryInstalled = false, primaryEnabled = true, + secondaryInstalled = false, secondaryEnabled = true, + codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) + ) + + installExisting(User.SECONDARY) + + assertState( + primaryInstalled = false, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + + installExisting(User.PRIMARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + } + + @Test + fun uninstallUpdatesAndEnablePrimaryFirst() { + device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME") + + // Uninstall-system-updates always disables system user 0 + // TODO: Is this intentional? There is no user argument for this command. + assertState( + primaryInstalled = true, primaryEnabled = false, + secondaryInstalled = true, secondaryEnabled = true, + // If any user is enabled when uninstalling updates, /data is re-uncompressed + codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) + ) + + toggleEnabled(true, User.PRIMARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + + // Test enabling secondary to ensure path does not change, even though it's already enabled + toggleEnabled(true, User.SECONDARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + } + + @Test + fun uninstallUpdatesAndEnableSecondaryFirst() { + device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME") + + // Uninstall-system-updates always disables system user 0 + assertState( + primaryInstalled = true, primaryEnabled = false, + secondaryInstalled = true, secondaryEnabled = true, + // If any user is enabled when uninstalling updates, /data is re-uncompressed + codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) + ) + + toggleEnabled(true, User.SECONDARY) + + assertState( + primaryInstalled = true, primaryEnabled = false, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + + toggleEnabled(true, User.PRIMARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + } + + @Test + fun disabledUninstallUpdatesAndEnablePrimaryFirst() { + toggleEnabled(false, User.PRIMARY) + toggleEnabled(false, User.SECONDARY) + + device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME") + + assertState( + primaryInstalled = true, primaryEnabled = false, + secondaryInstalled = true, secondaryEnabled = false, + codePaths = listOf(CodePath.SYSTEM) + ) + + toggleEnabled(true, User.PRIMARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = false, + codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) + ) + + toggleEnabled(true, User.SECONDARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + } + + @Test + fun disabledUninstallUpdatesAndEnableSecondaryFirst() { + toggleEnabled(false, User.PRIMARY) + toggleEnabled(false, User.SECONDARY) + + device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME") + + assertState( + primaryInstalled = true, primaryEnabled = false, + secondaryInstalled = true, secondaryEnabled = false, + codePaths = listOf(CodePath.SYSTEM) + ) + + toggleEnabled(true, User.SECONDARY) + + assertState( + primaryInstalled = true, primaryEnabled = false, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) + ) + + toggleEnabled(true, User.PRIMARY) + + assertState( + primaryInstalled = true, primaryEnabled = true, + secondaryInstalled = true, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + } + + @Test + fun uninstalledUninstallUpdatesAndEnablePrimaryFirst() { + uninstall(User.PRIMARY) + uninstall(User.SECONDARY) + + device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME") + + // Uninstall-system-updates always disables system user 0 + assertState( + primaryInstalled = false, primaryEnabled = false, + secondaryInstalled = false, secondaryEnabled = true, + codePaths = listOf(CodePath.SYSTEM) + ) + + toggleEnabled(true, User.PRIMARY) + + assertState( + primaryInstalled = false, primaryEnabled = true, + secondaryInstalled = false, secondaryEnabled = true, + codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) + ) + + toggleEnabled(true, User.SECONDARY) + + assertState( + primaryInstalled = false, primaryEnabled = true, + secondaryInstalled = false, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + } + + @Test + fun uninstalledUninstallUpdatesAndEnableSecondaryFirst() { + uninstall(User.PRIMARY) + uninstall(User.SECONDARY) + + device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME") + + // Uninstall-system-updates always disables system user 0 + assertState( + primaryInstalled = false, primaryEnabled = false, + secondaryInstalled = false, secondaryEnabled = true, + codePaths = listOf(CodePath.SYSTEM) + ) + + toggleEnabled(true, User.SECONDARY) + + assertState( + primaryInstalled = false, primaryEnabled = false, + secondaryInstalled = false, secondaryEnabled = true, + codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM) + ) + + toggleEnabled(true, User.PRIMARY) + + assertState( + primaryInstalled = false, primaryEnabled = true, + secondaryInstalled = false, secondaryEnabled = true, + codePaths = listOf(CodePath.SAME, CodePath.SYSTEM) + ) + } + + private fun ensureEnabled() { + toggleEnabled(true, User.PRIMARY) + toggleEnabled(true, User.SECONDARY) + + assertThat(HostUtils.getUserIdToPkgEnabledState(device, TEST_PKG_NAME).all { it.value }) + .isTrue() + } + + private fun toggleEnabled(enabled: Boolean, user: User, pkgName: String = TEST_PKG_NAME) { + val command = if (enabled) "enable" else "disable" + @Suppress("UNUSED_VARIABLE") val exhaust: Any = when (user) { + User.PRIMARY -> { + device.executeShellCommand("pm $command --user 0 $pkgName") + } + User.SECONDARY -> { + secondaryUsers.forEach { + device.executeShellCommand("pm $command --user $it $pkgName") + } + } + } + } + + private fun uninstall(user: User, pkgName: String = TEST_PKG_NAME) { + @Suppress("UNUSED_VARIABLE") val exhaust: Any = when (user) { + User.PRIMARY -> { + device.executeShellCommand("pm uninstall --user 0 $pkgName") + } + User.SECONDARY -> { + secondaryUsers.forEach { + device.executeShellCommand("pm uninstall --user $it $pkgName") + } + } + } + } + + private fun installExisting(user: User, pkgName: String = TEST_PKG_NAME) { + @Suppress("UNUSED_VARIABLE") val exhaust: Any = when (user) { + User.PRIMARY -> { + device.executeShellCommand("pm install-existing --user 0 $pkgName") + } + User.SECONDARY -> { + secondaryUsers.forEach { + device.executeShellCommand("pm install-existing --user $it $pkgName") + } + } + } + } + + private fun assertState( + primaryInstalled: Boolean, + primaryEnabled: Boolean, + secondaryInstalled: Boolean, + secondaryEnabled: Boolean, + codePaths: List<CodePath> + ) { + HostUtils.getUserIdToPkgInstalledState(device, TEST_PKG_NAME) + .forEach { (userId, installed) -> + if (userId == 0) { + assertThat(installed).isEqualTo(primaryInstalled) + } else { + assertThat(installed).isEqualTo(secondaryInstalled) + } + } + + HostUtils.getUserIdToPkgEnabledState(device, TEST_PKG_NAME) + .forEach { (userId, enabled) -> + if (userId == 0) { + assertThat(enabled).isEqualTo(primaryEnabled) + } else { + assertThat(enabled).isEqualTo(secondaryEnabled) + } + } + + assertCodePaths(codePaths.first(), codePaths.getOrNull(1)) + } + + private fun assertCodePaths(firstCodePath: CodePath, secondCodePath: CodePath? = null) { + val codePaths = HostUtils.getCodePaths(device, TEST_PKG_NAME) + assertThat(codePaths).hasSize(listOfNotNull(firstCodePath, secondCodePath).size) + + when (firstCodePath) { + CodePath.SAME -> { + assertThat(codePaths[0]).contains("/data/app") + assertThat(codePaths[0]).contains(TEST_PKG_NAME) + assertThat(codePaths[0]).isEqualTo(previousCodePaths.last()) + } + CodePath.DIFFERENT -> { + assertThat(codePaths[0]).contains("/data/app") + assertThat(codePaths[0]).contains(TEST_PKG_NAME) + assertThat(previousCodePaths).doesNotContain(codePaths[0]) + previousCodePaths.add(codePaths[0]) + } + CodePath.SYSTEM -> assertThat(codePaths[0]).isEqualTo(stubFile.parent.toString()) + } + + when (secondCodePath) { + CodePath.SAME, CodePath.DIFFERENT -> + throw AssertionError("secondDataPath cannot be a data path") + CodePath.SYSTEM -> assertThat(codePaths[1]).isEqualTo(stubFile.parent.toString()) + } + } + + enum class User { + /** The primary system user 0 */ + PRIMARY, + + /** + * All other users on the device that are not 0. This is split into an enum so that all + * methods that handle secondary act on all non-system users. Some behaviors only occur + * if a package state is marked for all non-primary users on the device, which can be + * more than just 1. + */ + SECONDARY + } + + enum class CodePath { + /** The data code path hasn't changed */ + SAME, + + /** New data code path */ + DIFFERENT, + + /** The static system code path */ + SYSTEM + } +} diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/Android.bp index c9b29275a731..4a3076e4736a 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/Android.bp +++ b/services/tests/PackageManagerServiceTests/host/test-apps/Android.bp @@ -13,26 +13,32 @@ // limitations under the License. android_test_helper_app { - name: "PackageManagerDummyAppVersion1", + name: "PackageManagerTestAppStub", + manifest: "AndroidManifestVersion1.xml", + srcs: [] +} + +android_test_helper_app { + name: "PackageManagerTestAppVersion1", manifest: "AndroidManifestVersion1.xml" } android_test_helper_app { - name: "PackageManagerDummyAppVersion2", + name: "PackageManagerTestAppVersion2", manifest: "AndroidManifestVersion2.xml" } android_test_helper_app { - name: "PackageManagerDummyAppVersion3", + name: "PackageManagerTestAppVersion3", manifest: "AndroidManifestVersion3.xml" } android_test_helper_app { - name: "PackageManagerDummyAppVersion4", + name: "PackageManagerTestAppVersion4", manifest: "AndroidManifestVersion4.xml" } android_test_helper_app { - name: "PackageManagerDummyAppOriginalOverride", + name: "PackageManagerTestAppOriginalOverride", manifest: "AndroidManifestOriginalOverride.xml" } diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestOriginalOverride.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestOriginalOverride.xml index f16e1bc8a927..cba580e44065 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestOriginalOverride.xml +++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestOriginalOverride.xml @@ -16,10 +16,10 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.server.pm.test.dummy_app.override" + package="com.android.server.pm.test.test_app.override" android:versionCode="2" > - <original-package android:name="com.android.server.pm.test.dummy_app"/> + <original-package android:name="com.android.server.pm.test.test_app"/> </manifest> diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml index b492a31349fc..efc7372a177c 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml +++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml @@ -16,12 +16,12 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.server.pm.test.dummy_app" + package="com.android.server.pm.test.test_app" android:versionCode="1" > <permission - android:name="com.android.server.pm.test.dummy_app.TEST_PERMISSION" + android:name="com.android.server.pm.test.test_app.TEST_PERMISSION" android:protectionLevel="normal" /> diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml index 25e9f8eb2a67..620054c5dd57 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml +++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml @@ -16,12 +16,12 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.server.pm.test.dummy_app" + package="com.android.server.pm.test.test_app" android:versionCode="2" > <permission - android:name="com.android.server.pm.test.dummy_app.TEST_PERMISSION" + android:name="com.android.server.pm.test.test_app.TEST_PERMISSION" android:protectionLevel="normal" /> diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml index 935f5e62f508..1997771783dd 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml +++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml @@ -16,12 +16,12 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.server.pm.test.dummy_app" + package="com.android.server.pm.test.test_app" android:versionCode="3" > <permission - android:name="com.android.server.pm.test.dummy_app.TEST_PERMISSION" + android:name="com.android.server.pm.test.test_app.TEST_PERMISSION" android:protectionLevel="normal" /> diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion4.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion4.xml index d0643cbb2aeb..d6ade0304189 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion4.xml +++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion4.xml @@ -16,12 +16,12 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.server.pm.test.dummy_app" + package="com.android.server.pm.test.test_app" android:versionCode="4" > <permission - android:name="com.android.server.pm.test.dummy_app.TEST_PERMISSION" + android:name="com.android.server.pm.test.test_app.TEST_PERMISSION" android:protectionLevel="normal" /> diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java index 2a267c413c31..82726c7ea20c 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java @@ -99,6 +99,7 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; +import java.io.File; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; @@ -165,7 +166,7 @@ public class MockingOomAdjusterTests { setFieldValue(ActivityManagerService.class, sService, "mHandler", mock(ActivityManagerService.MainHandler.class)); setFieldValue(ActivityManagerService.class, sService, "mProcessStats", - mock(ProcessStatsService.class)); + new ProcessStatsService(sService, new File(sContext.getFilesDir(), "procstats"))); setFieldValue(ActivityManagerService.class, sService, "mBackupTargets", mock(SparseArray.class)); setFieldValue(ActivityManagerService.class, sService, "mOomAdjProfiler", @@ -500,7 +501,7 @@ public class MockingOomAdjusterTests { public void testUpdateOomAdj_DoOne_Backup() { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); - BackupRecord backupTarget = new BackupRecord(null, 0, 0); + BackupRecord backupTarget = new BackupRecord(null, 0, 0, 0); backupTarget.app = app; doReturn(backupTarget).when(sService.mBackupTargets).get(anyInt()); sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; @@ -802,7 +803,7 @@ public class MockingOomAdjusterTests { ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, Context.BIND_ABOVE_CLIENT, mock(IBinder.class)); - BackupRecord backupTarget = new BackupRecord(null, 0, 0); + BackupRecord backupTarget = new BackupRecord(null, 0, 0, 0); backupTarget.app = client; doReturn(backupTarget).when(sService.mBackupTargets).get(anyInt()); sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; diff --git a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java index 1b6ac3c84210..7d6d90c4578c 100644 --- a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java @@ -26,7 +26,9 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.intThat; +import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; @@ -65,6 +67,7 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnit; @@ -277,7 +280,7 @@ public class VibratorServiceTest { assertTrue(service.isVibrating()); verify(mNativeWrapperMock).vibratorOff(); - verify(mNativeWrapperMock).vibratorOn(eq(100L)); + verify(mNativeWrapperMock).vibratorOn(eq(100L), any(VibratorService.Vibration.class)); verify(mNativeWrapperMock).vibratorSetAmplitude(eq(128)); } @@ -290,7 +293,7 @@ public class VibratorServiceTest { assertTrue(service.isVibrating()); verify(mNativeWrapperMock).vibratorOff(); - verify(mNativeWrapperMock).vibratorOn(eq(100L)); + verify(mNativeWrapperMock).vibratorOn(eq(100L), any(VibratorService.Vibration.class)); verify(mNativeWrapperMock, never()).vibratorSetAmplitude(anyInt()); } @@ -344,76 +347,157 @@ public class VibratorServiceTest { Mockito.clearInvocations(mNativeWrapperMock); VibrationEffect effect = VibrationEffect.createWaveform( - new long[] { 10, 10, 10 }, new int[] { 100, 200, 50 }, -1); + new long[]{10, 10, 10}, new int[]{100, 200, 50}, -1); vibrate(service, effect); verify(mNativeWrapperMock).vibratorOff(); + // Wait for VibrateThread to turn vibrator ON with total timing and no callback. Thread.sleep(5); - verify(mNativeWrapperMock).vibratorOn(eq(30L)); + verify(mNativeWrapperMock).vibratorOn(eq(30L), isNull()); + + // First amplitude set right away. verify(mNativeWrapperMock).vibratorSetAmplitude(eq(100)); + // Second amplitude set after first timing is finished. Thread.sleep(10); verify(mNativeWrapperMock).vibratorSetAmplitude(eq(200)); + // Third amplitude set after second timing is finished. Thread.sleep(10); verify(mNativeWrapperMock).vibratorSetAmplitude(eq(50)); } @Test - public void vibrate_withCallback_finishesVibrationWhenCallbackTriggered() { - mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); + public void vibrate_withOneShotAndNativeCallbackTriggered_finishesVibration() { + doAnswer(invocation -> { + ((VibratorService.Vibration) invocation.getArgument(1)).onComplete(); + return null; + }).when(mNativeWrapperMock).vibratorOn(anyLong(), any(VibratorService.Vibration.class)); + VibratorService service = createService(); + Mockito.clearInvocations(mNativeWrapperMock); + + vibrate(service, VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE)); + + InOrder inOrderVerifier = inOrder(mNativeWrapperMock); + inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); + inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(100L), + any(VibratorService.Vibration.class)); + inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); + } + + @Test + public void vibrate_withOneShotAndNativeCallbackNotTriggered_finishesVibrationViaFallback() { VibratorService service = createService(); Mockito.clearInvocations(mNativeWrapperMock); + vibrate(service, VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE)); + + verify(mNativeWrapperMock).vibratorOff(); + verify(mNativeWrapperMock).vibratorOn(eq(100L), any(VibratorService.Vibration.class)); + Mockito.clearInvocations(mNativeWrapperMock); + + // Run the scheduled callback to finish one-shot vibration. + mTestLooper.moveTimeForward(200); + mTestLooper.dispatchAll(); + + verify(mNativeWrapperMock).vibratorOff(); + } + + @Test + public void vibrate_withWaveformAndNativeCallback_callbackCannotBeTriggeredByNative() + throws Exception { + VibratorService service = createService(); + Mockito.clearInvocations(mNativeWrapperMock); + + VibrationEffect effect = VibrationEffect.createWaveform(new long[]{1, 3, 1, 2}, -1); + vibrate(service, effect); + + // Wait for VibrateThread to finish: 1ms OFF, 3ms ON, 1ms OFF, 2ms ON. + Thread.sleep(15); + InOrder inOrderVerifier = inOrder(mNativeWrapperMock); + inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); + inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(3L), isNull()); + inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(2L), isNull()); + inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); + } + + @Test + public void vibrate_withComposedAndNativeCallbackTriggered_finishesVibration() { + mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); doAnswer(invocation -> { ((VibratorService.Vibration) invocation.getArgument(1)).onComplete(); return null; }).when(mNativeWrapperMock).vibratorPerformComposedEffect( any(), any(VibratorService.Vibration.class)); + VibratorService service = createService(); + Mockito.clearInvocations(mNativeWrapperMock); - // Use vibration with delay so there is time for the callback to be triggered. VibrationEffect effect = VibrationEffect.startComposition() .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 10) .compose(); vibrate(service, effect); - // Vibration canceled once before perform and once by native callback. - verify(mNativeWrapperMock, times(2)).vibratorOff(); - verify(mNativeWrapperMock).vibratorPerformComposedEffect( + InOrder inOrderVerifier = inOrder(mNativeWrapperMock); + inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); + inOrderVerifier.verify(mNativeWrapperMock).vibratorPerformComposedEffect( any(VibrationEffect.Composition.PrimitiveEffect[].class), any(VibratorService.Vibration.class)); + inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); } @Test - public void vibrate_whenBinderDies_cancelsVibration() { + public void vibrate_withComposedAndNativeCallbackNotTriggered_finishesVibrationViaFallback() { mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); VibratorService service = createService(); Mockito.clearInvocations(mNativeWrapperMock); + VibrationEffect effect = VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 10) + .compose(); + vibrate(service, effect); + + verify(mNativeWrapperMock).vibratorOff(); + verify(mNativeWrapperMock).vibratorPerformComposedEffect( + any(VibrationEffect.Composition.PrimitiveEffect[].class), + any(VibratorService.Vibration.class)); + Mockito.clearInvocations(mNativeWrapperMock); + + // Run the scheduled callback to finish one-shot vibration. + mTestLooper.moveTimeForward(10000); // 10s + mTestLooper.dispatchAll(); + + verify(mNativeWrapperMock).vibratorOff(); + } + + @Test + public void vibrate_whenBinderDies_cancelsVibration() { + mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS); doAnswer(invocation -> { ((VibratorService.Vibration) invocation.getArgument(1)).binderDied(); return null; }).when(mNativeWrapperMock).vibratorPerformComposedEffect( any(), any(VibratorService.Vibration.class)); + VibratorService service = createService(); + Mockito.clearInvocations(mNativeWrapperMock); - // Use vibration with delay so there is time for the callback to be triggered. VibrationEffect effect = VibrationEffect.startComposition() .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 10) .compose(); vibrate(service, effect); - // Vibration canceled once before perform and once by native binder death. - verify(mNativeWrapperMock, times(2)).vibratorOff(); - verify(mNativeWrapperMock).vibratorPerformComposedEffect( + InOrder inOrderVerifier = inOrder(mNativeWrapperMock); + inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); + inOrderVerifier.verify(mNativeWrapperMock).vibratorPerformComposedEffect( any(VibrationEffect.Composition.PrimitiveEffect[].class), any(VibratorService.Vibration.class)); + inOrderVerifier.verify(mNativeWrapperMock).vibratorOff(); } @Test public void cancelVibrate_withDeviceVibrating_callsVibratorOff() { VibratorService service = createService(); - vibrate(service, VibrationEffect.createOneShot(100, 128)); + vibrate(service, VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE)); assertTrue(service.isVibrating()); Mockito.clearInvocations(mNativeWrapperMock); @@ -434,18 +518,20 @@ public class VibratorServiceTest { @Test public void registerVibratorStateListener_callbacksAreTriggered() throws Exception { + doAnswer(invocation -> { + ((VibratorService.Vibration) invocation.getArgument(1)).onComplete(); + return null; + }).when(mNativeWrapperMock).vibratorOn(anyLong(), any(VibratorService.Vibration.class)); VibratorService service = createService(); service.registerVibratorStateListener(mVibratorStateListenerMock); verify(mVibratorStateListenerMock).onVibrating(false); + Mockito.clearInvocations(mVibratorStateListenerMock); vibrate(service, VibrationEffect.createOneShot(10, VibrationEffect.DEFAULT_AMPLITUDE)); - verify(mVibratorStateListenerMock).onVibrating(true); - - // Run the scheduled callback to finish one-shot vibration. - mTestLooper.moveTimeForward(10); - mTestLooper.dispatchAll(); - verify(mVibratorStateListenerMock, times(2)).onVibrating(false); + InOrder inOrderVerifier = inOrder(mVibratorStateListenerMock); + inOrderVerifier.verify(mVibratorStateListenerMock).onVibrating(eq(true)); + inOrderVerifier.verify(mVibratorStateListenerMock).onVibrating(eq(false)); } @Test diff --git a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java index 6450a0fc1453..763654d24047 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java @@ -42,6 +42,7 @@ import android.os.SystemClock; import android.testing.DexmakerShareClassLoaderRule; import android.view.InputDevice; import android.view.MotionEvent; +import android.view.ViewConfiguration; import android.view.accessibility.AccessibilityEvent; import androidx.test.InstrumentationRegistry; @@ -87,6 +88,8 @@ public class TouchExplorerTest { private MotionEvent mLastEvent; private TestHandler mHandler; private TouchExplorer mTouchExplorer; + private Context mContext; + private int mTouchSlop; private long mLastDownTime = Integer.MIN_VALUE; // mock package-private GestureManifold class @@ -121,12 +124,13 @@ public class TouchExplorerTest { if (Looper.myLooper() == null) { Looper.prepare(); } - Context context = InstrumentationRegistry.getContext(); - AccessibilityManagerService ams = new AccessibilityManagerService(context); + mContext = InstrumentationRegistry.getContext(); + mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop(); + AccessibilityManagerService ams = new AccessibilityManagerService(mContext); GestureManifold detector = mock(GestureManifold.class); mCaptor = new EventCaptor(); mHandler = new TestHandler(); - mTouchExplorer = new TouchExplorer(context, ams, detector, mHandler); + mTouchExplorer = new TouchExplorer(mContext, ams, detector, mHandler); mTouchExplorer.setNext(mCaptor); } @@ -354,12 +358,12 @@ public class TouchExplorerTest { break; case STATE_DRAGGING_2FINGERS: goFromStateClearTo(STATE_TOUCH_EXPLORING_2FINGER); - moveEachPointers(mLastEvent, p(10, 0), p(10, 0)); + moveEachPointers(mLastEvent, p(mTouchSlop, 0), p(mTouchSlop, 0)); send(mLastEvent); break; case STATE_PINCH_2FINGERS: goFromStateClearTo(STATE_DRAGGING_2FINGERS); - moveEachPointers(mLastEvent, p(10, 0), p(-10, 1)); + moveEachPointers(mLastEvent, p(mTouchSlop, 0), p(-mTouchSlop, 1)); send(mLastEvent); break; case STATE_MOVING_3FINGERS: diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java index 960a7ab52c22..ef2365e6da3e 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java @@ -16,7 +16,6 @@ package com.android.server.hdmi; import static com.android.server.hdmi.Constants.ADDR_BROADCAST; -import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1; import static com.android.server.hdmi.Constants.ADDR_TV; import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC; @@ -62,6 +61,7 @@ public class HdmiCecLocalDevicePlaybackTest { private TestLooper mTestLooper = new TestLooper(); private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>(); private int mPlaybackPhysicalAddress; + private int mPlaybackLogicalAddress; private boolean mWokenUp; private boolean mStandby; @@ -129,6 +129,7 @@ public class HdmiCecLocalDevicePlaybackTest { mPlaybackPhysicalAddress = 0x2000; mNativeWrapper.setPhysicalAddress(mPlaybackPhysicalAddress); mTestLooper.dispatchAll(); + mPlaybackLogicalAddress = mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress(); mNativeWrapper.clearResultMessages(); } @@ -144,7 +145,7 @@ public class HdmiCecLocalDevicePlaybackTest { mPlaybackPhysicalAddress); HdmiCecMessage expectedMessage = - HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_1, + HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress, mPlaybackPhysicalAddress); assertThat(mHdmiCecLocalDevicePlayback.handleRoutingChange(message)).isTrue(); @@ -165,7 +166,7 @@ public class HdmiCecLocalDevicePlaybackTest { mPlaybackPhysicalAddress); HdmiCecMessage expectedMessage = - HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_1, + HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress, mPlaybackPhysicalAddress); assertThat(mHdmiCecLocalDevicePlayback.handleRoutingInformation(message)).isTrue(); @@ -186,7 +187,7 @@ public class HdmiCecLocalDevicePlaybackTest { mPlaybackPhysicalAddress); HdmiCecMessage expectedMessage = - HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_1, + HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress, mPlaybackPhysicalAddress); assertThat(mHdmiCecLocalDevicePlayback.handleRoutingChange(message)).isTrue(); @@ -207,7 +208,7 @@ public class HdmiCecLocalDevicePlaybackTest { mPlaybackPhysicalAddress); HdmiCecMessage expectedMessage = - HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_1, + HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress, mPlaybackPhysicalAddress); assertThat(mHdmiCecLocalDevicePlayback.handleRoutingInformation(message)).isTrue(); @@ -230,7 +231,7 @@ public class HdmiCecLocalDevicePlaybackTest { mPlaybackPhysicalAddress); HdmiCecMessage expectedMessage = - HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_1, + HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress, mPlaybackPhysicalAddress); assertThat(mHdmiCecLocalDevicePlayback.handleRoutingChange(message)).isTrue(); @@ -253,7 +254,7 @@ public class HdmiCecLocalDevicePlaybackTest { mPlaybackPhysicalAddress); HdmiCecMessage expectedMessage = - HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_1, + HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress, mPlaybackPhysicalAddress); assertThat(mHdmiCecLocalDevicePlayback.handleRoutingInformation(message)).isTrue(); @@ -262,6 +263,112 @@ public class HdmiCecLocalDevicePlaybackTest { assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage); } + @Test + public void handleRoutingChange_otherDevice_None() { + mHdmiCecLocalDevicePlayback.mPowerStateChangeOnActiveSourceLost = + HdmiProperties.power_state_change_on_active_source_lost_values.NONE; + mHdmiCecLocalDevicePlayback.setIsActiveSource(true); + mStandby = false; + HdmiCecMessage message = + HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x0000, 0x5000); + assertThat(mHdmiCecLocalDevicePlayback.handleRoutingChange(message)).isTrue(); + assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isFalse(); + assertThat(mStandby).isFalse(); + } + + @Test + public void handleRoutingChange_otherDevice_StandbyNow() { + mHdmiCecLocalDevicePlayback.mPowerStateChangeOnActiveSourceLost = + HdmiProperties.power_state_change_on_active_source_lost_values.STANDBY_NOW; + mHdmiCecLocalDevicePlayback.setIsActiveSource(true); + mStandby = false; + HdmiCecMessage message = + HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x0000, 0x5000); + assertThat(mHdmiCecLocalDevicePlayback.handleRoutingChange(message)).isTrue(); + assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isFalse(); + assertThat(mStandby).isTrue(); + } + + @Test + public void handleRoutingChange_otherDevice_StandbyNow_InactiveSource() { + mHdmiCecLocalDevicePlayback.mPowerStateChangeOnActiveSourceLost = + HdmiProperties.power_state_change_on_active_source_lost_values.STANDBY_NOW; + mHdmiCecLocalDevicePlayback.setIsActiveSource(false); + mStandby = false; + HdmiCecMessage message = + HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x0000, 0x5000); + assertThat(mHdmiCecLocalDevicePlayback.handleRoutingChange(message)).isTrue(); + assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isFalse(); + assertThat(mStandby).isFalse(); + } + + @Test + public void handleRoutingChange_sameDevice_StandbyNow_ActiveSource() { + mHdmiCecLocalDevicePlayback.mPowerStateChangeOnActiveSourceLost = + HdmiProperties.power_state_change_on_active_source_lost_values.STANDBY_NOW; + mHdmiCecLocalDevicePlayback.setIsActiveSource(true); + mStandby = false; + HdmiCecMessage message = + HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x0000, + mPlaybackPhysicalAddress); + assertThat(mHdmiCecLocalDevicePlayback.handleRoutingChange(message)).isTrue(); + assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isTrue(); + assertThat(mStandby).isFalse(); + } + + @Test + public void handleRoutingInformation_otherDevice_None() { + mHdmiCecLocalDevicePlayback.mPowerStateChangeOnActiveSourceLost = + HdmiProperties.power_state_change_on_active_source_lost_values.NONE; + mHdmiCecLocalDevicePlayback.setIsActiveSource(true); + mStandby = false; + HdmiCecMessage message = + HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV, 0x5000); + assertThat(mHdmiCecLocalDevicePlayback.handleRoutingInformation(message)).isTrue(); + assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isFalse(); + assertThat(mStandby).isFalse(); + } + + @Test + public void handleRoutingInformation_otherDevice_StandbyNow() { + mHdmiCecLocalDevicePlayback.mPowerStateChangeOnActiveSourceLost = + HdmiProperties.power_state_change_on_active_source_lost_values.STANDBY_NOW; + mHdmiCecLocalDevicePlayback.setIsActiveSource(true); + mStandby = false; + HdmiCecMessage message = + HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV, 0x5000); + assertThat(mHdmiCecLocalDevicePlayback.handleRoutingInformation(message)).isTrue(); + assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isFalse(); + assertThat(mStandby).isTrue(); + } + + @Test + public void handleRoutingInformation_otherDevice_StandbyNow_InactiveSource() { + mHdmiCecLocalDevicePlayback.mPowerStateChangeOnActiveSourceLost = + HdmiProperties.power_state_change_on_active_source_lost_values.STANDBY_NOW; + mHdmiCecLocalDevicePlayback.setIsActiveSource(false); + mStandby = false; + HdmiCecMessage message = + HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV, 0x5000); + assertThat(mHdmiCecLocalDevicePlayback.handleRoutingInformation(message)).isTrue(); + assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isFalse(); + assertThat(mStandby).isFalse(); + } + + @Test + public void handleRoutingInformation_sameDevice_StandbyNow_ActiveSource() { + mHdmiCecLocalDevicePlayback.mPowerStateChangeOnActiveSourceLost = + HdmiProperties.power_state_change_on_active_source_lost_values.STANDBY_NOW; + mHdmiCecLocalDevicePlayback.setIsActiveSource(true); + mStandby = false; + HdmiCecMessage message = + HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV, + mPlaybackPhysicalAddress); + assertThat(mHdmiCecLocalDevicePlayback.handleRoutingInformation(message)).isTrue(); + assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isTrue(); + assertThat(mStandby).isFalse(); + } + // Playback device does not handle routing control related feature right now @Ignore("b/120845532") @Test @@ -442,7 +549,7 @@ public class HdmiCecLocalDevicePlaybackTest { mHdmiCecLocalDevicePlayback.mPowerStateChangeOnActiveSourceLost = HdmiProperties.power_state_change_on_active_source_lost_values.NONE; mStandby = false; - HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_1, + HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress, mPlaybackPhysicalAddress); assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(message)).isTrue(); mTestLooper.dispatchAll(); @@ -465,7 +572,7 @@ public class HdmiCecLocalDevicePlaybackTest { mHdmiCecLocalDevicePlayback.mPowerStateChangeOnActiveSourceLost = HdmiProperties.power_state_change_on_active_source_lost_values.STANDBY_NOW; mStandby = false; - HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_1, + HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress, mPlaybackPhysicalAddress); assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(message)).isTrue(); mTestLooper.dispatchAll(); @@ -629,4 +736,43 @@ public class HdmiCecLocalDevicePlaybackTest { mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress()); assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isTrue(); } + + @Test + public void handleSetStreamPath_otherDevice_None() { + mHdmiCecLocalDevicePlayback.mPowerStateChangeOnActiveSourceLost = + HdmiProperties.power_state_change_on_active_source_lost_values.NONE; + mHdmiCecLocalDevicePlayback.setIsActiveSource(true); + mStandby = false; + HdmiCecMessage message = + HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x5000); + assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(message)).isTrue(); + assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isFalse(); + assertThat(mStandby).isFalse(); + } + + @Test + public void handleSetStreamPath_otherDevice_StandbyNow() { + mHdmiCecLocalDevicePlayback.mPowerStateChangeOnActiveSourceLost = + HdmiProperties.power_state_change_on_active_source_lost_values.STANDBY_NOW; + mHdmiCecLocalDevicePlayback.setIsActiveSource(true); + mStandby = false; + HdmiCecMessage message = + HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x5000); + assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(message)).isTrue(); + assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isFalse(); + assertThat(mStandby).isTrue(); + } + + @Test + public void handleSetStreamPath_otherDevice_StandbyNow_InactiveSource() { + mHdmiCecLocalDevicePlayback.mPowerStateChangeOnActiveSourceLost = + HdmiProperties.power_state_change_on_active_source_lost_values.STANDBY_NOW; + mHdmiCecLocalDevicePlayback.setIsActiveSource(false); + mStandby = false; + HdmiCecMessage message = + HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x5000); + assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(message)).isTrue(); + assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isFalse(); + assertThat(mStandby).isFalse(); + } } diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java index f991dff2797f..b2512d3ed8ca 100644 --- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java @@ -69,6 +69,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.Executor; @Presubmit @RunWith(JUnit4.class) @@ -88,6 +89,8 @@ public class AppsFilterTest { AppsFilter.FeatureConfig mFeatureConfigMock; @Mock AppsFilter.StateProvider mStateProvider; + @Mock + Executor mMockExecutor; private ArrayMap<String, PackageSetting> mExisting = new ArrayMap<>(); @@ -184,10 +187,15 @@ public class AppsFilterTest { doAnswer(invocation -> { ((AppsFilter.StateProvider.CurrentStateCallback) invocation.getArgument(0)) .currentState(mExisting, USER_INFO_LIST); - return null; + return new Object(); }).when(mStateProvider) .runWithState(any(AppsFilter.StateProvider.CurrentStateCallback.class)); + doAnswer(invocation -> { + ((Runnable) invocation.getArgument(0)).run(); + return new Object(); + }).when(mMockExecutor).execute(any(Runnable.class)); + when(mFeatureConfigMock.isGloballyEnabled()).thenReturn(true); when(mFeatureConfigMock.packageIsEnabled(any(AndroidPackage.class))).thenAnswer( (Answer<Boolean>) invocation -> @@ -198,7 +206,8 @@ public class AppsFilterTest { @Test public void testSystemReadyPropogates() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); appsFilter.onSystemReady(); verify(mFeatureConfigMock).onSystemReady(); } @@ -206,7 +215,8 @@ public class AppsFilterTest { @Test public void testQueriesAction_FilterMatches() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -222,7 +232,8 @@ public class AppsFilterTest { @Test public void testQueriesProtectedAction_FilterDoesNotMatch() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); final Signature frameworkSignature = Mockito.mock(Signature.class); final PackageParser.SigningDetails frameworkSigningDetails = new PackageParser.SigningDetails(new Signature[]{frameworkSignature}, 1); @@ -260,7 +271,8 @@ public class AppsFilterTest { @Test public void testQueriesProvider_FilterMatches() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -277,7 +289,8 @@ public class AppsFilterTest { @Test public void testQueriesDifferentProvider_Filters() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -294,7 +307,8 @@ public class AppsFilterTest { @Test public void testQueriesProviderWithSemiColon_FilterMatches() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -312,7 +326,8 @@ public class AppsFilterTest { @Test public void testQueriesAction_NoMatchingAction_Filters() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -328,7 +343,8 @@ public class AppsFilterTest { @Test public void testQueriesAction_NoMatchingActionFilterLowSdk_DoesntFilter() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -348,7 +364,8 @@ public class AppsFilterTest { @Test public void testNoQueries_Filters() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -364,7 +381,8 @@ public class AppsFilterTest { @Test public void testForceQueryable_DoesntFilter() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -381,7 +399,7 @@ public class AppsFilterTest { public void testForceQueryableByDevice_SystemCaller_DoesntFilter() throws Exception { final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{"com.some.package"}, - false, null); + false, null, mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -399,7 +417,8 @@ public class AppsFilterTest { @Test public void testSystemSignedTarget_DoesntFilter() throws CertificateException { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); appsFilter.onSystemReady(); final Signature frameworkSignature = Mockito.mock(Signature.class); @@ -428,7 +447,7 @@ public class AppsFilterTest { public void testForceQueryableByDevice_NonSystemCaller_Filters() throws Exception { final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{"com.some.package"}, - false, null); + false, null, mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -446,7 +465,7 @@ public class AppsFilterTest { public void testSystemQueryable_DoesntFilter() throws Exception { final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, - true /* system force queryable */, null); + true /* system force queryable */, null, mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -463,7 +482,8 @@ public class AppsFilterTest { @Test public void testQueriesPackage_DoesntFilter() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -481,7 +501,8 @@ public class AppsFilterTest { when(mFeatureConfigMock.packageIsEnabled(any(AndroidPackage.class))) .thenReturn(false); final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -497,7 +518,8 @@ public class AppsFilterTest { @Test public void testSystemUid_DoesntFilter() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -512,7 +534,8 @@ public class AppsFilterTest { @Test public void testSystemUidSecondaryUser_DoesntFilter() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -528,7 +551,8 @@ public class AppsFilterTest { @Test public void testNonSystemUid_NoCallingSetting_Filters() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -542,7 +566,8 @@ public class AppsFilterTest { @Test public void testNoTargetPackage_filters() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -550,7 +575,6 @@ public class AppsFilterTest { .setAppId(DUMMY_TARGET_APPID) .setName("com.some.package") .setCodePath("/") - .setResourcePath("/") .setPVersionCode(1L) .build(); PackageSetting calling = simulateAddPackage(appsFilter, @@ -600,7 +624,8 @@ public class AppsFilterTest { } return Collections.emptyMap(); } - }); + }, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -672,7 +697,8 @@ public class AppsFilterTest { } return Collections.emptyMap(); } - }); + }, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -697,7 +723,8 @@ public class AppsFilterTest { @Test public void testInitiatingApp_DoesntFilter() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -713,7 +740,8 @@ public class AppsFilterTest { @Test public void testUninstalledInitiatingApp_Filters() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -729,7 +757,8 @@ public class AppsFilterTest { @Test public void testOriginatingApp_Filters() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -745,7 +774,8 @@ public class AppsFilterTest { @Test public void testInstallingApp_DoesntFilter() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -761,7 +791,8 @@ public class AppsFilterTest { @Test public void testInstrumentation_DoesntFilter() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -783,7 +814,8 @@ public class AppsFilterTest { @Test public void testWhoCanSee() throws Exception { final AppsFilter appsFilter = - new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null); + new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -874,7 +906,6 @@ public class AppsFilterTest { .setAppId(appId) .setName(newPkg.getPackageName()) .setCodePath("/") - .setResourcePath("/") .setPVersionCode(1L); final PackageSetting setting = (action == null ? settingBuilder : action.withBuilder(settingBuilder)).build(); diff --git a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java index 164bd725dd66..90edaef4294f 100644 --- a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java @@ -38,8 +38,7 @@ public class KeySetManagerServiceTest extends AndroidTestCase { public PackageSetting generateFakePackageSetting(String name) { return new PackageSetting(name, name, new File(mContext.getCacheDir(), "fakeCodePath"), - new File(mContext.getCacheDir(), "fakeResPath"), "", "", "", - "", 1, 0, 0, 0 /*sharedUserId*/, null /*usesStaticLibraries*/, + "", "", "", "", 1, 0, 0, 0 /*sharedUserId*/, null /*usesStaticLibraries*/, null /*usesStaticLibrariesVersions*/, null /*mimeGroups*/); } diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java index c4e25b53d5d5..80f145b16147 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java @@ -86,8 +86,7 @@ public class PackageManagerServiceTest { // Create a real (non-null) PackageSetting and confirm that the removed // users are copied properly setting = new PackageSetting("name", "realName", new File("codePath"), - new File("resourcePath"), "legacyNativeLibraryPathString", - "primaryCpuAbiString", "secondaryCpuAbiString", + "legacyNativeLibraryPathString", "primaryCpuAbiString", "secondaryCpuAbiString", "cpuAbiOverrideString", 0, 0, 0, 0, null, null, null); pri.populateUsers(new int[] { diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java index aa92ba49d190..0bf06bb4dda7 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -439,7 +439,6 @@ public class PackageManagerSettingsTests { PACKAGE_NAME, REAL_PACKAGE_NAME, INITIAL_CODE_PATH /*codePath*/, - INITIAL_CODE_PATH /*resourcePath*/, null /*legacyNativeLibraryPathString*/, "x86_64" /*primaryCpuAbiString*/, "x86" /*secondaryCpuAbiString*/, @@ -461,7 +460,6 @@ public class PackageManagerSettingsTests { PACKAGE_NAME /*pkgName*/, REAL_PACKAGE_NAME /*realPkgName*/, INITIAL_CODE_PATH /*codePath*/, - INITIAL_CODE_PATH /*resourcePath*/, null /*legacyNativeLibraryPathString*/, "x86_64" /*primaryCpuAbiString*/, "x86" /*secondaryCpuAbiString*/, @@ -477,7 +475,6 @@ public class PackageManagerSettingsTests { PACKAGE_NAME /*pkgName*/, REAL_PACKAGE_NAME /*realPkgName*/, UPDATED_CODE_PATH /*codePath*/, - UPDATED_CODE_PATH /*resourcePath*/, null /*legacyNativeLibraryPathString*/, null /*primaryCpuAbiString*/, null /*secondaryCpuAbiString*/, @@ -507,7 +504,6 @@ public class PackageManagerSettingsTests { null /*disabledPkg*/, null /*sharedUser*/, UPDATED_CODE_PATH /*codePath*/, - UPDATED_CODE_PATH /*resourcePath*/, null /*legacyNativeLibraryPath*/, "arm64-v8a" /*primaryCpuAbi*/, "armeabi" /*secondaryCpuAbi*/, @@ -541,7 +537,6 @@ public class PackageManagerSettingsTests { null /*disabledPkg*/, null /*sharedUser*/, UPDATED_CODE_PATH /*codePath*/, - UPDATED_CODE_PATH /*resourcePath*/, null /*legacyNativeLibraryPath*/, "arm64-v8a" /*primaryCpuAbi*/, "armeabi" /*secondaryCpuAbi*/, @@ -581,7 +576,6 @@ public class PackageManagerSettingsTests { null /*disabledPkg*/, testUserSetting01 /*sharedUser*/, UPDATED_CODE_PATH /*codePath*/, - null /*resourcePath*/, null /*legacyNativeLibraryPath*/, "arm64-v8a" /*primaryCpuAbi*/, "armeabi" /*secondaryCpuAbi*/, @@ -609,7 +603,6 @@ public class PackageManagerSettingsTests { null /*realPkgName*/, null /*sharedUser*/, UPDATED_CODE_PATH /*codePath*/, - UPDATED_CODE_PATH /*resourcePath*/, null /*legacyNativeLibraryPath*/, "arm64-v8a" /*primaryCpuAbi*/, "armeabi" /*secondaryCpuAbi*/, @@ -624,12 +617,11 @@ public class PackageManagerSettingsTests { null /*usesStaticLibraries*/, null /*usesStaticLibrariesVersions*/, null /*mimeGroups*/); - assertThat(testPkgSetting01.codePath, is(UPDATED_CODE_PATH)); + assertThat(testPkgSetting01.getCodePath(), is(UPDATED_CODE_PATH)); assertThat(testPkgSetting01.name, is(PACKAGE_NAME)); assertThat(testPkgSetting01.pkgFlags, is(ApplicationInfo.FLAG_SYSTEM)); assertThat(testPkgSetting01.pkgPrivateFlags, is(ApplicationInfo.PRIVATE_FLAG_PRIVILEGED)); assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a")); - assertThat(testPkgSetting01.resourcePath, is(UPDATED_CODE_PATH)); assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi")); // signatures object must be different assertNotSame(testPkgSetting01.signatures, originalSignatures); @@ -649,7 +641,6 @@ public class PackageManagerSettingsTests { null /*realPkgName*/, null /*sharedUser*/, INITIAL_CODE_PATH /*codePath*/, - INITIAL_CODE_PATH /*resourcePath*/, null /*legacyNativeLibraryPath*/, "x86_64" /*primaryCpuAbiString*/, "x86" /*secondaryCpuAbiString*/, @@ -665,12 +656,11 @@ public class PackageManagerSettingsTests { null /*usesStaticLibrariesVersions*/, null /*mimeGroups*/); assertThat(testPkgSetting01.appId, is(0)); - assertThat(testPkgSetting01.codePath, is(INITIAL_CODE_PATH)); + assertThat(testPkgSetting01.getCodePath(), is(INITIAL_CODE_PATH)); assertThat(testPkgSetting01.name, is(PACKAGE_NAME)); assertThat(testPkgSetting01.pkgFlags, is(0)); assertThat(testPkgSetting01.pkgPrivateFlags, is(0)); assertThat(testPkgSetting01.primaryCpuAbiString, is("x86_64")); - assertThat(testPkgSetting01.resourcePath, is(INITIAL_CODE_PATH)); assertThat(testPkgSetting01.secondaryCpuAbiString, is("x86")); assertThat(testPkgSetting01.versionCode, is(INITIAL_VERSION_CODE)); // by default, the package is considered stopped @@ -695,7 +685,6 @@ public class PackageManagerSettingsTests { null /*realPkgName*/, testUserSetting01 /*sharedUser*/, INITIAL_CODE_PATH /*codePath*/, - INITIAL_CODE_PATH /*resourcePath*/, null /*legacyNativeLibraryPath*/, "x86_64" /*primaryCpuAbiString*/, "x86" /*secondaryCpuAbiString*/, @@ -711,12 +700,11 @@ public class PackageManagerSettingsTests { null /*usesStaticLibrariesVersions*/, null /*mimeGroups*/); assertThat(testPkgSetting01.appId, is(10064)); - assertThat(testPkgSetting01.codePath, is(INITIAL_CODE_PATH)); + assertThat(testPkgSetting01.getCodePath(), is(INITIAL_CODE_PATH)); assertThat(testPkgSetting01.name, is(PACKAGE_NAME)); assertThat(testPkgSetting01.pkgFlags, is(0)); assertThat(testPkgSetting01.pkgPrivateFlags, is(0)); assertThat(testPkgSetting01.primaryCpuAbiString, is("x86_64")); - assertThat(testPkgSetting01.resourcePath, is(INITIAL_CODE_PATH)); assertThat(testPkgSetting01.secondaryCpuAbiString, is("x86")); assertThat(testPkgSetting01.versionCode, is(INITIAL_VERSION_CODE)); final PackageUserState userState = testPkgSetting01.readUserState(0); @@ -738,7 +726,6 @@ public class PackageManagerSettingsTests { null /*realPkgName*/, null /*sharedUser*/, UPDATED_CODE_PATH /*codePath*/, - UPDATED_CODE_PATH /*resourcePath*/, null /*legacyNativeLibraryPath*/, "arm64-v8a" /*primaryCpuAbi*/, "armeabi" /*secondaryCpuAbi*/, @@ -754,12 +741,11 @@ public class PackageManagerSettingsTests { null /*usesStaticLibrariesVersions*/, null /*mimeGroups*/); assertThat(testPkgSetting01.appId, is(10064)); - assertThat(testPkgSetting01.codePath, is(UPDATED_CODE_PATH)); + assertThat(testPkgSetting01.getCodePath(), is(UPDATED_CODE_PATH)); assertThat(testPkgSetting01.name, is(PACKAGE_NAME)); assertThat(testPkgSetting01.pkgFlags, is(0)); assertThat(testPkgSetting01.pkgPrivateFlags, is(0)); assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a")); - assertThat(testPkgSetting01.resourcePath, is(UPDATED_CODE_PATH)); assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi")); assertNotSame(testPkgSetting01.signatures, disabledSignatures); assertThat(testPkgSetting01.versionCode, is(UPDATED_VERSION_CODE)); @@ -806,10 +792,10 @@ public class PackageManagerSettingsTests { private void verifySettingCopy(PackageSetting origPkgSetting, PackageSetting testPkgSetting) { assertThat(origPkgSetting, is(not(testPkgSetting))); assertThat(origPkgSetting.appId, is(testPkgSetting.appId)); - assertSame(origPkgSetting.codePath, testPkgSetting.codePath); - assertThat(origPkgSetting.codePath, is(testPkgSetting.codePath)); - assertSame(origPkgSetting.codePathString, testPkgSetting.codePathString); - assertThat(origPkgSetting.codePathString, is(testPkgSetting.codePathString)); + assertSame(origPkgSetting.getCodePath(), testPkgSetting.getCodePath()); + assertThat(origPkgSetting.getCodePath(), is(testPkgSetting.getCodePath())); + assertSame(origPkgSetting.getCodePathString(), testPkgSetting.getCodePathString()); + assertThat(origPkgSetting.getCodePathString(), is(testPkgSetting.getCodePathString())); assertSame(origPkgSetting.cpuAbiOverrideString, testPkgSetting.cpuAbiOverrideString); assertThat(origPkgSetting.cpuAbiOverrideString, is(testPkgSetting.cpuAbiOverrideString)); assertThat(origPkgSetting.firstInstallTime, is(testPkgSetting.firstInstallTime)); @@ -823,7 +809,9 @@ public class PackageManagerSettingsTests { testPkgSetting.legacyNativeLibraryPathString); assertThat(origPkgSetting.legacyNativeLibraryPathString, is(testPkgSetting.legacyNativeLibraryPathString)); - assertNotSame(origPkgSetting.mimeGroups, testPkgSetting.mimeGroups); + if (origPkgSetting.mimeGroups != null) { + assertNotSame(origPkgSetting.mimeGroups, testPkgSetting.mimeGroups); + } assertThat(origPkgSetting.mimeGroups, is(testPkgSetting.mimeGroups)); assertNotSame(origPkgSetting.mPermissionsState, testPkgSetting.mPermissionsState); assertThat(origPkgSetting.mPermissionsState, is(testPkgSetting.mPermissionsState)); @@ -839,10 +827,6 @@ public class PackageManagerSettingsTests { assertSame(origPkgSetting.primaryCpuAbiString, testPkgSetting.primaryCpuAbiString); assertThat(origPkgSetting.primaryCpuAbiString, is(testPkgSetting.primaryCpuAbiString)); assertThat(origPkgSetting.realName, is(testPkgSetting.realName)); - assertSame(origPkgSetting.resourcePath, testPkgSetting.resourcePath); - assertThat(origPkgSetting.resourcePath, is(testPkgSetting.resourcePath)); - assertSame(origPkgSetting.resourcePathString, testPkgSetting.resourcePathString); - assertThat(origPkgSetting.resourcePathString, is(testPkgSetting.resourcePathString)); assertSame(origPkgSetting.secondaryCpuAbiString, testPkgSetting.secondaryCpuAbiString); assertThat(origPkgSetting.secondaryCpuAbiString, is(testPkgSetting.secondaryCpuAbiString)); assertSame(origPkgSetting.sharedUser, testPkgSetting.sharedUser); @@ -874,7 +858,6 @@ public class PackageManagerSettingsTests { PACKAGE_NAME, REAL_PACKAGE_NAME, INITIAL_CODE_PATH /*codePath*/, - INITIAL_CODE_PATH /*resourcePath*/, null /*legacyNativeLibraryPathString*/, "x86_64" /*primaryCpuAbiString*/, "x86" /*secondaryCpuAbiString*/, @@ -893,7 +876,6 @@ public class PackageManagerSettingsTests { packageName, packageName, INITIAL_CODE_PATH /*codePath*/, - INITIAL_CODE_PATH /*resourcePath*/, null /*legacyNativeLibraryPathString*/, "x86_64" /*primaryCpuAbiString*/, "x86" /*secondaryCpuAbiString*/, diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java index b0b5386a49bd..2651cfa5449b 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java @@ -312,8 +312,7 @@ public class PackageParserTest { private static PackageSetting mockPkgSetting(AndroidPackage pkg) { return new PackageSetting(pkg.getPackageName(), pkg.getRealPackage(), - new File(pkg.getCodePath()), new File(pkg.getCodePath()), null, - pkg.getPrimaryCpuAbi(), pkg.getSecondaryCpuAbi(), + new File(pkg.getCodePath()), null, pkg.getPrimaryCpuAbi(), pkg.getSecondaryCpuAbi(), null, pkg.getVersionCode(), PackageInfoUtils.appInfoFlags(pkg, null), PackageInfoUtils.appInfoPrivateFlags(pkg, null), diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java index d12ea89469b7..f8e92ad71d8e 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java @@ -30,7 +30,6 @@ public class PackageSettingBuilder { private String mName; private String mRealName; private String mCodePath; - private String mResourcePath; private String mLegacyNativeLibraryPathString; private String mPrimaryCpuAbiString; private String mSecondaryCpuAbiString; @@ -74,11 +73,6 @@ public class PackageSettingBuilder { return this; } - public PackageSettingBuilder setResourcePath(String resourcePath) { - this.mResourcePath = resourcePath; - return this; - } - public PackageSettingBuilder setLegacyNativeLibraryPathString( String legacyNativeLibraryPathString) { this.mLegacyNativeLibraryPathString = legacyNativeLibraryPathString; @@ -162,10 +156,10 @@ public class PackageSettingBuilder { public PackageSetting build() { final PackageSetting packageSetting = new PackageSetting(mName, mRealName, - new File(mCodePath), new File(mResourcePath), - mLegacyNativeLibraryPathString, mPrimaryCpuAbiString, mSecondaryCpuAbiString, - mCpuAbiOverrideString, mPVersionCode, mPkgFlags, mPrivateFlags, mSharedUserId, - mUsesStaticLibraries, mUsesStaticLibrariesVersions, mMimeGroups); + new File(mCodePath), mLegacyNativeLibraryPathString, mPrimaryCpuAbiString, + mSecondaryCpuAbiString, mCpuAbiOverrideString, mPVersionCode, mPkgFlags, + mPrivateFlags, mSharedUserId, mUsesStaticLibraries, mUsesStaticLibrariesVersions, + mMimeGroups); packageSetting.signatures = mSigningDetails != null ? new PackageSignatures(mSigningDetails) : new PackageSignatures(); diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java index 55bc6812328a..7108490f80f8 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java @@ -465,9 +465,9 @@ public class PackageSignaturesTest { // Generic PackageSetting object with values from a test app installed on a device to be // used to test the methods under the PackageSignatures signatures data member. File appPath = new File("/data/app/app"); - PackageSetting result = new PackageSetting("test.app", null, appPath, appPath, - "/data/app/app", null, null, null, - 1, 940097092, 0, 0 /*userId*/, null, null, null /*mimeGroups*/); + PackageSetting result = new PackageSetting("test.app", null, appPath, + "/data/app/app", null, null, null, 1, 940097092, 0, 0 /*userId*/, null, null, + null /*mimeGroups*/); return result; } } diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java index e7eff00c472e..56dddb0a8112 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java @@ -484,8 +484,7 @@ public class ScanTests { private static PackageSettingBuilder createBasicPackageSettingBuilder(String packageName) { return new PackageSettingBuilder() .setName(packageName) - .setCodePath(createCodePath(packageName)) - .setResourcePath(createCodePath(packageName)); + .setCodePath(createCodePath(packageName)); } private static ScanRequestBuilder createBasicScanRequestBuilder(ParsingPackage pkg) { @@ -534,8 +533,7 @@ public class ScanTests { arrayContaining("some.static.library", "some.other.static.library")); assertThat(pkgSetting.usesStaticLibrariesVersions, is(new long[]{234L, 456L})); assertThat(pkgSetting.pkg, is(scanResult.request.parsedPackage)); - assertThat(pkgSetting.codePath, is(new File(createCodePath(packageName)))); - assertThat(pkgSetting.resourcePath, is(new File(createCodePath(packageName)))); + assertThat(pkgSetting.getCodePath(), is(new File(createCodePath(packageName)))); assertThat(pkgSetting.versionCode, is(PackageInfo.composeLongVersionCode(1, 2345))); } diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java index 153634548c17..8034cacc6923 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java @@ -170,7 +170,7 @@ public class TimeZoneDetectorServiceTest { ITimeZoneConfigurationListener mockListener = mock(ITimeZoneConfigurationListener.class); try { mTimeZoneDetectorService.removeConfigurationListener(mockListener); - fail(); + fail("Expected a SecurityException"); } finally { verify(mMockContext).enforceCallingPermission( eq(android.Manifest.permission.WRITE_SECURE_SETTINGS), diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java index 1d75967756c3..88b1d191dc4d 100644 --- a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java @@ -16,6 +16,7 @@ package com.android.server; +import android.Manifest; import android.app.AlarmManager; import android.app.IUiModeManager; import android.content.BroadcastReceiver; @@ -24,6 +25,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; +import android.content.pm.UserInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.os.Handler; @@ -67,6 +69,7 @@ import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -230,6 +233,17 @@ public class UiModeManagerServiceTest extends UiServiceTestCase { } @Test + public void setNightModeActivated_permissiontoChangeOtherUsers() throws RemoteException { + SystemService.TargetUser user = mock(SystemService.TargetUser.class); + doReturn(9).when(user).getUserIdentifier(); + mUiManagerService.onUserSwitching(user, user); + when(mContext.checkCallingOrSelfPermission( + eq(Manifest.permission.INTERACT_ACROSS_USERS))) + .thenReturn(PackageManager.PERMISSION_DENIED); + assertFalse(mService.setNightModeActivated(true)); + } + + @Test public void autoNightModeSwitch_batterySaverOn() throws RemoteException { mService.setNightMode(MODE_NIGHT_NO); when(mTwilightState.isNight()).thenReturn(false); 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 980772bb08bc..5b2d738eb760 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -4856,6 +4856,70 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void backgroundSystemCustomToast_callsSetProcessImportantAsForegroundForToast() throws + Exception { + final String testPackage = "testPackageName"; + assertEquals(0, mService.mToastQueue.size()); + mService.isSystemUid = true; + + // package is not suspended + when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) + .thenReturn(false); + + // notifications from this package are blocked by the user + mService.setPreferencesHelper(mPreferencesHelper); + when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_NONE); + + setAppInForegroundForToasts(mUid, false); + + // enqueue toast -> toast should still enqueue + ((INotificationManager) mService.mService).enqueueToast(testPackage, new Binder(), + new TestableToastCallback(), 2000, 0); + assertEquals(1, mService.mToastQueue.size()); + verify(mAm).setProcessImportant(any(), anyInt(), eq(true), any()); + } + + @Test + public void foregroundTextToast_callsSetProcessImportantAsNotForegroundForToast() throws + Exception { + final String testPackage = "testPackageName"; + assertEquals(0, mService.mToastQueue.size()); + mService.isSystemUid = false; + + // package is not suspended + when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) + .thenReturn(false); + + setAppInForegroundForToasts(mUid, true); + + // enqueue toast -> toast should still enqueue + ((INotificationManager) mService.mService).enqueueTextToast(testPackage, new Binder(), + "Text", 2000, 0, null); + assertEquals(1, mService.mToastQueue.size()); + verify(mAm).setProcessImportant(any(), anyInt(), eq(false), any()); + } + + @Test + public void backgroundTextToast_callsSetProcessImportantAsNotForegroundForToast() throws + Exception { + final String testPackage = "testPackageName"; + assertEquals(0, mService.mToastQueue.size()); + mService.isSystemUid = false; + + // package is not suspended + when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid))) + .thenReturn(false); + + setAppInForegroundForToasts(mUid, false); + + // enqueue toast -> toast should still enqueue + ((INotificationManager) mService.mService).enqueueTextToast(testPackage, new Binder(), + "Text", 2000, 0, null); + assertEquals(1, mService.mToastQueue.size()); + verify(mAm).setProcessImportant(any(), anyInt(), eq(false), any()); + } + + @Test public void testTextToastsCallStatusBar() throws Exception { final String testPackage = "testPackageName"; assertEquals(0, mService.mToastQueue.size()); diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml index 4040fa6a675e..e3c795d03381 100644 --- a/services/tests/wmtests/AndroidManifest.xml +++ b/services/tests/wmtests/AndroidManifest.xml @@ -38,6 +38,7 @@ <uses-permission android:name="android.permission.REORDER_TASKS" /> <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" /> <uses-permission android:name="android.permission.STATUS_BAR" /> + <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT" /> <!-- TODO: Remove largeHeap hack when memory leak is fixed (b/123984854) --> <application android:debuggable="true" diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index e3830f686a42..6061f13fcee5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -22,6 +22,10 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION; import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT; +import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS; +import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT; +import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED; +import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; @@ -69,10 +73,8 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.clearInvocations; -import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import android.app.ActivityManager.TaskSnapshot; @@ -84,6 +86,7 @@ import android.app.servertransaction.PauseActivityItem; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Rect; @@ -1685,6 +1688,32 @@ public class ActivityRecordTests extends WindowTestsBase { assertFalse(mActivity.canTurnScreenOn()); } + @Test + public void testGetLockTaskLaunchMode() { + final ActivityOptions options = ActivityOptions.makeBasic().setLockTaskEnabled(true); + mActivity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT; + assertEquals(LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED, + ActivityRecord.getLockTaskLaunchMode(mActivity.info, options)); + + mActivity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_ALWAYS; + assertEquals(LOCK_TASK_LAUNCH_MODE_DEFAULT, + ActivityRecord.getLockTaskLaunchMode(mActivity.info, null /*options*/)); + + mActivity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_NEVER; + assertEquals(LOCK_TASK_LAUNCH_MODE_DEFAULT, + ActivityRecord.getLockTaskLaunchMode(mActivity.info, null /*options*/)); + + mActivity.info.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED; + mActivity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_ALWAYS; + assertEquals(LOCK_TASK_LAUNCH_MODE_ALWAYS, + ActivityRecord.getLockTaskLaunchMode(mActivity.info, null /*options*/)); + + mActivity.info.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_NEVER; + assertEquals(LOCK_TASK_LAUNCH_MODE_NEVER, + ActivityRecord.getLockTaskLaunchMode(mActivity.info, null /*options*/)); + + } + /** * Creates an activity on display. For non-default display request it will also create a new * display with custom DisplayInfo. diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java index addf1ffe40c2..96b970056c98 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java @@ -38,7 +38,13 @@ import static org.mockito.ArgumentMatchers.eq; import android.app.WaitResult; import android.content.pm.ActivityInfo; +import android.graphics.PixelFormat; +import android.hardware.display.DisplayManager; +import android.hardware.display.VirtualDisplay; +import android.media.ImageReader; import android.platform.test.annotations.Presubmit; +import android.view.Display; +import android.view.DisplayInfo; import androidx.test.filters.MediumTest; @@ -173,4 +179,50 @@ public class ActivityStackSupervisorTests extends WindowTestsBase { verify(taskChangeNotifier).notifyTaskFocusChanged(eq(taskB.mTaskId) /* taskId */, eq(true) /* focused */); } + + @Test + /** Ensures that a trusted virtual display can launch arbitrary activities. */ + public void testTrustedVirtualDisplayCanLaunchActivities() { + final DisplayContent newDisplay = addNewDisplayContentAt(DisplayContent.POSITION_TOP); + final Task stack = new StackBuilder(mRootWindowContainer) + .setDisplay(newDisplay).build(); + final ActivityRecord unresizableActivity = stack.getTopNonFinishingActivity(); + VirtualDisplay virtualDisplay = createVirtualDisplay(true); + final boolean allowed = mSupervisor.isCallerAllowedToLaunchOnDisplay(1234, 1234, + virtualDisplay.getDisplay().getDisplayId(), unresizableActivity.info); + + assertThat(allowed).isTrue(); + } + + @Test + /** Ensures that an untrusted virtual display cannot launch arbitrary activities. */ + public void testUntrustedVirtualDisplayCannotLaunchActivities() { + final DisplayContent newDisplay = addNewDisplayContentAt(DisplayContent.POSITION_TOP); + final Task stack = new StackBuilder(mRootWindowContainer) + .setDisplay(newDisplay).build(); + final ActivityRecord unresizableActivity = stack.getTopNonFinishingActivity(); + VirtualDisplay virtualDisplay = createVirtualDisplay(false); + final boolean allowed = mSupervisor.isCallerAllowedToLaunchOnDisplay(1234, 1234, + virtualDisplay.getDisplay().getDisplayId(), unresizableActivity.info); + + assertThat(allowed).isFalse(); + } + + private VirtualDisplay createVirtualDisplay(boolean trusted) { + final DisplayManager dm = mContext.getSystemService(DisplayManager.class); + final DisplayInfo displayInfo = new DisplayInfo(); + final Display defaultDisplay = dm.getDisplay(Display.DEFAULT_DISPLAY); + defaultDisplay.getDisplayInfo(displayInfo); + int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC; + if (trusted) { + flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED; + } + + final ImageReader imageReader = ImageReader.newInstance( + displayInfo.logicalWidth, displayInfo.logicalHeight, PixelFormat.RGBA_8888, 2); + + return dm.createVirtualDisplay("virtualDisplay", displayInfo.logicalWidth, + displayInfo.logicalHeight, + displayInfo.logicalDensityDpi, imageReader.getSurface(), flags); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java index d950344538a0..b4a13375aeec 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; @@ -87,7 +88,7 @@ public class TaskSnapshotSurfaceTest extends WindowTestsBase { 0 /* systemUiVisibility */, false /* isTranslucent */); mSurface = new TaskSnapshotSurface(mWm, new Window(), new SurfaceControl(), snapshot, "Test", createTaskDescription(Color.WHITE, Color.RED, Color.BLUE), sysuiVis, windowFlags, 0, - taskBounds, ORIENTATION_PORTRAIT, new InsetsState()); + taskBounds, ORIENTATION_PORTRAIT, ACTIVITY_TYPE_STANDARD, new InsetsState()); } private static TaskDescription createTaskDescription(int background, int statusBar, diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 2fd6c4249f99..78556ef41edb 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -763,11 +763,12 @@ public class UsageStatsService extends SystemService implements return; } - final LinkedList<Event> events = mReportedEvents.get(userId, new LinkedList<>()); - events.add(event); - if (mReportedEvents.get(userId) == null) { + LinkedList<Event> events = mReportedEvents.get(userId); + if (events == null) { + events = new LinkedList<>(); mReportedEvents.put(userId, events); } + events.add(event); if (events.size() == 1) { // Every time a file is persisted to disk, mReportedEvents is cleared for this user // so trigger a flush to disk every time the first event has been added. diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt index 2e4d390ceb60..c0658fe4422e 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt @@ -84,7 +84,7 @@ class CloseImeAutoOpenWindowToHomeTest( navBarLayerIsAlwaysVisible(bugId = 140855415) statusBarLayerIsAlwaysVisible(bugId = 140855415) noUncoveredRegions(rotation, Surface.ROTATION_0, allStates = false) - navBarLayerRotatesAndScales(rotation, Surface.ROTATION_0) + navBarLayerRotatesAndScales(rotation, Surface.ROTATION_0, bugId = 140855415) statusBarLayerRotatesScales(rotation, Surface.ROTATION_0) imeLayerBecomesInvisible(bugId = 141458352) imeAppLayerBecomesInvisible(testApp, bugId = 153739621) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt index 1c0da4f920bb..dcf308533ee6 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt @@ -92,7 +92,7 @@ open class CloseImeWindowToHomeTest( navBarLayerIsAlwaysVisible(bugId = 140855415) statusBarLayerIsAlwaysVisible(bugId = 140855415) noUncoveredRegions(rotation, Surface.ROTATION_0, allStates = false) - navBarLayerRotatesAndScales(rotation, Surface.ROTATION_0) + navBarLayerRotatesAndScales(rotation, Surface.ROTATION_0, bugId = 140855415) statusBarLayerRotatesScales(rotation, Surface.ROTATION_0) imeLayerBecomesInvisible(bugId = 153739621) imeAppLayerBecomesInvisible(testApp, bugId = 153739621) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt index e078f266e5ed..91ec211805f7 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt @@ -90,7 +90,7 @@ class OpenAppToSplitScreenTest( navBarLayerIsAlwaysVisible() statusBarLayerIsAlwaysVisible() noUncoveredRegions(rotation) - navBarLayerRotatesAndScales(rotation) + navBarLayerRotatesAndScales(rotation, bugId = 140855415) statusBarLayerRotatesScales(rotation) all("dividerLayerBecomesVisible") { @@ -102,7 +102,8 @@ class OpenAppToSplitScreenTest( eventLog { focusChanges(testApp.`package`, - "recents_animation_input_consumer", "NexusLauncherActivity") + "recents_animation_input_consumer", "NexusLauncherActivity", + bugId = 151179149) } } } diff --git a/tests/SilkFX/Android.bp b/tests/SilkFX/Android.bp new file mode 100644 index 000000000000..ca0a091e65bb --- /dev/null +++ b/tests/SilkFX/Android.bp @@ -0,0 +1,22 @@ +// +// Copyright (C) 2010 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +android_test { + name: "SilkFX", + srcs: ["**/*.java", "**/*.kt"], + platform_apis: true, + certificate: "platform", +} diff --git a/tests/SilkFX/AndroidManifest.xml b/tests/SilkFX/AndroidManifest.xml new file mode 100644 index 000000000000..ca9550a9eeab --- /dev/null +++ b/tests/SilkFX/AndroidManifest.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.test.silkfx"> + + <uses-sdk android:minSdkVersion="30"/> + + <uses-permission android:name="android.permission.CONTROL_DISPLAY_BRIGHTNESS" /> + + <application android:label="SilkFX" + android:theme="@android:style/Theme.Material"> + + <activity android:name=".Main" + android:label="SilkFX Demos" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.DEFAULT"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> + + <activity android:name=".app.CommonDemoActivity" /> + + <activity android:name=".hdr.GlowActivity" + android:label="Glow Examples"/> + + </application> +</manifest> diff --git a/tests/SilkFX/res/drawable-nodpi/dark_notification.png b/tests/SilkFX/res/drawable-nodpi/dark_notification.png Binary files differnew file mode 100644 index 000000000000..6de6c2ae785c --- /dev/null +++ b/tests/SilkFX/res/drawable-nodpi/dark_notification.png diff --git a/tests/SilkFX/res/drawable-nodpi/light_notification.png b/tests/SilkFX/res/drawable-nodpi/light_notification.png Binary files differnew file mode 100644 index 000000000000..81a67cd3d388 --- /dev/null +++ b/tests/SilkFX/res/drawable-nodpi/light_notification.png diff --git a/tests/SilkFX/res/layout/bling_notifications.xml b/tests/SilkFX/res/layout/bling_notifications.xml new file mode 100644 index 000000000000..6d266b701a68 --- /dev/null +++ b/tests/SilkFX/res/layout/bling_notifications.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="wrap_content" + android:orientation="vertical"> + + <com.android.test.silkfx.hdr.BlingyNotification + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_margin="8dp" + android:src="@drawable/dark_notification" /> + + <com.android.test.silkfx.hdr.BlingyNotification + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_margin="8dp" + android:src="@drawable/light_notification" /> + +</LinearLayout>
\ No newline at end of file diff --git a/tests/SilkFX/res/layout/color_mode_controls.xml b/tests/SilkFX/res/layout/color_mode_controls.xml new file mode 100644 index 000000000000..c0c0bab8a605 --- /dev/null +++ b/tests/SilkFX/res/layout/color_mode_controls.xml @@ -0,0 +1,64 @@ +<?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. + --> + +<com.android.test.silkfx.common.ColorModeControls + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView + android:id="@+id/current_mode" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + + <Button + android:id="@+id/mode_default" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="Default (sRGB)" /> + + <Button + android:id="@+id/mode_wide" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="Wide Gamut (P3)" /> + + <Button + android:id="@+id/mode_hdr" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="HDR" /> + + <Button + android:id="@+id/mode_hdr10" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="HDR10" /> + + </LinearLayout> + +</com.android.test.silkfx.common.ColorModeControls>
\ No newline at end of file diff --git a/tests/SilkFX/res/layout/common_base.xml b/tests/SilkFX/res/layout/common_base.xml new file mode 100644 index 000000000000..944c6846fbf7 --- /dev/null +++ b/tests/SilkFX/res/layout/common_base.xml @@ -0,0 +1,39 @@ +<?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="match_parent" + android:orientation="vertical"> + + <include layout="@layout/color_mode_controls" /> + + <FrameLayout android:id="@+id/demo_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + + <View + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" /> + + <com.android.test.silkfx.common.HDRIndicator + android:layout_width="match_parent" + android:layout_height="50dp" + android:layout_margin="8dp" /> + +</LinearLayout>
\ No newline at end of file diff --git a/tests/SilkFX/res/layout/hdr_glows.xml b/tests/SilkFX/res/layout/hdr_glows.xml new file mode 100644 index 000000000000..b6050645866a --- /dev/null +++ b/tests/SilkFX/res/layout/hdr_glows.xml @@ -0,0 +1,51 @@ +<?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="match_parent" + android:orientation="vertical"> + + <include layout="@layout/color_mode_controls" /> + + <com.android.test.silkfx.hdr.GlowingCard + android:layout_width="match_parent" + android:layout_height="100dp" + android:layout_margin="8dp" /> + + <com.android.test.silkfx.hdr.GlowingCard + android:id="@+id/card2" + android:layout_width="match_parent" + android:layout_height="100dp" + android:layout_margin="8dp"/> + + <com.android.test.silkfx.hdr.RadialGlow + android:layout_width="match_parent" + android:layout_height="200dp" + android:layout_margin="8dp" /> + + <View + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" /> + + <com.android.test.silkfx.common.HDRIndicator + android:layout_width="match_parent" + android:layout_height="50dp" + android:layout_margin="8dp" /> + +</LinearLayout>
\ No newline at end of file diff --git a/tests/SilkFX/src/com/android/test/silkfx/Main.kt b/tests/SilkFX/src/com/android/test/silkfx/Main.kt new file mode 100644 index 000000000000..76e62a6c8cff --- /dev/null +++ b/tests/SilkFX/src/com/android/test/silkfx/Main.kt @@ -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.test.silkfx + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.BaseExpandableListAdapter +import android.widget.ExpandableListView +import android.widget.TextView +import com.android.test.silkfx.app.CommonDemoActivity +import com.android.test.silkfx.app.EXTRA_LAYOUT +import com.android.test.silkfx.app.EXTRA_TITLE +import com.android.test.silkfx.hdr.GlowActivity +import kotlin.reflect.KClass + +class Demo(val name: String, val makeIntent: (Context) -> Intent) { + constructor(name: String, activity: KClass<out Activity>) : this(name, { context -> + Intent(context, activity.java) + }) + constructor(name: String, layout: Int) : this(name, { context -> + Intent(context, CommonDemoActivity::class.java).apply { + putExtra(EXTRA_LAYOUT, layout) + putExtra(EXTRA_TITLE, name) + } + }) +} +data class DemoGroup(val groupName: String, val demos: List<Demo>) + +private val AllDemos = listOf( + DemoGroup("HDR", listOf( + Demo("Glow", GlowActivity::class), + Demo("Blingy Notifications", R.layout.bling_notifications) + )) +) + +class Main : Activity() { + + public override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val list = ExpandableListView(this) + + setContentView(list) + + val inflater = LayoutInflater.from(this) + list.setAdapter(object : BaseExpandableListAdapter() { + override fun getGroup(groupPosition: Int): DemoGroup { + return AllDemos[groupPosition] + } + + override fun isChildSelectable(groupPosition: Int, childPosition: Int): Boolean = true + + override fun hasStableIds(): Boolean = true + + override fun getGroupView( + groupPosition: Int, + isExpanded: Boolean, + convertView: View?, + parent: ViewGroup? + ): View { + val view = (convertView ?: inflater.inflate( + android.R.layout.simple_expandable_list_item_1, parent, false)) as TextView + view.text = AllDemos[groupPosition].groupName + return view + } + + override fun getChildrenCount(groupPosition: Int): Int { + return AllDemos[groupPosition].demos.size + } + + override fun getChild(groupPosition: Int, childPosition: Int): Demo { + return AllDemos[groupPosition].demos[childPosition] + } + + override fun getGroupId(groupPosition: Int): Long = groupPosition.toLong() + + override fun getChildView( + groupPosition: Int, + childPosition: Int, + isLastChild: Boolean, + convertView: View?, + parent: ViewGroup? + ): View { + val view = (convertView ?: inflater.inflate( + android.R.layout.simple_expandable_list_item_1, parent, false)) as TextView + view.text = AllDemos[groupPosition].demos[childPosition].name + return view + } + + override fun getChildId(groupPosition: Int, childPosition: Int): Long { + return (groupPosition.toLong() shl 32) or childPosition.toLong() + } + + override fun getGroupCount(): Int { + return AllDemos.size + } + }) + + list.setOnChildClickListener { _, _, groupPosition, childPosition, _ -> + val demo = AllDemos[groupPosition].demos[childPosition] + startActivity(demo.makeIntent(this)) + return@setOnChildClickListener true + } + + AllDemos.forEachIndexed { index, _ -> list.expandGroup(index) } + } +}
\ No newline at end of file diff --git a/tests/SilkFX/src/com/android/test/silkfx/app/BaseDemoActivity.kt b/tests/SilkFX/src/com/android/test/silkfx/app/BaseDemoActivity.kt new file mode 100644 index 000000000000..89011b51b8d6 --- /dev/null +++ b/tests/SilkFX/src/com/android/test/silkfx/app/BaseDemoActivity.kt @@ -0,0 +1,77 @@ +/* + * 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.test.silkfx.app + +import android.app.Activity +import android.content.Context +import android.os.Bundle +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.MenuItem +import android.view.View + +open class BaseDemoActivity : Activity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val inflater = LayoutInflater.from(this) + inflater.factory2 = object : LayoutInflater.Factory2 { + private val sClassPrefixList = arrayOf( + "android.widget.", + "android.webkit.", + "android.app.", + null + ) + override fun onCreateView( + parent: View?, + name: String, + context: Context, + attrs: AttributeSet + ): View? { + return onCreateView(name, context, attrs) + } + + override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? { + for (prefix in sClassPrefixList) { + try { + val view = inflater.createView(name, prefix, attrs) + if (view != null) { + if (view is WindowObserver) { + view.setWindow(window) + } + return view + } + } catch (e: ClassNotFoundException) { } + } + return null + } + } + } + + override fun onStart() { + super.onStart() + actionBar?.setDisplayHomeAsUpEnabled(true) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + if (item.itemId == android.R.id.home) { + onBackPressed() + return true + } + return super.onOptionsItemSelected(item) + } +}
\ No newline at end of file diff --git a/tests/SilkFX/src/com/android/test/silkfx/app/CommonDemoActivity.kt b/tests/SilkFX/src/com/android/test/silkfx/app/CommonDemoActivity.kt new file mode 100644 index 000000000000..e0a0a20bc0a0 --- /dev/null +++ b/tests/SilkFX/src/com/android/test/silkfx/app/CommonDemoActivity.kt @@ -0,0 +1,45 @@ +/* + * 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.test.silkfx.app + +import com.android.test.silkfx.R +import android.os.Bundle +import android.view.LayoutInflater + +const val EXTRA_LAYOUT = "layout" +const val EXTRA_TITLE = "title" + +class CommonDemoActivity : BaseDemoActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val extras = intent.extras ?: return finish() + + val layout = extras.getInt(EXTRA_LAYOUT, -1) + if (layout == -1) { + finish() + return + } + val title = extras.getString(EXTRA_TITLE, "SilkFX") + window.setTitle(title) + + setContentView(R.layout.common_base) + actionBar?.title = title + LayoutInflater.from(this).inflate(layout, findViewById(R.id.demo_container), true) + } +}
\ No newline at end of file diff --git a/tests/SilkFX/src/com/android/test/silkfx/app/WindowObserver.kt b/tests/SilkFX/src/com/android/test/silkfx/app/WindowObserver.kt new file mode 100644 index 000000000000..3d989a54cf27 --- /dev/null +++ b/tests/SilkFX/src/com/android/test/silkfx/app/WindowObserver.kt @@ -0,0 +1,23 @@ +/* + * 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.test.silkfx.app + +import android.view.Window + +interface WindowObserver { + fun setWindow(window: Window) +}
\ No newline at end of file diff --git a/tests/SilkFX/src/com/android/test/silkfx/common/BaseDrawingView.kt b/tests/SilkFX/src/com/android/test/silkfx/common/BaseDrawingView.kt new file mode 100644 index 000000000000..4b85953a24b9 --- /dev/null +++ b/tests/SilkFX/src/com/android/test/silkfx/common/BaseDrawingView.kt @@ -0,0 +1,43 @@ +/* + * 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.test.silkfx.common + +import android.content.Context +import android.graphics.Color +import android.graphics.ColorSpace +import android.util.AttributeSet +import android.view.View + +open class BaseDrawingView : View { + val scRGB = ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB) + val bt2020 = ColorSpace.get(ColorSpace.Named.BT2020) + val lab = ColorSpace.get(ColorSpace.Named.CIE_LAB) + + val density: Float + val dp: Int.() -> Float + + fun color(red: Float, green: Float, blue: Float, alpha: Float = 1f): Long { + return Color.pack(red, green, blue, alpha, scRGB) + } + + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) { + setWillNotDraw(false) + isClickable = true + density = resources.displayMetrics.density + dp = { this * density } + } +}
\ No newline at end of file diff --git a/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt b/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt new file mode 100644 index 000000000000..9b15b0445642 --- /dev/null +++ b/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt @@ -0,0 +1,122 @@ +/* + * 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.test.silkfx.common + +import android.content.Context +import android.content.pm.ActivityInfo +import android.hardware.display.DisplayManager +import android.util.AttributeSet +import android.view.Window +import android.widget.Button +import android.widget.LinearLayout +import android.widget.TextView +import com.android.test.silkfx.R +import com.android.test.silkfx.app.WindowObserver + +class ColorModeControls : LinearLayout, WindowObserver { + private val COLOR_MODE_HDR10 = 3 + private val SDR_WHITE_POINTS = floatArrayOf(200f, 250f, 300f, 350f, 400f, 100f, 150f) + + constructor(context: Context) : this(context, null) + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) { + displayManager = context.getSystemService(DisplayManager::class.java)!! + } + + private var window: Window? = null + private var currentModeDisplay: TextView? = null + private val displayManager: DisplayManager + private var targetSdrWhitePointIndex = 0 + + private val whitePoint get() = SDR_WHITE_POINTS[targetSdrWhitePointIndex] + + override fun onFinishInflate() { + super.onFinishInflate() + val window = window ?: throw IllegalStateException("Failed to attach window") + + currentModeDisplay = findViewById(R.id.current_mode)!! + setColorMode(window.colorMode) + + findViewById<Button>(R.id.mode_default)!!.setOnClickListener { + setColorMode(ActivityInfo.COLOR_MODE_DEFAULT) + } + findViewById<Button>(R.id.mode_wide)!!.setOnClickListener { + setColorMode(ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT) + } + findViewById<Button>(R.id.mode_hdr)!!.setOnClickListener { + setColorMode(ActivityInfo.COLOR_MODE_HDR) + } + findViewById<Button>(R.id.mode_hdr10)!!.setOnClickListener { + setColorMode(COLOR_MODE_HDR10) + } + } + + private fun setColorMode(newMode: Int) { + val window = window!! + var sdrWhitepointChanged = false + // Need to do this before setting the colorMode, as setting the colorMode will + // trigger the attribute change listener + if (newMode == ActivityInfo.COLOR_MODE_HDR || + newMode == COLOR_MODE_HDR10) { + if (window.colorMode == newMode) { + targetSdrWhitePointIndex = (targetSdrWhitePointIndex + 1) % SDR_WHITE_POINTS.size + sdrWhitepointChanged = true + } + setBrightness(1.0f) + } else { + setBrightness(.4f) + } + window.colorMode = newMode + if (sdrWhitepointChanged) { + threadedRenderer?.setColorMode(newMode, whitePoint) + } + val whitePoint = whitePoint.toInt() + currentModeDisplay?.run { + text = "Current Mode: " + when (newMode) { + ActivityInfo.COLOR_MODE_DEFAULT -> "Default/SRGB" + ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT -> "Wide Gamut" + ActivityInfo.COLOR_MODE_HDR -> "HDR (sdr white point $whitePoint)" + COLOR_MODE_HDR10 -> "HDR10 (sdr white point $whitePoint)" + else -> "Unknown" + } + } + } + + override fun setWindow(window: Window) { + this.window = window + } + + private fun setBrightness(level: Float) { + // To keep window state in sync + window?.attributes?.screenBrightness = level + invalidate() + // To force an 'immediate' snap to what we want + // Imperfect, but close enough, synchronization by waiting for frame commit to set the value + viewTreeObserver.registerFrameCommitCallback { + try { + displayManager.setTemporaryBrightness(level) + } catch (ex: Exception) { + // Ignore a permission denied rejection - it doesn't meaningfully change much + } + } + } + + override fun onAttachedToWindow() { + super.onAttachedToWindow() + + threadedRenderer?.setColorMode(window!!.colorMode, whitePoint) + } +}
\ No newline at end of file diff --git a/tests/SilkFX/src/com/android/test/silkfx/common/HDRIndicator.kt b/tests/SilkFX/src/com/android/test/silkfx/common/HDRIndicator.kt new file mode 100644 index 000000000000..f42161f63811 --- /dev/null +++ b/tests/SilkFX/src/com/android/test/silkfx/common/HDRIndicator.kt @@ -0,0 +1,50 @@ +/* + * 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.test.silkfx.common + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.ColorSpace +import android.graphics.Paint +import android.graphics.RectF +import android.util.AttributeSet +import android.view.View + +class HDRIndicator(context: Context) : View(context) { + constructor(context: Context, attrs: AttributeSet?) : this(context) + + val scRGB = ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB) + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + + val paint = Paint() + paint.isAntiAlias = true + val rect = RectF(0f, 0f, width.toFloat(), height.toFloat()) + paint.textSize = height.toFloat() + + canvas.drawColor(Color.pack(1f, 1f, 1f, 1f, scRGB)) + + paint.setColor(Color.pack(1.1f, 1.1f, 1.1f, 1f, scRGB)) + canvas.drawText("H", rect.left, rect.bottom, paint) + paint.setColor(Color.pack(1.2f, 1.2f, 1.2f, 1f, scRGB)) + canvas.drawText("D", rect.left + height.toFloat(), rect.bottom, paint) + paint.setColor(Color.pack(1.3f, 1.3f, 1.3f, 1f, scRGB)) + canvas.drawText("R", rect.left + height.toFloat() * 2, rect.bottom, paint) + } +}
\ No newline at end of file diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/BlingyNotification.kt b/tests/SilkFX/src/com/android/test/silkfx/hdr/BlingyNotification.kt new file mode 100644 index 000000000000..4ad21faec9d4 --- /dev/null +++ b/tests/SilkFX/src/com/android/test/silkfx/hdr/BlingyNotification.kt @@ -0,0 +1,99 @@ +/* + * 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.test.silkfx.hdr + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.BlendMode +import android.graphics.Canvas +import android.graphics.LinearGradient +import android.graphics.Paint +import android.graphics.Rect +import android.graphics.Shader +import android.graphics.drawable.BitmapDrawable +import android.util.AttributeSet +import com.android.test.silkfx.common.BaseDrawingView + +class BlingyNotification : BaseDrawingView { + + private val image: Bitmap? + private val bounds = Rect() + private val paint = Paint() + + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) { + val typed = context.obtainStyledAttributes(attrs, intArrayOf(android.R.attr.src)) + val drawable = typed.getDrawable(0) + image = if (drawable is BitmapDrawable) { + drawable.bitmap + } else { + null + } + typed.recycle() + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + val image = image ?: return super.onMeasure(widthMeasureSpec, heightMeasureSpec) + + val widthMode = MeasureSpec.getMode(widthMeasureSpec) + val heightMode = MeasureSpec.getMode(heightMeasureSpec) + + // Currently only used in this mode, so that's all we'll bother to support + if (widthMode == MeasureSpec.EXACTLY && heightMode != MeasureSpec.EXACTLY) { + val width = MeasureSpec.getSize(widthMeasureSpec) + + var height = image.height * width / image.width + if (heightMode == MeasureSpec.AT_MOST) { + height = minOf(MeasureSpec.getSize(heightMeasureSpec), height) + } + setMeasuredDimension(width, height) + } else { + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + } + } + + override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { + super.onSizeChanged(w, h, oldw, oldh) + bounds.set(0, 0, w, h) + paint.shader = LinearGradient(0f, 0f, w.toFloat(), 0f, + longArrayOf( + color(1f, 1f, 1f, 0f), + color(1f, 1f, 1f, .1f), + color(2f, 2f, 2f, .3f), + color(1f, 1f, 1f, .2f), + color(1f, 1f, 1f, 0f) + ), + floatArrayOf(.2f, .4f, .5f, .6f, .8f), + Shader.TileMode.CLAMP) + paint.blendMode = BlendMode.PLUS + } + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + + val image = image ?: return + + canvas.drawBitmap(image, null, bounds, null) + + canvas.save() + val frac = ((drawingTime % 2000) / 300f) - 1f + canvas.translate(width * frac, 0f) + canvas.rotate(-45f) + canvas.drawPaint(paint) + canvas.restore() + invalidate() + } +}
\ No newline at end of file diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/GlowActivity.kt b/tests/SilkFX/src/com/android/test/silkfx/hdr/GlowActivity.kt new file mode 100644 index 000000000000..64dbb22ace43 --- /dev/null +++ b/tests/SilkFX/src/com/android/test/silkfx/hdr/GlowActivity.kt @@ -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 com.android.test.silkfx.hdr + +import android.os.Bundle +import com.android.test.silkfx.R +import com.android.test.silkfx.app.BaseDemoActivity + +class GlowActivity : BaseDemoActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.hdr_glows) + findViewById<GlowingCard>(R.id.card2)!!.setGlowIntensity(4f) + } +} diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/GlowingCard.kt b/tests/SilkFX/src/com/android/test/silkfx/hdr/GlowingCard.kt new file mode 100644 index 000000000000..b388bb659685 --- /dev/null +++ b/tests/SilkFX/src/com/android/test/silkfx/hdr/GlowingCard.kt @@ -0,0 +1,85 @@ +/* + * 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.test.silkfx.hdr + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.LinearGradient +import android.graphics.Paint +import android.graphics.RectF +import android.graphics.Shader +import android.util.AttributeSet +import com.android.test.silkfx.common.BaseDrawingView + +class GlowingCard : BaseDrawingView { + + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) + + val radius: Float + var COLOR_MAXIMIZER = 1f + + init { + radius = 10.dp() + } + + fun setGlowIntensity(multiplier: Float) { + COLOR_MAXIMIZER = multiplier + invalidate() + } + + override fun setPressed(pressed: Boolean) { + super.setPressed(pressed) + invalidate() + } + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + val paint = Paint() + paint.isAntiAlias = true + val rect = RectF(0f, 0f, width.toFloat(), height.toFloat()) + val glowColor = Color.pack(.5f * COLOR_MAXIMIZER, .4f * COLOR_MAXIMIZER, + .75f * COLOR_MAXIMIZER, 1f, scRGB) + + if (isPressed) { + paint.setColor(Color.pack(2f, 2f, 2f, 1f, scRGB)) + paint.strokeWidth = 4.dp() + paint.style = Paint.Style.FILL + paint.shader = LinearGradient(rect.left, rect.bottom, rect.right, rect.top, + glowColor, + Color.pack(0f, 0f, 0f, 0f, scRGB), + Shader.TileMode.CLAMP) + canvas.drawRoundRect(rect, radius, radius, paint) + } + + rect.inset(3.dp(), 3.dp()) + + paint.setColor(Color.pack(.14f, .14f, .14f, .8f, scRGB)) + paint.style = Paint.Style.FILL + paint.shader = null + canvas.drawRoundRect(rect, radius, radius, paint) + + rect.inset(5.dp(), 5.dp()) + paint.textSize = 14.dp() + paint.isFakeBoldText = true + + paint.color = Color.WHITE + canvas.drawText("glow = scRGB{${Color.red(glowColor)}, ${Color.green(glowColor)}, " + + "${Color.blue(glowColor)}}", rect.left, rect.centerY(), paint) + canvas.drawText("(press to activate)", rect.left, rect.bottom, paint) + } +}
\ No newline at end of file diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/RadialGlow.kt b/tests/SilkFX/src/com/android/test/silkfx/hdr/RadialGlow.kt new file mode 100644 index 000000000000..599585e9d125 --- /dev/null +++ b/tests/SilkFX/src/com/android/test/silkfx/hdr/RadialGlow.kt @@ -0,0 +1,91 @@ +/* + * 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.test.silkfx.hdr + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.RadialGradient +import android.graphics.RectF +import android.graphics.Shader +import android.util.AttributeSet +import com.android.test.silkfx.common.BaseDrawingView +import kotlin.math.min + +class RadialGlow : BaseDrawingView { + + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) + + var glowToggle = false + + val glowColor = color(4f, 3.3f, 2.8f) + val bgColor = color(.15f, .15f, .15f) + val fgColor = color(.51f, .52f, .50f, .4f) + var glow: RadialGradient + + init { + glow = RadialGradient(0f, 0f, 100.dp(), glowColor, bgColor, Shader.TileMode.CLAMP) + isClickable = true + setOnClickListener { + glowToggle = !glowToggle + invalidate() + } + } + + override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { + super.onSizeChanged(w, h, oldw, oldh) + glow = RadialGradient(0f, 0f, + min(w, h).toFloat(), glowColor, bgColor, Shader.TileMode.CLAMP) + } + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + val radius = 10.dp() + + val paint = Paint() + paint.isDither = true + paint.isAntiAlias = true + paint.textSize = 18.dp() + paint.textAlign = Paint.Align.CENTER + + val rect = RectF(0f, 0f, width.toFloat(), height.toFloat()) + + paint.setColor(bgColor) + canvas.drawRoundRect(rect, radius, radius, paint) + + if (glowToggle) { + paint.shader = glow + canvas.save() + val frac = (drawingTime % 5000) / 5000f + canvas.translate(rect.width() * frac, rect.height() - (rect.height() * frac)) + canvas.drawPaint(paint) + canvas.restore() + paint.shader = null + invalidate() + } + + paint.setColor(fgColor) + val innerRect = RectF(rect) + innerRect.inset(rect.width() / 4, rect.height() / 4) + canvas.drawRoundRect(innerRect, radius, radius, paint) + + paint.setColor(color(1f, 1f, 1f)) + canvas.drawText("Tap to toggle animation", rect.centerX(), innerRect.top - 4.dp(), paint) + canvas.drawText("Outside text", rect.centerX(), rect.bottom - 4.dp(), paint) + canvas.drawText("Inside text", innerRect.centerX(), innerRect.bottom - 4.dp(), paint) + } +}
\ No newline at end of file diff --git a/tests/StagedInstallTest/Android.bp b/tests/StagedInstallTest/Android.bp index da6018e2e2c9..530d0e492f2e 100644 --- a/tests/StagedInstallTest/Android.bp +++ b/tests/StagedInstallTest/Android.bp @@ -29,6 +29,7 @@ java_test_host { "compatibility-tradefed", "frameworks-base-hostutils", "module_test_util", + "cts-install-lib-host", ], data: [ ":com.android.apex.cts.shim.v2_prebuilt", diff --git a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java index 7cfbdc2b5062..5285b04f67d7 100644 --- a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java +++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java @@ -22,6 +22,7 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.junit.Assume.assumeTrue; +import com.android.cts.install.lib.host.InstallUtilsHost; import com.android.ddmlib.Log; import com.android.tests.rollback.host.AbandonSessionsRule; import com.android.tests.util.ModuleTestUtils; @@ -49,6 +50,7 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { private static final String APK_A = "TestAppAv1.apk"; private final ModuleTestUtils mTestUtils = new ModuleTestUtils(this); + private final InstallUtilsHost mHostUtils = new InstallUtilsHost(this); /** * Runs the given phase of a test by calling into the device. @@ -93,7 +95,7 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { @Test public void testAdbStagedInstallWaitForReadyFlagWorks() throws Exception { assumeTrue("Device does not support updating APEX", - mTestUtils.isApexUpdateSupported()); + mHostUtils.isApexUpdateSupported()); File apexFile = mTestUtils.getTestFile(SHIM_V2); String output = getDevice().executeAdbCommand("install", "--staged", @@ -107,7 +109,7 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { @Test public void testAdbStagedInstallNoWaitFlagWorks() throws Exception { assumeTrue("Device does not support updating APEX", - mTestUtils.isApexUpdateSupported()); + mHostUtils.isApexUpdateSupported()); File apexFile = mTestUtils.getTestFile(SHIM_V2); String output = getDevice().executeAdbCommand("install", "--staged", @@ -122,7 +124,7 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { @Test public void testAdbInstallMultiPackageCommandWorks() throws Exception { assumeTrue("Device does not support updating APEX", - mTestUtils.isApexUpdateSupported()); + mHostUtils.isApexUpdateSupported()); File apexFile = mTestUtils.getTestFile(SHIM_V2); File apkFile = mTestUtils.getTestFile(APK_A); diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java index 4ccf79a0cb37..de1c5759ee87 100644 --- a/tests/net/java/com/android/server/connectivity/VpnTest.java +++ b/tests/net/java/com/android/server/connectivity/VpnTest.java @@ -30,6 +30,7 @@ import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_VPN; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -49,6 +50,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.AppOpsManager; import android.app.NotificationManager; @@ -65,6 +67,7 @@ import android.net.InetAddresses; import android.net.IpPrefix; import android.net.IpSecManager; import android.net.LinkProperties; +import android.net.LocalSocket; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkInfo.DetailedState; @@ -74,6 +77,7 @@ import android.net.VpnManager; import android.net.VpnService; import android.os.Build.VERSION_CODES; import android.os.Bundle; +import android.os.ConditionVariable; import android.os.INetworkManagementService; import android.os.Looper; import android.os.Process; @@ -101,13 +105,20 @@ import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; import java.net.Inet4Address; +import java.net.InetAddress; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; import java.util.stream.Stream; /** @@ -133,7 +144,8 @@ public class VpnTest { managedProfileA.profileGroupId = primaryUser.id; } - static final String TEST_VPN_PKG = "com.dummy.vpn"; + static final String EGRESS_IFACE = "wlan0"; + static final String TEST_VPN_PKG = "com.testvpn.vpn"; private static final String TEST_VPN_SERVER = "1.2.3.4"; private static final String TEST_VPN_IDENTITY = "identity"; private static final byte[] TEST_VPN_PSK = "psk".getBytes(); @@ -1012,31 +1024,190 @@ public class VpnTest { // a subsequent CL. } - @Test - public void testStartLegacyVpn() throws Exception { + public Vpn startLegacyVpn(final VpnProfile vpnProfile) throws Exception { final Vpn vpn = createVpn(primaryUser.id); setMockedUsers(primaryUser); // Dummy egress interface - final String egressIface = "DUMMY0"; final LinkProperties lp = new LinkProperties(); - lp.setInterfaceName(egressIface); + lp.setInterfaceName(EGRESS_IFACE); final RouteInfo defaultRoute = new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), - InetAddresses.parseNumericAddress("192.0.2.0"), egressIface); + InetAddresses.parseNumericAddress("192.0.2.0"), EGRESS_IFACE); lp.addRoute(defaultRoute); - vpn.startLegacyVpn(mVpnProfile, mKeyStore, lp); + vpn.startLegacyVpn(vpnProfile, mKeyStore, lp); + return vpn; + } + @Test + public void testStartPlatformVpn() throws Exception { + startLegacyVpn(mVpnProfile); // TODO: Test the Ikev2VpnRunner started up properly. Relies on utility methods added in - // a subsequent CL. + // a subsequent patch. + } + + @Test + public void testStartRacoonNumericAddress() throws Exception { + startRacoon("1.2.3.4", "1.2.3.4"); + } + + @Test + public void testStartRacoonHostname() throws Exception { + startRacoon("hostname", "5.6.7.8"); // address returned by deps.resolve + } + + public void startRacoon(final String serverAddr, final String expectedAddr) + throws Exception { + final ConditionVariable legacyRunnerReady = new ConditionVariable(); + final VpnProfile profile = new VpnProfile("testProfile" /* key */); + profile.type = VpnProfile.TYPE_L2TP_IPSEC_PSK; + profile.name = "testProfileName"; + profile.username = "userName"; + profile.password = "thePassword"; + profile.server = serverAddr; + profile.ipsecIdentifier = "id"; + profile.ipsecSecret = "secret"; + profile.l2tpSecret = "l2tpsecret"; + when(mConnectivityManager.getAllNetworks()) + .thenReturn(new Network[] { new Network(101) }); + when(mConnectivityManager.registerNetworkAgent(any(), any(), any(), any(), + anyInt(), any(), anyInt())).thenAnswer(invocation -> { + // The runner has registered an agent and is now ready. + legacyRunnerReady.open(); + return new Network(102); + }); + final Vpn vpn = startLegacyVpn(profile); + final TestDeps deps = (TestDeps) vpn.mDeps; + try { + // udppsk and 1701 are the values for TYPE_L2TP_IPSEC_PSK + assertArrayEquals( + new String[] { EGRESS_IFACE, expectedAddr, "udppsk", + profile.ipsecIdentifier, profile.ipsecSecret, "1701" }, + deps.racoonArgs.get(10, TimeUnit.SECONDS)); + // literal values are hardcoded in Vpn.java for mtpd args + assertArrayEquals( + new String[] { EGRESS_IFACE, "l2tp", expectedAddr, "1701", profile.l2tpSecret, + "name", profile.username, "password", profile.password, + "linkname", "vpn", "refuse-eap", "nodefaultroute", "usepeerdns", + "idle", "1800", "mtu", "1400", "mru", "1400" }, + deps.mtpdArgs.get(10, TimeUnit.SECONDS)); + // Now wait for the runner to be ready before testing for the route. + legacyRunnerReady.block(10_000); + // In this test the expected address is always v4 so /32 + final RouteInfo expectedRoute = new RouteInfo(new IpPrefix(expectedAddr + "/32"), + RouteInfo.RTN_THROW); + assertTrue("Routes lack the expected throw route (" + expectedRoute + ") : " + + vpn.mConfig.routes, + vpn.mConfig.routes.contains(expectedRoute)); + } finally { + // Now interrupt the thread, unblock the runner and clean up. + vpn.mVpnRunner.exitVpnRunner(); + deps.getStateFile().delete(); // set to delete on exit, but this deletes it earlier + vpn.mVpnRunner.join(10_000); // wait for up to 10s for the runner to die and cleanup + } + } + + private static final class TestDeps extends Vpn.Dependencies { + public final CompletableFuture<String[]> racoonArgs = new CompletableFuture(); + public final CompletableFuture<String[]> mtpdArgs = new CompletableFuture(); + public final File mStateFile; + + private final HashMap<String, Boolean> mRunningServices = new HashMap<>(); + + TestDeps() { + try { + mStateFile = File.createTempFile("vpnTest", ".tmp"); + mStateFile.deleteOnExit(); + } catch (final IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public void startService(final String serviceName) { + mRunningServices.put(serviceName, true); + } + + @Override + public void stopService(final String serviceName) { + mRunningServices.put(serviceName, false); + } + + @Override + public boolean isServiceRunning(final String serviceName) { + return mRunningServices.getOrDefault(serviceName, false); + } + + @Override + public boolean isServiceStopped(final String serviceName) { + return !isServiceRunning(serviceName); + } + + @Override + public File getStateFile() { + return mStateFile; + } + + @Override + public void sendArgumentsToDaemon( + final String daemon, final LocalSocket socket, final String[] arguments, + final Vpn.RetryScheduler interruptChecker) throws IOException { + if ("racoon".equals(daemon)) { + racoonArgs.complete(arguments); + } else if ("mtpd".equals(daemon)) { + writeStateFile(arguments); + mtpdArgs.complete(arguments); + } else { + throw new UnsupportedOperationException("Unsupported daemon : " + daemon); + } + } + + private void writeStateFile(final String[] arguments) throws IOException { + mStateFile.delete(); + mStateFile.createNewFile(); + mStateFile.deleteOnExit(); + final BufferedWriter writer = new BufferedWriter( + new FileWriter(mStateFile, false /* append */)); + writer.write(EGRESS_IFACE); + writer.write("\n"); + // addresses + writer.write("10.0.0.1/24\n"); + // routes + writer.write("192.168.6.0/24\n"); + // dns servers + writer.write("192.168.6.1\n"); + // search domains + writer.write("vpn.searchdomains.com\n"); + // endpoint - intentionally empty + writer.write("\n"); + writer.flush(); + writer.close(); + } + + @Override + @NonNull + public InetAddress resolve(final String endpoint) { + try { + // If a numeric IP address, return it. + return InetAddress.parseNumericAddress(endpoint); + } catch (IllegalArgumentException e) { + // Otherwise, return some token IP to test for. + return InetAddress.parseNumericAddress("5.6.7.8"); + } + } + + @Override + public boolean checkInterfacePresent(final Vpn vpn, final String iface) { + return true; + } } /** * Mock some methods of vpn object. */ private Vpn createVpn(@UserIdInt int userId) { - return new Vpn(Looper.myLooper(), mContext, mNetService, + return new Vpn(Looper.myLooper(), mContext, new TestDeps(), mNetService, userId, mKeyStore, mSystemServices, mIkev2SessionCreator); } diff --git a/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java b/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java index f30c35aca8da..c2a5459ae125 100644 --- a/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java +++ b/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java @@ -34,6 +34,8 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import javax.annotation.Nullable; @@ -49,7 +51,7 @@ import javax.annotation.Nullable; public class SystemPreparer extends ExternalResource { private static final long OVERLAY_ENABLE_TIMEOUT_MS = 30000; - // The paths of the files pushed onto the device through this rule. + // The paths of the files pushed onto the device through this rule to be removed after. private ArrayList<String> mPushedFiles = new ArrayList<>(); // The package names of packages installed through this rule. @@ -81,7 +83,7 @@ public class SystemPreparer extends ExternalResource { final ITestDevice device = mDeviceProvider.getDevice(); remount(); assertTrue(device.pushFile(copyResourceToTemp(filePath), outputPath)); - mPushedFiles.add(outputPath); + addPushedFile(device, outputPath); return this; } @@ -91,10 +93,23 @@ public class SystemPreparer extends ExternalResource { final ITestDevice device = mDeviceProvider.getDevice(); remount(); assertTrue(device.pushFile(file, outputPath)); - mPushedFiles.add(outputPath); + addPushedFile(device, outputPath); return this; } + private void addPushedFile(ITestDevice device, String outputPath) + throws DeviceNotAvailableException { + Path pathCreated = Paths.get(outputPath); + + // Find the top most parent that is new to the device + while (pathCreated.getParent() != null + && !device.doesFileExist(pathCreated.getParent().toString())) { + pathCreated = pathCreated.getParent(); + } + + mPushedFiles.add(pathCreated.toString()); + } + /** Deletes the given path from the device */ public SystemPreparer deleteFile(String file) throws DeviceNotAvailableException { final ITestDevice device = mDeviceProvider.getDevice(); @@ -203,7 +218,7 @@ public class SystemPreparer extends ExternalResource { /** Removes installed packages and files that were pushed to the device. */ @Override - protected void after() { + public void after() { final ITestDevice device = mDeviceProvider.getDevice(); try { remount(); diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp index f9c54f645e2c..d84ca3d92f67 100644 --- a/tools/aapt2/cmd/Link.cpp +++ b/tools/aapt2/cmd/Link.cpp @@ -78,6 +78,8 @@ using ::android::base::StringPrintf; namespace aapt { +constexpr uint8_t kAndroidPackageId = 0x01; + class LinkContext : public IAaptContext { public: explicit LinkContext(IDiagnostics* diagnostics) @@ -1805,7 +1807,7 @@ class Linker { // Override the package ID when it is "android". if (context_->GetCompilationPackage() == "android") { - context_->SetPackageId(0x01); + context_->SetPackageId(kAndroidPackageId); // Verify we're building a regular app. if (context_->GetPackageType() != PackageType::kApp) { @@ -1862,7 +1864,8 @@ class Linker { if (context_->GetPackageType() != PackageType::kStaticLib) { PrivateAttributeMover mover; - if (!mover.Consume(context_, &final_table_)) { + if (context_->GetPackageId() == kAndroidPackageId && + !mover.Consume(context_, &final_table_)) { context_->GetDiagnostics()->Error(DiagMessage() << "failed moving private attributes"); return 1; } diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index 3cdfb00d288c..e4937892e2f7 100644 --- a/wifi/java/android/net/wifi/IWifiManager.aidl +++ b/wifi/java/android/net/wifi/IWifiManager.aidl @@ -122,7 +122,7 @@ interface IWifiManager DhcpInfo getDhcpInfo(); - void setScanAlwaysAvailable(boolean isAvailable); + void setScanAlwaysAvailable(boolean isAvailable, String packageName); boolean isScanAlwaysAvailable(); @@ -142,9 +142,9 @@ interface IWifiManager void updateInterfaceIpState(String ifaceName, int mode); - boolean startSoftAp(in WifiConfiguration wifiConfig); + boolean startSoftAp(in WifiConfiguration wifiConfig, String packageName); - boolean startTetheredHotspot(in SoftApConfiguration softApConfig); + boolean startTetheredHotspot(in SoftApConfiguration softApConfig, String packageName); boolean stopSoftAp(); diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index ae834f929691..b28b902910bf 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -2802,7 +2802,7 @@ public class WifiManager { @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setScanAlwaysAvailable(boolean isAvailable) { try { - mService.setScanAlwaysAvailable(isAvailable); + mService.setScanAlwaysAvailable(isAvailable, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -3035,7 +3035,7 @@ public class WifiManager { }) public boolean startSoftAp(@Nullable WifiConfiguration wifiConfig) { try { - return mService.startSoftAp(wifiConfig); + return mService.startSoftAp(wifiConfig, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -3059,7 +3059,7 @@ public class WifiManager { }) public boolean startTetheredHotspot(@Nullable SoftApConfiguration softApConfig) { try { - return mService.startTetheredHotspot(softApConfig); + return mService.startTetheredHotspot(softApConfig, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java index cba1690ac635..e7f1916c9e82 100644 --- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java +++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java @@ -218,10 +218,10 @@ public class WifiManagerTest { */ @Test public void testStartSoftApCallsServiceWithWifiConfig() throws Exception { - when(mWifiService.startSoftAp(eq(mApConfig))).thenReturn(true); + when(mWifiService.startSoftAp(mApConfig, TEST_PACKAGE_NAME)).thenReturn(true); assertTrue(mWifiManager.startSoftAp(mApConfig)); - when(mWifiService.startSoftAp(eq(mApConfig))).thenReturn(false); + when(mWifiService.startSoftAp(mApConfig, TEST_PACKAGE_NAME)).thenReturn(false); assertFalse(mWifiManager.startSoftAp(mApConfig)); } @@ -231,10 +231,10 @@ public class WifiManagerTest { */ @Test public void testStartSoftApCallsServiceWithNullConfig() throws Exception { - when(mWifiService.startSoftAp(eq(null))).thenReturn(true); + when(mWifiService.startSoftAp(null, TEST_PACKAGE_NAME)).thenReturn(true); assertTrue(mWifiManager.startSoftAp(null)); - when(mWifiService.startSoftAp(eq(null))).thenReturn(false); + when(mWifiService.startSoftAp(null, TEST_PACKAGE_NAME)).thenReturn(false); assertFalse(mWifiManager.startSoftAp(null)); } @@ -257,10 +257,12 @@ public class WifiManagerTest { @Test public void testStartTetheredHotspotCallsServiceWithSoftApConfig() throws Exception { SoftApConfiguration softApConfig = generatorTestSoftApConfig(); - when(mWifiService.startTetheredHotspot(eq(softApConfig))).thenReturn(true); + when(mWifiService.startTetheredHotspot(softApConfig, TEST_PACKAGE_NAME)) + .thenReturn(true); assertTrue(mWifiManager.startTetheredHotspot(softApConfig)); - when(mWifiService.startTetheredHotspot(eq(softApConfig))).thenReturn(false); + when(mWifiService.startTetheredHotspot(softApConfig, TEST_PACKAGE_NAME)) + .thenReturn(false); assertFalse(mWifiManager.startTetheredHotspot(softApConfig)); } @@ -270,10 +272,10 @@ public class WifiManagerTest { */ @Test public void testStartTetheredHotspotCallsServiceWithNullConfig() throws Exception { - when(mWifiService.startTetheredHotspot(eq(null))).thenReturn(true); + when(mWifiService.startTetheredHotspot(null, TEST_PACKAGE_NAME)).thenReturn(true); assertTrue(mWifiManager.startTetheredHotspot(null)); - when(mWifiService.startTetheredHotspot(eq(null))).thenReturn(false); + when(mWifiService.startTetheredHotspot(null, TEST_PACKAGE_NAME)).thenReturn(false); assertFalse(mWifiManager.startTetheredHotspot(null)); } @@ -2375,7 +2377,7 @@ public class WifiManagerTest { @Test public void testScanAvailable() throws Exception { mWifiManager.setScanAlwaysAvailable(true); - verify(mWifiService).setScanAlwaysAvailable(true); + verify(mWifiService).setScanAlwaysAvailable(true, TEST_PACKAGE_NAME); when(mWifiService.isScanAlwaysAvailable()).thenReturn(false); assertFalse(mWifiManager.isScanAlwaysAvailable()); |