diff options
310 files changed, 3912 insertions, 6396 deletions
diff --git a/apct-tests/perftests/core/src/android/os/OWNERS b/apct-tests/perftests/core/src/android/os/OWNERS new file mode 100644 index 000000000000..a1719c9c31d1 --- /dev/null +++ b/apct-tests/perftests/core/src/android/os/OWNERS @@ -0,0 +1 @@ +per-file PackageParsingPerfTest.kt = file:/services/core/java/com/android/server/pm/OWNERS
\ No newline at end of file diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java index ed55f0056ea0..3bbc945c8b87 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java @@ -64,7 +64,7 @@ public class AppSearchManagerService extends SystemService { public void onStart() { publishBinderService(Context.APP_SEARCH_SERVICE, new Stub()); mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); - mImplInstanceManager = new ImplInstanceManager(getContext()); + mImplInstanceManager = ImplInstanceManager.getInstance(getContext()); } private class Stub extends IAppSearchManager.Stub { @@ -102,7 +102,8 @@ public class AppSearchManagerService extends SystemService { } schemasPackageAccessible.put(entry.getKey(), packageIdentifiers); } - AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId); + AppSearchImpl impl = + mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); impl.setSchema( packageName, databaseName, @@ -133,7 +134,8 @@ public class AppSearchManagerService extends SystemService { final long callingIdentity = Binder.clearCallingIdentity(); try { verifyCallingPackage(callingUid, packageName); - AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId); + AppSearchImpl impl = + mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); List<AppSearchSchema> schemas = impl.getSchema(packageName, databaseName); List<Bundle> schemaBundles = new ArrayList<>(schemas.size()); for (int i = 0; i < schemas.size(); i++) { @@ -166,7 +168,8 @@ public class AppSearchManagerService extends SystemService { verifyCallingPackage(callingUid, packageName); AppSearchBatchResult.Builder<String, Void> resultBuilder = new AppSearchBatchResult.Builder<>(); - AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId); + AppSearchImpl impl = + mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); for (int i = 0; i < documentBundles.size(); i++) { GenericDocument document = new GenericDocument(documentBundles.get(i)); try { @@ -207,12 +210,18 @@ public class AppSearchManagerService extends SystemService { verifyCallingPackage(callingUid, packageName); AppSearchBatchResult.Builder<String, Bundle> resultBuilder = new AppSearchBatchResult.Builder<>(); - AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId); + AppSearchImpl impl = + mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); for (int i = 0; i < uris.size(); i++) { String uri = uris.get(i); try { - GenericDocument document = impl.getDocument(packageName, databaseName, - namespace, uri, typePropertyPaths); + GenericDocument document = + impl.getDocument( + packageName, + databaseName, + namespace, + uri, + typePropertyPaths); resultBuilder.setSuccess(uri, document.getBundle()); } catch (Throwable t) { resultBuilder.setResult(uri, throwableToFailedResult(t)); @@ -245,7 +254,8 @@ public class AppSearchManagerService extends SystemService { final long callingIdentity = Binder.clearCallingIdentity(); try { verifyCallingPackage(callingUid, packageName); - AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId); + AppSearchImpl impl = + mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); SearchResultPage searchResultPage = impl.query( packageName, @@ -278,12 +288,14 @@ public class AppSearchManagerService extends SystemService { final long callingIdentity = Binder.clearCallingIdentity(); try { verifyCallingPackage(callingUid, packageName); - AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId); - SearchResultPage searchResultPage = impl.globalQuery( - queryExpression, - new SearchSpec(searchSpecBundle), - packageName, - callingUid); + AppSearchImpl impl = + mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); + SearchResultPage searchResultPage = + impl.globalQuery( + queryExpression, + new SearchSpec(searchSpecBundle), + packageName, + callingUid); invokeCallbackOnResult( callback, AppSearchResult.newSuccessfulResult(searchResultPage.getBundle())); @@ -306,7 +318,8 @@ public class AppSearchManagerService extends SystemService { // TODO(b/162450968) check nextPageToken is being advanced by the same uid as originally // opened it try { - AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId); + AppSearchImpl impl = + mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); SearchResultPage searchResultPage = impl.getNextPage(nextPageToken); invokeCallbackOnResult( callback, @@ -324,7 +337,8 @@ public class AppSearchManagerService extends SystemService { int callingUserId = handleIncomingUser(userId, callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { - AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId); + AppSearchImpl impl = + mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); impl.invalidateNextPageToken(nextPageToken); } catch (Throwable t) { Log.e(TAG, "Unable to invalidate the query page token", t); @@ -350,15 +364,11 @@ public class AppSearchManagerService extends SystemService { int callingUserId = handleIncomingUser(userId, callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { - AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId); - impl.reportUsage( - packageName, - databaseName, - namespace, - uri, - usageTimeMillis); - invokeCallbackOnResult(callback, - AppSearchResult.newSuccessfulResult(/*result=*/ null)); + AppSearchImpl impl = + mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); + impl.reportUsage(packageName, databaseName, namespace, uri, usageTimeMillis); + invokeCallbackOnResult( + callback, AppSearchResult.newSuccessfulResult(/*result=*/ null)); } catch (Throwable t) { invokeCallbackOnError(callback, t); } finally { @@ -385,7 +395,8 @@ public class AppSearchManagerService extends SystemService { verifyCallingPackage(callingUid, packageName); AppSearchBatchResult.Builder<String, Void> resultBuilder = new AppSearchBatchResult.Builder<>(); - AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId); + AppSearchImpl impl = + mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); for (int i = 0; i < uris.size(); i++) { String uri = uris.get(i); try { @@ -421,7 +432,8 @@ public class AppSearchManagerService extends SystemService { final long callingIdentity = Binder.clearCallingIdentity(); try { verifyCallingPackage(callingUid, packageName); - AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId); + AppSearchImpl impl = + mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); impl.removeByQuery( packageName, databaseName, @@ -441,7 +453,8 @@ public class AppSearchManagerService extends SystemService { int callingUserId = handleIncomingUser(userId, callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { - AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId); + AppSearchImpl impl = + mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); impl.persistToDisk(); } catch (Throwable t) { Log.e(TAG, "Unable to persist the data to disk", t); @@ -457,7 +470,7 @@ public class AppSearchManagerService extends SystemService { int callingUserId = handleIncomingUser(userId, callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { - mImplInstanceManager.getInstance(callingUserId); + mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId); invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null)); } catch (Throwable t) { invokeCallbackOnError(callback, t); diff --git a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java index fe3c2e1d1604..97b1a8cd6d50 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java @@ -41,14 +41,33 @@ import java.io.File; public final class ImplInstanceManager { private static final String APP_SEARCH_DIR = "appSearch"; - private static final SparseArray<AppSearchImpl> sInstances = new SparseArray<>(); + private static ImplInstanceManager sImplInstanceManager; - private final Context mContext; + private final SparseArray<AppSearchImpl> mInstances = new SparseArray<>(); private final String mGlobalQuerierPackage; - public ImplInstanceManager(@NonNull Context context) { - mContext = context; - mGlobalQuerierPackage = getGlobalAppSearchDataQuerierPackageName(mContext); + private ImplInstanceManager(@NonNull String globalQuerierPackage) { + mGlobalQuerierPackage = globalQuerierPackage; + } + + /** + * Gets an instance of ImplInstanceManager to be used. + * + * <p>If no instance has been initialized yet, a new one will be created. Otherwise, the + * existing instance will be returned. + */ + @NonNull + public static ImplInstanceManager getInstance(@NonNull Context context) { + if (sImplInstanceManager == null) { + synchronized (ImplInstanceManager.class) { + if (sImplInstanceManager == null) { + sImplInstanceManager = + new ImplInstanceManager( + getGlobalAppSearchDataQuerierPackageName(context)); + } + } + } + return sImplInstanceManager; } /** @@ -57,30 +76,30 @@ public final class ImplInstanceManager { * <p>If no AppSearchImpl instance exists for this user, Icing will be initialized and one will * be created. * + * @param context The context * @param userId The multi-user userId of the device user calling AppSearch * @return An initialized {@link AppSearchImpl} for this user */ @NonNull - public AppSearchImpl getInstance(@UserIdInt int userId) + public AppSearchImpl getAppSearchImpl(@NonNull Context context, @UserIdInt int userId) throws AppSearchException { - AppSearchImpl instance = sInstances.get(userId); + AppSearchImpl instance = mInstances.get(userId); if (instance == null) { synchronized (ImplInstanceManager.class) { - instance = sInstances.get(userId); + instance = mInstances.get(userId); if (instance == null) { - instance = createImpl(userId); - sInstances.put(userId, instance); + instance = createImpl(context, userId); + mInstances.put(userId, instance); } } } return instance; } - private AppSearchImpl createImpl(@UserIdInt int userId) + private AppSearchImpl createImpl(@NonNull Context context, @UserIdInt int userId) throws AppSearchException { - File appSearchDir = getAppSearchDir(mContext, userId); - return AppSearchImpl.create( - appSearchDir, mContext, userId, mGlobalQuerierPackage); + File appSearchDir = getAppSearchDir(context, userId); + return AppSearchImpl.create(appSearchDir, context, userId, mGlobalQuerierPackage); } private static File getAppSearchDir(@NonNull Context context, @UserIdInt int userId) { @@ -96,7 +115,8 @@ public final class ImplInstanceManager { * * @param context Context of the system service. */ - private static String getGlobalAppSearchDataQuerierPackageName(Context context) { + @NonNull + private static String getGlobalAppSearchDataQuerierPackageName(@NonNull Context context) { String globalAppSearchDataQuerierPackage = context.getString(R.string.config_globalAppSearchDataQuerierPackage); try { diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java index d44169d16d5d..d4ee9bb813c9 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -151,8 +151,7 @@ public class AlarmManagerService extends SystemService { static final boolean DEBUG_BG_LIMIT = localLOGV || false; static final boolean DEBUG_STANDBY = localLOGV || false; static final boolean RECORD_ALARMS_IN_HISTORY = true; - // TODO (b/178484639): Turn off once allow-while-idle revamp is completed. - static final boolean RECORD_DEVICE_IDLE_ALARMS = true; + static final boolean RECORD_DEVICE_IDLE_ALARMS = false; static final String TIMEZONE_PROPERTY = "persist.sys.timezone"; static final int TICK_HISTORY_DEPTH = 10; diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java index dba1d4b0a2e2..7cad4ab0183d 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java @@ -25,6 +25,7 @@ import android.content.IntentFilter; import android.os.Handler; import android.os.PowerManager; import android.os.RemoteException; +import android.provider.DeviceConfig; import android.util.IndentingPrintWriter; import android.util.Slog; import android.util.TimeUtils; @@ -35,7 +36,6 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.procstats.ProcessStats; import com.android.internal.util.StatLogger; import com.android.server.JobSchedulerBackgroundThread; -import com.android.server.job.JobSchedulerService.Constants; import com.android.server.job.JobSchedulerService.MaxJobCountsPerMemoryTrimLevel; import com.android.server.job.controllers.JobStatus; import com.android.server.job.controllers.StateController; @@ -51,6 +51,11 @@ class JobConcurrencyManager { private static final String TAG = JobSchedulerService.TAG; private static final boolean DEBUG = JobSchedulerService.DEBUG; + static final String CONFIG_KEY_PREFIX_CONCURRENCY = "concurrency_"; + private static final String KEY_SCREEN_OFF_ADJUSTMENT_DELAY_MS = + CONFIG_KEY_PREFIX_CONCURRENCY + "screen_off_adjustment_delay_ms"; + private static final long DEFAULT_SCREEN_OFF_ADJUSTMENT_DELAY_MS = 30_000; + private final Object mLock; private final JobSchedulerService mService; private final JobSchedulerService.Constants mConstants; @@ -84,6 +89,9 @@ class JobConcurrencyManager { private final JobCountTracker mJobCountTracker = new JobCountTracker(); + /** Wait for this long after screen off before adjusting the job concurrency. */ + private long mScreenOffAdjustmentDelayMs = DEFAULT_SCREEN_OFF_ADJUSTMENT_DELAY_MS; + /** Current memory trim level. */ private int mLastMemoryTrimLevel; @@ -165,8 +173,7 @@ class JobConcurrencyManager { // Note: we can't directly do postDelayed(this::rampUpForScreenOn), because // we need the exact same instance for removeCallbacks(). - mHandler.postDelayed(mRampUpForScreenOff, - mConstants.SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS); + mHandler.postDelayed(mRampUpForScreenOff, mScreenOffAdjustmentDelayMs); } } } @@ -174,7 +181,7 @@ class JobConcurrencyManager { private final Runnable mRampUpForScreenOff = this::rampUpForScreenOff; /** - * Called in {@link Constants#SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS} after + * Called in {@link #mScreenOffAdjustmentDelayMs} after * the screen turns off, in order to increase concurrency. */ private void rampUpForScreenOff() { @@ -188,9 +195,7 @@ class JobConcurrencyManager { return; } final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); - if ((mLastScreenOffRealtime - + mConstants.SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS) - > now) { + if ((mLastScreenOffRealtime + mScreenOffAdjustmentDelayMs) > now) { return; } @@ -473,12 +478,24 @@ class JobConcurrencyManager { return s.toString(); } + void updateConfigLocked() { + DeviceConfig.Properties properties = + DeviceConfig.getProperties(DeviceConfig.NAMESPACE_JOB_SCHEDULER); + + mScreenOffAdjustmentDelayMs = properties.getLong( + KEY_SCREEN_OFF_ADJUSTMENT_DELAY_MS, DEFAULT_SCREEN_OFF_ADJUSTMENT_DELAY_MS); + } public void dumpLocked(IndentingPrintWriter pw, long now, long nowRealtime) { pw.println("Concurrency:"); pw.increaseIndent(); try { + pw.print("Configuration:"); + pw.increaseIndent(); + pw.print(KEY_SCREEN_OFF_ADJUSTMENT_DELAY_MS, mScreenOffAdjustmentDelayMs).println(); + pw.decreaseIndent(); + pw.print("Screen state: current "); pw.print(mCurrentInteractiveState ? "ON" : "OFF"); pw.print(" effective "); diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index 97ba815d935c..885662c24147 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -369,12 +369,6 @@ public class JobSchedulerService extends com.android.server.SystemService case Constants.KEY_MODERATE_USE_FACTOR: mConstants.updateUseFactorConstantsLocked(); break; - case Constants.KEY_SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS: - if (!concurrencyUpdated) { - mConstants.updateConcurrencyConstantsLocked(); - concurrencyUpdated = true; - } - break; case Constants.KEY_MIN_LINEAR_BACKOFF_TIME_MS: case Constants.KEY_MIN_EXP_BACKOFF_TIME_MS: mConstants.updateBackoffConstantsLocked(); @@ -384,10 +378,10 @@ public class JobSchedulerService extends com.android.server.SystemService mConstants.updateConnectivityConstantsLocked(); break; default: - // Too many max_job_* strings to list. - if (name.startsWith(Constants.KEY_PREFIX_MAX_JOB) + if (name.startsWith(JobConcurrencyManager.CONFIG_KEY_PREFIX_CONCURRENCY) && !concurrencyUpdated) { mConstants.updateConcurrencyConstantsLocked(); + mConcurrencyManager.updateConfigLocked(); concurrencyUpdated = true; } else { for (int ctrlr = 0; ctrlr < mControllers.size(); ctrlr++) { @@ -552,9 +546,6 @@ public class JobSchedulerService extends com.android.server.SystemService private static final String KEY_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = "aq_schedule_return_failure"; - private static final String KEY_SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS = - "screen_off_job_concurrency_increase_delay_ms"; - private static final int DEFAULT_MIN_READY_NON_ACTIVE_JOBS_COUNT = 5; private static final long DEFAULT_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = 31 * MINUTE_IN_MILLIS; private static final float DEFAULT_HEAVY_USE_FACTOR = .9f; @@ -568,7 +559,6 @@ public class JobSchedulerService extends com.android.server.SystemService private static final long DEFAULT_API_QUOTA_SCHEDULE_WINDOW_MS = MINUTE_IN_MILLIS; private static final boolean DEFAULT_API_QUOTA_SCHEDULE_THROW_EXCEPTION = true; private static final boolean DEFAULT_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = false; - private static final long DEFAULT_SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS = 30_000; /** * Minimum # of non-ACTIVE jobs for which the JMS will be happy running some work early. @@ -591,7 +581,8 @@ public class JobSchedulerService extends com.android.server.SystemService float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR; /** Prefix for all of the max_job constants. */ - private static final String KEY_PREFIX_MAX_JOB = "max_job_"; + private static final String KEY_PREFIX_MAX_JOB = + JobConcurrencyManager.CONFIG_KEY_PREFIX_CONCURRENCY + "max_job_"; // Max job counts for screen on / off, for each memory trim level. final MaxJobCountsPerMemoryTrimLevel MAX_JOB_COUNTS_SCREEN_ON = @@ -632,11 +623,6 @@ public class JobSchedulerService extends com.android.server.SystemService 1, KEY_PREFIX_MAX_JOB + "max_bg_off_critical", 1, KEY_PREFIX_MAX_JOB + "min_bg_off_critical")); - - /** Wait for this long after screen off before increasing the job concurrency. */ - long SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS = - DEFAULT_SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS; - /** * The minimum backoff time to allow for linear backoff. */ @@ -710,11 +696,6 @@ public class JobSchedulerService extends com.android.server.SystemService MAX_JOB_COUNTS_SCREEN_OFF.moderate.update(); MAX_JOB_COUNTS_SCREEN_OFF.low.update(); MAX_JOB_COUNTS_SCREEN_OFF.critical.update(); - - SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS = DeviceConfig.getLong( - DeviceConfig.NAMESPACE_JOB_SCHEDULER, - KEY_SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS, - DEFAULT_SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS); } private void updateBackoffConstantsLocked() { @@ -776,9 +757,6 @@ public class JobSchedulerService extends com.android.server.SystemService MAX_JOB_COUNTS_SCREEN_OFF.low.dump(pw, ""); MAX_JOB_COUNTS_SCREEN_OFF.critical.dump(pw, ""); - pw.print(KEY_SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS, - SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS).println(); - pw.print(KEY_MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME_MS).println(); pw.print(KEY_MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME_MS).println(); pw.print(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println(); @@ -806,9 +784,6 @@ public class JobSchedulerService extends com.android.server.SystemService MAX_JOB_COUNTS_SCREEN_ON.dumpProto(proto, ConstantsProto.MAX_JOB_COUNTS_SCREEN_ON); MAX_JOB_COUNTS_SCREEN_OFF.dumpProto(proto, ConstantsProto.MAX_JOB_COUNTS_SCREEN_OFF); - proto.write(ConstantsProto.SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS, - SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS); - proto.write(ConstantsProto.MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME_MS); proto.write(ConstantsProto.MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME_MS); proto.write(ConstantsProto.CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC); diff --git a/apex/permission/Android.bp b/apex/permission/Android.bp deleted file mode 100644 index be51143e4730..000000000000 --- a/apex/permission/Android.bp +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (C) 2019 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -apex { - name: "com.android.permission", - defaults: ["com.android.permission-defaults"], - manifest: "apex_manifest.json", -} - -apex_defaults { - name: "com.android.permission-defaults", - updatable: true, - min_sdk_version: "30", - key: "com.android.permission.key", - certificate: ":com.android.permission.certificate", - java_libs: [ - "framework-permission", - "framework-permission-s", - "service-permission", - ], - apps: ["PermissionController"], -} - -apex_key { - name: "com.android.permission.key", - public_key: "com.android.permission.avbpubkey", - private_key: "com.android.permission.pem", -} - -android_app_certificate { - name: "com.android.permission.certificate", - certificate: "com.android.permission", -} - -filegroup { - name: "permission-jarjar-rules", - srcs: ["jarjar-rules.txt"], -} diff --git a/apex/permission/OWNERS b/apex/permission/OWNERS deleted file mode 100644 index 957e10a582a0..000000000000 --- a/apex/permission/OWNERS +++ /dev/null @@ -1,6 +0,0 @@ -svetoslavganov@google.com -moltmann@google.com -eugenesusla@google.com -zhanghai@google.com -evanseverson@google.com -ntmyren@google.com diff --git a/apex/permission/TEST_MAPPING b/apex/permission/TEST_MAPPING deleted file mode 100644 index 6e67ce92a27e..000000000000 --- a/apex/permission/TEST_MAPPING +++ /dev/null @@ -1,7 +0,0 @@ -{ - "presubmit" : [ - { - "name" : "PermissionApexTests" - } - ] -} diff --git a/apex/permission/apex_manifest.json b/apex/permission/apex_manifest.json deleted file mode 100644 index 6350d54d695e..000000000000 --- a/apex/permission/apex_manifest.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "com.android.permission", - "version": 309999999 -} diff --git a/apex/permission/com.android.permission.avbpubkey b/apex/permission/com.android.permission.avbpubkey Binary files differdeleted file mode 100644 index 9eaf85259637..000000000000 --- a/apex/permission/com.android.permission.avbpubkey +++ /dev/null diff --git a/apex/permission/com.android.permission.pem b/apex/permission/com.android.permission.pem deleted file mode 100644 index 3d584be5440d..000000000000 --- a/apex/permission/com.android.permission.pem +++ /dev/null @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIJKgIBAAKCAgEA6snt4eqoz85xiL9Sf6w1S1b9FgSHK05zYTh2JYPvQKQ3yeZp -E6avJ6FN6XcbmkDzSd658BvUGDBSPhOlzuUO4BsoKBuLMxP6TxIQXFKidzDqY0vQ -4qkS++bdIhUjwBP3OSZ3Czu0BiihK8GC75Abr//EyCyObGIGGfHEGANiOgrpP4X5 -+OmLzQLCjk4iE1kg+U6cRSRI/XLaoWC0TvIIuzxznrQ6r5GmzgTOwyBWyIB+bj73 -bmsweHTU+w9Y7kGOx4hO3XCLIhoBWEw0EbuW9nZmQ4sZls5Jo/CbyJlCclF11yVo -SCf2LG/T+9pah5NOmDQ1kPbU+0iKZIV4YFHGTIhyGDE/aPOuUT05ziCGDifgHr0u -SG1x/RLqsVh/POvNxnvP9cQFMQ08BvbEJaTTgB785iwKsvdqCfmng/SAyxSetmzP -StXVB3fh1OoZ8vunRbQYxnmUxycVqaA96zmBx2wLvbvzKo7pZFDE6nbhnT5+MRAM -z/VIK89W26uB4gj8sBFslqZjT0jPqsAZuvDm7swOtMwIcEolyGJuFLqlhN7UwMz2 -9y8+IpYixR+HvD1TZI9NtmuCmv3kPrWgoMZg6yvaBayTIr8RdYzi6FO/C1lLiraz -48dH3sXWRa8cgw6VcSUwYrEBIc3sotdsupO1iOjcFybIwaee0YTZJfjvbqkCAwEA -AQKCAgEArRnfdpaJi1xLPGTCMDsIt9kUku0XswgN7PmxsYsKFAB+2S40/jYAIRm9 -1YjpItsMA8RgFfSOdJ77o6TctCMQyo17F8bm4+uwuic5RLfv7Cx2QmsdQF8jDfFx -y7UGPJD7znjbf76uxXOjEB2FqZX3s9TAgkzHXIUQtoQW7RVhkCWHPjxKxgd5+NY2 -FrDoUpd9xhD9CcTsw1+wbRZdGW88nL6/B50dP2AFORM2VYo8MWr6y9FEn3YLsGOC -uu7fxBk1aUrHyl81VRkTMMROB1zkuiUk1FtzrEm+5U15rXXBFYOVe9+qeLhtuOlh -wueDoz0pzvF/JLe24uTik6YL0Ae6SD0pFXQ2KDrdH3cUHLok3r76/yGzaDNTFjS2 -2WbQ8dEJV8veNHk8gjGpFTJIsBUlcZpmUCDHlfvVMb3+2ahQ+28piQUt5t3zqJdZ -NDqsOHzY6BRPc+Wm85Xii/lWiQceZSee/b1Enu+nchsyXhSenBfC6bIGZReyMI0K -KKKuVhyR6OSOiR5ZdZ/NyXGqsWy05fn/h0X9hnpETsNaNYNKWvpHLfKll+STJpf7 -AZquJPIclQyiq5NONx6kfPztoCLkKV/zOgIj3Sx5oSZq+5gpO91nXWVwkTbqK1d1 -004q2Mah6UQyAk1XGQc2pHx7ouVcWawjU30vZ4C015Hv2lm/gVkCggEBAPltATYS -OqOSL1YAtIHPiHxMjNAgUdglq8JiJFXVfkocGU9eNub3Ed3sSWu6GB9Myu/sSKje -bJ5DZqxJnvB2Fqmu9I9OunLGFSD0aXs4prwsQ1Rm5FcbImtrxcciASdkoo8Pj0z4 -vk2r2NZD3VtER5Uh+YjSDkxcS9gBStXUpCL6gj69UpOxMmWqZVjyHatVB4lEvYJl -N82uT7N7QVNL1DzcZ9z4C4r7ks1Pm7ka12s5m/oaAlAMdVeofiPJe1xA9zRToSr4 -tIbMkOeXFLVRLuji/7XsOgal5Rl59p+OwLshX5cswPVOMrH6zt+hbsJ5q8M5dqnX -VAOBK7KNQ/EKZwcCggEBAPD6KVvyCim46n5EbcEqCkO7gevwZkw/9vLwmM5YsxTh -z9FQkPO0iB7mwbX8w04I91Pre4NdfcgMG0pP1b13Sb4KHBchqW1a+TCs3kSGC6gn -1SxmXHnA9jRxAkrWlGkoAQEz+aP61cXiiy2tXpQwJ8xQCKprfoqWZwhkCtEVU6CE -S7v9cscOHIqgNxx4WoceMmq4EoihHAZzHxTcNVbByckMjb2XQJ0iNw3lDP4ddvc+ -a4HzHfHkhzeQ5ZNc8SvWU8z80aSCOKRsSD3aUTZzxhZ4O2tZSW7v7p+FpvVee7bC -g8YCfszTdpVUMlLRLjScimAcovcFLSvtyupinxWg4M8CggEAN9YGEmOsSte7zwXj -YrfhtumwEBtcFwX/2Ej+F1Tuq4p0xAa0RaoDjumJWhtTsRYQy/raHSuFpzwxbNoi -QXQ+CIhI6RfXtz/OlQ0B2/rHoJJMFEXgUfuaDfAXW0eqeHYXyezSyIlamKqipPyW -Pgsf9yue39keKEv1EorfhNTQVaA8rezV4oglXwrxGyNALw2e3UTNI7ai8mFWKDis -XAg6n9E7UwUYGGnO6DUtCBgRJ0jDOQ6/e8n+LrxiWIKPIgzNCiK6jpMUXqTGv4Fb -umdNGAdQ9RnHt5tFmRlrczaSwJFtA7uaCpAR2zPpQbiywchZAiAIB2dTwGEXNiZX -kksg2wKCAQEA6pNad3qhkgPDoK6T+Jkn7M82paoaqtcJWWwEE7oceZNnbWZz9Agl -CY+vuawXonrv5+0vCq2Tp4zBdBFLC2h3jFrjBVFrUFxifpOIukOSTVqZFON/2bWQ -9XOcu6UuSz7522Xw+UNPnZXtzcUacD6AP08ZYGvLfrTyDyTzspyED5k48ALEHCkM -d5WGkFxII4etpF0TDZVnZo/iDbhe49k4yFFEGO6Ho26PESOLBkNAb2V/2bwDxlij -l9+g21Z6HiZA5SamHPH2mXgeyrcen1cL2QupK9J6vVcqfnboE6qp2zp2c+Yx8MlY -gfy4EA44YFaSDQVTTgrn8f9Eq+zc130H2QKCAQEAqOKgv68nIPdDSngNyCVyWego -boFiDaEJoBBg8FrBjTJ6wFLrNAnXmbvfTtgNmNAzF1cUPJZlIIsHgGrMCfpehbXq -WQQIw+E+yFbTGLxseGRfsLrV0CsgnAoOVeod+yIHmqc3livaUbrWhL1V2f6Ue+sE -7YLp/iP43NaMfA4kYk2ep7+ZJoEVkCjHJJaHWgAG3RynPJHkTJlSgu7wLYvGc9uE -ZsEFUM46lX02t7rrtMfasVGrUy1c2xOxFb4v1vG6iEZ7+YWeq5o3AkxUwEGn+mG4 -/3p+k4AaTXJDXgyZ0Sv6CkGuPHenAYG4cswcUUEf/G4Ag77x6LBNMgycJBxUJA== ------END RSA PRIVATE KEY----- diff --git a/apex/permission/com.android.permission.pk8 b/apex/permission/com.android.permission.pk8 Binary files differdeleted file mode 100644 index d51673dbc2fc..000000000000 --- a/apex/permission/com.android.permission.pk8 +++ /dev/null diff --git a/apex/permission/com.android.permission.x509.pem b/apex/permission/com.android.permission.x509.pem deleted file mode 100644 index 4b146c9edd4f..000000000000 --- a/apex/permission/com.android.permission.x509.pem +++ /dev/null @@ -1,35 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIGKzCCBBOgAwIBAgIUezo3fQeVZsmLpm/dkpGWJ/G/MN8wDQYJKoZIhvcNAQEL -BQAwgaMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH -DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy -b2lkMR8wHQYDVQQDDBZjb20uYW5kcm9pZC5wZXJtaXNzaW9uMSIwIAYJKoZIhvcN -AQkBFhNhbmRyb2lkQGFuZHJvaWQuY29tMCAXDTE5MTAwOTIxMzExOVoYDzQ3NTcw -OTA0MjEzMTE5WjCBozELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx -FjAUBgNVBAcMDU1vdW50YWluIFZpZXcxEDAOBgNVBAoMB0FuZHJvaWQxEDAOBgNV -BAsMB0FuZHJvaWQxHzAdBgNVBAMMFmNvbS5hbmRyb2lkLnBlcm1pc3Npb24xIjAg -BgkqhkiG9w0BCQEWE2FuZHJvaWRAYW5kcm9pZC5jb20wggIiMA0GCSqGSIb3DQEB -AQUAA4ICDwAwggIKAoICAQCxefguRJ7E6tBCTEOeU2HJEGs6AQQapLz9hMed0aaJ -Qr7aTQiYJEk+sG4+jPYbjpxa8JDDzJHp+4g7DjfSb+dvT9n84A8lWaI/yRXTZTQN -Hu5m/bgHhi0LbySpiaFyodXBKUAnOhZyGPtYjtBFywFylueub8ryc1Z6UxxU7udH -1mkIr7sE48Qkq5SyjFROE96iFmYA+vS/JXOfS0NBHiMB4GBxx4V7kXpvrTI7hhZG -HiyhKvNh7wyHIhO9nDEw1rwtAH6CsL3YkQEVBeAU98m+0Au+qStLYkKHh2l8zT4W -7sVK1VSqfB+VqOUmeIGdzlBfqMsoXD+FJz6KnIdUHIwjFDjL7Xr+hd+7xve+Q3S+ -U3Blk/U6atY8PM09wNfilG+SvwcKk5IgriDcu3rWKgIFxbUUaxLrDW7pLlu6wt/d -GGtKK+Bc0jF+9Z901Tl33i5xhc5yOktT0btkKs7lSeE6VzP/Nk5g0SuzixmuRoh9 -f5Ge41N2ZCEHNXx3wZeVZwHIIPfYrL7Yql1Xoxbfs4ETFk6ChzVQcvjfDQQuK58J -uNc+TOCoI/qflXwGCwpuHl0ier8V5Z4tpMUl5rWyVR/QGRtLPvs2lLuxczDw1OXq -wEVtCMn9aNnd4y7R9PZ52hi53HAvDjpWefrLYi+Q04J6iGFQ1qAFBClK9DquBvmR -swIDAQABo1MwUTAdBgNVHQ4EFgQULpfus5s5SrqLkoUKyPXA0D1iHPMwHwYDVR0j -BBgwFoAULpfus5s5SrqLkoUKyPXA0D1iHPMwDwYDVR0TAQH/BAUwAwEB/zANBgkq -hkiG9w0BAQsFAAOCAgEAjxQG5EFv8V/9yV2glI53VOmlWMjfEgvUjd39s/XLyPlr -OzPOKSB0NFo8To3l4l+MsManxPK8y0OyfEVKbWVz9onv0ovo5MVokBmV/2G0jmsV -B4e9yjOq+DmqIvY/Qh63Ywb97sTgcFI8620MhQDbh2IpEGv4ZNV0H6rgXmgdSCBw -1EjBoYfFpN5aMgZjeyzZcq+d1IapdWqdhuEJQkMvoYS4WIumNIJlEXPQRoq/F5Ih -nszdbKI/jVyiGFa2oeZ3rja1Y6GCRU8TYEoKx1pjS8uQDOEDTwsG/QnUe9peEj0V -SsCkIidJWTomAmq9Tub9vpBe1zuTpuRAwxwR0qwgSxozV1Mvow1dJ19oFtHX0yD6 -ZjCpRn5PW9kMvSWSlrcrFs1NJf0j1Cvf7bHpkEDqLqpMnnh9jaFQq3nzDY+MWcIR -jDcgQpI+AiE2/qtauZnFEVhbce49nCnk9+5bpTTIZJdzqeaExe5KXHwEtZLaEDh4 -atLY9LuEvPsjmDIMOR6hycD9FvwGXhJOQBjESIWFwigtSb1Yud9n6201jw3MLJ4k -+WhkbmZgWy+xc+Mdm5H3XyB1lvHaHGkxu+QB9KyQuVQKwbUVcbwZIfTFPN6Zr/dS -ZXJqAbBhG/dBgF0LazuLaPVpibi+a3Y+tb9b8eXGkz4F97PWZIEDkELQ+9KOvhc= ------END CERTIFICATE----- diff --git a/apex/permission/framework-s/Android.bp b/apex/permission/framework-s/Android.bp deleted file mode 100644 index e71cc43f13fe..000000000000 --- a/apex/permission/framework-s/Android.bp +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (C) 2021 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. - -filegroup { - name: "framework-permission-s-sources", - srcs: [ - "java/**/*.java", - "java/**/*.aidl", - ], - path: "java", - visibility: ["//frameworks/base"], -} - -java_library { - name: "framework-permission-s-shared", - srcs: [":framework-permission-s-shared-srcs"], - libs: [ - "framework-annotations-lib", - "unsupportedappusage", - ], - apex_available: [ - "com.android.permission", - "test_com.android.permission", - ], - installable: false, - min_sdk_version: "30", - sdk_version: "module_current", -} - -java_sdk_library { - name: "framework-permission-s", - defaults: ["framework-module-defaults"], - srcs: [ - ":framework-permission-s-sources", - ], - libs: [ - "framework-annotations-lib" - ], - static_libs: [ - "framework-permission-s-shared", - ], - apex_available: [ - "com.android.permission", - "test_com.android.permission", - ], - hostdex: true, - // Restrict access to implementation library. - impl_library_visibility: [ - "//frameworks/base/apex/permission:__subpackages__", - "//packages/modules/Permission:__subpackages__", - ], - installable: true, - jarjar_rules: ":permission-jarjar-rules", - min_sdk_version: "30", - permitted_packages: [ - "android.permission", - "android.app.role", - // For com.android.permission.jarjar. - "com.android.permission", - ], -} diff --git a/apex/permission/framework-s/api/current.txt b/apex/permission/framework-s/api/current.txt deleted file mode 100644 index 4ecc98980c43..000000000000 --- a/apex/permission/framework-s/api/current.txt +++ /dev/null @@ -1,19 +0,0 @@ -// Signature format: 2.0 -package android.app.role { - - public final class RoleManager { - method @NonNull public android.content.Intent createRequestRoleIntent(@NonNull String); - method public boolean isRoleAvailable(@NonNull String); - method public boolean isRoleHeld(@NonNull String); - field public static final String ROLE_ASSISTANT = "android.app.role.ASSISTANT"; - field public static final String ROLE_BROWSER = "android.app.role.BROWSER"; - field public static final String ROLE_CALL_REDIRECTION = "android.app.role.CALL_REDIRECTION"; - field public static final String ROLE_CALL_SCREENING = "android.app.role.CALL_SCREENING"; - field public static final String ROLE_DIALER = "android.app.role.DIALER"; - field public static final String ROLE_EMERGENCY = "android.app.role.EMERGENCY"; - field public static final String ROLE_HOME = "android.app.role.HOME"; - field public static final String ROLE_SMS = "android.app.role.SMS"; - } - -} - diff --git a/apex/permission/framework-s/api/module-lib-current.txt b/apex/permission/framework-s/api/module-lib-current.txt deleted file mode 100644 index d7c9a2395c04..000000000000 --- a/apex/permission/framework-s/api/module-lib-current.txt +++ /dev/null @@ -1,11 +0,0 @@ -// Signature format: 2.0 -package android.app.role { - - public final class RoleManager { - method @Nullable public String getBrowserRoleHolder(int); - method @Nullable public String getSmsRoleHolder(int); - method @Nullable @RequiresPermission(android.Manifest.permission.SET_PREFERRED_APPLICATIONS) public boolean setBrowserRoleHolder(@Nullable String, int); - } - -} - diff --git a/apex/permission/framework-s/api/module-lib-removed.txt b/apex/permission/framework-s/api/module-lib-removed.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/apex/permission/framework-s/api/module-lib-removed.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/apex/permission/framework-s/api/removed.txt b/apex/permission/framework-s/api/removed.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/apex/permission/framework-s/api/removed.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/apex/permission/framework-s/api/system-current.txt b/apex/permission/framework-s/api/system-current.txt deleted file mode 100644 index 6778d4826841..000000000000 --- a/apex/permission/framework-s/api/system-current.txt +++ /dev/null @@ -1,43 +0,0 @@ -// Signature format: 2.0 -package android.app.role { - - public interface OnRoleHoldersChangedListener { - method public void onRoleHoldersChanged(@NonNull String, @NonNull android.os.UserHandle); - } - - @Deprecated public abstract class RoleControllerService extends android.app.Service { - ctor @Deprecated public RoleControllerService(); - method @Deprecated @WorkerThread public abstract boolean onAddRoleHolder(@NonNull String, @NonNull String, int); - method @Deprecated @Nullable public final android.os.IBinder onBind(@Nullable android.content.Intent); - method @Deprecated @WorkerThread public abstract boolean onClearRoleHolders(@NonNull String, int); - method @Deprecated @WorkerThread public abstract boolean onGrantDefaultRoles(); - method @Deprecated public abstract boolean onIsApplicationQualifiedForRole(@NonNull String, @NonNull String); - method @Deprecated public boolean onIsApplicationVisibleForRole(@NonNull String, @NonNull String); - method @Deprecated public abstract boolean onIsRoleVisible(@NonNull String); - method @Deprecated @WorkerThread public abstract boolean onRemoveRoleHolder(@NonNull String, @NonNull String, int); - field @Deprecated public static final String SERVICE_INTERFACE = "android.app.role.RoleControllerService"; - } - - public class RoleFrameworkInitializer { - method public static void registerServiceWrappers(); - } - - public final class RoleManager { - method @RequiresPermission(android.Manifest.permission.OBSERVE_ROLE_HOLDERS) public void addOnRoleHoldersChangedListenerAsUser(@NonNull java.util.concurrent.Executor, @NonNull android.app.role.OnRoleHoldersChangedListener, @NonNull android.os.UserHandle); - method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void addRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); - method @Deprecated @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean addRoleHolderFromController(@NonNull String, @NonNull String); - method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void clearRoleHoldersAsUser(@NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); - method @Deprecated @NonNull @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public java.util.List<java.lang.String> getHeldRolesFromController(@NonNull String); - method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHolders(@NonNull String); - method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle); - method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void isApplicationVisibleForRole(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); - method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void isRoleVisible(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); - method @RequiresPermission(android.Manifest.permission.OBSERVE_ROLE_HOLDERS) public void removeOnRoleHoldersChangedListenerAsUser(@NonNull android.app.role.OnRoleHoldersChangedListener, @NonNull android.os.UserHandle); - method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void removeRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); - method @Deprecated @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean removeRoleHolderFromController(@NonNull String, @NonNull String); - method @Deprecated @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public void setRoleNamesFromController(@NonNull java.util.List<java.lang.String>); - field public static final int MANAGE_HOLDERS_FLAG_DONT_KILL_APP = 1; // 0x1 - } - -} - diff --git a/apex/permission/framework-s/api/system-removed.txt b/apex/permission/framework-s/api/system-removed.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/apex/permission/framework-s/api/system-removed.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/apex/permission/framework-s/java/android/app/role/IOnRoleHoldersChangedListener.aidl b/apex/permission/framework-s/java/android/app/role/IOnRoleHoldersChangedListener.aidl deleted file mode 100644 index 6cf961fad2c4..000000000000 --- a/apex/permission/framework-s/java/android/app/role/IOnRoleHoldersChangedListener.aidl +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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. - */ - -package android.app.role; - -/** - * @hide - */ -oneway interface IOnRoleHoldersChangedListener { - - void onRoleHoldersChanged(String roleName, int userId); -} diff --git a/apex/permission/framework-s/java/android/app/role/IRoleController.aidl b/apex/permission/framework-s/java/android/app/role/IRoleController.aidl deleted file mode 100644 index 8a43d7fa9036..000000000000 --- a/apex/permission/framework-s/java/android/app/role/IRoleController.aidl +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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. - */ - -package android.app.role; - -import android.os.RemoteCallback; - -/** - * @hide - */ -oneway interface IRoleController { - - void grantDefaultRoles(in RemoteCallback callback); - - void onAddRoleHolder(in String roleName, in String packageName, int flags, - in RemoteCallback callback); - - void onRemoveRoleHolder(in String roleName, in String packageName, int flags, - in RemoteCallback callback); - - void onClearRoleHolders(in String roleName, int flags, in RemoteCallback callback); - - void isApplicationQualifiedForRole(in String roleName, in String packageName, - in RemoteCallback callback); - - void isApplicationVisibleForRole(in String roleName, in String packageName, - in RemoteCallback callback); - - void isRoleVisible(in String roleName, in RemoteCallback callback); -} diff --git a/apex/permission/framework-s/java/android/app/role/IRoleManager.aidl b/apex/permission/framework-s/java/android/app/role/IRoleManager.aidl deleted file mode 100644 index 5fc25f0422e2..000000000000 --- a/apex/permission/framework-s/java/android/app/role/IRoleManager.aidl +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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. - */ - -package android.app.role; - -import android.app.role.IOnRoleHoldersChangedListener; -import android.os.Bundle; -import android.os.RemoteCallback; - -/** - * @hide - */ -interface IRoleManager { - - boolean isRoleAvailable(in String roleName); - - boolean isRoleHeld(in String roleName, in String packageName); - - List<String> getRoleHoldersAsUser(in String roleName, int userId); - - void addRoleHolderAsUser(in String roleName, in String packageName, int flags, int userId, - in RemoteCallback callback); - - void removeRoleHolderAsUser(in String roleName, in String packageName, int flags, int userId, - in RemoteCallback callback); - - void clearRoleHoldersAsUser(in String roleName, int flags, int userId, - in RemoteCallback callback); - - void addOnRoleHoldersChangedListenerAsUser(IOnRoleHoldersChangedListener listener, int userId); - - void removeOnRoleHoldersChangedListenerAsUser(IOnRoleHoldersChangedListener listener, - int userId); - - void setRoleNamesFromController(in List<String> roleNames); - - boolean addRoleHolderFromController(in String roleName, in String packageName); - - boolean removeRoleHolderFromController(in String roleName, in String packageName); - - List<String> getHeldRolesFromController(in String packageName); - - String getBrowserRoleHolder(int userId); - - boolean setBrowserRoleHolder(String packageName, int userId); - - String getSmsRoleHolder(int userId); -} diff --git a/apex/permission/framework-s/java/android/app/role/OnRoleHoldersChangedListener.java b/apex/permission/framework-s/java/android/app/role/OnRoleHoldersChangedListener.java deleted file mode 100644 index 5958debc86dd..000000000000 --- a/apex/permission/framework-s/java/android/app/role/OnRoleHoldersChangedListener.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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. - */ - -package android.app.role; - -import android.annotation.NonNull; -import android.annotation.SystemApi; -import android.os.UserHandle; - -/** - * Listener for role holder changes. - * - * @hide - */ -@SystemApi -public interface OnRoleHoldersChangedListener { - - /** - * Called when the holders of roles are changed. - * - * @param roleName the name of the role whose holders are changed - * @param user the user for this role holder change - */ - void onRoleHoldersChanged(@NonNull String roleName, @NonNull UserHandle user); -} diff --git a/apex/permission/framework-s/java/android/app/role/RoleControllerManager.java b/apex/permission/framework-s/java/android/app/role/RoleControllerManager.java deleted file mode 100644 index 93a7ae0c82c0..000000000000 --- a/apex/permission/framework-s/java/android/app/role/RoleControllerManager.java +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.app.role; - -import android.Manifest; -import android.annotation.CallbackExecutor; -import android.annotation.NonNull; -import android.annotation.RequiresPermission; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ServiceInfo; -import android.os.Binder; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.os.RemoteCallback; -import android.util.Log; -import android.util.SparseArray; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.infra.AndroidFuture; -import com.android.internal.infra.ServiceConnector; - -import java.util.concurrent.Executor; -import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; - -/** - * Interface for communicating with the role controller. - * - * @hide - */ -public class RoleControllerManager { - - private static final String LOG_TAG = RoleControllerManager.class.getSimpleName(); - - private static final long REQUEST_TIMEOUT_MILLIS = 15 * 1000; - - private static volatile ComponentName sRemoteServiceComponentName; - - private static final Object sRemoteServicesLock = new Object(); - - /** - * Global remote services (per user) used by all {@link RoleControllerManager managers}. - */ - @GuardedBy("sRemoteServicesLock") - private static final SparseArray<ServiceConnector<IRoleController>> sRemoteServices = - new SparseArray<>(); - - @NonNull - private final ServiceConnector<IRoleController> mRemoteService; - - /** - * Initialize the remote service component name once so that we can avoid acquiring the - * PackageManagerService lock in constructor. - * - * @see #createWithInitializedRemoteServiceComponentName(Handler, Context) - * - * @hide - */ - public static void initializeRemoteServiceComponentName(@NonNull Context context) { - sRemoteServiceComponentName = getRemoteServiceComponentName(context); - } - - /** - * Create a {@link RoleControllerManager} instance with the initialized remote service component - * name so that we can avoid acquiring the PackageManagerService lock in constructor. - * - * @see #initializeRemoteServiceComponentName(Context) - * - * @hide - */ - @NonNull - public static RoleControllerManager createWithInitializedRemoteServiceComponentName( - @NonNull Handler handler, @NonNull Context context) { - return new RoleControllerManager(sRemoteServiceComponentName, handler, context); - } - - private RoleControllerManager(@NonNull ComponentName remoteServiceComponentName, - @NonNull Handler handler, @NonNull Context context) { - synchronized (sRemoteServicesLock) { - int userId = context.getUser().getIdentifier(); - ServiceConnector<IRoleController> remoteService = sRemoteServices.get(userId); - if (remoteService == null) { - remoteService = new ServiceConnector.Impl<IRoleController>(context, - new Intent(RoleControllerService.SERVICE_INTERFACE) - .setComponent(remoteServiceComponentName), - 0 /* bindingFlags */, userId, IRoleController.Stub::asInterface) { - - @Override - protected Handler getJobHandler() { - return handler; - } - }; - sRemoteServices.put(userId, remoteService); - } - mRemoteService = remoteService; - } - } - - /** - * @hide - */ - public RoleControllerManager(@NonNull Context context) { - this(getRemoteServiceComponentName(context), new Handler(Looper.getMainLooper()), context); - } - - @NonNull - private static ComponentName getRemoteServiceComponentName(@NonNull Context context) { - Intent intent = new Intent(RoleControllerService.SERVICE_INTERFACE); - PackageManager packageManager = context.getPackageManager(); - intent.setPackage(packageManager.getPermissionControllerPackageName()); - ServiceInfo serviceInfo = packageManager.resolveService(intent, 0).serviceInfo; - return new ComponentName(serviceInfo.packageName, serviceInfo.name); - } - - /** - * @see RoleControllerService#onGrantDefaultRoles() - * - * @hide - */ - public void grantDefaultRoles(@NonNull @CallbackExecutor Executor executor, - @NonNull Consumer<Boolean> callback) { - AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> { - AndroidFuture<Bundle> future = new AndroidFuture<>(); - service.grantDefaultRoles(new RemoteCallback(future::complete)); - return future; - }); - propagateCallback(operation, "grantDefaultRoles", executor, callback); - } - - /** - * @see RoleControllerService#onAddRoleHolder(String, String, int) - * - * @hide - */ - public void onAddRoleHolder(@NonNull String roleName, @NonNull String packageName, - @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) { - AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> { - AndroidFuture<Bundle> future = new AndroidFuture<>(); - service.onAddRoleHolder(roleName, packageName, flags, - new RemoteCallback(future::complete)); - return future; - }); - propagateCallback(operation, "onAddRoleHolder", callback); - } - - /** - * @see RoleControllerService#onRemoveRoleHolder(String, String, int) - * - * @hide - */ - public void onRemoveRoleHolder(@NonNull String roleName, @NonNull String packageName, - @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) { - AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> { - AndroidFuture<Bundle> future = new AndroidFuture<>(); - service.onRemoveRoleHolder(roleName, packageName, flags, - new RemoteCallback(future::complete)); - return future; - }); - propagateCallback(operation, "onRemoveRoleHolder", callback); - } - - /** - * @see RoleControllerService#onClearRoleHolders(String, int) - * - * @hide - */ - public void onClearRoleHolders(@NonNull String roleName, - @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) { - AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> { - AndroidFuture<Bundle> future = new AndroidFuture<>(); - service.onClearRoleHolders(roleName, flags, - new RemoteCallback(future::complete)); - return future; - }); - propagateCallback(operation, "onClearRoleHolders", callback); - } - - /** - * @see RoleControllerService#onIsApplicationVisibleForRole(String, String) - * - * @hide - */ - @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) - public void isApplicationVisibleForRole(@NonNull String roleName, @NonNull String packageName, - @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) { - AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> { - AndroidFuture<Bundle> future = new AndroidFuture<>(); - service.isApplicationVisibleForRole(roleName, packageName, - new RemoteCallback(future::complete)); - return future; - }); - propagateCallback(operation, "isApplicationVisibleForRole", executor, callback); - } - - /** - * @see RoleControllerService#onIsRoleVisible(String) - * - * @hide - */ - @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) - public void isRoleVisible(@NonNull String roleName, - @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) { - AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> { - AndroidFuture<Bundle> future = new AndroidFuture<>(); - service.isRoleVisible(roleName, new RemoteCallback(future::complete)); - return future; - }); - propagateCallback(operation, "isRoleVisible", executor, callback); - } - - private void propagateCallback(AndroidFuture<Bundle> operation, String opName, - @CallbackExecutor @NonNull Executor executor, - Consumer<Boolean> destination) { - operation.orTimeout(REQUEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS) - .whenComplete((res, err) -> executor.execute(() -> { - final long token = Binder.clearCallingIdentity(); - try { - if (err != null) { - Log.e(LOG_TAG, "Error calling " + opName + "()", err); - destination.accept(false); - } else { - destination.accept(res != null); - } - } finally { - Binder.restoreCallingIdentity(token); - } - })); - } - - private void propagateCallback(AndroidFuture<Bundle> operation, String opName, - RemoteCallback destination) { - operation.orTimeout(REQUEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS) - .whenComplete((res, err) -> { - final long token = Binder.clearCallingIdentity(); - try { - if (err != null) { - Log.e(LOG_TAG, "Error calling " + opName + "()", err); - destination.sendResult(null); - } else { - destination.sendResult(res); - } - } finally { - Binder.restoreCallingIdentity(token); - } - }); - } -} diff --git a/apex/permission/framework-s/java/android/app/role/RoleControllerService.java b/apex/permission/framework-s/java/android/app/role/RoleControllerService.java deleted file mode 100644 index cf7872913f26..000000000000 --- a/apex/permission/framework-s/java/android/app/role/RoleControllerService.java +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.app.role; - -import android.Manifest; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.SystemApi; -import android.annotation.WorkerThread; -import android.app.Service; -import android.content.Intent; -import android.os.Binder; -import android.os.Bundle; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.IBinder; -import android.os.Process; -import android.os.RemoteCallback; -import android.os.UserHandle; - -import com.android.internal.util.Preconditions; - -import java.util.Objects; -import java.util.concurrent.Executor; - -/** - * Abstract base class for the role controller service. - * <p> - * Subclass should implement the business logic for role management, including enforcing role - * requirements and granting or revoking relevant privileges of roles. This class can only be - * implemented by the permission controller app which is registered in {@code PackageManager}. - * - * @deprecated The role controller service is an internal implementation detail inside role, and it - * may be replaced by other mechanisms in the future and no longer be called. - * - * @hide - */ -@Deprecated -@SystemApi -public abstract class RoleControllerService extends Service { - - /** - * The {@link Intent} that must be declared as handled by the service. - */ - public static final String SERVICE_INTERFACE = "android.app.role.RoleControllerService"; - - private HandlerThread mWorkerThread; - private Handler mWorkerHandler; - - @Override - public void onCreate() { - super.onCreate(); - - mWorkerThread = new HandlerThread(RoleControllerService.class.getSimpleName()); - mWorkerThread.start(); - mWorkerHandler = new Handler(mWorkerThread.getLooper()); - } - - @Override - public void onDestroy() { - super.onDestroy(); - - mWorkerThread.quitSafely(); - } - - @Nullable - @Override - public final IBinder onBind(@Nullable Intent intent) { - return new IRoleController.Stub() { - - @Override - public void grantDefaultRoles(RemoteCallback callback) { - enforceCallerSystemUid("grantDefaultRoles"); - - Objects.requireNonNull(callback, "callback cannot be null"); - - mWorkerHandler.post(() -> RoleControllerService.this.grantDefaultRoles(callback)); - } - - @Override - public void onAddRoleHolder(String roleName, String packageName, int flags, - RemoteCallback callback) { - enforceCallerSystemUid("onAddRoleHolder"); - - Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); - Preconditions.checkStringNotEmpty(packageName, - "packageName cannot be null or empty"); - Objects.requireNonNull(callback, "callback cannot be null"); - - mWorkerHandler.post(() -> RoleControllerService.this.onAddRoleHolder(roleName, - packageName, flags, callback)); - } - - @Override - public void onRemoveRoleHolder(String roleName, String packageName, int flags, - RemoteCallback callback) { - enforceCallerSystemUid("onRemoveRoleHolder"); - - Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); - Preconditions.checkStringNotEmpty(packageName, - "packageName cannot be null or empty"); - Objects.requireNonNull(callback, "callback cannot be null"); - - mWorkerHandler.post(() -> RoleControllerService.this.onRemoveRoleHolder(roleName, - packageName, flags, callback)); - } - - @Override - public void onClearRoleHolders(String roleName, int flags, RemoteCallback callback) { - enforceCallerSystemUid("onClearRoleHolders"); - - Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); - Objects.requireNonNull(callback, "callback cannot be null"); - - mWorkerHandler.post(() -> RoleControllerService.this.onClearRoleHolders(roleName, - flags, callback)); - } - - private void enforceCallerSystemUid(@NonNull String methodName) { - if (Binder.getCallingUid() != Process.SYSTEM_UID) { - throw new SecurityException("Only the system process can call " + methodName - + "()"); - } - } - - @Override - public void isApplicationQualifiedForRole(String roleName, String packageName, - RemoteCallback callback) { - enforceCallingPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, null); - - Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); - Preconditions.checkStringNotEmpty(packageName, - "packageName cannot be null or empty"); - Objects.requireNonNull(callback, "callback cannot be null"); - - boolean qualified = onIsApplicationQualifiedForRole(roleName, packageName); - callback.sendResult(qualified ? Bundle.EMPTY : null); - } - - @Override - public void isApplicationVisibleForRole(String roleName, String packageName, - RemoteCallback callback) { - enforceCallingPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, null); - - Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); - Preconditions.checkStringNotEmpty(packageName, - "packageName cannot be null or empty"); - Objects.requireNonNull(callback, "callback cannot be null"); - - boolean visible = onIsApplicationVisibleForRole(roleName, packageName); - callback.sendResult(visible ? Bundle.EMPTY : null); - } - - @Override - public void isRoleVisible(String roleName, RemoteCallback callback) { - enforceCallingPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, null); - - Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); - Objects.requireNonNull(callback, "callback cannot be null"); - - boolean visible = onIsRoleVisible(roleName); - callback.sendResult(visible ? Bundle.EMPTY : null); - } - }; - } - - private void grantDefaultRoles(@NonNull RemoteCallback callback) { - boolean successful = onGrantDefaultRoles(); - callback.sendResult(successful ? Bundle.EMPTY : null); - } - - private void onAddRoleHolder(@NonNull String roleName, @NonNull String packageName, - @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) { - boolean successful = onAddRoleHolder(roleName, packageName, flags); - callback.sendResult(successful ? Bundle.EMPTY : null); - } - - private void onRemoveRoleHolder(@NonNull String roleName, @NonNull String packageName, - @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) { - boolean successful = onRemoveRoleHolder(roleName, packageName, flags); - callback.sendResult(successful ? Bundle.EMPTY : null); - } - - private void onClearRoleHolders(@NonNull String roleName, - @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) { - boolean successful = onClearRoleHolders(roleName, flags); - callback.sendResult(successful ? Bundle.EMPTY : null); - } - - /** - * Called by system to grant default permissions and roles. - * <p> - * This is typically when creating a new user or upgrading either system or - * permission controller package - * - * @return whether this call was successful - */ - @WorkerThread - public abstract boolean onGrantDefaultRoles(); - - /** - * Add a specific application to the holders of a role. If the role is exclusive, the previous - * holder will be replaced. - * <p> - * Implementation should enforce the role requirements and grant or revoke the relevant - * privileges of roles. - * - * @param roleName the name of the role to add the role holder for - * @param packageName the package name of the application to add to the role holders - * @param flags optional behavior flags - * - * @return whether this call was successful - * - * @see RoleManager#addRoleHolderAsUser(String, String, int, UserHandle, Executor, - * RemoteCallback) - */ - @WorkerThread - public abstract boolean onAddRoleHolder(@NonNull String roleName, @NonNull String packageName, - @RoleManager.ManageHoldersFlags int flags); - - /** - * Remove a specific application from the holders of a role. - * - * @param roleName the name of the role to remove the role holder for - * @param packageName the package name of the application to remove from the role holders - * @param flags optional behavior flags - * - * @return whether this call was successful - * - * @see RoleManager#removeRoleHolderAsUser(String, String, int, UserHandle, Executor, - * RemoteCallback) - */ - @WorkerThread - public abstract boolean onRemoveRoleHolder(@NonNull String roleName, - @NonNull String packageName, @RoleManager.ManageHoldersFlags int flags); - - /** - * Remove all holders of a role. - * - * @param roleName the name of the role to remove role holders for - * @param flags optional behavior flags - * - * @return whether this call was successful - * - * @see RoleManager#clearRoleHoldersAsUser(String, int, UserHandle, Executor, RemoteCallback) - */ - @WorkerThread - public abstract boolean onClearRoleHolders(@NonNull String roleName, - @RoleManager.ManageHoldersFlags int flags); - - /** - * Check whether an application is qualified for a role. - * - * @param roleName name of the role to check for - * @param packageName package name of the application to check for - * - * @return whether the application is qualified for the role - * - * @deprecated Implement {@link #onIsApplicationVisibleForRole(String, String)} instead. - */ - @Deprecated - public abstract boolean onIsApplicationQualifiedForRole(@NonNull String roleName, - @NonNull String packageName); - - /** - * Check whether an application is visible for a role. - * - * While an application can be qualified for a role, it can still stay hidden from user (thus - * not visible). If an application is visible for a role, we may show things related to the role - * for it, e.g. showing an entry pointing to the role settings in its application info page. - * - * @param roleName name of the role to check for - * @param packageName package name of the application to check for - * - * @return whether the application is visible for the role - */ - public boolean onIsApplicationVisibleForRole(@NonNull String roleName, - @NonNull String packageName) { - return onIsApplicationQualifiedForRole(roleName, packageName); - } - - /** - * Check whether a role should be visible to user. - * - * @param roleName name of the role to check for - * - * @return whether the role should be visible to user - */ - public abstract boolean onIsRoleVisible(@NonNull String roleName); -} diff --git a/apex/permission/framework-s/java/android/app/role/RoleFrameworkInitializer.java b/apex/permission/framework-s/java/android/app/role/RoleFrameworkInitializer.java deleted file mode 100644 index 7a97770ecf0f..000000000000 --- a/apex/permission/framework-s/java/android/app/role/RoleFrameworkInitializer.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2021 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.role; - -import android.annotation.SystemApi; -import android.app.SystemServiceRegistry; -import android.content.Context; - -/** - * Class holding initialization code for role in the permission module. - * - * @hide - */ -@SystemApi -public class RoleFrameworkInitializer { - private RoleFrameworkInitializer() {} - - /** - * Called by {@link SystemServiceRegistry}'s static initializer and registers - * {@link RoleManager} to {@link Context}, so that {@link Context#getSystemService} can return - * it. - * - * <p>If this is called from other places, it throws a {@link IllegalStateException). - */ - public static void registerServiceWrappers() { - SystemServiceRegistry.registerContextAwareService(Context.ROLE_SERVICE, RoleManager.class, - (context, serviceBinder) -> new RoleManager(context, - IRoleManager.Stub.asInterface(serviceBinder))); - } -} diff --git a/apex/permission/framework-s/java/android/app/role/RoleManager.java b/apex/permission/framework-s/java/android/app/role/RoleManager.java deleted file mode 100644 index ceccc4cfc9f7..000000000000 --- a/apex/permission/framework-s/java/android/app/role/RoleManager.java +++ /dev/null @@ -1,773 +0,0 @@ -/* - * 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. - */ - -package android.app.role; - -import android.Manifest; -import android.annotation.CallbackExecutor; -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.RequiresPermission; -import android.annotation.SystemApi; -import android.annotation.SystemService; -import android.annotation.UserIdInt; -import android.content.Context; -import android.content.Intent; -import android.os.Binder; -import android.os.Process; -import android.os.RemoteCallback; -import android.os.RemoteException; -import android.os.UserHandle; -import android.util.ArrayMap; -import android.util.SparseArray; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.Preconditions; - -import java.util.List; -import java.util.Objects; -import java.util.concurrent.Executor; -import java.util.function.Consumer; - -/** - * This class provides information about and manages roles. - * <p> - * A role is a unique name within the system associated with certain privileges. The list of - * available roles might change with a system app update, so apps should not make assumption about - * the availability of roles. Instead, they should always query if the role is available using - * {@link #isRoleAvailable(String)} before trying to do anything with it. Some predefined role names - * are available as constants in this class, and a list of possibly available roles can be found in - * the <a href="{@docRoot}reference/androidx/core/role/package-summary.html">AndroidX Role - * library</a>. - * <p> - * There can be multiple applications qualifying for a role, but only a subset of them can become - * role holders. To qualify for a role, an application must meet certain requirements, including - * defining certain components in its manifest. These requirements can be found in the AndroidX - * Libraries. Then the application will need user consent to become a role holder, which can be - * requested using {@link android.app.Activity#startActivityForResult(Intent, int)} with the - * {@code Intent} obtained from {@link #createRequestRoleIntent(String)}. - * <p> - * Upon becoming a role holder, the application may be granted certain privileges that are role - * specific. When the application loses its role, these privileges will also be revoked. - */ -@SystemService(Context.ROLE_SERVICE) -public final class RoleManager { - - private static final String LOG_TAG = RoleManager.class.getSimpleName(); - - /** - * The name of the assistant app role. - * - * @see android.service.voice.VoiceInteractionService - */ - public static final String ROLE_ASSISTANT = "android.app.role.ASSISTANT"; - - /** - * The name of the browser role. - * - * @see Intent#CATEGORY_APP_BROWSER - */ - public static final String ROLE_BROWSER = "android.app.role.BROWSER"; - - /** - * The name of the dialer role. - * - * @see Intent#ACTION_DIAL - * @see android.telecom.InCallService - */ - public static final String ROLE_DIALER = "android.app.role.DIALER"; - - /** - * The name of the SMS role. - * - * @see Intent#CATEGORY_APP_MESSAGING - */ - public static final String ROLE_SMS = "android.app.role.SMS"; - - /** - * The name of the emergency role - */ - public static final String ROLE_EMERGENCY = "android.app.role.EMERGENCY"; - - /** - * The name of the home role. - * - * @see Intent#CATEGORY_HOME - */ - public static final String ROLE_HOME = "android.app.role.HOME"; - - /** - * The name of the call redirection role. - * <p> - * A call redirection app provides a means to re-write the phone number for an outgoing call to - * place the call through a call redirection service. - * - * @see android.telecom.CallRedirectionService - */ - public static final String ROLE_CALL_REDIRECTION = "android.app.role.CALL_REDIRECTION"; - - /** - * The name of the call screening and caller id role. - * - * @see android.telecom.CallScreeningService - */ - public static final String ROLE_CALL_SCREENING = "android.app.role.CALL_SCREENING"; - - /** - * @hide - */ - @IntDef(flag = true, value = { MANAGE_HOLDERS_FLAG_DONT_KILL_APP }) - public @interface ManageHoldersFlags {} - - /** - * Flag parameter for {@link #addRoleHolderAsUser}, {@link #removeRoleHolderAsUser} and - * {@link #clearRoleHoldersAsUser} to indicate that apps should not be killed when changing - * their role holder status. - * - * @hide - */ - @SystemApi - public static final int MANAGE_HOLDERS_FLAG_DONT_KILL_APP = 1; - - /** - * The action used to request user approval of a role for an application. - * - * @hide - */ - public static final String ACTION_REQUEST_ROLE = "android.app.role.action.REQUEST_ROLE"; - - /** - * The permission required to manage records of role holders in {@link RoleManager} directly. - * - * @hide - */ - public static final String PERMISSION_MANAGE_ROLES_FROM_CONTROLLER = - "com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER"; - - @NonNull - private final Context mContext; - - @NonNull - private final IRoleManager mService; - - @GuardedBy("mListenersLock") - @NonNull - private final SparseArray<ArrayMap<OnRoleHoldersChangedListener, - OnRoleHoldersChangedListenerDelegate>> mListeners = new SparseArray<>(); - @NonNull - private final Object mListenersLock = new Object(); - - @GuardedBy("mRoleControllerManagerLock") - @Nullable - private RoleControllerManager mRoleControllerManager; - private final Object mRoleControllerManagerLock = new Object(); - - /** - * Create a new instance of this class. - * - * @param context the {@link Context} - * @param service the {@link IRoleManager} service - * - * @hide - */ - public RoleManager(@NonNull Context context, @NonNull IRoleManager service) { - mContext = context; - mService = service; - } - - /** - * Returns an {@code Intent} suitable for passing to - * {@link android.app.Activity#startActivityForResult(Intent, int)} which prompts the user to - * grant a role to this application. - * <p> - * If the role is granted, the {@code resultCode} will be - * {@link android.app.Activity#RESULT_OK}, otherwise it will be - * {@link android.app.Activity#RESULT_CANCELED}. - * - * @param roleName the name of requested role - * - * @return the {@code Intent} to prompt user to grant the role - */ - @NonNull - public Intent createRequestRoleIntent(@NonNull String roleName) { - Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); - Intent intent = new Intent(ACTION_REQUEST_ROLE); - intent.setPackage(mContext.getPackageManager().getPermissionControllerPackageName()); - intent.putExtra(Intent.EXTRA_ROLE_NAME, roleName); - return intent; - } - - /** - * Check whether a role is available in the system. - * - * @param roleName the name of role to checking for - * - * @return whether the role is available in the system - */ - public boolean isRoleAvailable(@NonNull String roleName) { - Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); - try { - return mService.isRoleAvailable(roleName); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Check whether the calling application is holding a particular role. - * - * @param roleName the name of the role to check for - * - * @return whether the calling application is holding the role - */ - public boolean isRoleHeld(@NonNull String roleName) { - Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); - try { - return mService.isRoleHeld(roleName, mContext.getPackageName()); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Get package names of the applications holding the role. - * <p> - * <strong>Note:</strong> Using this API requires holding - * {@code android.permission.MANAGE_ROLE_HOLDERS}. - * - * @param roleName the name of the role to get the role holder for - * - * @return a list of package names of the role holders, or an empty list if none. - * - * @see #getRoleHoldersAsUser(String, UserHandle) - * - * @hide - */ - @NonNull - @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) - @SystemApi - public List<String> getRoleHolders(@NonNull String roleName) { - return getRoleHoldersAsUser(roleName, Process.myUserHandle()); - } - - /** - * Get package names of the applications holding the role. - * <p> - * <strong>Note:</strong> Using this API requires holding - * {@code android.permission.MANAGE_ROLE_HOLDERS} and if the user id is not the current user - * {@code android.permission.INTERACT_ACROSS_USERS_FULL}. - * - * @param roleName the name of the role to get the role holder for - * @param user the user to get the role holder for - * - * @return a list of package names of the role holders, or an empty list if none. - * - * @see #addRoleHolderAsUser(String, String, int, UserHandle, Executor, Consumer) - * @see #removeRoleHolderAsUser(String, String, int, UserHandle, Executor, Consumer) - * @see #clearRoleHoldersAsUser(String, int, UserHandle, Executor, Consumer) - * - * @hide - */ - @NonNull - @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) - @SystemApi - public List<String> getRoleHoldersAsUser(@NonNull String roleName, @NonNull UserHandle user) { - Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); - Objects.requireNonNull(user, "user cannot be null"); - try { - return mService.getRoleHoldersAsUser(roleName, user.getIdentifier()); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Add a specific application to the holders of a role. If the role is exclusive, the previous - * holder will be replaced. - * <p> - * <strong>Note:</strong> Using this API requires holding - * {@code android.permission.MANAGE_ROLE_HOLDERS} and if the user id is not the current user - * {@code android.permission.INTERACT_ACROSS_USERS_FULL}. - * - * @param roleName the name of the role to add the role holder for - * @param packageName the package name of the application to add to the role holders - * @param flags optional behavior flags - * @param user the user to add the role holder for - * @param executor the {@code Executor} to run the callback on. - * @param callback the callback for whether this call is successful - * - * @see #getRoleHoldersAsUser(String, UserHandle) - * @see #removeRoleHolderAsUser(String, String, int, UserHandle, Executor, Consumer) - * @see #clearRoleHoldersAsUser(String, int, UserHandle, Executor, Consumer) - * - * @hide - */ - @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) - @SystemApi - public void addRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName, - @ManageHoldersFlags int flags, @NonNull UserHandle user, - @CallbackExecutor @NonNull Executor executor, @NonNull Consumer<Boolean> callback) { - Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); - Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty"); - Objects.requireNonNull(user, "user cannot be null"); - Objects.requireNonNull(executor, "executor cannot be null"); - Objects.requireNonNull(callback, "callback cannot be null"); - try { - mService.addRoleHolderAsUser(roleName, packageName, flags, user.getIdentifier(), - createRemoteCallback(executor, callback)); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Remove a specific application from the holders of a role. - * <p> - * <strong>Note:</strong> Using this API requires holding - * {@code android.permission.MANAGE_ROLE_HOLDERS} and if the user id is not the current user - * {@code android.permission.INTERACT_ACROSS_USERS_FULL}. - * - * @param roleName the name of the role to remove the role holder for - * @param packageName the package name of the application to remove from the role holders - * @param flags optional behavior flags - * @param user the user to remove the role holder for - * @param executor the {@code Executor} to run the callback on. - * @param callback the callback for whether this call is successful - * - * @see #getRoleHoldersAsUser(String, UserHandle) - * @see #addRoleHolderAsUser(String, String, int, UserHandle, Executor, Consumer) - * @see #clearRoleHoldersAsUser(String, int, UserHandle, Executor, Consumer) - * - * @hide - */ - @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) - @SystemApi - public void removeRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName, - @ManageHoldersFlags int flags, @NonNull UserHandle user, - @CallbackExecutor @NonNull Executor executor, @NonNull Consumer<Boolean> callback) { - Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); - Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty"); - Objects.requireNonNull(user, "user cannot be null"); - Objects.requireNonNull(executor, "executor cannot be null"); - Objects.requireNonNull(callback, "callback cannot be null"); - try { - mService.removeRoleHolderAsUser(roleName, packageName, flags, user.getIdentifier(), - createRemoteCallback(executor, callback)); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Remove all holders of a role. - * <p> - * <strong>Note:</strong> Using this API requires holding - * {@code android.permission.MANAGE_ROLE_HOLDERS} and if the user id is not the current user - * {@code android.permission.INTERACT_ACROSS_USERS_FULL}. - * - * @param roleName the name of the role to remove role holders for - * @param flags optional behavior flags - * @param user the user to remove role holders for - * @param executor the {@code Executor} to run the callback on. - * @param callback the callback for whether this call is successful - * - * @see #getRoleHoldersAsUser(String, UserHandle) - * @see #addRoleHolderAsUser(String, String, int, UserHandle, Executor, Consumer) - * @see #removeRoleHolderAsUser(String, String, int, UserHandle, Executor, Consumer) - * - * @hide - */ - @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) - @SystemApi - public void clearRoleHoldersAsUser(@NonNull String roleName, @ManageHoldersFlags int flags, - @NonNull UserHandle user, @CallbackExecutor @NonNull Executor executor, - @NonNull Consumer<Boolean> callback) { - Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); - Objects.requireNonNull(user, "user cannot be null"); - Objects.requireNonNull(executor, "executor cannot be null"); - Objects.requireNonNull(callback, "callback cannot be null"); - try { - mService.clearRoleHoldersAsUser(roleName, flags, user.getIdentifier(), - createRemoteCallback(executor, callback)); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - @NonNull - private static RemoteCallback createRemoteCallback(@NonNull Executor executor, - @NonNull Consumer<Boolean> callback) { - return new RemoteCallback(result -> executor.execute(() -> { - boolean successful = result != null; - final long token = Binder.clearCallingIdentity(); - try { - callback.accept(successful); - } finally { - Binder.restoreCallingIdentity(token); - } - })); - } - - /** - * Add a listener to observe role holder changes - * <p> - * <strong>Note:</strong> Using this API requires holding - * {@code android.permission.OBSERVE_ROLE_HOLDERS} and if the user id is not the current user - * {@code android.permission.INTERACT_ACROSS_USERS_FULL}. - * - * @param executor the {@code Executor} to call the listener on. - * @param listener the listener to be added - * @param user the user to add the listener for - * - * @see #removeOnRoleHoldersChangedListenerAsUser(OnRoleHoldersChangedListener, UserHandle) - * - * @hide - */ - @RequiresPermission(Manifest.permission.OBSERVE_ROLE_HOLDERS) - @SystemApi - public void addOnRoleHoldersChangedListenerAsUser(@CallbackExecutor @NonNull Executor executor, - @NonNull OnRoleHoldersChangedListener listener, @NonNull UserHandle user) { - Objects.requireNonNull(executor, "executor cannot be null"); - Objects.requireNonNull(listener, "listener cannot be null"); - Objects.requireNonNull(user, "user cannot be null"); - int userId = user.getIdentifier(); - synchronized (mListenersLock) { - ArrayMap<OnRoleHoldersChangedListener, OnRoleHoldersChangedListenerDelegate> listeners = - mListeners.get(userId); - if (listeners == null) { - listeners = new ArrayMap<>(); - mListeners.put(userId, listeners); - } else { - if (listeners.containsKey(listener)) { - return; - } - } - OnRoleHoldersChangedListenerDelegate listenerDelegate = - new OnRoleHoldersChangedListenerDelegate(executor, listener); - try { - mService.addOnRoleHoldersChangedListenerAsUser(listenerDelegate, userId); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - listeners.put(listener, listenerDelegate); - } - } - - /** - * Remove a listener observing role holder changes - * <p> - * <strong>Note:</strong> Using this API requires holding - * {@code android.permission.OBSERVE_ROLE_HOLDERS} and if the user id is not the current user - * {@code android.permission.INTERACT_ACROSS_USERS_FULL}. - * - * @param listener the listener to be removed - * @param user the user to remove the listener for - * - * @see #addOnRoleHoldersChangedListenerAsUser(Executor, OnRoleHoldersChangedListener, - * UserHandle) - * - * @hide - */ - @RequiresPermission(Manifest.permission.OBSERVE_ROLE_HOLDERS) - @SystemApi - public void removeOnRoleHoldersChangedListenerAsUser( - @NonNull OnRoleHoldersChangedListener listener, @NonNull UserHandle user) { - Objects.requireNonNull(listener, "listener cannot be null"); - Objects.requireNonNull(user, "user cannot be null"); - int userId = user.getIdentifier(); - synchronized (mListenersLock) { - ArrayMap<OnRoleHoldersChangedListener, OnRoleHoldersChangedListenerDelegate> listeners = - mListeners.get(userId); - if (listeners == null) { - return; - } - OnRoleHoldersChangedListenerDelegate listenerDelegate = listeners.get(listener); - if (listenerDelegate == null) { - return; - } - try { - mService.removeOnRoleHoldersChangedListenerAsUser(listenerDelegate, - user.getIdentifier()); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - listeners.remove(listener); - if (listeners.isEmpty()) { - mListeners.remove(userId); - } - } - } - - /** - * Set the names of all the available roles. Should only be called from - * {@link android.app.role.RoleControllerService}. - * <p> - * <strong>Note:</strong> Using this API requires holding - * {@link #PERMISSION_MANAGE_ROLES_FROM_CONTROLLER}. - * - * @param roleNames the names of all the available roles - * - * @deprecated This is only usable by the role controller service, which is an internal - * implementation detail inside role. - * - * @hide - */ - @Deprecated - @RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER) - @SystemApi - public void setRoleNamesFromController(@NonNull List<String> roleNames) { - Objects.requireNonNull(roleNames, "roleNames cannot be null"); - try { - mService.setRoleNamesFromController(roleNames); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Add a specific application to the holders of a role, only modifying records inside - * {@link RoleManager}. Should only be called from - * {@link android.app.role.RoleControllerService}. - * <p> - * <strong>Note:</strong> Using this API requires holding - * {@link #PERMISSION_MANAGE_ROLES_FROM_CONTROLLER}. - * - * @param roleName the name of the role to add the role holder for - * @param packageName the package name of the application to add to the role holders - * - * @return whether the operation was successful, and will also be {@code true} if a matching - * role holder is already found. - * - * @see #getRoleHolders(String) - * @see #removeRoleHolderFromController(String, String) - * - * @deprecated This is only usable by the role controller service, which is an internal - * implementation detail inside role. - * - * @hide - */ - @Deprecated - @RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER) - @SystemApi - public boolean addRoleHolderFromController(@NonNull String roleName, - @NonNull String packageName) { - Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); - Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty"); - try { - return mService.addRoleHolderFromController(roleName, packageName); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Remove a specific application from the holders of a role, only modifying records inside - * {@link RoleManager}. Should only be called from - * {@link android.app.role.RoleControllerService}. - * <p> - * <strong>Note:</strong> Using this API requires holding - * {@link #PERMISSION_MANAGE_ROLES_FROM_CONTROLLER}. - * - * @param roleName the name of the role to remove the role holder for - * @param packageName the package name of the application to remove from the role holders - * - * @return whether the operation was successful, and will also be {@code true} if no matching - * role holder was found to remove. - * - * @see #getRoleHolders(String) - * @see #addRoleHolderFromController(String, String) - * - * @deprecated This is only usable by the role controller service, which is an internal - * implementation detail inside role. - * - * @hide - */ - @Deprecated - @RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER) - @SystemApi - public boolean removeRoleHolderFromController(@NonNull String roleName, - @NonNull String packageName) { - Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); - Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty"); - try { - return mService.removeRoleHolderFromController(roleName, packageName); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Returns the list of all roles that the given package is currently holding - * - * @param packageName the package name - * @return the list of role names - * - * @deprecated This is only usable by the role controller service, which is an internal - * implementation detail inside role. - * - * @hide - */ - @Deprecated - @NonNull - @RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER) - @SystemApi - public List<String> getHeldRolesFromController(@NonNull String packageName) { - Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty"); - try { - return mService.getHeldRolesFromController(packageName); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Get the role holder of {@link #ROLE_BROWSER} without requiring - * {@link Manifest.permission#OBSERVE_ROLE_HOLDERS}, as in - * {@link android.content.pm.PackageManager#getDefaultBrowserPackageNameAsUser(int)} - * - * @param userId the user ID - * @return the package name of the default browser, or {@code null} if none - * - * @hide - */ - @Nullable - @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) - public String getBrowserRoleHolder(@UserIdInt int userId) { - try { - return mService.getBrowserRoleHolder(userId); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Set the role holder of {@link #ROLE_BROWSER} requiring - * {@link Manifest.permission.SET_PREFERRED_APPLICATIONS} instead of - * {@link Manifest.permission#MANAGE_ROLE_HOLDERS}, as in - * {@link android.content.pm.PackageManager#setDefaultBrowserPackageNameAsUser(String, int)} - * - * @param packageName the package name of the default browser, or {@code null} if none - * @param userId the user ID - * @return whether the default browser was set successfully - * - * @hide - */ - @Nullable - @RequiresPermission(Manifest.permission.SET_PREFERRED_APPLICATIONS) - @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) - public boolean setBrowserRoleHolder(@Nullable String packageName, @UserIdInt int userId) { - try { - return mService.setBrowserRoleHolder(packageName, userId); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Allows getting the role holder for {@link #ROLE_SMS} without requiring - * {@link Manifest.permission#OBSERVE_ROLE_HOLDERS}, as in - * {@link android.provider.Telephony.Sms#getDefaultSmsPackage(Context)}. - * - * @param userId the user ID to get the default SMS package for - * @return the package name of the default SMS app, or {@code null} if none - * - * @hide - */ - @Nullable - @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) - public String getSmsRoleHolder(@UserIdInt int userId) { - try { - return mService.getSmsRoleHolder(userId); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Check whether a role should be visible to user. - * - * @param roleName name of the role to check for - * @param executor the executor to execute callback on - * @param callback the callback to receive whether the role should be visible to user - * - * @hide - */ - @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) - @SystemApi - public void isRoleVisible(@NonNull String roleName, - @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) { - getRoleControllerManager().isRoleVisible(roleName, executor, callback); - } - - /** - * Check whether an application is visible for a role. - * - * While an application can be qualified for a role, it can still stay hidden from user (thus - * not visible). If an application is visible for a role, we may show things related to the role - * for it, e.g. showing an entry pointing to the role settings in its application info page. - * - * @param roleName the name of the role to check for - * @param packageName the package name of the application to check for - * @param executor the executor to execute callback on - * @param callback the callback to receive whether the application is visible for the role - * - * @hide - */ - @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) - @SystemApi - public void isApplicationVisibleForRole(@NonNull String roleName, @NonNull String packageName, - @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) { - getRoleControllerManager().isApplicationVisibleForRole(roleName, packageName, executor, - callback); - } - - @NonNull - private RoleControllerManager getRoleControllerManager() { - synchronized (mRoleControllerManagerLock) { - if (mRoleControllerManager == null) { - mRoleControllerManager = new RoleControllerManager(mContext); - } - return mRoleControllerManager; - } - } - - private static class OnRoleHoldersChangedListenerDelegate - extends IOnRoleHoldersChangedListener.Stub { - - @NonNull - private final Executor mExecutor; - @NonNull - private final OnRoleHoldersChangedListener mListener; - - OnRoleHoldersChangedListenerDelegate(@NonNull Executor executor, - @NonNull OnRoleHoldersChangedListener listener) { - mExecutor = executor; - mListener = listener; - } - - @Override - public void onRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId) { - final long token = Binder.clearCallingIdentity(); - try { - mExecutor.execute(() -> - mListener.onRoleHoldersChanged(roleName, UserHandle.of(userId))); - } finally { - Binder.restoreCallingIdentity(token); - } - } - } -} diff --git a/apex/permission/framework-s/java/android/app/role/TEST_MAPPING b/apex/permission/framework-s/java/android/app/role/TEST_MAPPING deleted file mode 100644 index f8f140dd716e..000000000000 --- a/apex/permission/framework-s/java/android/app/role/TEST_MAPPING +++ /dev/null @@ -1,12 +0,0 @@ -{ - "presubmit": [ - { - "name": "CtsRoleTestCases", - "options": [ - { - "include-filter": "android.app.role.cts.RoleManagerTest" - } - ] - } - ] -} diff --git a/apex/permission/framework/Android.bp b/apex/permission/framework/Android.bp deleted file mode 100644 index 52a61674a596..000000000000 --- a/apex/permission/framework/Android.bp +++ /dev/null @@ -1,50 +0,0 @@ -// 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. - -filegroup { - name: "framework-permission-sources", - srcs: [ - "java/**/*.java", - "java/**/*.aidl", - ], - path: "java", - visibility: ["//frameworks/base"], -} - -java_sdk_library { - name: "framework-permission", - defaults: ["framework-module-defaults"], - - // Restrict access to implementation library. - impl_library_visibility: [ - "//frameworks/base/apex/permission:__subpackages__", - "//packages/modules/Permission:__subpackages__", - ], - - srcs: [ - ":framework-permission-sources", - ], - - apex_available: [ - "com.android.permission", - "test_com.android.permission", - ], - min_sdk_version: "30", - permitted_packages: [ - "android.permission", - "android.app.role", - ], - hostdex: true, - installable: true, -} diff --git a/apex/permission/framework/api/current.txt b/apex/permission/framework/api/current.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/apex/permission/framework/api/current.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/apex/permission/framework/api/module-lib-current.txt b/apex/permission/framework/api/module-lib-current.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/apex/permission/framework/api/module-lib-current.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/apex/permission/framework/api/module-lib-removed.txt b/apex/permission/framework/api/module-lib-removed.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/apex/permission/framework/api/module-lib-removed.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/apex/permission/framework/api/removed.txt b/apex/permission/framework/api/removed.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/apex/permission/framework/api/removed.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/apex/permission/framework/api/system-current.txt b/apex/permission/framework/api/system-current.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/apex/permission/framework/api/system-current.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/apex/permission/framework/api/system-removed.txt b/apex/permission/framework/api/system-removed.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/apex/permission/framework/api/system-removed.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/apex/permission/jarjar-rules.txt b/apex/permission/jarjar-rules.txt deleted file mode 100644 index 4729ed13dc4c..000000000000 --- a/apex/permission/jarjar-rules.txt +++ /dev/null @@ -1,5 +0,0 @@ -rule android.os.HandlerExecutor com.android.permission.jarjar.@0 -rule android.util.IndentingPrintWriter com.android.permission.jarjar.@0 -rule com.android.internal.** com.android.permission.jarjar.@0 -rule com.android.modules.** com.android.permission.jarjar.@0 -rule com.android.role.*Proto com.android.permission.jarjar.@0 diff --git a/apex/permission/service/Android.bp b/apex/permission/service/Android.bp deleted file mode 100644 index d0fc5b9d7c14..000000000000 --- a/apex/permission/service/Android.bp +++ /dev/null @@ -1,106 +0,0 @@ -// 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. - -filegroup { - name: "service-permission-sources", - srcs: [ - "java/**/*.java", - ], - path: "java", - visibility: ["//frameworks/base/services"], -} - -filegroup { - name: "service-permission-protos", - srcs: [ - "proto/**/*.proto", - ], - visibility: ["//frameworks/base"], -} - -gensrcs { - name: "service-permission-javastream-protos", - depfile: true, - - tools: [ - "aprotoc", - "protoc-gen-javastream", - "soong_zip", - ], - - cmd: "mkdir -p $(genDir)/$(in) " + - "&& $(location aprotoc) " + - " --plugin=$(location protoc-gen-javastream) " + - " --dependency_out=$(depfile) " + - " --javastream_out=$(genDir)/$(in) " + - " -Iexternal/protobuf/src " + - " -I . " + - " $(in) " + - "&& $(location soong_zip) -jar -o $(out) -C $(genDir)/$(in) -D $(genDir)/$(in)", - - srcs: [ - ":service-permission-protos", - ], - output_extension: "srcjar", -} - -java_library { - name: "service-permission-shared", - srcs: [":service-permission-shared-srcs"], - libs: [ - "framework-annotations-lib", - "framework-permission-s-shared", - ], - apex_available: [ - "com.android.permission", - "test_com.android.permission", - ], - installable: false, - min_sdk_version: "30", - sdk_version: "system_server_current", -} - -java_sdk_library { - name: "service-permission", - defaults: ["framework-system-server-module-defaults"], - impl_library_visibility: [ - "//frameworks/base/apex/permission/tests", - "//frameworks/base/services/tests/mockingservicestests", - "//frameworks/base/services/tests/servicestests", - "//packages/modules/Permission/tests", - ], - srcs: [ - ":service-permission-sources", - ":service-permission-javastream-protos", - ], - libs: [ - "framework-permission", - "framework-permission-s.impl", - "framework-permission-s-shared", - ], - static_libs: [ - "modules-utils-os", - "service-permission-shared", - ], - jarjar_rules: ":permission-jarjar-rules", - min_sdk_version: "30", - sdk_version: "system_server_current", - apex_available: [ - "com.android.permission", - "test_com.android.permission", - ], - installable: true, - // We don't have last-api tracking files for the public part of this jar's API. - unsafe_ignore_missing_latest_api: true, -} diff --git a/apex/permission/service/api/current.txt b/apex/permission/service/api/current.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/apex/permission/service/api/current.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/apex/permission/service/api/removed.txt b/apex/permission/service/api/removed.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/apex/permission/service/api/removed.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/apex/permission/service/api/system-server-current.txt b/apex/permission/service/api/system-server-current.txt deleted file mode 100644 index b1869c2c731d..000000000000 --- a/apex/permission/service/api/system-server-current.txt +++ /dev/null @@ -1,54 +0,0 @@ -// Signature format: 2.0 -package com.android.permission.persistence { - - public interface RuntimePermissionsPersistence { - method @NonNull public static com.android.permission.persistence.RuntimePermissionsPersistence createInstance(); - method public void deleteForUser(@NonNull android.os.UserHandle); - method @Nullable public com.android.permission.persistence.RuntimePermissionsState readForUser(@NonNull android.os.UserHandle); - method public void writeForUser(@NonNull com.android.permission.persistence.RuntimePermissionsState, @NonNull android.os.UserHandle); - } - - public final class RuntimePermissionsState { - ctor public RuntimePermissionsState(int, @Nullable String, @NonNull java.util.Map<java.lang.String,java.util.List<com.android.permission.persistence.RuntimePermissionsState.PermissionState>>, @NonNull java.util.Map<java.lang.String,java.util.List<com.android.permission.persistence.RuntimePermissionsState.PermissionState>>); - method @Nullable public String getFingerprint(); - method @NonNull public java.util.Map<java.lang.String,java.util.List<com.android.permission.persistence.RuntimePermissionsState.PermissionState>> getPackagePermissions(); - method @NonNull public java.util.Map<java.lang.String,java.util.List<com.android.permission.persistence.RuntimePermissionsState.PermissionState>> getSharedUserPermissions(); - method public int getVersion(); - field public static final int NO_VERSION = -1; // 0xffffffff - } - - public static final class RuntimePermissionsState.PermissionState { - ctor public RuntimePermissionsState.PermissionState(@NonNull String, boolean, int); - method public int getFlags(); - method @NonNull public String getName(); - method public boolean isGranted(); - } - -} - -package com.android.role { - - public interface RoleManagerLocal { - method @NonNull public java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getRolesAndHolders(int); - } - -} - -package com.android.role.persistence { - - public interface RolesPersistence { - method @NonNull public static com.android.role.persistence.RolesPersistence createInstance(); - method public void deleteForUser(@NonNull android.os.UserHandle); - method @Nullable public com.android.role.persistence.RolesState readForUser(@NonNull android.os.UserHandle); - method public void writeForUser(@NonNull com.android.role.persistence.RolesState, @NonNull android.os.UserHandle); - } - - public final class RolesState { - ctor public RolesState(int, @Nullable String, @NonNull java.util.Map<java.lang.String,java.util.Set<java.lang.String>>); - method @Nullable public String getPackagesHash(); - method @NonNull public java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getRoles(); - method public int getVersion(); - } - -} - diff --git a/apex/permission/service/api/system-server-removed.txt b/apex/permission/service/api/system-server-removed.txt deleted file mode 100644 index d802177e249b..000000000000 --- a/apex/permission/service/api/system-server-removed.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/apex/permission/service/java/com/android/permission/compat/UserHandleCompat.java b/apex/permission/service/java/com/android/permission/compat/UserHandleCompat.java deleted file mode 100644 index 7c711d301f00..000000000000 --- a/apex/permission/service/java/com/android/permission/compat/UserHandleCompat.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2021 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.permission.compat; - -import android.annotation.UserIdInt; -import android.os.UserHandle; - -/** - * Helper for accessing features in {@link UserHandle}. - */ -public final class UserHandleCompat { - /** - * A user ID to indicate all users on the device. - */ - public static final int USER_ALL = UserHandle.ALL.getIdentifier(); - - /** - * A user ID to indicate the "system" user of the device. - */ - public static final int USER_SYSTEM = UserHandle.SYSTEM.getIdentifier(); - - private UserHandleCompat() {} - - /** - * Get the user ID of a given UID. - * - * @param uid the UID - * @return the user ID - */ - @UserIdInt - public static int getUserId(int uid) { - return UserHandle.getUserHandleForUid(uid).getIdentifier(); - } -} diff --git a/apex/permission/service/java/com/android/permission/compat/package-info.java b/apex/permission/service/java/com/android/permission/compat/package-info.java deleted file mode 100644 index c89cc8eabb92..000000000000 --- a/apex/permission/service/java/com/android/permission/compat/package-info.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2021 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. - */ - -/** - * @hide - * TODO(b/146466118) remove this javadoc tag - */ -@android.annotation.Hide -package com.android.permission.compat; diff --git a/apex/permission/service/java/com/android/permission/persistence/IoUtils.java b/apex/permission/service/java/com/android/permission/persistence/IoUtils.java deleted file mode 100644 index 569a78c0ab41..000000000000 --- a/apex/permission/service/java/com/android/permission/persistence/IoUtils.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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.permission.persistence; - -import android.annotation.NonNull; - -/** - * Utility class for IO. - * - * @hide - */ -public class IoUtils { - - private IoUtils() {} - - /** - * Close 'closeable' ignoring any exceptions. - */ - public static void closeQuietly(@NonNull AutoCloseable closeable) { - try { - closeable.close(); - } catch (Exception ignored) { - // Ignored. - } - } -} diff --git a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java deleted file mode 100644 index aedba290db1f..000000000000 --- a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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.permission.persistence; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.SystemApi; -import android.annotation.SystemApi.Client; -import android.os.UserHandle; - -/** - * Persistence for runtime permissions. - * - * TODO(b/147914847): Remove @hide when it becomes the default. - * @hide - */ -@SystemApi(client = Client.SYSTEM_SERVER) -public interface RuntimePermissionsPersistence { - - /** - * Read the runtime permissions from persistence. - * - * This will perform I/O operations synchronously. - * - * @param user the user to read for - * @return the runtime permissions read - */ - @Nullable - RuntimePermissionsState readForUser(@NonNull UserHandle user); - - /** - * Write the runtime permissions to persistence. - * - * This will perform I/O operations synchronously. - * - * @param runtimePermissions the runtime permissions to write - * @param user the user to write for - */ - void writeForUser(@NonNull RuntimePermissionsState runtimePermissions, - @NonNull UserHandle user); - - /** - * Delete the runtime permissions from persistence. - * - * This will perform I/O operations synchronously. - * - * @param user the user to delete for - */ - void deleteForUser(@NonNull UserHandle user); - - /** - * Create a new instance of {@link RuntimePermissionsPersistence} implementation. - * - * @return the new instance. - */ - @NonNull - static RuntimePermissionsPersistence createInstance() { - return new RuntimePermissionsPersistenceImpl(); - } -} diff --git a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java deleted file mode 100644 index e43f59a3377a..000000000000 --- a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java +++ /dev/null @@ -1,265 +0,0 @@ -/* - * 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.permission.persistence; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.ApexEnvironment; -import android.content.pm.PackageManager; -import android.os.UserHandle; -import android.util.ArrayMap; -import android.util.AtomicFile; -import android.util.Log; -import android.util.Xml; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -/** - * Persistence implementation for runtime permissions. - * - * TODO(b/147914847): Remove @hide when it becomes the default. - * @hide - */ -public class RuntimePermissionsPersistenceImpl implements RuntimePermissionsPersistence { - - private static final String LOG_TAG = RuntimePermissionsPersistenceImpl.class.getSimpleName(); - - private static final String APEX_MODULE_NAME = "com.android.permission"; - - private static final String RUNTIME_PERMISSIONS_FILE_NAME = "runtime-permissions.xml"; - - private static final String TAG_PACKAGE = "package"; - private static final String TAG_PERMISSION = "permission"; - private static final String TAG_RUNTIME_PERMISSIONS = "runtime-permissions"; - private static final String TAG_SHARED_USER = "shared-user"; - - private static final String ATTRIBUTE_FINGERPRINT = "fingerprint"; - private static final String ATTRIBUTE_FLAGS = "flags"; - private static final String ATTRIBUTE_GRANTED = "granted"; - private static final String ATTRIBUTE_NAME = "name"; - private static final String ATTRIBUTE_VERSION = "version"; - - @Nullable - @Override - public RuntimePermissionsState readForUser(@NonNull UserHandle user) { - File file = getFile(user); - try (FileInputStream inputStream = new AtomicFile(file).openRead()) { - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(inputStream, null); - return parseXml(parser); - } catch (FileNotFoundException e) { - Log.i(LOG_TAG, "runtime-permissions.xml not found"); - return null; - } catch (XmlPullParserException | IOException e) { - throw new IllegalStateException("Failed to read runtime-permissions.xml: " + file , e); - } - } - - @NonNull - private static RuntimePermissionsState parseXml(@NonNull XmlPullParser parser) - throws IOException, XmlPullParserException { - int type; - int depth; - int innerDepth = parser.getDepth() + 1; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT - && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) { - if (depth > innerDepth || type != XmlPullParser.START_TAG) { - continue; - } - - if (parser.getName().equals(TAG_RUNTIME_PERMISSIONS)) { - return parseRuntimePermissions(parser); - } - } - throw new IllegalStateException("Missing <" + TAG_RUNTIME_PERMISSIONS - + "> in runtime-permissions.xml"); - } - - @NonNull - private static RuntimePermissionsState parseRuntimePermissions(@NonNull XmlPullParser parser) - throws IOException, XmlPullParserException { - String versionValue = parser.getAttributeValue(null, ATTRIBUTE_VERSION); - int version = versionValue != null ? Integer.parseInt(versionValue) - : RuntimePermissionsState.NO_VERSION; - String fingerprint = parser.getAttributeValue(null, ATTRIBUTE_FINGERPRINT); - - Map<String, List<RuntimePermissionsState.PermissionState>> packagePermissions = - new ArrayMap<>(); - Map<String, List<RuntimePermissionsState.PermissionState>> sharedUserPermissions = - new ArrayMap<>(); - int type; - int depth; - int innerDepth = parser.getDepth() + 1; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT - && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) { - if (depth > innerDepth || type != XmlPullParser.START_TAG) { - continue; - } - - switch (parser.getName()) { - case TAG_PACKAGE: { - String packageName = parser.getAttributeValue(null, ATTRIBUTE_NAME); - List<RuntimePermissionsState.PermissionState> permissions = parsePermissions( - parser); - packagePermissions.put(packageName, permissions); - break; - } - case TAG_SHARED_USER: { - String sharedUserName = parser.getAttributeValue(null, ATTRIBUTE_NAME); - List<RuntimePermissionsState.PermissionState> permissions = parsePermissions( - parser); - sharedUserPermissions.put(sharedUserName, permissions); - break; - } - } - } - - return new RuntimePermissionsState(version, fingerprint, packagePermissions, - sharedUserPermissions); - } - - @NonNull - private static List<RuntimePermissionsState.PermissionState> parsePermissions( - @NonNull XmlPullParser parser) throws IOException, XmlPullParserException { - List<RuntimePermissionsState.PermissionState> permissions = new ArrayList<>(); - int type; - int depth; - int innerDepth = parser.getDepth() + 1; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT - && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) { - if (depth > innerDepth || type != XmlPullParser.START_TAG) { - continue; - } - - if (parser.getName().equals(TAG_PERMISSION)) { - String name = parser.getAttributeValue(null, ATTRIBUTE_NAME); - boolean granted = Boolean.parseBoolean(parser.getAttributeValue(null, - ATTRIBUTE_GRANTED)); - int flags = Integer.parseInt(parser.getAttributeValue(null, - ATTRIBUTE_FLAGS), 16); - RuntimePermissionsState.PermissionState permission = - new RuntimePermissionsState.PermissionState(name, granted, flags); - permissions.add(permission); - } - } - return permissions; - } - - @Override - public void writeForUser(@NonNull RuntimePermissionsState runtimePermissions, - @NonNull UserHandle user) { - File file = getFile(user); - AtomicFile atomicFile = new AtomicFile(file); - FileOutputStream outputStream = null; - try { - outputStream = atomicFile.startWrite(); - - XmlSerializer serializer = Xml.newSerializer(); - serializer.setOutput(outputStream, StandardCharsets.UTF_8.name()); - serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); - serializer.startDocument(null, true); - - serializeRuntimePermissions(serializer, runtimePermissions); - - serializer.endDocument(); - atomicFile.finishWrite(outputStream); - } catch (Exception e) { - Log.wtf(LOG_TAG, "Failed to write runtime-permissions.xml, restoring backup: " + file, - e); - atomicFile.failWrite(outputStream); - } finally { - IoUtils.closeQuietly(outputStream); - } - } - - private static void serializeRuntimePermissions(@NonNull XmlSerializer serializer, - @NonNull RuntimePermissionsState runtimePermissions) throws IOException { - serializer.startTag(null, TAG_RUNTIME_PERMISSIONS); - - int version = runtimePermissions.getVersion(); - serializer.attribute(null, ATTRIBUTE_VERSION, Integer.toString(version)); - String fingerprint = runtimePermissions.getFingerprint(); - if (fingerprint != null) { - serializer.attribute(null, ATTRIBUTE_FINGERPRINT, fingerprint); - } - - for (Map.Entry<String, List<RuntimePermissionsState.PermissionState>> entry - : runtimePermissions.getPackagePermissions().entrySet()) { - String packageName = entry.getKey(); - List<RuntimePermissionsState.PermissionState> permissions = entry.getValue(); - - serializer.startTag(null, TAG_PACKAGE); - serializer.attribute(null, ATTRIBUTE_NAME, packageName); - serializePermissions(serializer, permissions); - serializer.endTag(null, TAG_PACKAGE); - } - - for (Map.Entry<String, List<RuntimePermissionsState.PermissionState>> entry - : runtimePermissions.getSharedUserPermissions().entrySet()) { - String sharedUserName = entry.getKey(); - List<RuntimePermissionsState.PermissionState> permissions = entry.getValue(); - - serializer.startTag(null, TAG_SHARED_USER); - serializer.attribute(null, ATTRIBUTE_NAME, sharedUserName); - serializePermissions(serializer, permissions); - serializer.endTag(null, TAG_SHARED_USER); - } - - serializer.endTag(null, TAG_RUNTIME_PERMISSIONS); - } - - private static void serializePermissions(@NonNull XmlSerializer serializer, - @NonNull List<RuntimePermissionsState.PermissionState> permissions) throws IOException { - int permissionsSize = permissions.size(); - for (int i = 0; i < permissionsSize; i++) { - RuntimePermissionsState.PermissionState permissionState = permissions.get(i); - - serializer.startTag(null, TAG_PERMISSION); - serializer.attribute(null, ATTRIBUTE_NAME, permissionState.getName()); - serializer.attribute(null, ATTRIBUTE_GRANTED, Boolean.toString( - permissionState.isGranted() && (permissionState.getFlags() - & PackageManager.FLAG_PERMISSION_ONE_TIME) == 0)); - serializer.attribute(null, ATTRIBUTE_FLAGS, Integer.toHexString( - permissionState.getFlags())); - serializer.endTag(null, TAG_PERMISSION); - } - } - - @Override - public void deleteForUser(@NonNull UserHandle user) { - getFile(user).delete(); - } - - @NonNull - private static File getFile(@NonNull UserHandle user) { - ApexEnvironment apexEnvironment = ApexEnvironment.getApexEnvironment(APEX_MODULE_NAME); - File dataDirectory = apexEnvironment.getDeviceProtectedDataDirForUser(user); - return new File(dataDirectory, RUNTIME_PERMISSIONS_FILE_NAME); - } -} diff --git a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsState.java b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsState.java deleted file mode 100644 index c6bfc6d32989..000000000000 --- a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsState.java +++ /dev/null @@ -1,222 +0,0 @@ -/* - * 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.permission.persistence; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.SystemApi; -import android.annotation.SystemApi.Client; - -import java.util.List; -import java.util.Map; -import java.util.Objects; - -/** - * State of all runtime permissions. - * - * TODO(b/147914847): Remove @hide when it becomes the default. - * @hide - */ -@SystemApi(client = Client.SYSTEM_SERVER) -public final class RuntimePermissionsState { - - /** - * Special value for {@link #mVersion} to indicate that no version was read. - */ - public static final int NO_VERSION = -1; - - /** - * The version of the runtime permissions. - */ - private final int mVersion; - - /** - * The fingerprint of the runtime permissions. - */ - @Nullable - private final String mFingerprint; - - /** - * The runtime permissions by packages. - */ - @NonNull - private final Map<String, List<PermissionState>> mPackagePermissions; - - /** - * The runtime permissions by shared users. - */ - @NonNull - private final Map<String, List<PermissionState>> mSharedUserPermissions; - - /** - * Create a new instance of this class. - * - * @param version the version of the runtime permissions - * @param fingerprint the fingerprint of the runtime permissions - * @param packagePermissions the runtime permissions by packages - * @param sharedUserPermissions the runtime permissions by shared users - */ - public RuntimePermissionsState(int version, @Nullable String fingerprint, - @NonNull Map<String, List<PermissionState>> packagePermissions, - @NonNull Map<String, List<PermissionState>> sharedUserPermissions) { - mVersion = version; - mFingerprint = fingerprint; - mPackagePermissions = packagePermissions; - mSharedUserPermissions = sharedUserPermissions; - } - - /** - * Get the version of the runtime permissions. - * - * @return the version of the runtime permissions - */ - public int getVersion() { - return mVersion; - } - - /** - * Get the fingerprint of the runtime permissions. - * - * @return the fingerprint of the runtime permissions - */ - @Nullable - public String getFingerprint() { - return mFingerprint; - } - - /** - * Get the runtime permissions by packages. - * - * @return the runtime permissions by packages - */ - @NonNull - public Map<String, List<PermissionState>> getPackagePermissions() { - return mPackagePermissions; - } - - /** - * Get the runtime permissions by shared users. - * - * @return the runtime permissions by shared users - */ - @NonNull - public Map<String, List<PermissionState>> getSharedUserPermissions() { - return mSharedUserPermissions; - } - - @Override - public boolean equals(Object object) { - if (this == object) { - return true; - } - if (object == null || getClass() != object.getClass()) { - return false; - } - RuntimePermissionsState that = (RuntimePermissionsState) object; - return mVersion == that.mVersion - && Objects.equals(mFingerprint, that.mFingerprint) - && Objects.equals(mPackagePermissions, that.mPackagePermissions) - && Objects.equals(mSharedUserPermissions, that.mSharedUserPermissions); - } - - @Override - public int hashCode() { - return Objects.hash(mVersion, mFingerprint, mPackagePermissions, mSharedUserPermissions); - } - - /** - * State of a single permission. - */ - public static final class PermissionState { - - /** - * The name of the permission. - */ - @NonNull - private final String mName; - - /** - * Whether the permission is granted. - */ - private final boolean mGranted; - - /** - * The flags of the permission. - */ - private final int mFlags; - - /** - * Create a new instance of this class. - * - * @param name the name of the permission - * @param granted whether the permission is granted - * @param flags the flags of the permission - */ - public PermissionState(@NonNull String name, boolean granted, int flags) { - mName = name; - mGranted = granted; - mFlags = flags; - } - - /** - * Get the name of the permission. - * - * @return the name of the permission - */ - @NonNull - public String getName() { - return mName; - } - - /** - * Get whether the permission is granted. - * - * @return whether the permission is granted - */ - public boolean isGranted() { - return mGranted; - } - - /** - * Get the flags of the permission. - * - * @return the flags of the permission - */ - public int getFlags() { - return mFlags; - } - - @Override - public boolean equals(Object object) { - if (this == object) { - return true; - } - if (object == null || getClass() != object.getClass()) { - return false; - } - PermissionState that = (PermissionState) object; - return mGranted == that.mGranted - && mFlags == that.mFlags - && Objects.equals(mName, that.mName); - } - - @Override - public int hashCode() { - return Objects.hash(mName, mGranted, mFlags); - } - } -} diff --git a/apex/permission/service/java/com/android/permission/util/ArrayUtils.java b/apex/permission/service/java/com/android/permission/util/ArrayUtils.java deleted file mode 100644 index 5d5cd78201bd..000000000000 --- a/apex/permission/service/java/com/android/permission/util/ArrayUtils.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2021 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.permission.util; - -import android.annotation.Nullable; - -import java.util.Objects; - -/** - * Array utilities. - */ -public final class ArrayUtils { - private ArrayUtils() {} - - /** - * @see java.util.List#contains(Object) - */ - public static <T> boolean contains(@Nullable T[] array, T value) { - return indexOf(array, value) != -1; - } - - /** - * Get the first element of an array, or {@code null} if none. - * - * @param array the array - * @param <T> the type of the elements of the array - * @return first element of an array, or {@code null} if none - */ - public static <T> T firstOrNull(@Nullable T[] array) { - return !isEmpty(array) ? array[0] : null; - } - - /** - * @see java.util.List#indexOf(Object) - */ - public static <T> int indexOf(@Nullable T[] array, T value) { - if (array == null) { - return -1; - } - final int length = array.length; - for (int i = 0; i < length; i++) { - final T element = array[i]; - if (Objects.equals(element, value)) { - return i; - } - } - return -1; - } - - /** - * @see java.util.List#isEmpty() - */ - public static <T> boolean isEmpty(@Nullable T[] array) { - return array == null || array.length == 0; - } -} diff --git a/apex/permission/service/java/com/android/permission/util/BackgroundThread.java b/apex/permission/service/java/com/android/permission/util/BackgroundThread.java deleted file mode 100644 index 7308eec98500..000000000000 --- a/apex/permission/service/java/com/android/permission/util/BackgroundThread.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2021 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.permission.util; - -import android.annotation.NonNull; -import android.os.Handler; -import android.os.HandlerExecutor; -import android.os.HandlerThread; - -import com.android.internal.annotations.GuardedBy; - -import java.util.concurrent.Executor; - -/** - * Shared singleton background thread. - */ -public class BackgroundThread extends HandlerThread { - private static final Object sLock = new Object(); - - @GuardedBy("sLock") - private static BackgroundThread sInstance; - @GuardedBy("sLock") - private static Handler sHandler; - @GuardedBy("sLock") - private static Executor sExecutor; - - private BackgroundThread() { - super(BackgroundThread.class.getName()); - } - - @GuardedBy("sLock") - private static void ensureInstanceLocked() { - if (sInstance == null) { - sInstance = new BackgroundThread(); - sInstance.start(); - sHandler = new Handler(sInstance.getLooper()); - sExecutor = new HandlerExecutor(sHandler); - } - } - - /** - * Get the singleton instance of thi class. - * - * @return the singleton instance of thi class - */ - @NonNull - public static BackgroundThread get() { - synchronized (sLock) { - ensureInstanceLocked(); - return sInstance; - } - } - - /** - * Get the {@link Handler} for this thread. - * - * @return the {@link Handler} for this thread. - */ - @NonNull - public static Handler getHandler() { - synchronized (sLock) { - ensureInstanceLocked(); - return sHandler; - } - } - - /** - * Get the {@link Executor} for this thread. - * - * @return the {@link Executor} for this thread. - */ - @NonNull - public static Executor getExecutor() { - synchronized (sLock) { - ensureInstanceLocked(); - return sExecutor; - } - } -} diff --git a/apex/permission/service/java/com/android/permission/util/CollectionUtils.java b/apex/permission/service/java/com/android/permission/util/CollectionUtils.java deleted file mode 100644 index ea4952404179..000000000000 --- a/apex/permission/service/java/com/android/permission/util/CollectionUtils.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2021 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.permission.util; - -import android.annotation.Nullable; - -import java.util.Collection; -import java.util.List; -import java.util.Map; - -/** - * {@link Collection} utilities. - */ -public class CollectionUtils { - private CollectionUtils() {} - - /** - * Get the first element of a {@link List}, or {@code null} if none. - * - * @param list the {@link List}, or {@code null} - * @param <E> the element type of the {@link List} - * @return the first element of the {@link List}, or {@code 0} if none - */ - @Nullable - public static <E> E firstOrNull(@Nullable List<E> list) { - return !isEmpty(list) ? list.get(0) : null; - } - - /** - * Check whether a {@link Collection} is empty or {@code null}. - * - * @param collection the {@link Collection}, or {@code null} - * @return whether the {@link Collection} is empty or {@code null} - */ - public static boolean isEmpty(@Nullable Collection<?> collection) { - return collection == null || collection.isEmpty(); - } - - /** - * Get the size of a {@link Collection}, or {@code 0} if {@code null}. - * - * @param collection the {@link Collection}, or {@code null} - * @return the size of the {@link Collection}, or {@code 0} if {@code null} - */ - public static int size(@Nullable Collection<?> collection) { - return collection != null ? collection.size() : 0; - } - - /** - * Get the size of a {@link Map}, or {@code 0} if {@code null}. - * - * @param collection the {@link Map}, or {@code null} - * @return the size of the {@link Map}, or {@code 0} if {@code null} - */ - public static int size(@Nullable Map<?, ?> collection) { - return collection != null ? collection.size() : 0; - } -} diff --git a/apex/permission/service/java/com/android/permission/util/ForegroundThread.java b/apex/permission/service/java/com/android/permission/util/ForegroundThread.java deleted file mode 100644 index cd6f6057030b..000000000000 --- a/apex/permission/service/java/com/android/permission/util/ForegroundThread.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2021 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.permission.util; - -import android.annotation.NonNull; -import android.os.Handler; -import android.os.HandlerExecutor; -import android.os.HandlerThread; - -import com.android.internal.annotations.GuardedBy; - -import java.util.concurrent.Executor; - -/** - * Shared singleton foreground thread. - */ -public class ForegroundThread extends HandlerThread { - private static final Object sLock = new Object(); - - @GuardedBy("sLock") - private static ForegroundThread sInstance; - @GuardedBy("sLock") - private static Handler sHandler; - @GuardedBy("sLock") - private static Executor sExecutor; - - private ForegroundThread() { - super(ForegroundThread.class.getName()); - } - - @GuardedBy("sLock") - private static void ensureInstanceLocked() { - if (sInstance == null) { - sInstance = new ForegroundThread(); - sInstance.start(); - sHandler = new Handler(sInstance.getLooper()); - sExecutor = new HandlerExecutor(sHandler); - } - } - - /** - * Get the singleton instance of thi class. - * - * @return the singleton instance of thi class - */ - @NonNull - public static ForegroundThread get() { - synchronized (sLock) { - ensureInstanceLocked(); - return sInstance; - } - } - - /** - * Get the {@link Handler} for this thread. - * - * @return the {@link Handler} for this thread. - */ - @NonNull - public static Handler getHandler() { - synchronized (sLock) { - ensureInstanceLocked(); - return sHandler; - } - } - - /** - * Get the {@link Executor} for this thread. - * - * @return the {@link Executor} for this thread. - */ - @NonNull - public static Executor getExecutor() { - synchronized (sLock) { - ensureInstanceLocked(); - return sExecutor; - } - } -} diff --git a/apex/permission/service/java/com/android/permission/util/ThrottledRunnable.java b/apex/permission/service/java/com/android/permission/util/ThrottledRunnable.java deleted file mode 100644 index ba1c3939f73e..000000000000 --- a/apex/permission/service/java/com/android/permission/util/ThrottledRunnable.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.permission.util; - -import android.annotation.NonNull; -import android.os.Handler; -import android.os.SystemClock; - -import com.android.internal.annotations.GuardedBy; - -/** - * A throttled runnable that can wrap around a runnable and throttle calls to its run(). - * - * The throttling logic makes sure that the original runnable will be called only after the - * specified interval passes since the last actual call. The first call in a while (after the - * specified interval passes since the last actual call) will always result in the original runnable - * being called immediately, and then subsequent calls will start to be throttled. It is guaranteed - * that any call to this throttled runnable will always result in the original runnable being called - * afterwards, within the specified interval. - */ -public class ThrottledRunnable implements Runnable { - - @NonNull - private final Handler mHandler; - private final long mIntervalMillis; - @NonNull - private final Runnable mRunnable; - - @NonNull - private final Object mLock = new Object(); - - @GuardedBy("mLock") - private long mScheduledUptimeMillis; - - public ThrottledRunnable(@NonNull Handler handler, long intervalMillis, - @NonNull Runnable runnable) { - mHandler = handler; - mIntervalMillis = intervalMillis; - mRunnable = runnable; - } - - @Override - public void run() { - synchronized (mLock) { - if (mHandler.hasCallbacks(mRunnable)) { - // We have a scheduled runnable. - return; - } - long currentUptimeMillis = SystemClock.uptimeMillis(); - if (mScheduledUptimeMillis == 0 - || currentUptimeMillis > mScheduledUptimeMillis + mIntervalMillis) { - // First time in a while, schedule immediately. - mScheduledUptimeMillis = currentUptimeMillis; - } else { - // We were scheduled not long ago, so schedule with delay for throttling. - mScheduledUptimeMillis = mScheduledUptimeMillis + mIntervalMillis; - } - mHandler.postAtTime(mRunnable, mScheduledUptimeMillis); - } - } -} diff --git a/apex/permission/service/java/com/android/permission/util/package-info.java b/apex/permission/service/java/com/android/permission/util/package-info.java deleted file mode 100644 index 18fada534424..000000000000 --- a/apex/permission/service/java/com/android/permission/util/package-info.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2021 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. - */ - -/** - * @hide - * TODO(b/146466118) remove this javadoc tag - */ -@android.annotation.Hide -package com.android.permission.util; diff --git a/apex/permission/service/java/com/android/role/RoleManagerLocal.java b/apex/permission/service/java/com/android/role/RoleManagerLocal.java deleted file mode 100644 index e243e2e0db66..000000000000 --- a/apex/permission/service/java/com/android/role/RoleManagerLocal.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.role; - -import android.annotation.NonNull; -import android.annotation.SystemApi; -import android.annotation.UserIdInt; - -import java.util.Map; -import java.util.Set; - -/** - * Internal calls into {@link RoleService}. - * - * @hide - */ -@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) -public interface RoleManagerLocal { - /** - * Get all roles and their holders. - * - * @param userId The user to query to roles for - * - * @return The roles and their holders - */ - @NonNull - Map<String, Set<String>> getRolesAndHolders(@UserIdInt int userId); -} diff --git a/apex/permission/service/java/com/android/role/RoleService.java b/apex/permission/service/java/com/android/role/RoleService.java deleted file mode 100644 index 5f7eb22a42a7..000000000000 --- a/apex/permission/service/java/com/android/role/RoleService.java +++ /dev/null @@ -1,736 +0,0 @@ -/* - * 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. - */ - -package com.android.role; - -import android.Manifest; -import android.annotation.AnyThread; -import android.annotation.MainThread; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.UserIdInt; -import android.annotation.WorkerThread; -import android.app.AppOpsManager; -import android.app.role.IOnRoleHoldersChangedListener; -import android.app.role.IRoleManager; -import android.app.role.RoleControllerManager; -import android.app.role.RoleManager; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.PackageManager; -import android.os.Binder; -import android.os.Handler; -import android.os.ParcelFileDescriptor; -import android.os.Process; -import android.os.RemoteCallback; -import android.os.RemoteCallbackList; -import android.os.RemoteException; -import android.os.UserHandle; -import android.os.UserManager; -import android.text.TextUtils; -import android.util.ArraySet; -import android.util.IndentingPrintWriter; -import android.util.Log; -import android.util.SparseArray; -import android.util.proto.ProtoOutputStream; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.infra.AndroidFuture; -import com.android.internal.util.Preconditions; -import com.android.internal.util.dump.DualDumpOutputStream; -import com.android.permission.compat.UserHandleCompat; -import com.android.permission.util.ArrayUtils; -import com.android.permission.util.CollectionUtils; -import com.android.permission.util.ForegroundThread; -import com.android.permission.util.ThrottledRunnable; -import com.android.server.LocalManagerRegistry; -import com.android.server.SystemService; -import com.android.server.role.RoleServicePlatformHelper; - -import java.io.FileDescriptor; -import java.io.FileOutputStream; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -/** - * Service for role management. - * - * @see RoleManager - */ -public class RoleService extends SystemService implements RoleUserState.Callback { - private static final String LOG_TAG = RoleService.class.getSimpleName(); - - private static final boolean DEBUG = false; - - private static final long GRANT_DEFAULT_ROLES_INTERVAL_MILLIS = 1000; - - @NonNull - private final AppOpsManager mAppOpsManager; - @NonNull - private final UserManager mUserManager; - - @NonNull - private final Object mLock = new Object(); - - @NonNull - private final RoleServicePlatformHelper mPlatformHelper; - - /** - * Maps user id to its state. - */ - @GuardedBy("mLock") - @NonNull - private final SparseArray<RoleUserState> mUserStates = new SparseArray<>(); - - /** - * Maps user id to its controller. - */ - @GuardedBy("mLock") - @NonNull - private final SparseArray<RoleControllerManager> mControllers = new SparseArray<>(); - - /** - * Maps user id to its list of listeners. - */ - @GuardedBy("mLock") - @NonNull - private final SparseArray<RemoteCallbackList<IOnRoleHoldersChangedListener>> mListeners = - new SparseArray<>(); - - @NonNull - private final Handler mListenerHandler = ForegroundThread.getHandler(); - - /** - * Maps user id to its throttled runnable for granting default roles. - */ - @GuardedBy("mLock") - @NonNull - private final SparseArray<ThrottledRunnable> mGrantDefaultRolesThrottledRunnables = - new SparseArray<>(); - - public RoleService(@NonNull Context context) { - super(context); - - mPlatformHelper = LocalManagerRegistry.getManager(RoleServicePlatformHelper.class); - - RoleControllerManager.initializeRemoteServiceComponentName(context); - - mAppOpsManager = context.getSystemService(AppOpsManager.class); - mUserManager = context.getSystemService(UserManager.class); - - LocalManagerRegistry.addManager(RoleManagerLocal.class, new Local()); - - registerUserRemovedReceiver(); - } - - private void registerUserRemovedReceiver() { - IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(Intent.ACTION_USER_REMOVED); - getContext().registerReceiverForAllUsers(new BroadcastReceiver() { - @Override - public void onReceive(@NonNull Context context, @NonNull Intent intent) { - if (TextUtils.equals(intent.getAction(), Intent.ACTION_USER_REMOVED)) { - int userId = intent.<UserHandle>getParcelableExtra(Intent.EXTRA_USER) - .getIdentifier(); - onRemoveUser(userId); - } - } - }, intentFilter, null, null); - } - - @Override - public void onStart() { - publishBinderService(Context.ROLE_SERVICE, new Stub()); - - IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); - intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); - intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); - intentFilter.addDataScheme("package"); - intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); - getContext().registerReceiverForAllUsers(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - int userId = UserHandleCompat.getUserId(intent.getIntExtra(Intent.EXTRA_UID, -1)); - if (DEBUG) { - Log.i(LOG_TAG, "Packages changed - re-running initial grants for user " - + userId); - } - if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction()) - && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { - // Package is being upgraded - we're about to get ACTION_PACKAGE_ADDED - return; - } - maybeGrantDefaultRolesAsync(userId); - } - }, intentFilter, null, null); - } - - @Override - public void onUserStarting(@NonNull TargetUser user) { - maybeGrantDefaultRolesSync(user.getUserHandle().getIdentifier()); - } - - @MainThread - private void maybeGrantDefaultRolesSync(@UserIdInt int userId) { - AndroidFuture<Void> future = maybeGrantDefaultRolesInternal(userId); - try { - future.get(30, TimeUnit.SECONDS); - } catch (InterruptedException | ExecutionException | TimeoutException e) { - Log.e(LOG_TAG, "Failed to grant default roles for user " + userId, e); - } - } - - private void maybeGrantDefaultRolesAsync(@UserIdInt int userId) { - ThrottledRunnable runnable; - synchronized (mLock) { - runnable = mGrantDefaultRolesThrottledRunnables.get(userId); - if (runnable == null) { - runnable = new ThrottledRunnable(ForegroundThread.getHandler(), - GRANT_DEFAULT_ROLES_INTERVAL_MILLIS, - () -> maybeGrantDefaultRolesInternal(userId)); - mGrantDefaultRolesThrottledRunnables.put(userId, runnable); - } - } - runnable.run(); - } - - @AnyThread - @NonNull - private AndroidFuture<Void> maybeGrantDefaultRolesInternal(@UserIdInt int userId) { - RoleUserState userState = getOrCreateUserState(userId); - String oldPackagesHash = userState.getPackagesHash(); - String newPackagesHash = mPlatformHelper.computePackageStateHash(userId); - if (Objects.equals(oldPackagesHash, newPackagesHash)) { - if (DEBUG) { - Log.i(LOG_TAG, "Already granted default roles for packages hash " - + newPackagesHash); - } - return AndroidFuture.completedFuture(null); - } - - // Some package state has changed, so grant default roles again. - Log.i(LOG_TAG, "Granting default roles..."); - AndroidFuture<Void> future = new AndroidFuture<>(); - getOrCreateController(userId).grantDefaultRoles(ForegroundThread.getExecutor(), - successful -> { - if (successful) { - userState.setPackagesHash(newPackagesHash); - future.complete(null); - } else { - future.completeExceptionally(new RuntimeException()); - } - }); - return future; - } - - @NonNull - private RoleUserState getOrCreateUserState(@UserIdInt int userId) { - synchronized (mLock) { - RoleUserState userState = mUserStates.get(userId); - if (userState == null) { - userState = new RoleUserState(userId, mPlatformHelper, this); - mUserStates.put(userId, userState); - } - return userState; - } - } - - @NonNull - private RoleControllerManager getOrCreateController(@UserIdInt int userId) { - synchronized (mLock) { - RoleControllerManager controller = mControllers.get(userId); - if (controller == null) { - Context systemContext = getContext(); - Context context; - try { - context = systemContext.createPackageContextAsUser( - systemContext.getPackageName(), 0, UserHandle.of(userId)); - } catch (PackageManager.NameNotFoundException e) { - throw new RuntimeException(e); - } - controller = RoleControllerManager.createWithInitializedRemoteServiceComponentName( - ForegroundThread.getHandler(), context); - mControllers.put(userId, controller); - } - return controller; - } - } - - @Nullable - private RemoteCallbackList<IOnRoleHoldersChangedListener> getListeners(@UserIdInt int userId) { - synchronized (mLock) { - return mListeners.get(userId); - } - } - - @NonNull - private RemoteCallbackList<IOnRoleHoldersChangedListener> getOrCreateListeners( - @UserIdInt int userId) { - synchronized (mLock) { - RemoteCallbackList<IOnRoleHoldersChangedListener> listeners = mListeners.get(userId); - if (listeners == null) { - listeners = new RemoteCallbackList<>(); - mListeners.put(userId, listeners); - } - return listeners; - } - } - - private void onRemoveUser(@UserIdInt int userId) { - RemoteCallbackList<IOnRoleHoldersChangedListener> listeners; - RoleUserState userState; - synchronized (mLock) { - mGrantDefaultRolesThrottledRunnables.remove(userId); - listeners = mListeners.get(userId); - mListeners.remove(userId); - mControllers.remove(userId); - userState = mUserStates.get(userId); - mUserStates.remove(userId); - } - if (listeners != null) { - listeners.kill(); - } - if (userState != null) { - userState.destroy(); - } - } - - @Override - public void onRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId) { - mListenerHandler.post(() -> notifyRoleHoldersChanged(roleName, userId)); - } - - @WorkerThread - private void notifyRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId) { - RemoteCallbackList<IOnRoleHoldersChangedListener> listeners = getListeners(userId); - if (listeners != null) { - notifyRoleHoldersChangedForListeners(listeners, roleName, userId); - } - - RemoteCallbackList<IOnRoleHoldersChangedListener> allUsersListeners = getListeners( - UserHandleCompat.USER_ALL); - if (allUsersListeners != null) { - notifyRoleHoldersChangedForListeners(allUsersListeners, roleName, userId); - } - } - - @WorkerThread - private void notifyRoleHoldersChangedForListeners( - @NonNull RemoteCallbackList<IOnRoleHoldersChangedListener> listeners, - @NonNull String roleName, @UserIdInt int userId) { - int broadcastCount = listeners.beginBroadcast(); - try { - for (int i = 0; i < broadcastCount; i++) { - IOnRoleHoldersChangedListener listener = listeners.getBroadcastItem(i); - try { - listener.onRoleHoldersChanged(roleName, userId); - } catch (RemoteException e) { - Log.e(LOG_TAG, "Error calling OnRoleHoldersChangedListener", e); - } - } - } finally { - listeners.finishBroadcast(); - } - } - - private class Stub extends IRoleManager.Stub { - - @Override - public boolean isRoleAvailable(@NonNull String roleName) { - Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); - - int userId = UserHandleCompat.getUserId(getCallingUid()); - return getOrCreateUserState(userId).isRoleAvailable(roleName); - } - - @Override - public boolean isRoleHeld(@NonNull String roleName, @NonNull String packageName) { - int callingUid = getCallingUid(); - mAppOpsManager.checkPackage(callingUid, packageName); - - Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); - Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty"); - - int userId = UserHandleCompat.getUserId(callingUid); - ArraySet<String> roleHolders = getOrCreateUserState(userId).getRoleHolders(roleName); - if (roleHolders == null) { - return false; - } - return roleHolders.contains(packageName); - } - - @NonNull - @Override - public List<String> getRoleHoldersAsUser(@NonNull String roleName, @UserIdInt int userId) { - if (!isUserExistent(userId)) { - Log.e(LOG_TAG, "user " + userId + " does not exist"); - return Collections.emptyList(); - } - enforceCrossUserPermission(userId, false, "getRoleHoldersAsUser"); - getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, - "getRoleHoldersAsUser"); - - Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); - - ArraySet<String> roleHolders = getOrCreateUserState(userId).getRoleHolders(roleName); - if (roleHolders == null) { - return Collections.emptyList(); - } - return new ArrayList<>(roleHolders); - } - - @Override - public void addRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName, - @RoleManager.ManageHoldersFlags int flags, @UserIdInt int userId, - @NonNull RemoteCallback callback) { - if (!isUserExistent(userId)) { - Log.e(LOG_TAG, "user " + userId + " does not exist"); - return; - } - enforceCrossUserPermission(userId, false, "addRoleHolderAsUser"); - getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, - "addRoleHolderAsUser"); - - Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); - Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty"); - Objects.requireNonNull(callback, "callback cannot be null"); - - getOrCreateController(userId).onAddRoleHolder(roleName, packageName, flags, - callback); - } - - @Override - public void removeRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName, - @RoleManager.ManageHoldersFlags int flags, @UserIdInt int userId, - @NonNull RemoteCallback callback) { - if (!isUserExistent(userId)) { - Log.e(LOG_TAG, "user " + userId + " does not exist"); - return; - } - enforceCrossUserPermission(userId, false, "removeRoleHolderAsUser"); - getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, - "removeRoleHolderAsUser"); - - Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); - Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty"); - Objects.requireNonNull(callback, "callback cannot be null"); - - getOrCreateController(userId).onRemoveRoleHolder(roleName, packageName, flags, - callback); - } - - @Override - public void clearRoleHoldersAsUser(@NonNull String roleName, - @RoleManager.ManageHoldersFlags int flags, @UserIdInt int userId, - @NonNull RemoteCallback callback) { - if (!isUserExistent(userId)) { - Log.e(LOG_TAG, "user " + userId + " does not exist"); - return; - } - enforceCrossUserPermission(userId, false, "clearRoleHoldersAsUser"); - getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, - "clearRoleHoldersAsUser"); - - Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); - Objects.requireNonNull(callback, "callback cannot be null"); - - getOrCreateController(userId).onClearRoleHolders(roleName, flags, callback); - } - - @Override - public void addOnRoleHoldersChangedListenerAsUser( - @NonNull IOnRoleHoldersChangedListener listener, @UserIdInt int userId) { - if (userId != UserHandleCompat.USER_ALL && !isUserExistent(userId)) { - Log.e(LOG_TAG, "user " + userId + " does not exist"); - return; - } - enforceCrossUserPermission(userId, true, "addOnRoleHoldersChangedListenerAsUser"); - getContext().enforceCallingOrSelfPermission(Manifest.permission.OBSERVE_ROLE_HOLDERS, - "addOnRoleHoldersChangedListenerAsUser"); - - Objects.requireNonNull(listener, "listener cannot be null"); - - RemoteCallbackList<IOnRoleHoldersChangedListener> listeners = getOrCreateListeners( - userId); - listeners.register(listener); - } - - @Override - public void removeOnRoleHoldersChangedListenerAsUser( - @NonNull IOnRoleHoldersChangedListener listener, @UserIdInt int userId) { - if (userId != UserHandleCompat.USER_ALL && !isUserExistent(userId)) { - Log.e(LOG_TAG, "user " + userId + " does not exist"); - return; - } - enforceCrossUserPermission(userId, true, "removeOnRoleHoldersChangedListenerAsUser"); - getContext().enforceCallingOrSelfPermission(Manifest.permission.OBSERVE_ROLE_HOLDERS, - "removeOnRoleHoldersChangedListenerAsUser"); - - Objects.requireNonNull(listener, "listener cannot be null"); - - RemoteCallbackList<IOnRoleHoldersChangedListener> listeners = getListeners(userId); - if (listener == null) { - return; - } - listeners.unregister(listener); - } - - @Override - public void setRoleNamesFromController(@NonNull List<String> roleNames) { - getContext().enforceCallingOrSelfPermission( - RoleManager.PERMISSION_MANAGE_ROLES_FROM_CONTROLLER, - "setRoleNamesFromController"); - - Objects.requireNonNull(roleNames, "roleNames cannot be null"); - - int userId = UserHandleCompat.getUserId(Binder.getCallingUid()); - getOrCreateUserState(userId).setRoleNames(roleNames); - } - - @Override - public boolean addRoleHolderFromController(@NonNull String roleName, - @NonNull String packageName) { - getContext().enforceCallingOrSelfPermission( - RoleManager.PERMISSION_MANAGE_ROLES_FROM_CONTROLLER, - "addRoleHolderFromController"); - - Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); - Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty"); - - int userId = UserHandleCompat.getUserId(Binder.getCallingUid()); - return getOrCreateUserState(userId).addRoleHolder(roleName, packageName); - } - - @Override - public boolean removeRoleHolderFromController(@NonNull String roleName, - @NonNull String packageName) { - getContext().enforceCallingOrSelfPermission( - RoleManager.PERMISSION_MANAGE_ROLES_FROM_CONTROLLER, - "removeRoleHolderFromController"); - - Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); - Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty"); - - int userId = UserHandleCompat.getUserId(Binder.getCallingUid()); - return getOrCreateUserState(userId).removeRoleHolder(roleName, packageName); - } - - @Override - public List<String> getHeldRolesFromController(@NonNull String packageName) { - getContext().enforceCallingOrSelfPermission( - RoleManager.PERMISSION_MANAGE_ROLES_FROM_CONTROLLER, - "getRolesHeldFromController"); - - Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty"); - - int userId = UserHandleCompat.getUserId(Binder.getCallingUid()); - return getOrCreateUserState(userId).getHeldRoles(packageName); - } - - private boolean isUserExistent(@UserIdInt int userId) { - // FIXME: This checks whether the user is alive, but we should check for whether the - // user is existent. - return mUserManager.getUserHandles(true).contains(UserHandle.of(userId)); - } - - private void enforceCrossUserPermission(@UserIdInt int userId, boolean allowAll, - @NonNull String message) { - final int callingUid = Binder.getCallingUid(); - final int callingUserId = UserHandleCompat.getUserId(callingUid); - if (userId == callingUserId) { - return; - } - Preconditions.checkArgument(userId >= UserHandleCompat.USER_SYSTEM - || (allowAll && userId == UserHandleCompat.USER_ALL), "Invalid user " + userId); - getContext().enforceCallingOrSelfPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message); - if (callingUid == Process.SHELL_UID && userId >= UserHandleCompat.USER_SYSTEM) { - if (mUserManager.hasUserRestrictionForUser(UserManager.DISALLOW_DEBUGGING_FEATURES, - UserHandle.of(userId))) { - throw new SecurityException("Shell does not have permission to access user " - + userId); - } - } - } - - @Override - public int handleShellCommand(@NonNull ParcelFileDescriptor in, - @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err, - @NonNull String[] args) { - return new RoleShellCommand(this).exec(this, in.getFileDescriptor(), - out.getFileDescriptor(), err.getFileDescriptor(), args); - } - - @Nullable - @Override - public String getBrowserRoleHolder(@UserIdInt int userId) { - final int callingUid = Binder.getCallingUid(); - if (UserHandleCompat.getUserId(callingUid) != userId) { - getContext().enforceCallingOrSelfPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); - } - if (isInstantApp(callingUid)) { - return null; - } - - final long identity = Binder.clearCallingIdentity(); - try { - return CollectionUtils.firstOrNull(getRoleHoldersAsUser(RoleManager.ROLE_BROWSER, - userId)); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - private boolean isInstantApp(int uid) { - final long identity = Binder.clearCallingIdentity(); - try { - final UserHandle user = UserHandle.getUserHandleForUid(uid); - final Context userContext = getContext().createContextAsUser(user, 0); - final PackageManager userPackageManager = userContext.getPackageManager(); - // Instant apps can not have shared UID, so it's safe to check only the first - // package name here. - final String packageName = ArrayUtils.firstOrNull( - userPackageManager.getPackagesForUid(uid)); - if (packageName == null) { - return false; - } - return userPackageManager.isInstantApp(packageName); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - @Override - public boolean setBrowserRoleHolder(@Nullable String packageName, @UserIdInt int userId) { - final Context context = getContext(); - context.enforceCallingOrSelfPermission( - android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); - if (UserHandleCompat.getUserId(Binder.getCallingUid()) != userId) { - context.enforceCallingOrSelfPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); - } - - if (!isUserExistent(userId)) { - return false; - } - - final AndroidFuture<Void> future = new AndroidFuture<>(); - final RemoteCallback callback = new RemoteCallback(result -> { - boolean successful = result != null; - if (successful) { - future.complete(null); - } else { - future.completeExceptionally(new RuntimeException()); - } - }); - final long identity = Binder.clearCallingIdentity(); - try { - if (packageName != null) { - addRoleHolderAsUser(RoleManager.ROLE_BROWSER, packageName, 0, userId, callback); - } else { - clearRoleHoldersAsUser(RoleManager.ROLE_BROWSER, 0, userId, callback); - } - try { - future.get(5, TimeUnit.SECONDS); - } catch (InterruptedException | ExecutionException | TimeoutException e) { - Log.e(LOG_TAG, "Exception while setting default browser: " + packageName, e); - return false; - } - } finally { - Binder.restoreCallingIdentity(identity); - } - - return true; - } - - @Override - public String getSmsRoleHolder(int userId) { - final long identity = Binder.clearCallingIdentity(); - try { - return CollectionUtils.firstOrNull(getRoleHoldersAsUser(RoleManager.ROLE_SMS, - userId)); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - @Override - protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout, - @Nullable String[] args) { - if (!checkDumpPermission("role", fout)) { - return; - } - - boolean dumpAsProto = args != null && ArrayUtils.contains(args, "--proto"); - DualDumpOutputStream dumpOutputStream; - if (dumpAsProto) { - dumpOutputStream = new DualDumpOutputStream(new ProtoOutputStream( - new FileOutputStream(fd))); - } else { - fout.println("ROLE STATE (dumpsys role):"); - dumpOutputStream = new DualDumpOutputStream(new IndentingPrintWriter(fout, " ")); - } - - synchronized (mLock) { - final int userStatesSize = mUserStates.size(); - for (int i = 0; i < userStatesSize; i++) { - final RoleUserState userState = mUserStates.valueAt(i); - - userState.dump(dumpOutputStream, "user_states", - RoleServiceDumpProto.USER_STATES); - } - } - - dumpOutputStream.flush(); - } - - private boolean checkDumpPermission(@NonNull String serviceName, - @NonNull PrintWriter writer) { - if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP) - != PackageManager.PERMISSION_GRANTED) { - writer.println("Permission Denial: can't dump " + serviceName + " from from pid=" - + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() - + " due to missing " + android.Manifest.permission.DUMP + " permission"); - return false; - } else { - return true; - } - } - } - - private class Local implements RoleManagerLocal { - @NonNull - @Override - public Map<String, Set<String>> getRolesAndHolders(@UserIdInt int userId) { - // Convert ArrayMap<String, ArraySet<String>> to Map<String, Set<String>> for the API. - //noinspection unchecked - return (Map<String, Set<String>>) (Map<String, ?>) - getOrCreateUserState(userId).getRolesAndHolders(); - } - } -} diff --git a/apex/permission/service/java/com/android/role/RoleShellCommand.java b/apex/permission/service/java/com/android/role/RoleShellCommand.java deleted file mode 100644 index 03b7c76d2df5..000000000000 --- a/apex/permission/service/java/com/android/role/RoleShellCommand.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * 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. - */ - -package com.android.role; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.role.IRoleManager; -import android.os.RemoteCallback; -import android.os.RemoteException; - -import com.android.modules.utils.BasicShellCommandHandler; -import com.android.permission.compat.UserHandleCompat; - -import java.io.PrintWriter; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; - -class RoleShellCommand extends BasicShellCommandHandler { - @NonNull - private final IRoleManager mRoleManager; - - RoleShellCommand(@NonNull IRoleManager roleManager) { - mRoleManager = roleManager; - } - - private class CallbackFuture extends CompletableFuture<Void> { - @NonNull - public RemoteCallback createCallback() { - return new RemoteCallback(result -> { - boolean successful = result != null; - if (successful) { - complete(null); - } else { - completeExceptionally(new RuntimeException("Failed")); - } - }); - } - - public int waitForResult() { - try { - get(5, TimeUnit.SECONDS); - return 0; - } catch (Exception e) { - getErrPrintWriter().println("Error: see logcat for details.\n" + e); - return -1; - } - } - } - - @Override - public int onCommand(@Nullable String cmd) { - if (cmd == null) { - return handleDefaultCommands(cmd); - } - - PrintWriter pw = getOutPrintWriter(); - try { - switch (cmd) { - case "add-role-holder": - return runAddRoleHolder(); - case "remove-role-holder": - return runRemoveRoleHolder(); - case "clear-role-holders": - return runClearRoleHolders(); - default: - return handleDefaultCommands(cmd); - } - } catch (RemoteException e) { - pw.println("Remote exception: " + e); - } - return -1; - } - - private int getUserIdMaybe() { - int userId = UserHandleCompat.USER_SYSTEM; - String option = getNextOption(); - if (option != null && option.equals("--user")) { - userId = Integer.parseInt(getNextArgRequired()); - } - return userId; - } - - private int getFlagsMaybe() { - String flags = getNextArg(); - if (flags == null) { - return 0; - } - return Integer.parseInt(flags); - } - - private int runAddRoleHolder() throws RemoteException { - int userId = getUserIdMaybe(); - String roleName = getNextArgRequired(); - String packageName = getNextArgRequired(); - int flags = getFlagsMaybe(); - - CallbackFuture future = new CallbackFuture(); - mRoleManager.addRoleHolderAsUser(roleName, packageName, flags, userId, - future.createCallback()); - return future.waitForResult(); - } - - private int runRemoveRoleHolder() throws RemoteException { - int userId = getUserIdMaybe(); - String roleName = getNextArgRequired(); - String packageName = getNextArgRequired(); - int flags = getFlagsMaybe(); - - CallbackFuture future = new CallbackFuture(); - mRoleManager.removeRoleHolderAsUser(roleName, packageName, flags, userId, - future.createCallback()); - return future.waitForResult(); - } - - private int runClearRoleHolders() throws RemoteException { - int userId = getUserIdMaybe(); - String roleName = getNextArgRequired(); - int flags = getFlagsMaybe(); - - CallbackFuture future = new CallbackFuture(); - mRoleManager.clearRoleHoldersAsUser(roleName, flags, userId, future.createCallback()); - return future.waitForResult(); - } - - @Override - public void onHelp() { - PrintWriter pw = getOutPrintWriter(); - pw.println("Role (role) commands:"); - pw.println(" help or -h"); - pw.println(" Print this help text."); - pw.println(); - pw.println(" add-role-holder [--user USER_ID] ROLE PACKAGE [FLAGS]"); - pw.println(" remove-role-holder [--user USER_ID] ROLE PACKAGE [FLAGS]"); - pw.println(" clear-role-holders [--user USER_ID] ROLE [FLAGS]"); - pw.println(); - } -} diff --git a/apex/permission/service/java/com/android/role/RoleUserState.java b/apex/permission/service/java/com/android/role/RoleUserState.java deleted file mode 100644 index 78d8d15bbe60..000000000000 --- a/apex/permission/service/java/com/android/role/RoleUserState.java +++ /dev/null @@ -1,476 +0,0 @@ -/* - * 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. - */ - -package com.android.role; - -import android.annotation.CheckResult; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.UserIdInt; -import android.annotation.WorkerThread; -import android.os.Handler; -import android.os.UserHandle; -import android.util.ArrayMap; -import android.util.ArraySet; -import android.util.Log; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.dump.DualDumpOutputStream; -import com.android.permission.util.BackgroundThread; -import com.android.permission.util.CollectionUtils; -import com.android.role.persistence.RolesPersistence; -import com.android.role.persistence.RolesState; -import com.android.server.role.RoleServicePlatformHelper; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; - -/** - * Stores the state of roles for a user. - */ -class RoleUserState { - private static final String LOG_TAG = RoleUserState.class.getSimpleName(); - - public static final int VERSION_UNDEFINED = -1; - - private static final long WRITE_DELAY_MILLIS = 200; - - private final RolesPersistence mPersistence = RolesPersistence.createInstance(); - - @UserIdInt - private final int mUserId; - - @NonNull - private final RoleServicePlatformHelper mPlatformHelper; - - @NonNull - private final Callback mCallback; - - @NonNull - private final Object mLock = new Object(); - - @GuardedBy("mLock") - private int mVersion = VERSION_UNDEFINED; - - @GuardedBy("mLock") - @Nullable - private String mPackagesHash; - - /** - * Maps role names to its holders' package names. The values should never be null. - */ - @GuardedBy("mLock") - @NonNull - private ArrayMap<String, ArraySet<String>> mRoles = new ArrayMap<>(); - - @GuardedBy("mLock") - private boolean mWriteScheduled; - - @GuardedBy("mLock") - private boolean mDestroyed; - - @NonNull - private final Handler mWriteHandler = new Handler(BackgroundThread.get().getLooper()); - - /** - * Create a new user state, and read its state from disk if previously persisted. - * - * @param userId the user id for this user state - * @param platformHelper the platform helper - * @param callback the callback for this user state - */ - public RoleUserState(@UserIdInt int userId, @NonNull RoleServicePlatformHelper platformHelper, - @NonNull Callback callback) { - mUserId = userId; - mPlatformHelper = platformHelper; - mCallback = callback; - - readFile(); - } - - /** - * Get the version of this user state. - */ - public int getVersion() { - synchronized (mLock) { - return mVersion; - } - } - - /** - * Set the version of this user state. - * - * @param version the version to set - */ - public void setVersion(int version) { - synchronized (mLock) { - if (mVersion == version) { - return; - } - mVersion = version; - scheduleWriteFileLocked(); - } - } - - /** - * Get the hash representing the state of packages during the last time initial grants was run. - * - * @return the hash representing the state of packages - */ - @Nullable - public String getPackagesHash() { - synchronized (mLock) { - return mPackagesHash; - } - } - - /** - * Set the hash representing the state of packages during the last time initial grants was run. - * - * @param packagesHash the hash representing the state of packages - */ - public void setPackagesHash(@Nullable String packagesHash) { - synchronized (mLock) { - if (Objects.equals(mPackagesHash, packagesHash)) { - return; - } - mPackagesHash = packagesHash; - scheduleWriteFileLocked(); - } - } - - /** - * Get whether the role is available. - * - * @param roleName the name of the role to get the holders for - * - * @return whether the role is available - */ - public boolean isRoleAvailable(@NonNull String roleName) { - synchronized (mLock) { - return mRoles.containsKey(roleName); - } - } - - /** - * Get the holders of a role. - * - * @param roleName the name of the role to query for - * - * @return the set of role holders, or {@code null} if and only if the role is not found - */ - @Nullable - public ArraySet<String> getRoleHolders(@NonNull String roleName) { - synchronized (mLock) { - ArraySet<String> packageNames = mRoles.get(roleName); - if (packageNames == null) { - return null; - } - return new ArraySet<>(packageNames); - } - } - - /** - * Adds the given role, effectively marking it as {@link #isRoleAvailable available} - * - * @param roleName the name of the role - * - * @return whether any changes were made - */ - public boolean addRoleName(@NonNull String roleName) { - synchronized (mLock) { - if (!mRoles.containsKey(roleName)) { - mRoles.put(roleName, new ArraySet<>()); - Log.i(LOG_TAG, "Added new role: " + roleName); - scheduleWriteFileLocked(); - return true; - } else { - return false; - } - } - } - - /** - * Set the names of all available roles. - * - * @param roleNames the names of all the available roles - */ - public void setRoleNames(@NonNull List<String> roleNames) { - synchronized (mLock) { - boolean changed = false; - - for (int i = mRoles.size() - 1; i >= 0; i--) { - String roleName = mRoles.keyAt(i); - - if (!roleNames.contains(roleName)) { - ArraySet<String> packageNames = mRoles.valueAt(i); - if (!packageNames.isEmpty()) { - Log.e(LOG_TAG, "Holders of a removed role should have been cleaned up," - + " role: " + roleName + ", holders: " + packageNames); - } - mRoles.removeAt(i); - changed = true; - } - } - - int roleNamesSize = roleNames.size(); - for (int i = 0; i < roleNamesSize; i++) { - changed |= addRoleName(roleNames.get(i)); - } - - if (changed) { - scheduleWriteFileLocked(); - } - } - } - - /** - * Add a holder to a role. - * - * @param roleName the name of the role to add the holder to - * @param packageName the package name of the new holder - * - * @return {@code false} if and only if the role is not found - */ - @CheckResult - public boolean addRoleHolder(@NonNull String roleName, @NonNull String packageName) { - boolean changed; - - synchronized (mLock) { - ArraySet<String> roleHolders = mRoles.get(roleName); - if (roleHolders == null) { - Log.e(LOG_TAG, "Cannot add role holder for unknown role, role: " + roleName - + ", package: " + packageName); - return false; - } - changed = roleHolders.add(packageName); - if (changed) { - scheduleWriteFileLocked(); - } - } - - if (changed) { - mCallback.onRoleHoldersChanged(roleName, mUserId); - } - return true; - } - - /** - * Remove a holder from a role. - * - * @param roleName the name of the role to remove the holder from - * @param packageName the package name of the holder to remove - * - * @return {@code false} if and only if the role is not found - */ - @CheckResult - public boolean removeRoleHolder(@NonNull String roleName, @NonNull String packageName) { - boolean changed; - - synchronized (mLock) { - ArraySet<String> roleHolders = mRoles.get(roleName); - if (roleHolders == null) { - Log.e(LOG_TAG, "Cannot remove role holder for unknown role, role: " + roleName - + ", package: " + packageName); - return false; - } - - changed = roleHolders.remove(packageName); - if (changed) { - scheduleWriteFileLocked(); - } - } - - if (changed) { - mCallback.onRoleHoldersChanged(roleName, mUserId); - } - return true; - } - - /** - * @see android.app.role.RoleManager#getHeldRolesFromController - */ - @NonNull - public List<String> getHeldRoles(@NonNull String packageName) { - synchronized (mLock) { - List<String> roleNames = new ArrayList<>(); - int size = mRoles.size(); - for (int i = 0; i < size; i++) { - if (mRoles.valueAt(i).contains(packageName)) { - roleNames.add(mRoles.keyAt(i)); - } - } - return roleNames; - } - } - - /** - * Schedule writing the state to file. - */ - @GuardedBy("mLock") - private void scheduleWriteFileLocked() { - if (mDestroyed) { - return; - } - - if (!mWriteScheduled) { - mWriteHandler.postDelayed(this::writeFile, WRITE_DELAY_MILLIS); - mWriteScheduled = true; - } - } - - @WorkerThread - private void writeFile() { - RolesState roles; - synchronized (mLock) { - if (mDestroyed) { - return; - } - - mWriteScheduled = false; - - roles = new RolesState(mVersion, mPackagesHash, - (Map<String, Set<String>>) (Map<String, ?>) snapshotRolesLocked()); - } - - mPersistence.writeForUser(roles, UserHandle.of(mUserId)); - } - - private void readFile() { - synchronized (mLock) { - RolesState roleState = mPersistence.readForUser(UserHandle.of(mUserId)); - - Map<String, Set<String>> roles; - if (roleState != null) { - mVersion = roleState.getVersion(); - mPackagesHash = roleState.getPackagesHash(); - roles = roleState.getRoles(); - } else { - roles = mPlatformHelper.getLegacyRoleState(mUserId); - } - mRoles.clear(); - for (Map.Entry<String, Set<String>> entry : roles.entrySet()) { - String roleName = entry.getKey(); - ArraySet<String> roleHolders = new ArraySet<>(entry.getValue()); - mRoles.put(roleName, roleHolders); - } - - if (roleState == null) { - scheduleWriteFileLocked(); - } - } - } - - /** - * Dump this user state. - * - * @param dumpOutputStream the output stream to dump to - */ - public void dump(@NonNull DualDumpOutputStream dumpOutputStream, @NonNull String fieldName, - long fieldId) { - int version; - String packagesHash; - ArrayMap<String, ArraySet<String>> roles; - synchronized (mLock) { - version = mVersion; - packagesHash = mPackagesHash; - roles = snapshotRolesLocked(); - } - - long fieldToken = dumpOutputStream.start(fieldName, fieldId); - dumpOutputStream.write("user_id", RoleUserStateProto.USER_ID, mUserId); - dumpOutputStream.write("version", RoleUserStateProto.VERSION, version); - dumpOutputStream.write("packages_hash", RoleUserStateProto.PACKAGES_HASH, packagesHash); - - int rolesSize = roles.size(); - for (int rolesIndex = 0; rolesIndex < rolesSize; rolesIndex++) { - String roleName = roles.keyAt(rolesIndex); - ArraySet<String> roleHolders = roles.valueAt(rolesIndex); - - long rolesToken = dumpOutputStream.start("roles", RoleUserStateProto.ROLES); - dumpOutputStream.write("name", RoleProto.NAME, roleName); - - int roleHoldersSize = roleHolders.size(); - for (int roleHoldersIndex = 0; roleHoldersIndex < roleHoldersSize; roleHoldersIndex++) { - String roleHolder = roleHolders.valueAt(roleHoldersIndex); - - dumpOutputStream.write("holders", RoleProto.HOLDERS, roleHolder); - } - - dumpOutputStream.end(rolesToken); - } - - dumpOutputStream.end(fieldToken); - } - - /** - * Get the roles and their holders. - * - * @return A copy of the roles and their holders - */ - @NonNull - public ArrayMap<String, ArraySet<String>> getRolesAndHolders() { - synchronized (mLock) { - return snapshotRolesLocked(); - } - } - - @GuardedBy("mLock") - @NonNull - private ArrayMap<String, ArraySet<String>> snapshotRolesLocked() { - ArrayMap<String, ArraySet<String>> roles = new ArrayMap<>(); - for (int i = 0, size = CollectionUtils.size(mRoles); i < size; ++i) { - String roleName = mRoles.keyAt(i); - ArraySet<String> roleHolders = mRoles.valueAt(i); - - roleHolders = new ArraySet<>(roleHolders); - roles.put(roleName, roleHolders); - } - return roles; - } - - /** - * Destroy this user state and delete the corresponding file. Any pending writes to the file - * will be cancelled, and any future interaction with this state will throw an exception. - */ - public void destroy() { - synchronized (mLock) { - if (mDestroyed) { - throw new IllegalStateException("This RoleUserState has already been destroyed"); - } - mWriteHandler.removeCallbacksAndMessages(null); - mPersistence.deleteForUser(UserHandle.of(mUserId)); - mDestroyed = true; - } - } - - /** - * Callback for a user state. - */ - public interface Callback { - - /** - * Called when the holders of roles are changed. - * - * @param roleName the name of the role whose holders are changed - * @param userId the user id for this role holder change - */ - void onRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId); - } -} diff --git a/apex/permission/service/java/com/android/role/TEST_MAPPING b/apex/permission/service/java/com/android/role/TEST_MAPPING deleted file mode 100644 index 0d7bc1476bd1..000000000000 --- a/apex/permission/service/java/com/android/role/TEST_MAPPING +++ /dev/null @@ -1,20 +0,0 @@ -{ - "presubmit": [ - { - "name": "CtsStatsdHostTestCases", - "options": [ - { - "include-filter": "android.cts.statsd.atom.UidAtomTests#testRoleHolder" - } - ] - }, - { - "name": "CtsRoleTestCases", - "options": [ - { - "exclude-annotation": "androidx.test.filters.FlakyTest" - } - ] - } - ] -} diff --git a/apex/permission/service/java/com/android/role/package-info.java b/apex/permission/service/java/com/android/role/package-info.java deleted file mode 100644 index 8b5b2516105f..000000000000 --- a/apex/permission/service/java/com/android/role/package-info.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2021 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. - */ - -/** - * @hide - * TODO(b/146466118) remove this javadoc tag - */ -@android.annotation.Hide -package com.android.role; diff --git a/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java b/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java deleted file mode 100644 index 2e5a28aa1d6a..000000000000 --- a/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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.role.persistence; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.SystemApi; -import android.annotation.SystemApi.Client; -import android.os.UserHandle; - -/** - * Persistence for roles. - * - * TODO(b/147914847): Remove @hide when it becomes the default. - * @hide - */ -@SystemApi(client = Client.SYSTEM_SERVER) -public interface RolesPersistence { - - /** - * Read the roles from persistence. - * - * This will perform I/O operations synchronously. - * - * @param user the user to read for - * @return the roles read - */ - @Nullable - RolesState readForUser(@NonNull UserHandle user); - - /** - * Write the roles to persistence. - * - * This will perform I/O operations synchronously. - * - * @param roles the roles to write - * @param user the user to write for - */ - void writeForUser(@NonNull RolesState roles, @NonNull UserHandle user); - - /** - * Delete the roles from persistence. - * - * This will perform I/O operations synchronously. - * - * @param user the user to delete for - */ - void deleteForUser(@NonNull UserHandle user); - - /** - * Create a new instance of {@link RolesPersistence} implementation. - * - * @return the new instance. - */ - @NonNull - static RolesPersistence createInstance() { - return new RolesPersistenceImpl(); - } -} diff --git a/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java b/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java deleted file mode 100644 index f66257f13ef6..000000000000 --- a/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * 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.role.persistence; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.ApexEnvironment; -import android.os.UserHandle; -import android.util.ArrayMap; -import android.util.ArraySet; -import android.util.AtomicFile; -import android.util.Log; -import android.util.Xml; - -import com.android.permission.persistence.IoUtils; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.Map; -import java.util.Set; - -/** - * Persistence implementation for roles. - * - * TODO(b/147914847): Remove @hide when it becomes the default. - * @hide - */ -public class RolesPersistenceImpl implements RolesPersistence { - - private static final String LOG_TAG = RolesPersistenceImpl.class.getSimpleName(); - - private static final String APEX_MODULE_NAME = "com.android.permission"; - - private static final String ROLES_FILE_NAME = "roles.xml"; - - private static final String TAG_ROLES = "roles"; - private static final String TAG_ROLE = "role"; - private static final String TAG_HOLDER = "holder"; - - private static final String ATTRIBUTE_VERSION = "version"; - private static final String ATTRIBUTE_NAME = "name"; - private static final String ATTRIBUTE_PACKAGES_HASH = "packagesHash"; - - @Nullable - @Override - public RolesState readForUser(@NonNull UserHandle user) { - File file = getFile(user); - try (FileInputStream inputStream = new AtomicFile(file).openRead()) { - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(inputStream, null); - return parseXml(parser); - } catch (FileNotFoundException e) { - Log.i(LOG_TAG, "roles.xml not found"); - return null; - } catch (XmlPullParserException | IOException e) { - throw new IllegalStateException("Failed to read roles.xml: " + file , e); - } - } - - @NonNull - private static RolesState parseXml(@NonNull XmlPullParser parser) - throws IOException, XmlPullParserException { - int type; - int depth; - int innerDepth = parser.getDepth() + 1; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT - && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) { - if (depth > innerDepth || type != XmlPullParser.START_TAG) { - continue; - } - - if (parser.getName().equals(TAG_ROLES)) { - return parseRoles(parser); - } - } - throw new IllegalStateException("Missing <" + TAG_ROLES + "> in roles.xml"); - } - - @NonNull - private static RolesState parseRoles(@NonNull XmlPullParser parser) - throws IOException, XmlPullParserException { - int version = Integer.parseInt(parser.getAttributeValue(null, ATTRIBUTE_VERSION)); - String packagesHash = parser.getAttributeValue(null, ATTRIBUTE_PACKAGES_HASH); - - Map<String, Set<String>> roles = new ArrayMap<>(); - int type; - int depth; - int innerDepth = parser.getDepth() + 1; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT - && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) { - if (depth > innerDepth || type != XmlPullParser.START_TAG) { - continue; - } - - if (parser.getName().equals(TAG_ROLE)) { - String roleName = parser.getAttributeValue(null, ATTRIBUTE_NAME); - Set<String> roleHolders = parseRoleHolders(parser); - roles.put(roleName, roleHolders); - } - } - - return new RolesState(version, packagesHash, roles); - } - - @NonNull - private static Set<String> parseRoleHolders(@NonNull XmlPullParser parser) - throws IOException, XmlPullParserException { - Set<String> roleHolders = new ArraySet<>(); - int type; - int depth; - int innerDepth = parser.getDepth() + 1; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT - && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) { - if (depth > innerDepth || type != XmlPullParser.START_TAG) { - continue; - } - - if (parser.getName().equals(TAG_HOLDER)) { - String roleHolder = parser.getAttributeValue(null, ATTRIBUTE_NAME); - roleHolders.add(roleHolder); - } - } - return roleHolders; - } - - @Override - public void writeForUser(@NonNull RolesState roles, @NonNull UserHandle user) { - File file = getFile(user); - AtomicFile atomicFile = new AtomicFile(file); - FileOutputStream outputStream = null; - try { - outputStream = atomicFile.startWrite(); - - XmlSerializer serializer = Xml.newSerializer(); - serializer.setOutput(outputStream, StandardCharsets.UTF_8.name()); - serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); - serializer.startDocument(null, true); - - serializeRoles(serializer, roles); - - serializer.endDocument(); - atomicFile.finishWrite(outputStream); - } catch (Exception e) { - Log.wtf(LOG_TAG, "Failed to write roles.xml, restoring backup: " + file, - e); - atomicFile.failWrite(outputStream); - } finally { - IoUtils.closeQuietly(outputStream); - } - } - - private static void serializeRoles(@NonNull XmlSerializer serializer, - @NonNull RolesState roles) throws IOException { - serializer.startTag(null, TAG_ROLES); - - int version = roles.getVersion(); - serializer.attribute(null, ATTRIBUTE_VERSION, Integer.toString(version)); - String packagesHash = roles.getPackagesHash(); - if (packagesHash != null) { - serializer.attribute(null, ATTRIBUTE_PACKAGES_HASH, packagesHash); - } - - for (Map.Entry<String, Set<String>> entry : roles.getRoles().entrySet()) { - String roleName = entry.getKey(); - Set<String> roleHolders = entry.getValue(); - - serializer.startTag(null, TAG_ROLE); - serializer.attribute(null, ATTRIBUTE_NAME, roleName); - serializeRoleHolders(serializer, roleHolders); - serializer.endTag(null, TAG_ROLE); - } - - serializer.endTag(null, TAG_ROLES); - } - - private static void serializeRoleHolders(@NonNull XmlSerializer serializer, - @NonNull Set<String> roleHolders) throws IOException { - for (String roleHolder : roleHolders) { - serializer.startTag(null, TAG_HOLDER); - serializer.attribute(null, ATTRIBUTE_NAME, roleHolder); - serializer.endTag(null, TAG_HOLDER); - } - } - - @Override - public void deleteForUser(@NonNull UserHandle user) { - getFile(user).delete(); - } - - @NonNull - private static File getFile(@NonNull UserHandle user) { - ApexEnvironment apexEnvironment = ApexEnvironment.getApexEnvironment(APEX_MODULE_NAME); - File dataDirectory = apexEnvironment.getDeviceProtectedDataDirForUser(user); - return new File(dataDirectory, ROLES_FILE_NAME); - } -} diff --git a/apex/permission/service/java/com/android/role/persistence/RolesState.java b/apex/permission/service/java/com/android/role/persistence/RolesState.java deleted file mode 100644 index f61efa0e840d..000000000000 --- a/apex/permission/service/java/com/android/role/persistence/RolesState.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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.role.persistence; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.SystemApi; -import android.annotation.SystemApi.Client; - -import java.util.Map; -import java.util.Objects; -import java.util.Set; - -/** - * State of all roles. - * - * TODO(b/147914847): Remove @hide when it becomes the default. - * @hide - */ -@SystemApi(client = Client.SYSTEM_SERVER) -public final class RolesState { - - /** - * The version of the roles. - */ - private final int mVersion; - - /** - * The hash of all packages in the system. - */ - @Nullable - private final String mPackagesHash; - - /** - * The roles. - */ - @NonNull - private final Map<String, Set<String>> mRoles; - - /** - * Create a new instance of this class. - * - * @param version the version of the roles - * @param packagesHash the hash of all packages in the system - * @param roles the roles - */ - public RolesState(int version, @Nullable String packagesHash, - @NonNull Map<String, Set<String>> roles) { - mVersion = version; - mPackagesHash = packagesHash; - mRoles = roles; - } - - /** - * Get the version of the roles. - * - * @return the version of the roles - */ - public int getVersion() { - return mVersion; - } - - /** - * Get the hash of all packages in the system. - * - * @return the hash of all packages in the system - */ - @Nullable - public String getPackagesHash() { - return mPackagesHash; - } - - /** - * Get the roles. - * - * @return the roles - */ - @NonNull - public Map<String, Set<String>> getRoles() { - return mRoles; - } - - @Override - public boolean equals(Object object) { - if (this == object) { - return true; - } - if (object == null || getClass() != object.getClass()) { - return false; - } - RolesState that = (RolesState) object; - return mVersion == that.mVersion - && Objects.equals(mPackagesHash, that.mPackagesHash) - && Objects.equals(mRoles, that.mRoles); - } - - @Override - public int hashCode() { - return Objects.hash(mVersion, mPackagesHash, mRoles); - } -} diff --git a/apex/permission/service/proto/com/android/role/roleservice.proto b/apex/permission/service/proto/com/android/role/roleservice.proto deleted file mode 100644 index 79c42299207c..000000000000 --- a/apex/permission/service/proto/com/android/role/roleservice.proto +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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. - */ - -syntax = "proto2"; - -package com.android.role; - -option java_multiple_files = true; - -import "frameworks/base/core/proto/android/privacy.proto"; - -message RoleServiceDumpProto { - option (.android.msg_privacy).dest = DEST_AUTOMATIC; - - // List of per-user states for all users. - repeated RoleUserStateProto user_states = 1; -} - -message RoleUserStateProto { - option (.android.msg_privacy).dest = DEST_AUTOMATIC; - - // The user id of this state. - optional int32 user_id = 1; - - // The version of this state. - optional int32 version = 2; - - // The hash of packages for this state. - optional string packages_hash = 3; - - // The set of roles in this state. - repeated RoleProto roles = 4; -} - -message RoleProto { - option (.android.msg_privacy).dest = DEST_AUTOMATIC; - - // The name of this role, e.g. "android.app.role.DIALER". - optional string name = 1; - - // The package names of the holders of this role. - repeated string holders = 2; -} diff --git a/apex/permission/testing/Android.bp b/apex/permission/testing/Android.bp deleted file mode 100644 index 63bf0a08e956..000000000000 --- a/apex/permission/testing/Android.bp +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (C) 2019 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -apex_test { - name: "test_com.android.permission", - visibility: [ - "//system/apex/tests", - ], - defaults: ["com.android.permission-defaults"], - manifest: "test_manifest.json", - file_contexts: ":com.android.permission-file_contexts", - // Test APEX, should never be installed - installable: false, -} diff --git a/apex/permission/testing/test_manifest.json b/apex/permission/testing/test_manifest.json deleted file mode 100644 index bc19a9ea0172..000000000000 --- a/apex/permission/testing/test_manifest.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "com.android.permission", - "version": 2147483647 -} diff --git a/apex/permission/tests/Android.bp b/apex/permission/tests/Android.bp deleted file mode 100644 index 271e328c1139..000000000000 --- a/apex/permission/tests/Android.bp +++ /dev/null @@ -1,37 +0,0 @@ -// 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. - -android_test { - name: "PermissionApexTests", - sdk_version: "test_current", - srcs: [ - "java/**/*.kt", - ], - static_libs: [ - "service-permission.impl", - "androidx.test.rules", - "androidx.test.ext.junit", - "androidx.test.ext.truth", - "mockito-target-extended-minus-junit4", - ], - jni_libs: [ - "libdexmakerjvmtiagent", - "libstaticjvmtiagent", - ], - compile_multilib: "both", - test_suites: [ - "general-tests", - "mts", - ], -} diff --git a/apex/permission/tests/AndroidManifest.xml b/apex/permission/tests/AndroidManifest.xml deleted file mode 100644 index 57ee6417aeb3..000000000000 --- a/apex/permission/tests/AndroidManifest.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?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. - --> - -<manifest - xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.permission.test"> - - <!-- The application has to be debuggable for static mocking to work. --> - <application android:debuggable="true"> - <uses-library android:name="android.test.runner" /> - </application> - - <instrumentation - android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="com.android.permission.test" - android:label="Permission APEX Tests" /> -</manifest> diff --git a/apex/permission/tests/java/com/android/permission/persistence/RuntimePermissionsPersistenceTest.kt b/apex/permission/tests/java/com/android/permission/persistence/RuntimePermissionsPersistenceTest.kt deleted file mode 100644 index 2987da087e51..000000000000 --- a/apex/permission/tests/java/com/android/permission/persistence/RuntimePermissionsPersistenceTest.kt +++ /dev/null @@ -1,110 +0,0 @@ -/* - * 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.permission.persistence - -import android.content.ApexEnvironment -import android.content.Context -import android.os.Process -import android.os.UserHandle -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.platform.app.InstrumentationRegistry -import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession -import com.google.common.truth.Truth.assertThat -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers.any -import org.mockito.ArgumentMatchers.eq -import org.mockito.Mock -import org.mockito.Mockito.`when` -import org.mockito.MockitoAnnotations.initMocks -import org.mockito.MockitoSession -import org.mockito.quality.Strictness -import java.io.File - -@RunWith(AndroidJUnit4::class) -class RuntimePermissionsPersistenceTest { - private val context = InstrumentationRegistry.getInstrumentation().context - - private lateinit var mockDataDirectory: File - - private lateinit var mockitoSession: MockitoSession - @Mock - lateinit var apexEnvironment: ApexEnvironment - - private val persistence = RuntimePermissionsPersistence.createInstance() - private val permissionState = RuntimePermissionsState.PermissionState("permission", true, 3) - private val state = RuntimePermissionsState( - 1, "fingerprint", mapOf("package" to listOf(permissionState)), - mapOf("sharedUser" to listOf(permissionState)) - ) - private val user = Process.myUserHandle() - - @Before - fun createMockDataDirectory() { - mockDataDirectory = context.getDir("mock_data", Context.MODE_PRIVATE) - mockDataDirectory.listFiles()!!.forEach { assertThat(it.deleteRecursively()).isTrue() } - } - - @Before - fun mockApexEnvironment() { - initMocks(this) - mockitoSession = mockitoSession() - .mockStatic(ApexEnvironment::class.java) - .strictness(Strictness.LENIENT) - .startMocking() - `when`(ApexEnvironment.getApexEnvironment(eq(APEX_MODULE_NAME))).thenReturn(apexEnvironment) - `when`(apexEnvironment.getDeviceProtectedDataDirForUser(any(UserHandle::class.java))).then { - File(mockDataDirectory, it.arguments[0].toString()).also { it.mkdirs() } - } - } - - @After - fun finishMockingApexEnvironment() { - mockitoSession.finishMocking() - } - - @Test - fun testReadWrite() { - persistence.writeForUser(state, user) - val persistedState = persistence.readForUser(user) - - assertThat(persistedState).isEqualTo(state) - assertThat(persistedState!!.version).isEqualTo(state.version) - assertThat(persistedState.fingerprint).isEqualTo(state.fingerprint) - assertThat(persistedState.packagePermissions).isEqualTo(state.packagePermissions) - val persistedPermissionState = persistedState.packagePermissions.values.first().first() - assertThat(persistedPermissionState.name).isEqualTo(permissionState.name) - assertThat(persistedPermissionState.isGranted).isEqualTo(permissionState.isGranted) - assertThat(persistedPermissionState.flags).isEqualTo(permissionState.flags) - assertThat(persistedState.sharedUserPermissions).isEqualTo(state.sharedUserPermissions) - } - - @Test - fun testDelete() { - persistence.writeForUser(state, user) - persistence.deleteForUser(user) - val persistedState = persistence.readForUser(user) - - assertThat(persistedState).isNull() - } - - companion object { - private const val APEX_MODULE_NAME = "com.android.permission" - } -} diff --git a/apex/permission/tests/java/com/android/role/persistence/RolesPersistenceTest.kt b/apex/permission/tests/java/com/android/role/persistence/RolesPersistenceTest.kt deleted file mode 100644 index f9d9d5afb25d..000000000000 --- a/apex/permission/tests/java/com/android/role/persistence/RolesPersistenceTest.kt +++ /dev/null @@ -1,101 +0,0 @@ -/* - * 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.role.persistence - -import android.content.ApexEnvironment -import android.content.Context -import android.os.Process -import android.os.UserHandle -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.platform.app.InstrumentationRegistry -import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession -import com.google.common.truth.Truth.assertThat -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers.any -import org.mockito.ArgumentMatchers.eq -import org.mockito.Mock -import org.mockito.Mockito.`when` -import org.mockito.MockitoAnnotations.initMocks -import org.mockito.MockitoSession -import org.mockito.quality.Strictness -import java.io.File - -@RunWith(AndroidJUnit4::class) -class RolesPersistenceTest { - private val context = InstrumentationRegistry.getInstrumentation().context - - private lateinit var mockDataDirectory: File - - private lateinit var mockitoSession: MockitoSession - @Mock - lateinit var apexEnvironment: ApexEnvironment - - private val persistence = RolesPersistence.createInstance() - private val state = RolesState(1, "packagesHash", mapOf("role" to setOf("holder1", "holder2"))) - private val user = Process.myUserHandle() - - @Before - fun createMockDataDirectory() { - mockDataDirectory = context.getDir("mock_data", Context.MODE_PRIVATE) - mockDataDirectory.listFiles()!!.forEach { assertThat(it.deleteRecursively()).isTrue() } - } - - @Before - fun mockApexEnvironment() { - initMocks(this) - mockitoSession = mockitoSession() - .mockStatic(ApexEnvironment::class.java) - .strictness(Strictness.LENIENT) - .startMocking() - `when`(ApexEnvironment.getApexEnvironment(eq(APEX_MODULE_NAME))).thenReturn(apexEnvironment) - `when`(apexEnvironment.getDeviceProtectedDataDirForUser(any(UserHandle::class.java))).then { - File(mockDataDirectory, it.arguments[0].toString()).also { it.mkdirs() } - } - } - - @After - fun finishMockingApexEnvironment() { - mockitoSession.finishMocking() - } - - @Test - fun testReadWrite() { - persistence.writeForUser(state, user) - val persistedState = persistence.readForUser(user) - - assertThat(persistedState).isEqualTo(state) - assertThat(persistedState!!.version).isEqualTo(state.version) - assertThat(persistedState.packagesHash).isEqualTo(state.packagesHash) - assertThat(persistedState.roles).isEqualTo(state.roles) - } - - @Test - fun testDelete() { - persistence.writeForUser(state, user) - persistence.deleteForUser(user) - val persistedState = persistence.readForUser(user) - - assertThat(persistedState).isNull() - } - - companion object { - private const val APEX_MODULE_NAME = "com.android.permission" - } -} diff --git a/core/api/current.txt b/core/api/current.txt index 61d7aadce928..3dd1f598f94f 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -36822,9 +36822,10 @@ package android.security.identity { package android.security.keystore { public class BackendBusyException extends java.security.ProviderException { - ctor public BackendBusyException(); - ctor public BackendBusyException(@NonNull String); - ctor public BackendBusyException(@NonNull String, @NonNull Throwable); + ctor public BackendBusyException(long); + ctor public BackendBusyException(long, @NonNull String); + ctor public BackendBusyException(long, @NonNull String, @NonNull Throwable); + method public long getBackOffHintMillis(); } public class KeyExpiredException extends java.security.InvalidKeyException { @@ -39714,7 +39715,6 @@ package android.telecom { field public static final String EXTRA_HAS_PICTURE = "android.telecom.extra.HAS_PICTURE"; field public static final String EXTRA_INCOMING_CALL_ADDRESS = "android.telecom.extra.INCOMING_CALL_ADDRESS"; field public static final String EXTRA_INCOMING_CALL_EXTRAS = "android.telecom.extra.INCOMING_CALL_EXTRAS"; - field public static final String EXTRA_INCOMING_PICTURE = "android.telecom.extra.INCOMING_PICTURE"; field public static final String EXTRA_INCOMING_VIDEO_STATE = "android.telecom.extra.INCOMING_VIDEO_STATE"; field public static final String EXTRA_IS_DEFAULT_CALL_SCREENING_APP = "android.telecom.extra.IS_DEFAULT_CALL_SCREENING_APP"; field public static final String EXTRA_LOCATION = "android.telecom.extra.LOCATION"; @@ -39723,6 +39723,7 @@ package android.telecom { field public static final String EXTRA_OUTGOING_CALL_EXTRAS = "android.telecom.extra.OUTGOING_CALL_EXTRAS"; field public static final String EXTRA_OUTGOING_PICTURE = "android.telecom.extra.OUTGOING_PICTURE"; field public static final String EXTRA_PHONE_ACCOUNT_HANDLE = "android.telecom.extra.PHONE_ACCOUNT_HANDLE"; + field public static final String EXTRA_PICTURE_URI = "android.telecom.extra.PICTURE_URI"; field public static final String EXTRA_PRIORITY = "android.telecom.extra.PRIORITY"; field public static final String EXTRA_START_CALL_WITH_RTT = "android.telecom.extra.START_CALL_WITH_RTT"; field public static final String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.telecom.extra.START_CALL_WITH_SPEAKERPHONE"; @@ -46181,6 +46182,7 @@ package android.view { method @NonNull public android.graphics.Rect getBoundingRectRight(); method @NonNull public android.graphics.Rect getBoundingRectTop(); method @NonNull public java.util.List<android.graphics.Rect> getBoundingRects(); + method @Nullable public android.graphics.Path getCutoutPath(); method public int getSafeInsetBottom(); method public int getSafeInsetLeft(); method public int getSafeInsetRight(); diff --git a/core/api/system-current.txt b/core/api/system-current.txt index ca798b9aedbb..0a0f77e33ed4 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -5607,12 +5607,12 @@ package android.media.tv.tuner.filter { public class Filter implements java.lang.AutoCloseable { method public void close(); method public int configure(@NonNull android.media.tv.tuner.filter.FilterConfiguration); - method public int configureMonitorEvent(int); method public int flush(); method public int getId(); method public long getId64Bit(); method public int read(@NonNull byte[], long, long); method public int setDataSource(@Nullable android.media.tv.tuner.filter.Filter); + method public int setMonitorEventMask(int); method public int start(); method public int stop(); field public static final int MONITOR_EVENT_IP_CID_CHANGE = 2; // 0x2 @@ -7758,11 +7758,13 @@ package android.net.wifi.nl80211 { method public boolean setupInterfaceForSoftApMode(@NonNull String); method @Nullable public android.net.wifi.nl80211.WifiNl80211Manager.SignalPollResult signalPoll(@NonNull String); method public boolean startPnoScan(@NonNull String, @NonNull android.net.wifi.nl80211.PnoSettings, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.PnoScanRequestCallback); - method public boolean startScan(@NonNull String, int, @Nullable java.util.Set<java.lang.Integer>, @Nullable java.util.List<byte[]>); + method @Deprecated public boolean startScan(@NonNull String, int, @Nullable java.util.Set<java.lang.Integer>, @Nullable java.util.List<byte[]>); + method public boolean startScan(@NonNull String, int, @Nullable java.util.Set<java.lang.Integer>, @Nullable java.util.List<byte[]>, @Nullable android.os.Bundle); method public boolean stopPnoScan(@NonNull String); method public boolean tearDownClientInterface(@NonNull String); method public boolean tearDownInterfaces(); method public boolean tearDownSoftApInterface(@NonNull String); + field public static final String SCANNING_PARAM_ENABLE_6GHZ_RNR = "android.net.wifi.nl80211.SCANNING_PARAM_ENABLE_6GHZ_RNR"; field public static final int SCAN_TYPE_PNO_SCAN = 1; // 0x1 field public static final int SCAN_TYPE_SINGLE_SCAN = 0; // 0x0 field public static final int SEND_MGMT_FRAME_ERROR_ALREADY_STARTED = 5; // 0x5 @@ -12964,11 +12966,100 @@ package android.telephony.ims { field public static final String RCS_PROFILE_2_3 = "UP_2.3"; } + public final class RcsContactPresenceTuple implements android.os.Parcelable { + method public int describeContents(); + method @Nullable public android.net.Uri getContactUri(); + method @Nullable public android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities getServiceCapabilities(); + method @Nullable public String getServiceDescription(); + method @NonNull public String getServiceId(); + method @NonNull public String getServiceVersion(); + method @NonNull public String getStatus(); + method @Nullable public String getTimestamp(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsContactPresenceTuple> CREATOR; + field public static final String SERVICE_ID_CALL_COMPOSER = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.callcomposer"; + field public static final String SERVICE_ID_CHATBOT = "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.chatbot"; + field public static final String SERVICE_ID_CHATBOT_ROLE = "org.gsma.rcs.isbot"; + field public static final String SERVICE_ID_CHATBOT_STANDALONE = " org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.chatbot.sa"; + field public static final String SERVICE_ID_CHAT_V1 = "org.openmobilealliance:IM-session"; + field public static final String SERVICE_ID_CHAT_V2 = "org.openmobilealliance:ChatSession"; + field public static final String SERVICE_ID_FT = "org.openmobilealliance:File-Transfer-HTTP"; + field public static final String SERVICE_ID_FT_OVER_SMS = "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.ftsms"; + field public static final String SERVICE_ID_GEO_PUSH = "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.geopush"; + field public static final String SERVICE_ID_GEO_PUSH_VIA_SMS = "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.geosms"; + field public static final String SERVICE_ID_MMTEL = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.mmtel"; + field public static final String SERVICE_ID_POST_CALL = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.callunanswered"; + field public static final String SERVICE_ID_SHARED_MAP = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.sharedmap"; + field public static final String SERVICE_ID_SHARED_SKETCH = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.sharedsketch"; + field public static final String TUPLE_BASIC_STATUS_CLOSED = "closed"; + field public static final String TUPLE_BASIC_STATUS_OPEN = "open"; + } + + public static final class RcsContactPresenceTuple.Builder { + ctor public RcsContactPresenceTuple.Builder(@NonNull String, @NonNull String, @NonNull String); + method @NonNull public android.telephony.ims.RcsContactPresenceTuple build(); + method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setContactUri(@NonNull android.net.Uri); + method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setServiceCapabilities(@NonNull android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities); + method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setServiceDescription(@NonNull String); + method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setTimestamp(@NonNull String); + } + + public static final class RcsContactPresenceTuple.ServiceCapabilities implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.List<java.lang.String> getSupportedDuplexModes(); + method @NonNull public java.util.List<java.lang.String> getUnsupportedDuplexModes(); + method public boolean isAudioCapable(); + method public boolean isVideoCapable(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities> CREATOR; + field public static final String DUPLEX_MODE_FULL = "full"; + field public static final String DUPLEX_MODE_HALF = "half"; + field public static final String DUPLEX_MODE_RECEIVE_ONLY = "receive-only"; + field public static final String DUPLEX_MODE_SEND_ONLY = "send-only"; + } + + public static final class RcsContactPresenceTuple.ServiceCapabilities.Builder { + ctor public RcsContactPresenceTuple.ServiceCapabilities.Builder(boolean, boolean); + method @NonNull public android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities.Builder addSupportedDuplexMode(@NonNull String); + method @NonNull public android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities.Builder addUnsupportedDuplexMode(@NonNull String); + method @NonNull public android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities build(); + } + + public final class RcsContactUceCapability implements android.os.Parcelable { + method public int describeContents(); + method public int getCapabilityMechanism(); + method @Nullable public android.telephony.ims.RcsContactPresenceTuple getCapabilityTuple(@NonNull String); + method @NonNull public java.util.List<android.telephony.ims.RcsContactPresenceTuple> getCapabilityTuples(); + method @NonNull public android.net.Uri getContactUri(); + method public int getRequestResult(); + method public int getSourceType(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field public static final int CAPABILITY_MECHANISM_OPTIONS = 2; // 0x2 + field public static final int CAPABILITY_MECHANISM_PRESENCE = 1; // 0x1 + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsContactUceCapability> CREATOR; + field public static final int REQUEST_RESULT_FOUND = 3; // 0x3 + field public static final int REQUEST_RESULT_NOT_FOUND = 2; // 0x2 + field public static final int REQUEST_RESULT_NOT_ONLINE = 1; // 0x1 + field public static final int REQUEST_RESULT_UNKNOWN = 0; // 0x0 + field public static final int SOURCE_TYPE_CACHED = 1; // 0x1 + field public static final int SOURCE_TYPE_NETWORK = 0; // 0x0 + } + + public static final class RcsContactUceCapability.PresenceBuilder { + ctor public RcsContactUceCapability.PresenceBuilder(@NonNull android.net.Uri, int, int); + method @NonNull public android.telephony.ims.RcsContactUceCapability.PresenceBuilder addCapabilityTuple(@NonNull android.telephony.ims.RcsContactPresenceTuple); + method @NonNull public android.telephony.ims.RcsContactUceCapability.PresenceBuilder addCapabilityTuples(@NonNull java.util.List<android.telephony.ims.RcsContactPresenceTuple>); + method @NonNull public android.telephony.ims.RcsContactUceCapability build(); + } + public class RcsUceAdapter { method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void addOnPublishStateChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.OnPublishStateChangedListener) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getUcePublishState() throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void removeOnPublishStateChangedListener(@NonNull android.telephony.ims.RcsUceAdapter.OnPublishStateChangedListener) throws android.telephony.ims.ImsException; + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestAvailability(@NonNull android.net.Uri, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException; + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void requestCapabilities(@NonNull java.util.List<android.net.Uri>, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException; + field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2 field public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 1; // 0x1 field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 7; // 0x7 field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 6; // 0x6 @@ -12981,6 +13072,18 @@ package android.telephony.ims { field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED = 11; // 0xb field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN = 8; // 0x8 field public static final int CAPABILITY_UPDATE_TRIGGER_UNKNOWN = 0; // 0x0 + field public static final int ERROR_FORBIDDEN = 6; // 0x6 + field public static final int ERROR_GENERIC_FAILURE = 1; // 0x1 + field public static final int ERROR_INSUFFICIENT_MEMORY = 10; // 0xa + field public static final int ERROR_LOST_NETWORK = 11; // 0xb + field public static final int ERROR_NOT_AUTHORIZED = 5; // 0x5 + field public static final int ERROR_NOT_AVAILABLE = 3; // 0x3 + field public static final int ERROR_NOT_ENABLED = 2; // 0x2 + field public static final int ERROR_NOT_FOUND = 7; // 0x7 + field public static final int ERROR_NOT_REGISTERED = 4; // 0x4 + field public static final int ERROR_REQUEST_TIMEOUT = 9; // 0x9 + field public static final int ERROR_REQUEST_TOO_LARGE = 8; // 0x8 + field public static final int ERROR_SERVER_UNAVAILABLE = 12; // 0xc field public static final int PUBLISH_STATE_NOT_PUBLISHED = 2; // 0x2 field public static final int PUBLISH_STATE_OK = 1; // 0x1 field public static final int PUBLISH_STATE_OTHER_ERROR = 6; // 0x6 @@ -12989,6 +13092,12 @@ package android.telephony.ims { field public static final int PUBLISH_STATE_VOICE_PROVISION_ERROR = 3; // 0x3 } + public static interface RcsUceAdapter.CapabilitiesCallback { + method public void onCapabilitiesReceived(@NonNull java.util.List<android.telephony.ims.RcsContactUceCapability>); + method public void onComplete(); + method public void onError(int, long); + } + public static interface RcsUceAdapter.OnPublishStateChangedListener { method public void onPublishStateChange(int); } @@ -13400,6 +13509,7 @@ package android.telephony.ims.stub { public class RcsCapabilityExchangeImplBase { ctor public RcsCapabilityExchangeImplBase(@NonNull java.util.concurrent.Executor); method public void publishCapabilities(@NonNull String, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.PublishResponseCallback); + method public void subscribeForCapabilities(@NonNull java.util.List<android.net.Uri>, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.SubscribeResponseCallback); field public static final int COMMAND_CODE_FETCH_ERROR = 3; // 0x3 field public static final int COMMAND_CODE_GENERIC_FAILURE = 1; // 0x1 field public static final int COMMAND_CODE_INSUFFICIENT_MEMORY = 5; // 0x5 @@ -13418,6 +13528,14 @@ package android.telephony.ims.stub { method public void onNetworkResponse(@IntRange(from=100, to=699) int, @NonNull String) throws android.telephony.ims.ImsException; } + public static interface RcsCapabilityExchangeImplBase.SubscribeResponseCallback { + method public void onCommandError(int) throws android.telephony.ims.ImsException; + method public void onNetworkResponse(@IntRange(from=100, to=699) int, @NonNull String) throws android.telephony.ims.ImsException; + method public void onNotifyCapabilitiesUpdate(@NonNull java.util.List<java.lang.String>) throws android.telephony.ims.ImsException; + method public void onResourceTerminated(@NonNull java.util.List<android.util.Pair<android.net.Uri,java.lang.String>>) throws android.telephony.ims.ImsException; + method public void onTerminated(@NonNull String, long) throws android.telephony.ims.ImsException; + } + public interface SipDelegate { method public void closeDialog(@NonNull String); method public void notifyMessageReceiveError(@NonNull String, int); diff --git a/core/api/test-current.txt b/core/api/test-current.txt index c237d7d57238..ea7dc03c4007 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -684,6 +684,7 @@ package android.content.pm { method public void holdLock(android.os.IBinder, int); field public static final String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage"; field public static final String FEATURE_FILE_BASED_ENCRYPTION = "android.software.file_based_encryption"; + field public static final String FEATURE_HDMI_CEC = "android.hardware.hdmi.cec"; field public static final int FLAG_PERMISSION_REVOKE_WHEN_REQUESTED = 128; // 0x80 field public static final int MATCH_KNOWN_PACKAGES = 4202496; // 0x402000 field public static final String SYSTEM_SHARED_LIBRARY_SERVICES = "android.ext.services"; diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 68d3a9203f12..49f508d83f91 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -5255,6 +5255,7 @@ public class Notification implements Parcelable // We still want a time to be set but gone, such that we can show and hide it // on demand in case it's a child notification without anything in the header contentView.setLong(R.id.time, "setTime", mN.when != 0 ? mN.when : mN.creationTime); + setTextViewColorSecondary(contentView, R.id.time, p); } } diff --git a/apex/permission/framework/java/android/permission/PermissionState.java b/core/java/android/app/people/ConversationChannel.aidl index e810db8ecbfe..78df2f10c337 100644 --- a/apex/permission/framework/java/android/permission/PermissionState.java +++ b/core/java/android/app/people/ConversationChannel.aidl @@ -1,11 +1,11 @@ -/* - * Copyright (C) 2020 The Android Open Source Project +/** + * Copyright (c) 2021, 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 + * 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, @@ -14,9 +14,6 @@ * limitations under the License. */ -package android.permission; +package android.app.people; -/** - * @hide - */ -public class PermissionState {} +parcelable ConversationChannel;
\ No newline at end of file diff --git a/core/java/android/app/people/IPeopleManager.aidl b/core/java/android/app/people/IPeopleManager.aidl index 0d12ed02f610..ebe9f60dc150 100644 --- a/core/java/android/app/people/IPeopleManager.aidl +++ b/core/java/android/app/people/IPeopleManager.aidl @@ -17,6 +17,7 @@ package android.app.people; import android.app.people.ConversationStatus; +import android.app.people.ConversationChannel; import android.content.pm.ParceledListSlice; import android.net.Uri; import android.os.IBinder; @@ -26,6 +27,13 @@ import android.os.IBinder; * {@hide} */ interface IPeopleManager { + + /** + * Returns the specified conversation from the conversations list. If the conversation can't be + * found, returns null. + */ + ConversationChannel getConversation(in String packageName, int userId, in String shortcutId); + /** * Returns the recent conversations. The conversations that have customized notification * settings are excluded from the returned list. diff --git a/core/java/android/content/integrity/OWNERS b/core/java/android/content/integrity/OWNERS new file mode 100644 index 000000000000..20c758aedd67 --- /dev/null +++ b/core/java/android/content/integrity/OWNERS @@ -0,0 +1,5 @@ +# Bug component: 722021 + +toddke@android.com +toddke@google.com +patb@google.com diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 68792b2f47de..9ae9c25c8c08 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -3385,6 +3385,7 @@ public abstract class PackageManager { * {@link #hasSystemFeature}: This device supports HDMI-CEC. * @hide */ + @TestApi @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_HDMI_CEC = "android.hardware.hdmi.cec"; diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index fe8bf9d163c7..e6c0f6a4c2fa 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -6265,6 +6265,55 @@ public class PackageParser { } /** + * Returns whether this {@code SigningDetails} has a signer in common with the provided + * {@code otherDetails} with the specified {@code flags} capabilities provided by this + * signer. + * + * <p>Note this method allows for the signing lineage to diverge, so this should only be + * used for instances where the only requirement is a common signer in the lineage with + * the specified capabilities. If the current signer of this instance is an ancestor of + * {@code otherDetails} then {@code true} is immediately returned since the current signer + * has all capabilities granted. + */ + public boolean hasCommonSignerWithCapability(SigningDetails otherDetails, + @CertCapabilities int flags) { + if (this == UNKNOWN || otherDetails == UNKNOWN) { + return false; + } + // If either is signed with more than one signer then both must be signed by the same + // signers to consider the capabilities granted. + if (signatures.length > 1 || otherDetails.signatures.length > 1) { + return signaturesMatchExactly(otherDetails); + } + // The Signature class does not use the granted capabilities in the hashCode + // computation, so a Set can be used to check for a common signer. + Set<Signature> otherSignatures = new ArraySet<>(); + if (otherDetails.hasPastSigningCertificates()) { + otherSignatures.addAll(Arrays.asList(otherDetails.pastSigningCertificates)); + } else { + otherSignatures.addAll(Arrays.asList(otherDetails.signatures)); + } + // If the current signer of this instance is an ancestor of the other than return true + // since all capabilities are granted to the current signer. + if (otherSignatures.contains(signatures[0])) { + return true; + } + if (hasPastSigningCertificates()) { + // Since the current signer was checked above and the last signature in the + // pastSigningCertificates is the current signer skip checking the last element. + for (int i = 0; i < pastSigningCertificates.length - 1; i++) { + if (otherSignatures.contains(pastSigningCertificates[i])) { + // If the caller specified multiple capabilities ensure all are set. + if ((pastSigningCertificates[i].getFlags() & flags) == flags) { + return true; + } + } + } + } + return false; + } + + /** * Determines if the provided {@code oldDetails} is an ancestor of this one, and whether or * not this one grants it the provided capability, represented by the {@code flags} * parameter. In the event of signing certificate rotation, a package may still interact diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java index 75893d978749..588bc01d7d42 100644 --- a/core/java/android/hardware/face/FaceManager.java +++ b/core/java/android/hardware/face/FaceManager.java @@ -301,7 +301,8 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan @RequiresPermission(MANAGE_BIOMETRIC) public void enroll(int userId, byte[] hardwareAuthToken, CancellationSignal cancel, EnrollmentCallback callback, int[] disabledFeatures) { - enroll(userId, hardwareAuthToken, cancel, callback, disabledFeatures, null /* surface */); + enroll(userId, hardwareAuthToken, cancel, callback, disabledFeatures, null /* surface */, + false /* debugConsent */); } /** @@ -313,18 +314,20 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan * which point the object is no longer valid. The operation can be canceled by using the * provided cancel object. * - * @param token a unique token provided by a recent creation or verification of device - * credentials (e.g. pin, pattern or password). - * @param cancel an object that can be used to cancel enrollment - * @param userId the user to whom this face will belong to - * @param callback an object to receive enrollment events - * @param surface optional camera preview surface for a single-camera device. Must be null if - * not used. + * @param hardwareAuthToken a unique token provided by a recent creation or + * verification of device credentials (e.g. pin, pattern or password). + * @param cancel an object that can be used to cancel enrollment + * @param userId the user to whom this face will belong to + * @param callback an object to receive enrollment events + * @param surface optional camera preview surface for a single-camera device. + * Must be null if not used. + * @param debugConsent a feature flag that the user has consented to debug. * @hide */ @RequiresPermission(MANAGE_BIOMETRIC) public void enroll(int userId, byte[] hardwareAuthToken, CancellationSignal cancel, - EnrollmentCallback callback, int[] disabledFeatures, @Nullable Surface surface) { + EnrollmentCallback callback, int[] disabledFeatures, @Nullable Surface surface, + boolean debugConsent) { if (callback == null) { throw new IllegalArgumentException("Must supply an enrollment callback"); } @@ -343,7 +346,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan mEnrollmentCallback = callback; Trace.beginSection("FaceManager#enroll"); mService.enroll(userId, mToken, hardwareAuthToken, mServiceReceiver, - mContext.getOpPackageName(), disabledFeatures, surface); + mContext.getOpPackageName(), disabledFeatures, surface, debugConsent); } catch (RemoteException e) { Slog.w(TAG, "Remote exception in enroll: ", e); // Though this may not be a hardware issue, it will cause apps to give up or diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl index 1b188e87e90f..a3e7e2d2c5cb 100644 --- a/core/java/android/hardware/face/IFaceService.aidl +++ b/core/java/android/hardware/face/IFaceService.aidl @@ -74,7 +74,7 @@ interface IFaceService { // Start face enrollment void enroll(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver, - String opPackageName, in int [] disabledFeatures, in Surface surface); + String opPackageName, in int [] disabledFeatures, in Surface surface, boolean debugConsent); // Start remote face enrollment void enrollRemotely(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver, diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java index 9fdc72bbe6c6..59292baa110c 100644 --- a/core/java/android/os/incremental/IncrementalFileStorages.java +++ b/core/java/android/os/incremental/IncrementalFileStorages.java @@ -36,7 +36,6 @@ import android.annotation.Nullable; import android.content.Context; import android.content.pm.DataLoaderParams; import android.content.pm.IDataLoaderStatusListener; -import android.content.pm.IPackageLoadingProgressCallback; import android.content.pm.InstallationFileParcel; import android.text.TextUtils; @@ -71,8 +70,7 @@ public final class IncrementalFileStorages { @Nullable StorageHealthCheckParams healthCheckParams, @Nullable IStorageHealthListener healthListener, @NonNull List<InstallationFileParcel> addedFiles, - @NonNull PerUidReadTimeouts[] perUidReadTimeouts, - IPackageLoadingProgressCallback progressCallback) throws IOException { + @NonNull PerUidReadTimeouts[] perUidReadTimeouts) throws IOException { // TODO(b/136132412): validity check if session should not be incremental IncrementalManager incrementalManager = (IncrementalManager) context.getSystemService( Context.INCREMENTAL_SERVICE); @@ -97,11 +95,7 @@ public final class IncrementalFileStorages { throw new IOException("Unknown file location: " + file.location); } } - // Register progress loading callback after files have been added - if (progressCallback != null) { - incrementalManager.registerLoadingProgressCallback(stageDir.getAbsolutePath(), - progressCallback); - } + result.startLoading(); return result; @@ -186,7 +180,6 @@ public final class IncrementalFileStorages { try { mDefaultStorage.unBind(mStageDir.getAbsolutePath()); - mDefaultStorage.unregisterLoadingProgressListener(); } catch (IOException ignored) { } mDefaultStorage = null; diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java index b1b29254783c..8b6082b30dee 100644 --- a/core/java/android/permission/PermissionUsageHelper.java +++ b/core/java/android/permission/PermissionUsageHelper.java @@ -28,7 +28,10 @@ import static android.app.AppOpsManager.OPSTR_RECORD_AUDIO; import static android.app.AppOpsManager.OP_FLAGS_ALL_TRUSTED; import static android.app.AppOpsManager.opToPermission; import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED; +import static android.media.AudioSystem.MODE_IN_COMMUNICATION; +import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; +import android.Manifest; import android.annotation.NonNull; import android.app.AppOpsManager; import android.content.ComponentName; @@ -41,12 +44,14 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.icu.text.ListFormatter; import android.location.LocationManager; +import android.media.AudioManager; import android.os.Process; import android.os.UserHandle; import android.provider.DeviceConfig; import android.provider.Settings; import android.speech.RecognitionService; import android.speech.RecognizerIntent; +import android.telephony.TelephonyManager; import android.util.ArrayMap; import android.util.ArraySet; import android.view.inputmethod.InputMethodInfo; @@ -75,6 +80,9 @@ public class PermissionUsageHelper { private static final String PROPERTY_LOCATION_INDICATORS_ENABLED = "location_indicators_enabled"; + /** Whether to show the Permissions Hub. */ + private static final String PROPERTY_PERMISSIONS_HUB_2_ENABLED = "permissions_hub_2_enabled"; + /** How long after an access to show it as "recent" */ private static final String RECENT_ACCESS_TIME_MS = "recent_acccess_time_ms"; @@ -84,17 +92,25 @@ public class PermissionUsageHelper { /** The name of the expected voice IME subtype */ private static final String VOICE_IME_SUBTYPE = "voice"; + private static final String SYSTEM_PKG = "android"; + private static final long DEFAULT_RUNNING_TIME_MS = 5000L; private static final long DEFAULT_RECENT_TIME_MS = 30000L; + private static boolean shouldShowPermissionsHub() { + return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, + PROPERTY_PERMISSIONS_HUB_2_ENABLED, false); + } + private static boolean shouldShowIndicators() { return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, - PROPERTY_CAMERA_MIC_ICONS_ENABLED, true); + PROPERTY_CAMERA_MIC_ICONS_ENABLED, true) || shouldShowPermissionsHub(); } private static boolean shouldShowLocationIndicator() { return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, - PROPERTY_LOCATION_INDICATORS_ENABLED, false); + PROPERTY_LOCATION_INDICATORS_ENABLED, false) + || shouldShowPermissionsHub(); } private static long getRecentThreshold(Long now) { @@ -113,7 +129,7 @@ public class PermissionUsageHelper { ); private static final List<String> MIC_OPS = List.of( - OPSTR_PHONE_CALL_CAMERA, + OPSTR_PHONE_CALL_MICROPHONE, OPSTR_RECORD_AUDIO ); @@ -163,6 +179,13 @@ public class PermissionUsageHelper { return mUserContexts.get(user); } + // TODO ntmyren: Replace this with better check if this moves beyond teamfood + private boolean isAppPredictor(String packageName, UserHandle user) { + return shouldShowPermissionsHub() && getUserContext(user).getPackageManager() + .checkPermission(Manifest.permission.MANAGE_APP_PREDICTIONS, packageName) + == PackageManager.PERMISSION_GRANTED; + } + /** * @see PermissionManager.getIndicatorAppOpUsageData */ @@ -186,7 +209,28 @@ public class PermissionUsageHelper { Map<PackageAttribution, CharSequence> packagesWithAttributionLabels = getTrustedAttributions(rawUsages.get(MICROPHONE), proxyChains); - List<String> usedPermGroups = new ArrayList<>(rawUsages.keySet()); + ArrayList<String> usedPermGroups = new ArrayList<>(rawUsages.keySet()); + + // If we have a phone call, and a carrier privileged app using microphone, hide the + // phone call. + AudioManager audioManager = mContext.getSystemService(AudioManager.class); + boolean hasPhoneCall = usedPermGroups.contains(OPSTR_PHONE_CALL_CAMERA) + || usedPermGroups.contains(OPSTR_PHONE_CALL_MICROPHONE); + if (hasPhoneCall && usedPermGroups.contains(MICROPHONE) && audioManager.getMode() + == MODE_IN_COMMUNICATION) { + TelephonyManager telephonyManager = + mContext.getSystemService(TelephonyManager.class); + List<OpUsage> permUsages = rawUsages.get(MICROPHONE); + for (int usageNum = 0; usageNum < permUsages.size(); usageNum++) { + if (telephonyManager.checkCarrierPrivilegesForPackage( + permUsages.get(usageNum).packageName) + == CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { + usedPermGroups.remove(OPSTR_PHONE_CALL_CAMERA); + usedPermGroups.remove(OPSTR_PHONE_CALL_MICROPHONE); + } + } + } + for (int permGroupNum = 0; permGroupNum < usedPermGroups.size(); permGroupNum++) { boolean isPhone = false; String permGroup = usedPermGroups.get(permGroupNum); @@ -269,8 +313,11 @@ public class PermissionUsageHelper { if (lastAccessTime < recentThreshold && !attrOpEntry.isRunning()) { continue; } - if (!isUserSensitive(packageName, user, op) - && !isLocationProvider(packageName, user)) { + + if (packageName.equals(SYSTEM_PKG) + || (!isUserSensitive(packageName, user, op) + && !isLocationProvider(packageName, user) + && !isAppPredictor(packageName, user))) { continue; } diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java index 33e33ee5fa02..9bfd75ef2170 100644 --- a/core/java/android/provider/CallLog.java +++ b/core/java/android/provider/CallLog.java @@ -249,11 +249,13 @@ public class CallLog { // Nasty casework for the shadow calllog begins... // First see if we're just inserting for one user. If so, insert into the shadow // based on whether that user is unlocked. - if (user != null) { - Uri baseUri = userManager.isUserUnlocked(user) ? CALL_COMPOSER_PICTURE_URI + UserHandle realUser = UserHandle.CURRENT.equals(user) + ? android.os.Process.myUserHandle() : user; + if (realUser != null) { + Uri baseUri = userManager.isUserUnlocked(realUser) ? CALL_COMPOSER_PICTURE_URI : SHADOW_CALL_COMPOSER_PICTURE_URI; Uri pictureInsertionUri = ContentProvider.maybeAddUserId(baseUri, - user.getIdentifier()); + realUser.getIdentifier()); Log.i(LOG_TAG, "Inserting call composer for single user at " + pictureInsertionUri); diff --git a/core/java/android/rotationresolver/OWNERS b/core/java/android/rotationresolver/OWNERS index 81b6f05a1658..733fca934c4e 100644 --- a/core/java/android/rotationresolver/OWNERS +++ b/core/java/android/rotationresolver/OWNERS @@ -1 +1 @@ -include /core/java/android/rotationresolver/OWNERS +include /core/java/android/service/rotationresolver/OWNERS diff --git a/core/java/android/util/RotationUtils.java b/core/java/android/util/RotationUtils.java index a44ed59c14d4..698cb77947cf 100644 --- a/core/java/android/util/RotationUtils.java +++ b/core/java/android/util/RotationUtils.java @@ -21,7 +21,9 @@ import static android.view.Surface.ROTATION_180; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; +import android.annotation.Dimension; import android.graphics.Insets; +import android.graphics.Matrix; import android.view.Surface.Rotation; /** @@ -69,4 +71,34 @@ public class RotationUtils { } return rotated; } + + /** + * Sets a matrix such that given a rotation, it transforms physical display + * coordinates to that rotation's logical coordinates. + * + * @param rotation the rotation to which the matrix should transform + * @param out the matrix to be set + */ + public static void transformPhysicalToLogicalCoordinates(@Rotation int rotation, + @Dimension int physicalWidth, @Dimension int physicalHeight, Matrix out) { + switch (rotation) { + case ROTATION_0: + out.reset(); + break; + case ROTATION_90: + out.setRotate(270); + out.postTranslate(0, physicalWidth); + break; + case ROTATION_180: + out.setRotate(180); + out.postTranslate(physicalWidth, physicalHeight); + break; + case ROTATION_270: + out.setRotate(90); + out.postTranslate(physicalHeight, 0); + break; + default: + throw new IllegalArgumentException("Unknown rotation: " + rotation); + } + } } diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java index 525ac534612d..e1a4402d8964 100644 --- a/core/java/android/view/DisplayCutout.java +++ b/core/java/android/view/DisplayCutout.java @@ -23,6 +23,7 @@ import static android.view.DisplayCutoutProto.BOUND_LEFT; import static android.view.DisplayCutoutProto.BOUND_RIGHT; import static android.view.DisplayCutoutProto.BOUND_TOP; import static android.view.DisplayCutoutProto.INSETS; +import static android.view.Surface.ROTATION_0; import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE; @@ -31,13 +32,16 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.res.Resources; import android.graphics.Insets; +import android.graphics.Matrix; import android.graphics.Path; import android.graphics.Rect; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; import android.util.Pair; +import android.util.RotationUtils; import android.util.proto.ProtoOutputStream; +import android.view.Surface.Rotation; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; @@ -69,6 +73,9 @@ public final class DisplayCutout { "com.android.internal.display_cutout_emulation"; private static final Rect ZERO_RECT = new Rect(); + private static final CutoutPathParserInfo EMPTY_PARSER_INFO = new CutoutPathParserInfo( + 0 /* displayWidth */, 0 /* displayHeight */, 0f /* density */, "" /* cutoutSpec */, + 0 /* rotation */, 0f /* scale */); /** * An instance where {@link #isEmpty()} returns {@code true}. @@ -76,7 +83,7 @@ public final class DisplayCutout { * @hide */ public static final DisplayCutout NO_CUTOUT = new DisplayCutout( - ZERO_RECT, Insets.NONE, ZERO_RECT, ZERO_RECT, ZERO_RECT, ZERO_RECT, + ZERO_RECT, Insets.NONE, ZERO_RECT, ZERO_RECT, ZERO_RECT, ZERO_RECT, EMPTY_PARSER_INFO, false /* copyArguments */); @@ -96,11 +103,15 @@ public final class DisplayCutout { @GuardedBy("CACHE_LOCK") private static Insets sCachedWaterfallInsets; + @GuardedBy("CACHE_LOCK") + private static CutoutPathParserInfo sCachedCutoutPathParserInfo; + @GuardedBy("CACHE_LOCK") + private static Path sCachedCutoutPath; + private final Rect mSafeInsets; @NonNull private final Insets mWaterfallInsets; - /** * The bound is at the left of the screen. * @hide @@ -210,6 +221,7 @@ public final class DisplayCutout { } return result; } + @Override public boolean equals(@Nullable Object o) { if (o == this) { @@ -232,6 +244,106 @@ public final class DisplayCutout { private final Bounds mBounds; /** + * Stores all the needed info to create the cutout paths. + * + * @hide + */ + public static class CutoutPathParserInfo { + private final int mDisplayWidth; + private final int mDisplayHeight; + private final float mDensity; + private final String mCutoutSpec; + private final @Rotation int mRotation; + private final float mScale; + + public CutoutPathParserInfo(int displayWidth, int displayHeight, float density, + String cutoutSpec, @Rotation int rotation, float scale) { + mDisplayWidth = displayWidth; + mDisplayHeight = displayHeight; + mDensity = density; + mCutoutSpec = cutoutSpec == null ? "" : cutoutSpec; + mRotation = rotation; + mScale = scale; + } + + public CutoutPathParserInfo(CutoutPathParserInfo cutoutPathParserInfo) { + mDisplayWidth = cutoutPathParserInfo.mDisplayWidth; + mDisplayHeight = cutoutPathParserInfo.mDisplayHeight; + mDensity = cutoutPathParserInfo.mDensity; + mCutoutSpec = cutoutPathParserInfo.mCutoutSpec; + mRotation = cutoutPathParserInfo.mRotation; + mScale = cutoutPathParserInfo.mScale; + } + + public int getDisplayWidth() { + return mDisplayWidth; + } + + public int getDisplayHeight() { + return mDisplayHeight; + } + + public float getDensity() { + return mDensity; + } + + public @NonNull String getCutoutSpec() { + return mCutoutSpec; + } + + public int getRotation() { + return mRotation; + } + + public float getScale() { + return mScale; + } + + private boolean hasCutout() { + return !mCutoutSpec.isEmpty(); + } + + @Override + public int hashCode() { + int result = 0; + result = result * 48271 + Integer.hashCode(mDisplayWidth); + result = result * 48271 + Integer.hashCode(mDisplayHeight); + result = result * 48271 + Float.hashCode(mDensity); + result = result * 48271 + mCutoutSpec.hashCode(); + result = result * 48271 + Integer.hashCode(mRotation); + result = result * 48271 + Float.hashCode(mScale); + return result; + } + + @Override + public boolean equals(@Nullable Object o) { + if (o == this) { + return true; + } + if (o instanceof CutoutPathParserInfo) { + CutoutPathParserInfo c = (CutoutPathParserInfo) o; + return mDisplayWidth == c.mDisplayWidth && mDisplayHeight == c.mDisplayHeight + && mDensity == c.mDensity && mCutoutSpec.equals(c.mCutoutSpec) + && mRotation == c.mRotation && mScale == c.mScale; + } + return false; + } + + @Override + public String toString() { + return "CutoutPathParserInfo{displayWidth=" + mDisplayWidth + + " displayHeight=" + mDisplayHeight + + " density={" + mDensity + "}" + + " cutoutSpec={" + mCutoutSpec + "}" + + " rotation={" + mRotation + "}" + + " scale={" + mScale + "}" + + "}"; + } + } + + private final @NonNull CutoutPathParserInfo mCutoutPathParserInfo; + + /** * Creates a DisplayCutout instance. * * <p>Note that this is only useful for tests. For production code, developers should always @@ -251,7 +363,8 @@ public final class DisplayCutout { // TODO(b/73953958): @VisibleForTesting(visibility = PRIVATE) public DisplayCutout(@NonNull Insets safeInsets, @Nullable Rect boundLeft, @Nullable Rect boundTop, @Nullable Rect boundRight, @Nullable Rect boundBottom) { - this(safeInsets.toRect(), Insets.NONE, boundLeft, boundTop, boundRight, boundBottom, true); + this(safeInsets.toRect(), Insets.NONE, boundLeft, boundTop, boundRight, boundBottom, null, + true); } /** @@ -276,7 +389,7 @@ public final class DisplayCutout { @Nullable Rect boundTop, @Nullable Rect boundRight, @Nullable Rect boundBottom, @NonNull Insets waterfallInsets) { this(safeInsets.toRect(), waterfallInsets, boundLeft, boundTop, boundRight, boundBottom, - true); + null, true); } /** @@ -294,7 +407,7 @@ public final class DisplayCutout { // TODO(b/73953958): @VisibleForTesting(visibility = PRIVATE) @Deprecated public DisplayCutout(@Nullable Rect safeInsets, @Nullable List<Rect> boundingRects) { - this(safeInsets, Insets.NONE, extractBoundsFromList(safeInsets, boundingRects), + this(safeInsets, Insets.NONE, extractBoundsFromList(safeInsets, boundingRects), null, true /* copyArguments */); } @@ -303,28 +416,42 @@ public final class DisplayCutout { * * @param safeInsets the insets from each edge which avoid the display cutout as returned by * {@link #getSafeInsetTop()} etc. + * @param waterfallInsets the insets for the curved areas in waterfall display. + * @param boundLeft the left bounding rect of the display cutout in pixels. If null is passed, + * it's treated as an empty rectangle (0,0)-(0,0). + * @param boundTop the top bounding rect of the display cutout in pixels. If null is passed, + * it's treated as an empty rectangle (0,0)-(0,0). + * @param boundRight the right bounding rect of the display cutout in pixels. If null is + * passed, it's treated as an empty rectangle (0,0)-(0,0). + * @param boundBottom the bottom bounding rect of the display cutout in pixels. If null is + * passed, it's treated as an empty rectangle (0,0)-(0,0). + * @param info the cutout path parser info. * @param copyArguments if true, create a copy of the arguments. If false, the passed arguments * are not copied and MUST remain unchanged forever. */ - private DisplayCutout(Rect safeInsets, Insets waterfallInsets, Rect boundLeft, - Rect boundTop, Rect boundRight, Rect boundBottom, boolean copyArguments) { + private DisplayCutout(Rect safeInsets, Insets waterfallInsets, Rect boundLeft, Rect boundTop, + Rect boundRight, Rect boundBottom, CutoutPathParserInfo info, + boolean copyArguments) { mSafeInsets = getCopyOrRef(safeInsets, copyArguments); mWaterfallInsets = waterfallInsets == null ? Insets.NONE : waterfallInsets; mBounds = new Bounds(boundLeft, boundTop, boundRight, boundBottom, copyArguments); + mCutoutPathParserInfo = info == null ? EMPTY_PARSER_INFO : info; } private DisplayCutout(Rect safeInsets, Insets waterfallInsets, Rect[] bounds, - boolean copyArguments) { + CutoutPathParserInfo info, boolean copyArguments) { mSafeInsets = getCopyOrRef(safeInsets, copyArguments); mWaterfallInsets = waterfallInsets == null ? Insets.NONE : waterfallInsets; mBounds = new Bounds(bounds, copyArguments); + mCutoutPathParserInfo = info == null ? EMPTY_PARSER_INFO : info; } - private DisplayCutout(Rect safeInsets, Insets waterfallInsets, Bounds bounds) { + private DisplayCutout(Rect safeInsets, Insets waterfallInsets, Bounds bounds, + CutoutPathParserInfo info) { mSafeInsets = safeInsets; mWaterfallInsets = waterfallInsets == null ? Insets.NONE : waterfallInsets; mBounds = bounds; - + mCutoutPathParserInfo = info == null ? EMPTY_PARSER_INFO : info; } private static Rect getCopyOrRef(Rect r, boolean copyArguments) { @@ -534,10 +661,70 @@ public final class DisplayCutout { return mBounds.getRect(BOUNDS_POSITION_BOTTOM); } + /** + * Returns a {@link Path} that contains the cutout paths of all sides on the display. + * + * To get a cutout path for one specific side, apps can intersect the {@link Path} with the + * {@link Rect} obtained from {@link #getBoundingRectLeft()}, {@link #getBoundingRectTop()}, + * {@link #getBoundingRectRight()} or {@link #getBoundingRectBottom()}. + * + * @return a {@link Path} contains all the cutout paths based on display coordinate. Returns + * null if there is no cutout on the display. + */ + public @Nullable Path getCutoutPath() { + if (!mCutoutPathParserInfo.hasCutout()) { + return null; + } + synchronized (CACHE_LOCK) { + if (mCutoutPathParserInfo.equals(sCachedCutoutPathParserInfo)) { + return sCachedCutoutPath; + } + } + final CutoutSpecification cutoutSpec = new CutoutSpecification.Parser( + mCutoutPathParserInfo.getDensity(), mCutoutPathParserInfo.getDisplayWidth(), + mCutoutPathParserInfo.getDisplayHeight()) + .parse(mCutoutPathParserInfo.getCutoutSpec()); + + final Path cutoutPath = cutoutSpec.getPath(); + if (cutoutPath == null || cutoutPath.isEmpty()) { + return null; + } + final Matrix matrix = new Matrix(); + if (mCutoutPathParserInfo.getRotation() != ROTATION_0) { + RotationUtils.transformPhysicalToLogicalCoordinates( + mCutoutPathParserInfo.getRotation(), + mCutoutPathParserInfo.getDisplayWidth(), + mCutoutPathParserInfo.getDisplayHeight(), + matrix + ); + } + matrix.postScale(mCutoutPathParserInfo.getScale(), mCutoutPathParserInfo.getScale()); + cutoutPath.transform(matrix); + + synchronized (CACHE_LOCK) { + sCachedCutoutPathParserInfo = new CutoutPathParserInfo(mCutoutPathParserInfo); + sCachedCutoutPath = cutoutPath; + } + return cutoutPath; + } + + /** + * @return the {@link CutoutPathParserInfo}; + * + * @hide + */ + public CutoutPathParserInfo getCutoutPathParserInfo() { + return mCutoutPathParserInfo; + } + @Override public int hashCode() { - return (mSafeInsets.hashCode() * 48271 + mBounds.hashCode()) * 48271 - + mWaterfallInsets.hashCode(); + int result = 0; + result = 48271 * result + mSafeInsets.hashCode(); + result = 48271 * result + mBounds.hashCode(); + result = 48271 * result + mWaterfallInsets.hashCode(); + result = 48271 * result + mCutoutPathParserInfo.hashCode(); + return result; } @Override @@ -548,7 +735,8 @@ public final class DisplayCutout { if (o instanceof DisplayCutout) { DisplayCutout c = (DisplayCutout) o; return mSafeInsets.equals(c.mSafeInsets) && mBounds.equals(c.mBounds) - && mWaterfallInsets.equals(c.mWaterfallInsets); + && mWaterfallInsets.equals(c.mWaterfallInsets) + && mCutoutPathParserInfo.equals(c.mCutoutPathParserInfo); } return false; } @@ -558,6 +746,7 @@ public final class DisplayCutout { return "DisplayCutout{insets=" + mSafeInsets + " waterfall=" + mWaterfallInsets + " boundingRect={" + mBounds + "}" + + " cutoutPathParserInfo={" + mCutoutPathParserInfo + "}" + "}"; } @@ -607,7 +796,7 @@ public final class DisplayCutout { } return new DisplayCutout(safeInsets, Insets.of(waterfallInsets), bounds, - false /* copyArguments */); + mCutoutPathParserInfo, false /* copyArguments */); } private Rect insetInsets(int insetLeft, int insetTop, int insetRight, int insetBottom, @@ -638,7 +827,8 @@ public final class DisplayCutout { * @hide */ public DisplayCutout replaceSafeInsets(Rect safeInsets) { - return new DisplayCutout(new Rect(safeInsets), mWaterfallInsets, mBounds); + return new DisplayCutout(new Rect(safeInsets), mWaterfallInsets, mBounds, + mCutoutPathParserInfo); } private static int atLeastZero(int value) { @@ -658,16 +848,18 @@ public final class DisplayCutout { for (int i = 0; i < BOUNDS_POSITION_LENGTH; ++i) { bounds[i] = (pos == i) ? new Rect(left, top, right, bottom) : new Rect(); } - return new DisplayCutout(ZERO_RECT, Insets.NONE, bounds, false /* copyArguments */); + return new DisplayCutout(ZERO_RECT, Insets.NONE, bounds, null, false /* copyArguments */); } /** - * Creates an instance from a bounding and waterfall insets. + * Creates an instance from bounds, waterfall insets and CutoutPathParserInfo. * * @hide */ - public static DisplayCutout fromBoundsAndWaterfall(Rect[] bounds, Insets waterfallInsets) { - return new DisplayCutout(ZERO_RECT, waterfallInsets, bounds, false /* copyArguments */); + public static DisplayCutout constructDisplayCutout(Rect[] bounds, Insets waterfallInsets, + CutoutPathParserInfo info) { + return new DisplayCutout(ZERO_RECT, waterfallInsets, bounds, info, + false /* copyArguments */); } /** @@ -676,7 +868,8 @@ public final class DisplayCutout { * @hide */ public static DisplayCutout fromBounds(Rect[] bounds) { - return new DisplayCutout(ZERO_RECT, Insets.NONE, bounds, false /* copyArguments */); + return new DisplayCutout(ZERO_RECT, Insets.NONE, bounds, null /* cutoutPathParserInfo */, + false /* copyArguments */); } /** @@ -686,10 +879,12 @@ public final class DisplayCutout { * * @hide */ - public static DisplayCutout fromResourcesRectApproximation(Resources res, int displayWidth, int displayHeight) { - return fromSpec(res.getString(R.string.config_mainBuiltInDisplayCutoutRectApproximation), + public static DisplayCutout fromResourcesRectApproximation(Resources res, int displayWidth, + int displayHeight) { + return pathAndDisplayCutoutFromSpec(res.getString(R.string.config_mainBuiltInDisplayCutout), + res.getString(R.string.config_mainBuiltInDisplayCutoutRectApproximation), displayWidth, displayHeight, DENSITY_DEVICE_STABLE / (float) DENSITY_DEFAULT, - loadWaterfallInset(res)); + loadWaterfallInset(res)).second; } /** @@ -699,7 +894,7 @@ public final class DisplayCutout { */ public static Path pathFromResources(Resources res, int displayWidth, int displayHeight) { return pathAndDisplayCutoutFromSpec( - res.getString(R.string.config_mainBuiltInDisplayCutout), + res.getString(R.string.config_mainBuiltInDisplayCutout), null, displayWidth, displayHeight, DENSITY_DEVICE_STABLE / (float) DENSITY_DEFAULT, loadWaterfallInset(res)).first; } @@ -710,14 +905,30 @@ public final class DisplayCutout { * @hide */ @VisibleForTesting(visibility = PRIVATE) - public static DisplayCutout fromSpec(String spec, int displayWidth, int displayHeight, - float density, Insets waterfallInsets) { + public static DisplayCutout fromSpec(String pathSpec, int displayWidth, + int displayHeight, float density, Insets waterfallInsets) { return pathAndDisplayCutoutFromSpec( - spec, displayWidth, displayHeight, density, waterfallInsets).second; + pathSpec, null, displayWidth, displayHeight, density, waterfallInsets) + .second; } - private static Pair<Path, DisplayCutout> pathAndDisplayCutoutFromSpec(String spec, - int displayWidth, int displayHeight, float density, Insets waterfallInsets) { + /** + * Gets the cutout path and the corresponding DisplayCutout instance from the spec string. + * + * @param pathSpec the spec string read from config_mainBuiltInDisplayCutout. + * @param rectSpec the spec string read from config_mainBuiltInDisplayCutoutRectApproximation. + * @param displayWidth the display width. + * @param displayHeight the display height. + * @param density the display density. + * @param waterfallInsets the waterfall insets of the display. + * @return a Pair contains the cutout path and the corresponding DisplayCutout instance. + */ + private static Pair<Path, DisplayCutout> pathAndDisplayCutoutFromSpec( + String pathSpec, String rectSpec, int displayWidth, int displayHeight, float density, + Insets waterfallInsets) { + // Always use the rect approximation spec to create the cutout if it's not null because + // transforming and sending a Region constructed from a path is very costly. + String spec = rectSpec != null ? rectSpec : pathSpec; if (TextUtils.isEmpty(spec) && waterfallInsets.equals(Insets.NONE)) { return NULL_PAIR; } @@ -750,9 +961,12 @@ public final class DisplayCutout { Math.max(waterfallInsets.bottom, safeInset.bottom)); } + final CutoutPathParserInfo cutoutPathParserInfo = new CutoutPathParserInfo(displayWidth, + displayHeight, density, pathSpec.trim(), ROTATION_0, 1f /* scale */); + final DisplayCutout cutout = new DisplayCutout( - safeInset, waterfallInsets, boundLeft, boundTop, - boundRight, boundBottom, false /* copyArguments */); + safeInset, waterfallInsets, boundLeft, boundTop, boundRight, boundBottom, + cutoutPathParserInfo , false /* copyArguments */); final Pair<Path, DisplayCutout> result = new Pair<>(cutoutSpec.getPath(), cutout); synchronized (CACHE_LOCK) { sCachedSpec = spec; @@ -817,6 +1031,12 @@ public final class DisplayCutout { out.writeTypedObject(cutout.mSafeInsets, flags); out.writeTypedArray(cutout.mBounds.getRects(), flags); out.writeTypedObject(cutout.mWaterfallInsets, flags); + out.writeInt(cutout.mCutoutPathParserInfo.getDisplayWidth()); + out.writeInt(cutout.mCutoutPathParserInfo.getDisplayHeight()); + out.writeFloat(cutout.mCutoutPathParserInfo.getDensity()); + out.writeString(cutout.mCutoutPathParserInfo.getCutoutSpec()); + out.writeInt(cutout.mCutoutPathParserInfo.getRotation()); + out.writeFloat(cutout.mCutoutPathParserInfo.getScale()); } } @@ -860,9 +1080,17 @@ public final class DisplayCutout { Rect[] bounds = new Rect[BOUNDS_POSITION_LENGTH]; in.readTypedArray(bounds, Rect.CREATOR); Insets waterfallInsets = in.readTypedObject(Insets.CREATOR); + int displayWidth = in.readInt(); + int displayHeight = in.readInt(); + float density = in.readFloat(); + String cutoutSpec = in.readString(); + int rotation = in.readInt(); + float scale = in.readFloat(); + final CutoutPathParserInfo info = new CutoutPathParserInfo( + displayWidth, displayHeight, density, cutoutSpec, rotation, scale); return new DisplayCutout( - safeInsets, waterfallInsets, bounds, false /* copyArguments */); + safeInsets, waterfallInsets, bounds, info, false /* copyArguments */); } public DisplayCutout get() { @@ -884,7 +1112,15 @@ public final class DisplayCutout { bounds.scale(scale); final Rect waterfallInsets = mInner.mWaterfallInsets.toRect(); waterfallInsets.scale(scale); - mInner = new DisplayCutout(safeInsets, Insets.of(waterfallInsets), bounds); + final CutoutPathParserInfo info = new CutoutPathParserInfo( + mInner.mCutoutPathParserInfo.getDisplayWidth(), + mInner.mCutoutPathParserInfo.getDisplayHeight(), + mInner.mCutoutPathParserInfo.getDensity(), + mInner.mCutoutPathParserInfo.getCutoutSpec(), + mInner.mCutoutPathParserInfo.getRotation(), + scale); + + mInner = new DisplayCutout(safeInsets, Insets.of(waterfallInsets), bounds, info); } @Override diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 106e3927656f..0a1a23116941 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -414,6 +414,15 @@ public final class SurfaceControl implements Parcelable { */ public static final int SECURE = 0x00000080; + + /** + * Queue up BufferStateLayer buffers instead of dropping the oldest buffer when this flag is + * set. This blocks the client until all the buffers have been presented. If the buffers + * have presentation timestamps, then we may drop buffers. + * @hide + */ + public static final int ENABLE_BACKPRESSURE = 0x00000100; + /** * Surface creation flag: Creates a surface where color components are interpreted * as "non pre-multiplied" by their alpha channel. Of course this flag is diff --git a/core/java/android/view/SyncRtSurfaceTransactionApplier.java b/core/java/android/view/SyncRtSurfaceTransactionApplier.java index b10370aa5d4c..acbcbfad1a75 100644 --- a/core/java/android/view/SyncRtSurfaceTransactionApplier.java +++ b/core/java/android/view/SyncRtSurfaceTransactionApplier.java @@ -85,12 +85,13 @@ public class SyncRtSurfaceTransactionApplier { for (int i = params.length - 1; i >= 0; i--) { SurfaceParams surfaceParams = params[i]; SurfaceControl surface = surfaceParams.surface; - if (frame > 0) { - t.deferTransactionUntil(surface, mTargetSc, frame); - } applyParams(t, surfaceParams, mTmpFloat9); } - t.apply(); + if (mTargetViewRootImpl != null) { + mTargetViewRootImpl.mergeWithNextTransaction(t, frame); + } else { + t.apply(); + } } public static void applyParams(Transaction t, SurfaceParams params, float[] tmpFloat9) { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 844fc267c3cb..52d0062abdf2 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -10109,7 +10109,7 @@ public final class ViewRootImpl implements ViewParent, * Merges the transaction passed in with the next transaction in BLASTBufferQueue. This ensures * you can add transactions to the upcoming frame. */ - void mergeWithNextTransaction(Transaction t, long frameNumber) { + public void mergeWithNextTransaction(Transaction t, long frameNumber) { if (mBlastBufferQueue != null) { mBlastBufferQueue.mergeWithNextTransaction(t, frameNumber); } diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java index a21c68b4f01a..fcf8bb4e748b 100644 --- a/core/java/com/android/internal/os/BatteryStatsHelper.java +++ b/core/java/com/android/internal/os/BatteryStatsHelper.java @@ -41,7 +41,6 @@ import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.Log; import android.util.SparseArray; -import android.util.SparseLongArray; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; @@ -379,6 +378,7 @@ public class BatteryStatsHelper { mMaxDrainedPower = (mStats.getHighDischargeAmountSinceCharge() * mPowerProfile.getBatteryCapacity()) / 100; + // Create list of (almost all) sippers, calculate their usage, and put them in mUsageList. processAppUsage(asUsers); Collections.sort(mUsageList); @@ -556,8 +556,7 @@ public class BatteryStatsHelper { } /** - * Mark the {@link BatterySipper} that we should hide and smear the screen usage based on - * foreground activity time. + * Mark the {@link BatterySipper} that we should hide. * * @param sippers sipper list that need to check and remove * @return the total power of the hidden items of {@link BatterySipper} @@ -565,7 +564,6 @@ public class BatteryStatsHelper { */ public double removeHiddenBatterySippers(List<BatterySipper> sippers) { double proportionalSmearPowerMah = 0; - BatterySipper screenSipper = null; for (int i = sippers.size() - 1; i >= 0; i--) { final BatterySipper sipper = sippers.get(i); sipper.shouldHide = shouldHideSipper(sipper); @@ -581,45 +579,11 @@ public class BatteryStatsHelper { proportionalSmearPowerMah += sipper.totalPowerMah; } } - - if (sipper.drainType == BatterySipper.DrainType.SCREEN) { - screenSipper = sipper; - } } - - smearScreenBatterySipper(sippers, screenSipper); - return proportionalSmearPowerMah; } /** - * Smear the screen on power usage among {@code sippers}, based on ratio of foreground activity - * time. - */ - public void smearScreenBatterySipper(List<BatterySipper> sippers, BatterySipper screenSipper) { - long totalActivityTimeMs = 0; - final SparseLongArray activityTimeArray = new SparseLongArray(); - for (int i = 0, size = sippers.size(); i < size; i++) { - final BatteryStats.Uid uid = sippers.get(i).uidObj; - if (uid != null) { - final long timeMs = getProcessForegroundTimeMs(uid, - BatteryStats.STATS_SINCE_CHARGED); - activityTimeArray.put(uid.getUid(), timeMs); - totalActivityTimeMs += timeMs; - } - } - - if (screenSipper != null && totalActivityTimeMs >= 10 * DateUtils.MINUTE_IN_MILLIS) { - final double screenPowerMah = screenSipper.totalPowerMah; - for (int i = 0, size = sippers.size(); i < size; i++) { - final BatterySipper sipper = sippers.get(i); - sipper.screenPowerMah = screenPowerMah * activityTimeArray.get(sipper.getUid(), 0) - / totalActivityTimeMs; - } - } - } - - /** * Check whether we should hide the battery sipper. */ public boolean shouldHideSipper(BatterySipper sipper) { @@ -682,33 +646,6 @@ public class BatteryStatsHelper { } @VisibleForTesting - public long getForegroundActivityTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs) { - final BatteryStats.Timer timer = uid.getForegroundActivityTimer(); - if (timer != null) { - return timer.getTotalTimeLocked(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); - } - - return 0; - } - - @VisibleForTesting - public long getProcessForegroundTimeMs(BatteryStats.Uid uid, int which) { - final long rawRealTimeUs = convertMsToUs(SystemClock.elapsedRealtime()); - final int foregroundTypes[] = {BatteryStats.Uid.PROCESS_STATE_TOP}; - - long timeUs = 0; - for (int type : foregroundTypes) { - final long localTime = uid.getProcessStateTime(type, rawRealTimeUs, which); - timeUs += localTime; - } - - // Return the min value of STATE_TOP time and foreground activity time, since both of these - // time have some errors. - return convertUsToMs( - Math.min(timeUs, getForegroundActivityTotalTimeUs(uid, rawRealTimeUs))); - } - - @VisibleForTesting public void setPackageManager(PackageManager packageManager) { mPackageManager = packageManager; } diff --git a/core/java/com/android/internal/os/ScreenPowerCalculator.java b/core/java/com/android/internal/os/ScreenPowerCalculator.java index 25f6b4d16971..9c4a26724ba8 100644 --- a/core/java/com/android/internal/os/ScreenPowerCalculator.java +++ b/core/java/com/android/internal/os/ScreenPowerCalculator.java @@ -21,9 +21,14 @@ import android.os.BatteryStats; import android.os.BatteryUsageStats; import android.os.BatteryUsageStatsQuery; import android.os.SystemBatteryConsumer; +import android.os.SystemClock; import android.os.UserHandle; +import android.text.format.DateUtils; import android.util.Log; import android.util.SparseArray; +import android.util.SparseLongArray; + +import com.android.internal.annotations.VisibleForTesting; import java.util.List; @@ -57,6 +62,7 @@ public class ScreenPowerCalculator extends PowerCalculator { .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE, durationMs) .setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, powerMah); } + // TODO(b/178140704): Attribute screen usage similar to smearScreenBatterySipper. } /** @@ -73,6 +79,8 @@ public class ScreenPowerCalculator extends PowerCalculator { bs.usageTimeMs = durationMs; bs.sumPower(); sippers.add(bs); + + smearScreenBatterySipper(sippers, bs); } } @@ -96,4 +104,60 @@ public class ScreenPowerCalculator extends PowerCalculator { } return power; } + + /** + * Smear the screen on power usage among {@code sippers}, based on ratio of foreground activity + * time, and store this in the {@link BatterySipper#screenPowerMah} field. + */ + @VisibleForTesting + public void smearScreenBatterySipper(List<BatterySipper> sippers, BatterySipper screenSipper) { + + long totalActivityTimeMs = 0; + final SparseLongArray activityTimeArray = new SparseLongArray(); + for (int i = sippers.size() - 1; i >= 0; i--) { + final BatteryStats.Uid uid = sippers.get(i).uidObj; + if (uid != null) { + final long timeMs = getProcessForegroundTimeMs(uid); + activityTimeArray.put(uid.getUid(), timeMs); + totalActivityTimeMs += timeMs; + } + } + + if (screenSipper != null && totalActivityTimeMs >= 10 * DateUtils.MINUTE_IN_MILLIS) { + final double screenPowerMah = screenSipper.totalPowerMah; + for (int i = sippers.size() - 1; i >= 0; i--) { + final BatterySipper sipper = sippers.get(i); + sipper.screenPowerMah = screenPowerMah * activityTimeArray.get(sipper.getUid(), 0) + / totalActivityTimeMs; + } + } + } + + /** Get the minimum of the uid's ForegroundActivity time and its TOP time. */ + @VisibleForTesting + public long getProcessForegroundTimeMs(BatteryStats.Uid uid) { + final long rawRealTimeUs = SystemClock.elapsedRealtime() * 1000; + final int[] foregroundTypes = {BatteryStats.Uid.PROCESS_STATE_TOP}; + + long timeUs = 0; + for (int type : foregroundTypes) { + final long localTime = uid.getProcessStateTime(type, rawRealTimeUs, + BatteryStats.STATS_SINCE_CHARGED); + timeUs += localTime; + } + + // Return the min value of STATE_TOP time and foreground activity time, since both of these + // time have some errors. + return Math.min(timeUs, getForegroundActivityTotalTimeUs(uid, rawRealTimeUs)) / 1000; + } + + /** Get the ForegroundActivity time of the given uid. */ + @VisibleForTesting + public long getForegroundActivityTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs) { + final BatteryStats.Timer timer = uid.getForegroundActivityTimer(); + if (timer == null) { + return 0; + } + return timer.getTotalTimeLocked(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); + } } diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 0e878fcf0e24..4ef63ae93016 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -262,7 +262,7 @@ public: } } - binder::Status onScreenCaptureComplete( + binder::Status onScreenCaptureCompleted( const gui::ScreenCaptureResults& captureResults) override { JNIEnv* env = getenv(); if (captureResults.result != NO_ERROR || captureResults.buffer == nullptr) { @@ -270,6 +270,7 @@ public: gScreenCaptureListenerClassInfo.onScreenCaptureComplete, nullptr); return binder::Status::ok(); } + captureResults.fence->waitForever(""); jobject jhardwareBuffer = android_hardware_HardwareBuffer_createFromAHardwareBuffer( env, captureResults.buffer->toAHardwareBuffer()); const jint namedColorSpace = diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto index 48094198c2e9..2b665c0fe9fc 100644 --- a/core/proto/android/os/incident.proto +++ b/core/proto/android/os/incident.proto @@ -41,7 +41,6 @@ import "frameworks/base/core/proto/android/server/jobscheduler.proto"; import "frameworks/base/core/proto/android/server/location/context_hub.proto"; import "frameworks/base/core/proto/android/server/powermanagerservice.proto"; import "frameworks/base/core/proto/android/server/powerstatsservice.proto"; -import "frameworks/base/apex/permission/service/proto/com/android/role/roleservice.proto"; import "frameworks/base/core/proto/android/server/windowmanagerservice.proto"; import "frameworks/base/core/proto/android/service/appwidget.proto"; import "frameworks/base/core/proto/android/service/battery.proto"; @@ -63,6 +62,7 @@ import "frameworks/base/core/proto/android/privacy.proto"; import "frameworks/base/core/proto/android/section.proto"; import "frameworks/base/proto/src/ipconnectivity.proto"; import "frameworks/proto_logging/stats/enums/service/usb.proto"; +import "packages/modules/Permission/service/proto/com/android/role/roleservice.proto"; package android.os; diff --git a/core/proto/android/server/powerstatsservice.proto b/core/proto/android/server/powerstatsservice.proto index ec9bc112c6fb..0c5a36049a29 100644 --- a/core/proto/android/server/powerstatsservice.proto +++ b/core/proto/android/server/powerstatsservice.proto @@ -220,6 +220,9 @@ message ChannelProto { /** Name of the energy meter channel */ optional string name = 2; + + /** Name of the subsystem associated with this Channel. Opaque to framework */ + optional string subsystem = 3; } /** diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 396f95446bf6..4a0a35de7c17 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2697,11 +2697,11 @@ The app can check whether it has this authorization by calling {@link android.provider.Settings#canDrawOverlays Settings.canDrawOverlays()}. - <p>Protection level: signature|appop|preinstalled|pre23|development --> + <p>Protection level: signature|appop|installer|recents|appPredictor|pre23|development --> <permission android:name="android.permission.SYSTEM_ALERT_WINDOW" android:label="@string/permlab_systemAlertWindow" android:description="@string/permdesc_systemAlertWindow" - android:protectionLevel="signature|appop|preinstalled|pre23|development" /> + android:protectionLevel="signature|appop|installer|recents|appPredictor|pre23|development" /> <!-- @SystemApi @hide Allows an application to create windows using the type {@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY}, diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index 89407763a9b6..4f920dac1bcf 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -240,34 +240,34 @@ <color name="system_main_0">#ffffff</color> <!-- Shade of the main system color at 95% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_main_50">#ebf1f8</color> + <color name="system_main_50">#f0f0f0</color> <!-- Shade of the main system color at 90% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_main_100">#dde3ea</color> + <color name="system_main_100">#e2e2e2</color> <!-- Shade of the main system color at 80% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_main_200">#c1c7cd</color> + <color name="system_main_200">#c6c6c6</color> <!-- Shade of the main system color at 70% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_main_300">#a6acb2</color> + <color name="system_main_300">#ababab</color> <!-- Shade of the main system color at 60% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_main_400">#8b9197</color> + <color name="system_main_400">#909090</color> <!-- Shade of the main system color at 50% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_main_500">#72787d</color> + <color name="system_main_500">#777777</color> <!-- Shade of the main system color at 40% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_main_600">#595f64</color> + <color name="system_main_600">#5e5e5e</color> <!-- Shade of the main system color at 30% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_main_700">#42474d</color> + <color name="system_main_700">#464646</color> <!-- Shade of the main system color at 20% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_main_800">#2c3136</color> + <color name="system_main_800">#303030</color> <!-- Shade of the main system color at 10% lightness. This value can be overlaid at runtime by OverlayManager RROs. --> - <color name="system_main_900">#171c21</color> + <color name="system_main_900">#1b1b1b</color> <!-- Darkest shade of the main color used by the system. Black. This value can be overlaid at runtime by OverlayManager RROs. --> <color name="system_main_1000">#000000</color> diff --git a/core/res/res/values/colors_device_defaults.xml b/core/res/res/values/colors_device_defaults.xml index e46147ea9b7f..6e9eb8207922 100644 --- a/core/res/res/values/colors_device_defaults.xml +++ b/core/res/res/values/colors_device_defaults.xml @@ -17,14 +17,14 @@ <!-- Colors specific to DeviceDefault themes. These are mostly pass-throughs to enable overlaying new theme colors. --> <resources> - <color name="primary_device_default_dark">@color/primary_material_dark</color> - <color name="primary_device_default_light">@color/primary_material_light</color> - <color name="primary_device_default_settings">@color/primary_material_settings</color> - <color name="primary_device_default_settings_light">@color/primary_material_settings_light</color> - <color name="primary_dark_device_default_dark">@color/primary_dark_material_dark</color> - <color name="primary_dark_device_default_light">@color/primary_dark_material_light</color> - <color name="primary_dark_device_default_settings">@color/primary_dark_material_settings</color> - <color name="primary_dark_device_default_settings_light">@color/primary_dark_material_settings_light</color> + <color name="primary_device_default_dark">@color/system_main_900</color> + <color name="primary_device_default_light">@color/system_main_50</color> + <color name="primary_device_default_settings">@color/system_main_900</color> + <color name="primary_device_default_settings_light">@color/primary_device_default_light</color> + <color name="primary_dark_device_default_dark">@color/primary_device_default_dark</color> + <color name="primary_dark_device_default_light">@color/primary_device_default_light</color> + <color name="primary_dark_device_default_settings">@color/primary_device_default_dark</color> + <color name="primary_dark_device_default_settings_light">@color/primary_device_default_light</color> <color name="navigation_bar_divider_device_default_settings">#1f000000</color> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index a928408f68ed..5e0cda69911b 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -4632,6 +4632,10 @@ maximum screen area that can be occupied by the app in the letterbox mode. --> <item name="config_taskLetterboxAspectRatio" format="float" type="dimen">0.0</item> + <!-- Corners radius for activity presented the letterbox mode. Values < 0 will be ignored and + corners of the activity won't be rounded. --> + <integer name="config_letterboxActivityCornersRadius">0</integer> + <!-- If true, hide the display cutout with display area --> <bool name="config_hideDisplayCutoutWithDisplayArea">false</bool> @@ -4663,4 +4667,7 @@ <bool name="config_telephony5gStandalone">false</bool> <!-- Whether the device enable the non-standalone (NSA) mode of 5G NR.--> <bool name="config_telephony5gNonStandalone">false</bool> + + <!-- Whether to select voice/data/sms preference without user confirmation --> + <bool name="config_voice_data_sms_auto_fallback">false</bool> </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index bcef68072079..dfccdf4bd9a5 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -4132,6 +4132,7 @@ <java-symbol type="dimen" name="controls_thumbnail_image_max_width" /> <java-symbol type="dimen" name="config_taskLetterboxAspectRatio" /> + <java-symbol type="integer" name="config_letterboxActivityCornersRadius" /> <java-symbol type="bool" name="config_hideDisplayCutoutWithDisplayArea" /> @@ -4166,4 +4167,6 @@ <java-symbol type="bool" name="config_telephony5gStandalone" /> <java-symbol type="bool" name="config_telephony5gNonStandalone" /> + + <java-symbol type="bool" name="config_voice_data_sms_auto_fallback" /> </resources> diff --git a/core/tests/coretests/src/android/content/OWNERS b/core/tests/coretests/src/android/content/OWNERS index 912db1e835dc..c61a4b538a44 100644 --- a/core/tests/coretests/src/android/content/OWNERS +++ b/core/tests/coretests/src/android/content/OWNERS @@ -1,3 +1,4 @@ +per-file AssetTest.java = file:/core/java/android/content/res/OWNERS per-file ContextTest.java = file:/services/core/java/com/android/server/wm/OWNERS -per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS per-file *Launcher* = file:/core/java/android/content/pm/LAUNCHER_OWNERS +per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS diff --git a/core/tests/coretests/src/android/content/integrity/OWNERS b/core/tests/coretests/src/android/content/integrity/OWNERS new file mode 100644 index 000000000000..4ffc7041a527 --- /dev/null +++ b/core/tests/coretests/src/android/content/integrity/OWNERS @@ -0,0 +1 @@ +include /core/java/android/content/integrity/OWNERS diff --git a/core/tests/coretests/src/android/content/pm/OWNERS b/core/tests/coretests/src/android/content/pm/OWNERS index 7b7670696bfa..867336515ce3 100644 --- a/core/tests/coretests/src/android/content/pm/OWNERS +++ b/core/tests/coretests/src/android/content/pm/OWNERS @@ -1,3 +1,5 @@ +include /core/java/android/content/pm/OWNERS + per-file AppSearchPersonTest.java = file:/core/java/android/content/pm/SHORTCUT_OWNERS -per-file SigningDetailsTest.java = mpgroover@google.com per-file SigningDetailsTest.java = cbrubaker@google.com +per-file SigningDetailsTest.java = mpgroover@google.com diff --git a/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java b/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java index 24f45a57dabc..bffd1e4a86d6 100644 --- a/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java +++ b/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java @@ -28,6 +28,7 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import android.content.pm.PackageParser.SigningDetails; +import android.util.ArraySet; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; @@ -35,6 +36,8 @@ import androidx.test.filters.SmallTest; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.Set; + @RunWith(AndroidJUnit4.class) @SmallTest public class SigningDetailsTest { @@ -208,8 +211,8 @@ public class SigningDetailsTest { SigningDetails result1 = noLineageDetails.mergeLineageWith(lineageDetails); SigningDetails result2 = lineageDetails.mergeLineageWith(noLineageDetails); - assertTrue(result1 == lineageDetails); - assertTrue(result2 == lineageDetails); + assertSigningDetailsContainsLineage(result1, FIRST_SIGNATURE, SECOND_SIGNATURE); + assertSigningDetailsContainsLineage(result2, FIRST_SIGNATURE, SECOND_SIGNATURE); } @Test @@ -271,8 +274,10 @@ public class SigningDetailsTest { SigningDetails result1 = singleSignerDetails.mergeLineageWith(fullLineageDetails); SigningDetails result2 = fullLineageDetails.mergeLineageWith(singleSignerDetails); - assertTrue(result1 == fullLineageDetails); - assertTrue(result2 == fullLineageDetails); + assertSigningDetailsContainsLineage(result1, FIRST_SIGNATURE, SECOND_SIGNATURE, + THIRD_SIGNATURE); + assertSigningDetailsContainsLineage(result2, FIRST_SIGNATURE, SECOND_SIGNATURE, + THIRD_SIGNATURE); } @Test @@ -605,6 +610,213 @@ public class SigningDetailsTest { assertTrue(secondLineageDetails.hasCommonAncestor(firstLineageDetails)); } + @Test + public void hasCommonSignerWithCapabilities_singleMatchingSigner_returnsTrue() + throws Exception { + // The hasCommonSignerWithCapabilities method is intended to grant the specified + // capabilities to a requesting package that has a common signer in the lineage (or as the + // current signer) even if their signing identities have diverged. This test verifies if the + // two SigningDetails have the same single signer then the requested capability can be + // granted since the current signer always has all capabilities granted. + SigningDetails firstDetails = createSigningDetails(FIRST_SIGNATURE); + SigningDetails secondSignerDetails = createSigningDetails(FIRST_SIGNATURE); + + assertTrue(firstDetails.hasCommonSignerWithCapability(secondSignerDetails, PERMISSION)); + } + + @Test + public void hasCommonSignerWithCapabilities_singleDifferentSigners_returnsFalse() + throws Exception { + // If each package is signed by a single different signer then the method should return + // false since there is no shared signer. + SigningDetails firstDetails = createSigningDetails(FIRST_SIGNATURE); + SigningDetails secondDetails = createSigningDetails(SECOND_SIGNATURE); + + assertFalse(firstDetails.hasCommonSignerWithCapability(secondDetails, PERMISSION)); + assertFalse(secondDetails.hasCommonSignerWithCapability(firstDetails, PERMISSION)); + } + + @Test + public void hasCommonSignerWithCapabilities_oneWithMultipleSigners_returnsFalse() + throws Exception { + // If one of the packages is signed with multiple signers and the other only a single signer + // this method should return false since all signers must match exactly for multiple signer + // cases. + SigningDetails firstDetails = createSigningDetails(FIRST_SIGNATURE, SECOND_SIGNATURE); + SigningDetails secondDetails = createSigningDetails(FIRST_SIGNATURE); + + assertFalse(firstDetails.hasCommonSignerWithCapability(secondDetails, PERMISSION)); + assertFalse(secondDetails.hasCommonSignerWithCapability(firstDetails, PERMISSION)); + } + + @Test + public void hasCommonSignerWithCapabilities_multipleMatchingSigners_returnsTrue() + throws Exception { + // if both packages are signed by the same multiple signers then this method should return + // true since the current signer is granted all capabilities. + SigningDetails firstDetails = createSigningDetails(FIRST_SIGNATURE, SECOND_SIGNATURE); + SigningDetails secondDetails = createSigningDetails(SECOND_SIGNATURE, FIRST_SIGNATURE); + + assertTrue(firstDetails.hasCommonSignerWithCapability(secondDetails, PERMISSION)); + assertTrue(secondDetails.hasCommonSignerWithCapability(firstDetails, PERMISSION)); + } + + @Test + public void hasCommonSignerWithCapabilities_singleSignerInLineage_returnsTrue() + throws Exception { + // if a single signer is in the lineage and that previous signer has the requested + // capability then this method should return true. + SigningDetails lineageDetails = createSigningDetailsWithLineageAndCapabilities( + new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE}, + new int[]{DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES}); + SigningDetails singleSignerDetails = createSigningDetails(FIRST_SIGNATURE); + + assertTrue(lineageDetails.hasCommonSignerWithCapability(singleSignerDetails, PERMISSION)); + } + + @Test + public void hasCommonSignerWithCapabilities_singleSignerInLineageWOCapability_returnsFalse() + throws Exception { + // If a single signer is in the lineage and that previous signer does not have the requested + // capability then this method should return false. + SigningDetails lineageDetails = createSigningDetailsWithLineageAndCapabilities( + new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE}, + new int[]{SHARED_USER_ID, DEFAULT_CAPABILITIES}); + SigningDetails singleSignerDetails = createSigningDetails(FIRST_SIGNATURE); + + assertFalse(lineageDetails.hasCommonSignerWithCapability(singleSignerDetails, PERMISSION)); + } + + @Test + public void hasCommonSignerWithCapabilities_singleSignerMatchesCurrentSigner_returnsTrue() + throws Exception { + // If a requesting app is signed by the same current signer as an app with a lineage the + // method should return true since the current signer is granted all capabilities. + SigningDetails lineageDetails = createSigningDetailsWithLineageAndCapabilities( + new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE}, + new int[]{SHARED_USER_ID, DEFAULT_CAPABILITIES}); + SigningDetails singleSignerDetails = createSigningDetails(SECOND_SIGNATURE); + + assertTrue(lineageDetails.hasCommonSignerWithCapability(singleSignerDetails, PERMISSION)); + } + + @Test + public void hasCommonSignerWithCapabilities_divergingSignersWithCommonSigner_returnsTrue() + throws Exception { + // This method is intended to allow granting a capability to another app that has a common + // signer in the lineage with the capability still granted; this test verifies when the + // current signers diverge but a common ancestor has the requested capability this method + // returns true. + SigningDetails firstLineageDetails = createSigningDetailsWithLineageAndCapabilities( + new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE}, + new int[]{DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES}); + SigningDetails secondLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, + SECOND_SIGNATURE, FOURTH_SIGNATURE); + + assertTrue(firstLineageDetails.hasCommonSignerWithCapability(secondLineageDetails, + PERMISSION)); + } + + @Test + public void hasCommonSignerWithCapabilities_divergingSignersOneGrantsCapability_returnsTrue() + throws Exception { + // If apps have multiple common signers in the lineage with one denying the requested + // capability but the other granting it this method should return true. + SigningDetails firstLineageDetails = createSigningDetailsWithLineageAndCapabilities( + new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE}, + new int[]{SHARED_USER_ID, DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES}); + SigningDetails secondLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, + SECOND_SIGNATURE, FOURTH_SIGNATURE); + + assertTrue(firstLineageDetails.hasCommonSignerWithCapability(secondLineageDetails, + PERMISSION)); + } + + @Test + public void hasCommonSignerWithCapabilities_divergingSignersNoneGrantCapability_returnsFalse() + throws Exception { + // If apps have multiple common signers in the lineage with all denying the requested + // capability this method should return false. + SigningDetails firstLineageDetails = createSigningDetailsWithLineageAndCapabilities( + new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE}, + new int[]{SHARED_USER_ID, AUTH, DEFAULT_CAPABILITIES}); + SigningDetails secondLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, + SECOND_SIGNATURE, FOURTH_SIGNATURE); + + assertFalse(firstLineageDetails.hasCommonSignerWithCapability(secondLineageDetails, + PERMISSION)); + } + + @Test + public void + hasCommonSignerWithCapabilities_divergingSignersNoneGrantsAllCapabilities_returnsTrue() + throws Exception { + // If an app has multiple common signers in the lineage, each granting one of the requested + // capabilities but neither granting all this method should return false since a single + // common ancestor must grant all requested capabilities. + SigningDetails firstLineageDetails = createSigningDetailsWithLineageAndCapabilities( + new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE}, + new int[]{SHARED_USER_ID, PERMISSION, DEFAULT_CAPABILITIES}); + SigningDetails secondLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, + SECOND_SIGNATURE, FOURTH_SIGNATURE); + + assertFalse(firstLineageDetails.hasCommonSignerWithCapability(secondLineageDetails, + PERMISSION | SHARED_USER_ID)); + } + + @Test + public void hasCommonSignerWithCapabilities_currentSignerInLineageOfRequestingApp_returnsTrue() + throws Exception { + // If the current signer of an app is in the lineage of the requesting app then this method + // should return true since the current signer is granted all capabilities. + SigningDetails firstLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, + SECOND_SIGNATURE); + SigningDetails secondLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, + SECOND_SIGNATURE, THIRD_SIGNATURE); + + assertTrue(firstLineageDetails.hasCommonSignerWithCapability(secondLineageDetails, + PERMISSION)); + } + + @Test + public void hasCommonSignerWithCapabilities_currentSignerInLineageOfDeclaringApp_returnsTrue() + throws Exception { + // If the current signer of a requesting app with a lineage is in the lineage of the + // declaring app and that previous signature is granted the requested capability the method + // should return true. + SigningDetails declaringDetails = createSigningDetailsWithLineageAndCapabilities( + new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE}, + new int[]{SHARED_USER_ID, DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES}); + SigningDetails requestingDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE, + SECOND_SIGNATURE); + + assertTrue(declaringDetails.hasCommonSignerWithCapability(requestingDetails, PERMISSION)); + } + + @Test + public void hasCommonSignerWithCapabilities_oneSignerNullLineage_returns() throws Exception { + // While the pastSigningCertificates should only be null in the case of multiple current + // signers there are instances where this can be null with a single signer; verify that a + // null pastSigningCertificates array in either SigningDetails does not result in a + // NullPointerException. + SigningDetails firstDetails = createSigningDetails(true, FIRST_SIGNATURE); + SigningDetails secondDetails = createSigningDetails(SECOND_SIGNATURE); + + assertFalse(firstDetails.hasCommonSignerWithCapability(secondDetails, PERMISSION)); + assertFalse(secondDetails.hasCommonSignerWithCapability(firstDetails, PERMISSION)); + } + + @Test + public void hasCommonSignerWithCapabilities_unknownSigner_returnsFalse() throws Exception { + // An unknown SigningDetails for either instance should immediately result in false being + // returned. + SigningDetails firstDetails = SigningDetails.UNKNOWN; + SigningDetails secondDetails = createSigningDetails(FIRST_SIGNATURE); + + assertFalse(firstDetails.hasCommonSignerWithCapability(secondDetails, PERMISSION)); + assertFalse(secondDetails.hasCommonSignerWithCapability(firstDetails, PERMISSION)); + } + private SigningDetails createSigningDetailsWithLineage(String... signers) throws Exception { int[] capabilities = new int[signers.length]; for (int i = 0; i < capabilities.length; i++) { @@ -629,10 +841,34 @@ public class SigningDetailsTest { } private SigningDetails createSigningDetails(String... signers) throws Exception { + return createSigningDetails(false, signers); + } + + private SigningDetails createSigningDetails(boolean useNullPastSigners, String... signers) + throws Exception { Signature[] currentSignatures = new Signature[signers.length]; for (int i = 0; i < signers.length; i++) { currentSignatures[i] = new Signature(signers[i]); } - return new SigningDetails(currentSignatures, SIGNING_BLOCK_V3, null); + // If there are multiple signers then the pastSigningCertificates should be set to null, but + // if there is only a single signer both the current signer and the past signers should be + // set to that one signer. + if (signers.length > 1) { + return new SigningDetails(currentSignatures, SIGNING_BLOCK_V3, null); + } + return new SigningDetails(currentSignatures, SIGNING_BLOCK_V3, currentSignatures); + } + + private void assertSigningDetailsContainsLineage(SigningDetails details, + String... pastSigners) { + // This method should only be invoked for results that contain a single signer. + assertEquals(1, details.signatures.length); + assertTrue(details.signatures[0].toCharsString().equalsIgnoreCase( + pastSigners[pastSigners.length - 1])); + Set<String> signatures = new ArraySet<>(pastSigners); + for (Signature pastSignature : details.pastSigningCertificates) { + assertTrue(signatures.remove(pastSignature.toCharsString())); + } + assertEquals(0, signatures.size()); } } diff --git a/core/tests/coretests/src/android/content/res/OWNERS b/core/tests/coretests/src/android/content/res/OWNERS new file mode 100644 index 000000000000..3e79d8ff0bbe --- /dev/null +++ b/core/tests/coretests/src/android/content/res/OWNERS @@ -0,0 +1 @@ +include /core/java/android/content/res/OWNERS diff --git a/core/tests/coretests/src/android/view/DisplayCutoutTest.java b/core/tests/coretests/src/android/view/DisplayCutoutTest.java index d02c6d588585..a5261aecdbfb 100644 --- a/core/tests/coretests/src/android/view/DisplayCutoutTest.java +++ b/core/tests/coretests/src/android/view/DisplayCutoutTest.java @@ -22,6 +22,8 @@ import static android.view.DisplayCutout.fromSpec; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.sameInstance; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -30,6 +32,7 @@ import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import android.graphics.Insets; +import android.graphics.Path; import android.graphics.Rect; import android.os.Parcel; import android.platform.test.annotations.Presubmit; @@ -130,6 +133,8 @@ public class DisplayCutoutTest { @Test public void testHasCutout_noCutout() throws Exception { assertTrue(NO_CUTOUT.isBoundsEmpty()); + assertThat(NO_CUTOUT.getWaterfallInsets(), equalTo(Insets.NONE)); + assertThat(NO_CUTOUT.getCutoutPath(), nullValue()); } @Test @@ -165,6 +170,59 @@ public class DisplayCutoutTest { } @Test + public void testGetCutoutPath() throws Exception { + final String cutoutSpecString = "L1,0 L1,1 L0,1 z"; + final int displayWidth = 200; + final int displayHeight = 400; + final float density = 1f; + final DisplayCutout cutout = fromSpec(cutoutSpecString, displayWidth, displayHeight, + density, Insets.NONE); + assertThat(cutout.getCutoutPath(), notNullValue()); + } + + @Test + public void testGetCutoutPath_caches() throws Exception { + final String cutoutSpecString = "L1,0 L1,1 L0,1 z"; + final int displayWidth = 200; + final int displayHeight = 400; + final float density = 1f; + final Path first = fromSpec(cutoutSpecString, displayWidth, displayHeight, + density, Insets.NONE).getCutoutPath(); + final Path second = fromSpec(cutoutSpecString, displayWidth, displayHeight, + density, Insets.NONE).getCutoutPath(); + assertThat(first, equalTo(second)); + } + + @Test + public void testGetCutoutPath_wontCacheIfCutoutPathParerInfoChanged() throws Exception { + final int displayWidth = 200; + final int displayHeight = 400; + final float density = 1f; + final Path first = fromSpec("L1,0 L1,1 L0,1 z", displayWidth, displayHeight, + density, Insets.NONE).getCutoutPath(); + final Path second = fromSpec("L2,0 L2,2 L0,2 z", displayWidth, displayHeight, + density, Insets.NONE).getCutoutPath(); + assertThat(first, not(equalTo(second))); + } + + @Test + public void testGetCutoutPathParserInfo() throws Exception { + final String cutoutSpecString = "L1,0 L1,1 L0,1 z"; + final int displayWidth = 200; + final int displayHeight = 400; + final float density = 1f; + final DisplayCutout cutout = fromSpec(cutoutSpecString, displayWidth, displayHeight, + density, Insets.NONE); + assertThat(displayWidth, equalTo(cutout.getCutoutPathParserInfo().getDisplayWidth())); + assertThat(displayHeight, equalTo(cutout.getCutoutPathParserInfo().getDisplayHeight())); + assertThat(density, equalTo(cutout.getCutoutPathParserInfo().getDensity())); + assertThat(cutoutSpecString.trim(), + equalTo(cutout.getCutoutPathParserInfo().getCutoutSpec())); + assertThat(0, equalTo(cutout.getCutoutPathParserInfo().getRotation())); + assertThat(1f, equalTo(cutout.getCutoutPathParserInfo().getScale())); + } + + @Test public void testHashCode() throws Exception { assertEquals(mCutoutWithWaterfall.hashCode(), createCutoutWithWaterfall().hashCode()); assertNotEquals(mCutoutWithWaterfall.hashCode(), mCutoutNumbers.hashCode()); diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java index fbe16f2c39d1..d2107eaefefc 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java @@ -21,12 +21,10 @@ import static android.os.BatteryStats.Uid.PROCESS_STATE_TOP; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.RETURNS_DEEP_STUBS; -import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; @@ -194,7 +192,6 @@ public class BatteryStatsHelperTest extends TestCase { sippers.add(mBluetoothBatterySipper); sippers.add(mIdleBatterySipper); doReturn(true).when(mBatteryStatsHelper).isTypeSystem(mSystemBatterySipper); - doNothing().when(mBatteryStatsHelper).smearScreenBatterySipper(any(), any()); final double totalUsage = mBatteryStatsHelper.removeHiddenBatterySippers(sippers); @@ -208,19 +205,20 @@ public class BatteryStatsHelperTest extends TestCase { @Test public void testSmearScreenBatterySipper() { + final ScreenPowerCalculator spc = spy(ScreenPowerCalculator.class); final BatterySipper sipperNull = createTestSmearBatterySipper(TIME_FOREGROUND_ACTIVITY_ZERO, - BATTERY_APP_USAGE, 0 /* uid */, true /* isUidNull */); + BATTERY_APP_USAGE, 0 /* uid */, true /* isUidNull */, spc); final BatterySipper sipperBg = createTestSmearBatterySipper(TIME_FOREGROUND_ACTIVITY_ZERO, - BATTERY_APP_USAGE, 1 /* uid */, false /* isUidNull */); + BATTERY_APP_USAGE, 1 /* uid */, false /* isUidNull */, spc); final BatterySipper sipperFg = createTestSmearBatterySipper(TIME_FOREGROUND_ACTIVITY, - BATTERY_APP_USAGE, 2 /* uid */, false /* isUidNull */); + BATTERY_APP_USAGE, 2 /* uid */, false /* isUidNull */, spc); final List<BatterySipper> sippers = new ArrayList<>(); sippers.add(sipperNull); sippers.add(sipperBg); sippers.add(sipperFg); - mBatteryStatsHelper.smearScreenBatterySipper(sippers, mScreenBatterySipper); + spc.smearScreenBatterySipper(sippers, mScreenBatterySipper); assertThat(sipperNull.screenPowerMah).isWithin(PRECISION).of(0); assertThat(sipperBg.screenPowerMah).isWithin(PRECISION).of(0); @@ -249,13 +247,13 @@ public class BatteryStatsHelperTest extends TestCase { @Test public void testGetProcessForegroundTimeMs_largerActivityTime_returnMinTime() { - doReturn(TIME_STATE_FOREGROUND_US + 500).when(mBatteryStatsHelper) + final ScreenPowerCalculator spc = spy(ScreenPowerCalculator.class); + doReturn(TIME_STATE_FOREGROUND_US + 500).when(spc) .getForegroundActivityTotalTimeUs(eq(mUid), anyLong()); doReturn(TIME_STATE_FOREGROUND_US).when(mUid).getProcessStateTime(eq(PROCESS_STATE_TOP), anyLong(), anyInt()); - final long time = mBatteryStatsHelper.getProcessForegroundTimeMs(mUid, - BatteryStats.STATS_SINCE_CHARGED); + final long time = spc.getProcessForegroundTimeMs(mUid); assertThat(time).isEqualTo(TIME_STATE_FOREGROUND_MS); } @@ -291,15 +289,14 @@ public class BatteryStatsHelperTest extends TestCase { } private BatterySipper createTestSmearBatterySipper(long activityTime, double totalPowerMah, - int uidCode, boolean isUidNull) { + int uidCode, boolean isUidNull, ScreenPowerCalculator spc) { final BatterySipper sipper = mock(BatterySipper.class); sipper.drainType = BatterySipper.DrainType.APP; sipper.totalPowerMah = totalPowerMah; doReturn(uidCode).when(sipper).getUid(); if (!isUidNull) { final BatteryStats.Uid uid = mock(BatteryStats.Uid.class, RETURNS_DEEP_STUBS); - doReturn(activityTime).when(mBatteryStatsHelper).getProcessForegroundTimeMs(eq(uid), - anyInt()); + doReturn(activityTime).when(spc).getProcessForegroundTimeMs(eq(uid)); doReturn(uidCode).when(uid).getUid(); sipper.uidObj = uid; } diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java index ebf7cea98a81..dd64327ebfd8 100644 --- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java @@ -570,6 +570,13 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 { } } + @Override + protected void onBoundsChange(Rect bounds) { + if (mState.mNativePtr != 0) { + nSetBounds(mState.mNativePtr, bounds); + } + } + private static native long nCreate(long nativeImageDecoder, @Nullable ImageDecoder decoder, int width, int height, long colorSpaceHandle, @@ -601,4 +608,6 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 { private static native long nNativeByteSize(long nativePtr); @FastNative private static native void nSetMirrored(long nativePtr, boolean mirror); + @FastNative + private static native void nSetBounds(long nativePtr, Rect rect); } diff --git a/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java index 92d87aa0fed6..f7477bf92c81 100644 --- a/keystore/java/android/security/KeyStore2.java +++ b/keystore/java/android/security/KeyStore2.java @@ -23,6 +23,7 @@ import android.os.Build; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceSpecificException; +import android.security.keymaster.KeymasterDefs; import android.system.keystore2.IKeystoreService; import android.system.keystore2.KeyDescriptor; import android.system.keystore2.KeyEntryResponse; @@ -107,7 +108,7 @@ public class KeyStore2 { return request.execute(service); } catch (ServiceSpecificException e) { Log.e(TAG, "KeyStore exception", e); - throw new KeyStoreException(e.errorCode, ""); + throw getKeyStoreException(e.errorCode); } catch (RemoteException e) { if (firstTry) { Log.w(TAG, "Looks like we may have lost connection to the Keystore " @@ -274,4 +275,40 @@ public class KeyStore2 { } } + static KeyStoreException getKeyStoreException(int errorCode) { + if (errorCode > 0) { + // KeyStore layer error + switch (errorCode) { + case ResponseCode.LOCKED: + return new KeyStoreException(errorCode, "User authentication required"); + case ResponseCode.UNINITIALIZED: + return new KeyStoreException(errorCode, "Keystore not initialized"); + case ResponseCode.SYSTEM_ERROR: + return new KeyStoreException(errorCode, "System error"); + case ResponseCode.PERMISSION_DENIED: + return new KeyStoreException(errorCode, "Permission denied"); + case ResponseCode.KEY_NOT_FOUND: + return new KeyStoreException(errorCode, "Key not found"); + case ResponseCode.VALUE_CORRUPTED: + return new KeyStoreException(errorCode, "Key blob corrupted"); + case ResponseCode.KEY_PERMANENTLY_INVALIDATED: + return new KeyStoreException(errorCode, "Key permanently invalidated"); + default: + return new KeyStoreException(errorCode, String.valueOf(errorCode)); + } + } else { + // Keymaster layer error + switch (errorCode) { + case KeymasterDefs.KM_ERROR_INVALID_AUTHORIZATION_TIMEOUT: + // The name of this parameter significantly differs between Keymaster and + // framework APIs. Use the framework wording to make life easier for developers. + return new KeyStoreException(errorCode, + "Invalid user authentication validity duration"); + default: + return new KeyStoreException(errorCode, + KeymasterDefs.getErrorMessage(errorCode)); + } + } + } + } diff --git a/keystore/java/android/security/KeyStoreOperation.java b/keystore/java/android/security/KeyStoreOperation.java index 7ea9e1438845..a6552dddc630 100644 --- a/keystore/java/android/security/KeyStoreOperation.java +++ b/keystore/java/android/security/KeyStoreOperation.java @@ -73,8 +73,7 @@ public class KeyStoreOperation { ); } default: - // TODO Human readable string. Use something like KeyStore.getKeyStoreException - throw new KeyStoreException(e.errorCode, ""); + throw KeyStore2.getKeyStoreException(e.errorCode); } } catch (RemoteException e) { // Log exception and report invalid operation handle. diff --git a/keystore/java/android/security/KeyStoreSecurityLevel.java b/keystore/java/android/security/KeyStoreSecurityLevel.java index 3ef4aa5b7ec3..372add9b7ecb 100644 --- a/keystore/java/android/security/KeyStoreSecurityLevel.java +++ b/keystore/java/android/security/KeyStoreSecurityLevel.java @@ -52,7 +52,7 @@ public class KeyStoreSecurityLevel { try { return request.execute(); } catch (ServiceSpecificException e) { - throw new KeyStoreException(e.errorCode, ""); + throw KeyStore2.getKeyStoreException(e.errorCode); } catch (RemoteException e) { // Log exception and report invalid operation handle. // This should prompt the caller drop the reference to this operation and retry. @@ -96,25 +96,26 @@ public class KeyStoreSecurityLevel { } catch (ServiceSpecificException e) { switch (e.errorCode) { case ResponseCode.BACKEND_BUSY: { + long backOffHint = (long) (Math.random() * 80 + 20); if (CompatChanges.isChangeEnabled( KeyStore2.KEYSTORE_OPERATION_CREATION_MAY_FAIL)) { // Starting with Android S we inform the caller about the // backend being busy. - throw new BackendBusyException(); + throw new BackendBusyException(backOffHint); } else { // Before Android S operation creation must always succeed. So we // just have to retry. We do so with a randomized back-off between - // 50 and 250ms. + // 20 and 100ms. // It is a little awkward that we cannot break out of this loop // by interrupting this thread. But that is the expected behavior. // There is some comfort in the fact that interrupting a thread // also does not unblock a thread waiting for a binder transaction. - interruptedPreservingSleep((long) (Math.random() * 200 + 50)); + interruptedPreservingSleep(backOffHint); } break; } default: - throw new KeyStoreException(e.errorCode, ""); + throw KeyStore2.getKeyStoreException(e.errorCode); } } catch (RemoteException e) { Log.w(TAG, "Cannot connect to keystore", e); diff --git a/keystore/java/android/security/keystore/BackendBusyException.java b/keystore/java/android/security/keystore/BackendBusyException.java index 1a88469d7e54..a813e939a720 100644 --- a/keystore/java/android/security/keystore/BackendBusyException.java +++ b/keystore/java/android/security/keystore/BackendBusyException.java @@ -16,37 +16,66 @@ package android.security.keystore; +import android.annotation.DurationMillisLong; import android.annotation.NonNull; import java.security.ProviderException; /** * Indicates a transient error that prevented a key operation from being created. - * Callers should try again with a back-off period of 10-30 milliseconds. + * Callers should try again with a back-off period of {@link #getBackOffHintMillis()} + * milliseconds. */ public class BackendBusyException extends ProviderException { + private final long mBackOffHintMillis; + /** * Constructs a new {@code BackendBusyException} without detail message and cause. + * */ - public BackendBusyException() { + public BackendBusyException(@DurationMillisLong long backOffHintMillis) { super("The keystore backend has no operation slots available. Retry later."); + if (backOffHintMillis < 0) { + throw new IllegalArgumentException("Back-off hint cannot be negative."); + } + mBackOffHintMillis = backOffHintMillis; } /** * Constructs a new {@code BackendBusyException} with the provided detail message and * no cause. */ - public BackendBusyException(@NonNull String message) { + public BackendBusyException(@DurationMillisLong long backOffHintMillis, + @NonNull String message) { super(message); + if (backOffHintMillis < 0) { + throw new IllegalArgumentException("Back-off hint cannot be negative."); + } + mBackOffHintMillis = backOffHintMillis; } /** * Constructs a new {@code BackendBusyException} with the provided detail message and * cause. */ - public BackendBusyException(@NonNull String message, @NonNull Throwable cause) { + public BackendBusyException(@DurationMillisLong long backOffHintMillis, + @NonNull String message, @NonNull Throwable cause) { super(message, cause); + if (backOffHintMillis < 0) { + throw new IllegalArgumentException("Back-off hint cannot be negative."); + } + mBackOffHintMillis = backOffHintMillis; } + /** + * When retrying to start a Keystore operation after receiving this exception, this can be + * used to determine how long to wait before retrying. It is not guaranteed that the operation + * will succeeds after this time. Multiple retries may be necessary if the system is congested. + * + * @return Number of milliseconds to back off before retrying. + */ + public @DurationMillisLong long getBackOffHintMillis() { + return mBackOffHintMillis; + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java index 1df2a4a9030d..bb8a97344664 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java @@ -225,8 +225,9 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, mTaskOrganizer.applyTransaction(wct); // TODO(b/151449487): Only call callback once we enable synchronization if (mListener != null) { + final int taskId = mTaskInfo.taskId; mListenerExecutor.execute(() -> { - mListener.onTaskVisibilityChanged(mTaskInfo.taskId, mSurfaceCreated); + mListener.onTaskVisibilityChanged(taskId, mSurfaceCreated); }); } } @@ -256,8 +257,10 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, } if (mListener != null) { + final int taskId = taskInfo.taskId; + final ComponentName baseActivity = taskInfo.baseActivity; mListenerExecutor.execute(() -> { - mListener.onTaskCreated(taskInfo.taskId, taskInfo.baseActivity); + mListener.onTaskCreated(taskId, baseActivity); }); } } @@ -267,8 +270,9 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return; if (mListener != null) { + final int taskId = taskInfo.taskId; mListenerExecutor.execute(() -> { - mListener.onTaskRemovalStarted(taskInfo.taskId); + mListener.onTaskRemovalStarted(taskId); }); } mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, false); @@ -289,8 +293,9 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) { if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return; if (mListener != null) { + final int taskId = taskInfo.taskId; mListenerExecutor.execute(() -> { - mListener.onBackPressedOnTaskRoot(taskInfo.taskId); + mListener.onBackPressedOnTaskRoot(taskId); }); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java index 3181dbf74ace..58a4baf39614 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java @@ -356,11 +356,11 @@ public class DisplayLayout { if (cutout == null || cutout == DisplayCutout.NO_CUTOUT) { return null; } - final Insets waterfallInsets = - RotationUtils.rotateInsets(cutout.getWaterfallInsets(), rotation); if (rotation == ROTATION_0) { return computeSafeInsets(cutout, displayWidth, displayHeight); } + final Insets waterfallInsets = + RotationUtils.rotateInsets(cutout.getWaterfallInsets(), rotation); final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270); Rect[] cutoutRects = cutout.getBoundingRectsAll(); final Rect[] newBounds = new Rect[cutoutRects.length]; @@ -372,8 +372,12 @@ public class DisplayLayout { } newBounds[getBoundIndexFromRotation(i, rotation)] = rect; } + final DisplayCutout.CutoutPathParserInfo info = cutout.getCutoutPathParserInfo(); + final DisplayCutout.CutoutPathParserInfo newInfo = new DisplayCutout.CutoutPathParserInfo( + info.getDisplayWidth(), info.getDisplayHeight(), info.getDensity(), + info.getCutoutSpec(), rotation, info.getScale()); return computeSafeInsets( - DisplayCutout.fromBoundsAndWaterfall(newBounds, waterfallInsets), + DisplayCutout.constructDisplayCutout(newBounds, waterfallInsets, newInfo), rotated ? displayHeight : displayWidth, rotated ? displayWidth : displayHeight); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java index 4874d3ccae7e..a4cd3c5a583d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java @@ -47,6 +47,11 @@ public class HandlerExecutor implements ShellExecutor { } @Override + public void removeAllCallbacks() { + mHandler.removeCallbacksAndMessages(null); + } + + @Override public void removeCallbacks(@NonNull Runnable r) { mHandler.removeCallbacks(r); } @@ -55,9 +60,4 @@ public class HandlerExecutor implements ShellExecutor { public boolean hasCallback(Runnable r) { return mHandler.hasCallbacks(r); } - - @Override - public Looper getLooper() { - return mHandler.getLooper(); - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java index 1149cceb1068..b736fb0b9895 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java @@ -73,6 +73,11 @@ public interface ShellExecutor extends Executor { void executeDelayed(Runnable runnable, long delayMillis); /** + * Removes all pending callbacks. + */ + void removeAllCallbacks(); + + /** * See {@link android.os.Handler#removeCallbacks}. */ void removeCallbacks(Runnable runnable); @@ -81,9 +86,4 @@ public interface ShellExecutor extends Executor { * See {@link android.os.Handler#hasCallbacks(Runnable)}. */ boolean hasCallback(Runnable runnable); - - /** - * Returns the looper that this executor is running on. - */ - Looper getLooper(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java index d22abe4dd19b..125e322974bf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java @@ -24,6 +24,7 @@ import android.graphics.Rect; import android.view.SurfaceControl; import android.view.animation.Interpolator; import android.view.animation.OvershootInterpolator; +import android.window.WindowContainerToken; import androidx.annotation.VisibleForTesting; @@ -55,7 +56,7 @@ public class OneHandedAnimationController { private final Interpolator mOvershootInterpolator; private final OneHandedSurfaceTransactionHelper mSurfaceTransactionHelper; - private final HashMap<SurfaceControl, OneHandedTransitionAnimator> mAnimatorMap = + private final HashMap<WindowContainerToken, OneHandedTransitionAnimator> mAnimatorMap = new HashMap<>(); /** @@ -67,23 +68,23 @@ public class OneHandedAnimationController { } @SuppressWarnings("unchecked") - OneHandedTransitionAnimator getAnimator(SurfaceControl leash, Rect startBounds, - Rect endBounds) { - final OneHandedTransitionAnimator animator = mAnimatorMap.get(leash); + OneHandedTransitionAnimator getAnimator(WindowContainerToken token, SurfaceControl leash, + Rect startBounds, Rect endBounds) { + final OneHandedTransitionAnimator animator = mAnimatorMap.get(token); if (animator == null) { - mAnimatorMap.put(leash, setupOneHandedTransitionAnimator( - OneHandedTransitionAnimator.ofBounds(leash, startBounds, endBounds))); + mAnimatorMap.put(token, setupOneHandedTransitionAnimator( + OneHandedTransitionAnimator.ofBounds(token, leash, startBounds, endBounds))); } else if (animator.isRunning()) { animator.updateEndValue(endBounds); } else { animator.cancel(); - mAnimatorMap.put(leash, setupOneHandedTransitionAnimator( - OneHandedTransitionAnimator.ofBounds(leash, startBounds, endBounds))); + mAnimatorMap.put(token, setupOneHandedTransitionAnimator( + OneHandedTransitionAnimator.ofBounds(token, leash, startBounds, endBounds))); } - return mAnimatorMap.get(leash); + return mAnimatorMap.get(token); } - HashMap<SurfaceControl, OneHandedTransitionAnimator> getAnimatorMap() { + HashMap<WindowContainerToken, OneHandedTransitionAnimator> getAnimatorMap() { return mAnimatorMap; } @@ -91,8 +92,8 @@ public class OneHandedAnimationController { return mAnimatorMap.isEmpty(); } - void removeAnimator(SurfaceControl key) { - final OneHandedTransitionAnimator animator = mAnimatorMap.remove(key); + void removeAnimator(WindowContainerToken token) { + final OneHandedTransitionAnimator animator = mAnimatorMap.remove(token); if (animator != null && animator.isRunning()) { animator.cancel(); } @@ -116,6 +117,7 @@ public class OneHandedAnimationController { ValueAnimator.AnimatorListener { private final SurfaceControl mLeash; + private final WindowContainerToken mToken; private T mStartValue; private T mEndValue; private T mCurrentValue; @@ -128,8 +130,10 @@ public class OneHandedAnimationController { private @TransitionDirection int mTransitionDirection; - private OneHandedTransitionAnimator(SurfaceControl leash, T startValue, T endValue) { + private OneHandedTransitionAnimator(WindowContainerToken token, SurfaceControl leash, + T startValue, T endValue) { mLeash = leash; + mToken = token; mStartValue = startValue; mEndValue = endValue; addListener(this); @@ -208,8 +212,8 @@ public class OneHandedAnimationController { return this; } - SurfaceControl getLeash() { - return mLeash; + WindowContainerToken getToken() { + return mToken; } Rect getDestinationBounds() { @@ -254,10 +258,10 @@ public class OneHandedAnimationController { } @VisibleForTesting - static OneHandedTransitionAnimator<Rect> ofBounds(SurfaceControl leash, - Rect startValue, Rect endValue) { + static OneHandedTransitionAnimator<Rect> ofBounds(WindowContainerToken token, + SurfaceControl leash, Rect startValue, Rect endValue) { - return new OneHandedTransitionAnimator<Rect>(leash, new Rect(startValue), + return new OneHandedTransitionAnimator<Rect>(token, leash, new Rect(startValue), new Rect(endValue)) { private final Rect mTmpRect = new Rect(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java index a74f4761af0c..37a91d0c121c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java @@ -56,7 +56,7 @@ public class OneHandedBackgroundPanelOrganizer extends DisplayAreaOrganizer private final float[] mColor; private final float mAlpha; private final Rect mRect; - private final Handler mHandler; + private final Executor mMainExecutor; private final Point mDisplaySize = new Point(); private final OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory mSurfaceControlTransactionFactory; @@ -76,13 +76,13 @@ public class OneHandedBackgroundPanelOrganizer extends DisplayAreaOrganizer @Override public void onOneHandedAnimationStart( OneHandedAnimationController.OneHandedTransitionAnimator animator) { - mHandler.post(() -> showBackgroundPanelLayer()); + mMainExecutor.execute(() -> showBackgroundPanelLayer()); } }; @Override public void onStopFinished(Rect bounds) { - mHandler.post(() -> removeBackgroundPanelLayer()); + mMainExecutor.execute(() -> removeBackgroundPanelLayer()); } public OneHandedBackgroundPanelOrganizer(Context context, DisplayController displayController, @@ -94,7 +94,7 @@ public class OneHandedBackgroundPanelOrganizer extends DisplayAreaOrganizer mColor = new float[]{defaultRGB, defaultRGB, defaultRGB}; mAlpha = res.getFloat(R.dimen.config_one_handed_background_alpha); mRect = new Rect(0, 0, mDisplaySize.x, mDisplaySize.y); - mHandler = new Handler(); + mMainExecutor = executor; mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java index d2d5591100d4..1da72f8efbb8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java @@ -26,11 +26,11 @@ import android.graphics.Point; import android.graphics.Rect; import android.os.SystemProperties; import android.util.ArrayMap; -import android.util.Log; import android.view.SurfaceControl; import android.window.DisplayAreaAppearedInfo; import android.window.DisplayAreaInfo; import android.window.DisplayAreaOrganizer; +import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import androidx.annotation.NonNull; @@ -44,8 +44,6 @@ import com.android.wm.shell.common.ShellExecutor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; -import java.util.Objects; -import java.util.concurrent.Executor; /** * Manages OneHanded display areas such as offset. @@ -69,7 +67,7 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { private int mEnterExitAnimationDurationMs; @VisibleForTesting - ArrayMap<DisplayAreaInfo, SurfaceControl> mDisplayAreaMap = new ArrayMap(); + ArrayMap<WindowContainerToken, SurfaceControl> mDisplayAreaTokenMap = new ArrayMap(); private DisplayController mDisplayController; private OneHandedAnimationController mAnimationController; private OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory @@ -89,7 +87,7 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { @Override public void onOneHandedAnimationEnd(SurfaceControl.Transaction tx, OneHandedAnimationController.OneHandedTransitionAnimator animator) { - mAnimationController.removeAnimator(animator.getLeash()); + mAnimationController.removeAnimator(animator.getToken()); if (mAnimationController.isAnimatorsConsumed()) { finishOffset(animator.getDestinationOffset(), animator.getTransitionDirection()); @@ -99,7 +97,7 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { @Override public void onOneHandedAnimationCancel( OneHandedAnimationController.OneHandedTransitionAnimator animator) { - mAnimationController.removeAnimator(animator.getLeash()); + mAnimationController.removeAnimator(animator.getToken()); if (mAnimationController.isAnimatorsConsumed()) { finishOffset(animator.getDestinationOffset(), animator.getTransitionDirection()); @@ -119,7 +117,6 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { super(mainExecutor); mAnimationController = animationController; mDisplayController = displayController; - mDefaultDisplayBounds.set(getDisplayBounds()); mLastVisualDisplayBounds.set(getDisplayBounds()); final int animationDurationConfig = context.getResources().getInteger( R.integer.config_one_handed_translate_animation_duration); @@ -134,24 +131,12 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { @Override public void onDisplayAreaAppeared(@NonNull DisplayAreaInfo displayAreaInfo, @NonNull SurfaceControl leash) { - Objects.requireNonNull(displayAreaInfo, "displayAreaInfo must not be null"); - Objects.requireNonNull(leash, "leash must not be null"); - if (mDisplayAreaMap.get(displayAreaInfo) == null) { - // mDefaultDisplayBounds may out of date after removeDisplayChangingController() - mDefaultDisplayBounds.set(getDisplayBounds()); - mDisplayAreaMap.put(displayAreaInfo, leash); - } + mDisplayAreaTokenMap.put(displayAreaInfo.token, leash); } @Override public void onDisplayAreaVanished(@NonNull DisplayAreaInfo displayAreaInfo) { - Objects.requireNonNull(displayAreaInfo, - "Requires valid displayArea, and displayArea must not be null"); - if (!mDisplayAreaMap.containsKey(displayAreaInfo)) { - Log.w(TAG, "Unrecognized token: " + displayAreaInfo.token); - return; - } - mDisplayAreaMap.remove(displayAreaInfo); + mDisplayAreaTokenMap.remove(displayAreaInfo.token); } @Override @@ -162,6 +147,7 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { final DisplayAreaAppearedInfo info = displayAreaInfos.get(i); onDisplayAreaAppeared(info.getDisplayAreaInfo(), info.getLeash()); } + mDefaultDisplayBounds.set(getDisplayBounds()); return displayAreaInfos; } @@ -176,9 +162,9 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { * handles 90 degree display rotation changes {@link Surface.Rotation}. * * @param fromRotation starting rotation of the display. - * @param toRotation target rotation of the display (after rotating). - * @param wct A task transaction {@link WindowContainerTransaction} from - * {@link DisplayChangeController} to populate. + * @param toRotation target rotation of the display (after rotating). + * @param wct A task transaction {@link WindowContainerTransaction} from + * {@link DisplayChangeController} to populate. */ public void onRotateDisplay(int fromRotation, int toRotation, WindowContainerTransaction wct) { // Stop one handed without animation and reset cropped size immediately @@ -210,11 +196,11 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { : TRANSITION_DIRECTION_EXIT; final WindowContainerTransaction wct = new WindowContainerTransaction(); - mDisplayAreaMap.forEach( - (key, leash) -> { - animateWindows(leash, fromBounds, toBounds, direction, + mDisplayAreaTokenMap.forEach( + (token, leash) -> { + animateWindows(token, leash, fromBounds, toBounds, direction, mEnterExitAnimationDurationMs); - wct.setBounds(key.token, toBounds); + wct.setBounds(token, toBounds); }); applyTransaction(wct); } @@ -222,10 +208,10 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { private void resetWindowsOffset(WindowContainerTransaction wct) { final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); - mDisplayAreaMap.forEach( - (key, leash) -> { + mDisplayAreaTokenMap.forEach( + (token, leash) -> { final OneHandedAnimationController.OneHandedTransitionAnimator animator = - mAnimationController.getAnimatorMap().remove(leash); + mAnimationController.getAnimatorMap().remove(token); if (animator != null && animator.isRunning()) { animator.cancel(); } @@ -233,16 +219,17 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { .setWindowCrop(leash, -1/* reset */, -1/* reset */); // DisplayRotationController will applyTransaction() after finish rotating if (wct != null) { - wct.setBounds(key.token, null/* reset */); + wct.setBounds(token, null/* reset */); } }); tx.apply(); } - private void animateWindows(SurfaceControl leash, Rect fromBounds, Rect toBounds, - @OneHandedAnimationController.TransitionDirection int direction, int durationMs) { + private void animateWindows(WindowContainerToken token, SurfaceControl leash, Rect fromBounds, + Rect toBounds, @OneHandedAnimationController.TransitionDirection int direction, + int durationMs) { final OneHandedAnimationController.OneHandedTransitionAnimator animator = - mAnimationController.getAnimator(leash, fromBounds, toBounds); + mAnimationController.getAnimator(token, leash, fromBounds, toBounds); if (animator != null) { animator.setTransitionDirection(direction) .addOneHandedAnimationCallback(mOneHandedAnimationCallback) @@ -311,8 +298,8 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { pw.println(TAG + "states: "); pw.print(innerPrefix + "mIsInOneHanded="); pw.println(mIsInOneHanded); - pw.print(innerPrefix + "mDisplayAreaMap="); - pw.println(mDisplayAreaMap); + pw.print(innerPrefix + "mDisplayAreaTokenMap="); + pw.println(mDisplayAreaTokenMap); pw.print(innerPrefix + "mDefaultDisplayBounds="); pw.println(mDefaultDisplayBounds); pw.print(innerPrefix + "mLastVisualDisplayBounds="); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java index 1ed121f35a59..49b7e050c48b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java @@ -221,8 +221,14 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback, displaySize.y); mInputMonitor = InputManager.getInstance().monitorGestureInput( "onehanded-gesture-offset", DEFAULT_DISPLAY); - mInputEventReceiver = new EventReceiver( - mInputMonitor.getInputChannel(), mMainExecutor.getLooper()); + try { + mMainExecutor.executeBlocking(() -> { + mInputEventReceiver = new EventReceiver( + mInputMonitor.getInputChannel(), Looper.myLooper()); + }); + } catch (InterruptedException e) { + throw new RuntimeException("Failed to create input event receiver", e); + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTouchHandler.java index 60709bef4daf..c7a49ff01d15 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTouchHandler.java @@ -132,8 +132,14 @@ public class OneHandedTouchHandler implements OneHandedTransitionCallback { if (mIsEnabled) { mInputMonitor = InputManager.getInstance().monitorGestureInput( "onehanded-touch", DEFAULT_DISPLAY); - mInputEventReceiver = new EventReceiver( - mInputMonitor.getInputChannel(), mMainExecutor.getLooper()); + try { + mMainExecutor.executeBlocking(() -> { + mInputEventReceiver = new EventReceiver( + mInputMonitor.getInputChannel(), Looper.myLooper()); + }); + } catch (InterruptedException e) { + throw new RuntimeException("Failed to create input event receiver", e); + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java index aeea10d47a67..90992fb92324 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java @@ -469,6 +469,11 @@ public class PipAnimationController { getSurfaceTransactionHelper() .alpha(tx, leash, 1f) .round(tx, leash, shouldApplyCornerRadius()); + // TODO(b/178632364): this is a work around for the black background when + // entering PiP in buttion navigation mode. + if (isInPipDirection(direction)) { + tx.setWindowCrop(leash, getStartValue()); + } tx.show(leash); tx.apply(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java index 7a634c3eef78..6e3a20d5f2b2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java @@ -147,7 +147,7 @@ public class PipInputConsumer { // Choreographer.getSfInstance() must be called on the thread that the input event // receiver should be receiving events mInputEventReceiver = new InputEventReceiver(inputChannel, - mMainExecutor.getLooper(), Choreographer.getSfInstance()); + Looper.myLooper(), Choreographer.getSfInstance()); if (mRegistrationListener != null) { mRegistrationListener.onRegistrationChanged(true /* isRegistered */); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java index 41cc59d138fa..8fb358ad74d1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java @@ -212,8 +212,14 @@ public class PipResizeGestureHandler { // Register input event receiver mInputMonitor = InputManager.getInstance().monitorGestureInput( "pip-resize", mDisplayId); - mInputEventReceiver = new PipResizeInputEventReceiver( - mInputMonitor.getInputChannel(), mMainExecutor.getLooper()); + try { + mMainExecutor.executeBlocking(() -> { + mInputEventReceiver = new PipResizeInputEventReceiver( + mInputMonitor.getInputChannel(), Looper.myLooper()); + }); + } catch (InterruptedException e) { + throw new RuntimeException("Failed to create input event receiver", e); + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java index c9f5ae28175c..2b8b53cdf285 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java @@ -95,7 +95,6 @@ public class PipTouchHandler { private int mDeferResizeToNormalBoundsUntilRotation = -1; private int mDisplayRotation; - private final Handler mHandler = new Handler(); private final PipAccessibilityInteractionConnection mConnection; // Behaviour states diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterLegacySplitScreenTest.kt index af99543199ac..224cc197bddd 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterLegacySplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterLegacySplitScreenTest.kt @@ -18,6 +18,7 @@ package com.android.wm.shell.flicker.legacysplitscreen import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.dsl.runWithFlicker @@ -162,6 +163,7 @@ class EnterLegacySplitScreenTest( } } + @FlakyTest(bugId = 173875043) @Test fun testNonResizeableNotDocked() { val testTag = "testNonResizeableNotDocked" diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppTest.kt index 785ccf003504..870adb8e1733 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppTest.kt @@ -18,6 +18,7 @@ package com.android.wm.shell.flicker.legacysplitscreen import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.dsl.runWithFlicker @@ -113,6 +114,7 @@ class RotateTwoLaunchedAppTest( } } + @FlakyTest(bugId = 173875043) @Test fun testRotateAndEnterSplitScreenMode() { val testTag = "testRotateAndEnterSplitScreenMode" diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java index 5f5c30bb6207..bf84a6e30c98 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java @@ -40,6 +40,11 @@ public class TestShellExecutor implements ShellExecutor { } @Override + public void removeAllCallbacks() { + mRunnables.clear(); + } + + @Override public void removeCallbacks(Runnable r) { mRunnables.remove(r); } @@ -49,11 +54,6 @@ public class TestShellExecutor implements ShellExecutor { return mRunnables.contains(r); } - @Override - public Looper getLooper() { - return null; - } - public void flushAll() { for (Runnable r : mRunnables) { r.run(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedAnimationControllerTest.java index 17fc0578dd2b..8d5139b182f0 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedAnimationControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedAnimationControllerTest.java @@ -22,6 +22,7 @@ import android.graphics.Rect; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.SurfaceControl; +import android.window.WindowContainerToken; import androidx.test.filters.SmallTest; @@ -50,6 +51,8 @@ public class OneHandedAnimationControllerTest extends OneHandedTestCase { @Mock private SurfaceControl mMockLeash; + @Mock + private WindowContainerToken mMockToken; @Mock private ShellExecutor mMainExecutor; @@ -69,7 +72,7 @@ public class OneHandedAnimationControllerTest extends OneHandedTestCase { destinationBounds.offset(0, 300); final OneHandedAnimationController.OneHandedTransitionAnimator animator = mOneHandedAnimationController - .getAnimator(mMockLeash, originalBounds, destinationBounds); + .getAnimator(mMockToken, mMockLeash, originalBounds, destinationBounds); assertNotNull(animator); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java index 6cfd0c43724c..01162b5c0b83 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java @@ -24,13 +24,14 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.any; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.res.Configuration; -import android.os.Handler; +import android.os.Binder; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.Display; @@ -89,12 +90,14 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mTestableLooper = TestableLooper.get(this); + Binder binder = new Binder(); + doReturn(binder).when(mMockRealToken).asBinder(); mToken = new WindowContainerToken(mMockRealToken); mLeash = new SurfaceControl(); mDisplay = mContext.getDisplay(); mDisplayAreaInfo = new DisplayAreaInfo(mToken, DEFAULT_DISPLAY, FEATURE_ONE_HANDED); mDisplayAreaInfo.configuration.orientation = Configuration.ORIENTATION_PORTRAIT; - when(mMockAnimationController.getAnimator(any(), any(), any())).thenReturn(null); + when(mMockAnimationController.getAnimator(any(), any(), any(), any())).thenReturn(null); when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay); when(mMockSurfaceTransactionHelper.translate(any(), any(), anyFloat())).thenReturn( mMockSurfaceTransactionHelper); @@ -121,7 +124,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase { public void testOnDisplayAreaAppeared() { mDisplayAreaOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash); - verify(mMockAnimationController, never()).getAnimator(any(), any(), any()); + verify(mMockAnimationController, never()).getAnimator(any(), any(), any(), any()); } @Test @@ -129,7 +132,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase { mDisplayAreaOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash); mDisplayAreaOrganizer.onDisplayAreaVanished(mDisplayAreaInfo); - assertThat(mDisplayAreaOrganizer.mDisplayAreaMap).isEmpty(); + assertThat(mDisplayAreaOrganizer.mDisplayAreaTokenMap).isEmpty(); } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java index 9219f15afc7f..bbe8891817d6 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java @@ -33,6 +33,7 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; +import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.common.ShellExecutor; import org.junit.Before; @@ -48,7 +49,7 @@ import java.util.ArrayList; @RunWith(AndroidTestingRunner.class) public class OneHandedTimeoutHandlerTest extends OneHandedTestCase { private OneHandedTimeoutHandler mTimeoutHandler; - private ShellExecutor mMainExecutor; + private TestShellExecutor mMainExecutor; @Before public void setUp() throws Exception { @@ -104,34 +105,4 @@ public class OneHandedTimeoutHandlerTest extends OneHandedTestCase { mTimeoutHandler.resetTimer(); assertTrue(mTimeoutHandler.hasScheduledTimeout()); } - - private class TestShellExecutor implements ShellExecutor { - private ArrayList<Runnable> mExecuted = new ArrayList<>(); - private ArrayList<Runnable> mDelayed = new ArrayList<>(); - - @Override - public void execute(Runnable runnable) { - mExecuted.add(runnable); - } - - @Override - public void executeDelayed(Runnable r, long delayMillis) { - mDelayed.add(r); - } - - @Override - public void removeCallbacks(Runnable r) { - mDelayed.remove(r); - } - - @Override - public boolean hasCallback(Runnable r) { - return mDelayed.contains(r); - } - - @Override - public Looper getLooper() { - return Looper.myLooper(); - } - } } diff --git a/libs/hwui/hwui/AnimatedImageDrawable.cpp b/libs/hwui/hwui/AnimatedImageDrawable.cpp index 638de850a6c5..0d3d3e3f38fd 100644 --- a/libs/hwui/hwui/AnimatedImageDrawable.cpp +++ b/libs/hwui/hwui/AnimatedImageDrawable.cpp @@ -20,6 +20,7 @@ #endif #include "utils/TraceUtils.h" +#include "pipeline/skia/SkiaUtils.h" #include <SkPicture.h> #include <SkRefCnt.h> @@ -31,6 +32,7 @@ namespace android { AnimatedImageDrawable::AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage, size_t bytesUsed) : mSkAnimatedImage(std::move(animatedImage)), mBytesUsed(bytesUsed) { mTimeToShowNextSnapshot = ms2ns(mSkAnimatedImage->currentFrameDuration()); + setStagingBounds(mSkAnimatedImage->getBounds()); } void AnimatedImageDrawable::syncProperties() { @@ -127,21 +129,38 @@ AnimatedImageDrawable::Snapshot AnimatedImageDrawable::reset() { return snap; } +// Update the matrix to map from the intrinsic bounds of the SkAnimatedImage to +// the bounds specified by Drawable#setBounds. +static void handleBounds(SkMatrix* matrix, const SkRect& intrinsicBounds, const SkRect& bounds) { + matrix->preTranslate(bounds.left(), bounds.top()); + matrix->preScale(bounds.width() / intrinsicBounds.width(), + bounds.height() / intrinsicBounds.height()); +} + // Only called on the RenderThread. void AnimatedImageDrawable::onDraw(SkCanvas* canvas) { + // Store the matrix used to handle bounds and mirroring separate from the + // canvas. We may need to invert the matrix to determine the proper bounds + // to pass to saveLayer, and this matrix (as opposed to, potentially, the + // canvas' matrix) only uses scale and translate, so it must be invertible. + SkMatrix matrix; + SkAutoCanvasRestore acr(canvas, true); + handleBounds(&matrix, mSkAnimatedImage->getBounds(), mProperties.mBounds); + + if (mProperties.mMirrored) { + matrix.preTranslate(mSkAnimatedImage->getBounds().width(), 0); + matrix.preScale(-1, 1); + } + std::optional<SkPaint> lazyPaint; - SkAutoCanvasRestore acr(canvas, false); if (mProperties.mAlpha != SK_AlphaOPAQUE || mProperties.mColorFilter.get()) { lazyPaint.emplace(); lazyPaint->setAlpha(mProperties.mAlpha); lazyPaint->setColorFilter(mProperties.mColorFilter); lazyPaint->setFilterQuality(kLow_SkFilterQuality); } - if (mProperties.mMirrored) { - canvas->save(); - canvas->translate(mSkAnimatedImage->getBounds().width(), 0); - canvas->scale(-1, 1); - } + + canvas->concat(matrix); const bool starting = mStarting; mStarting = false; @@ -151,7 +170,11 @@ void AnimatedImageDrawable::onDraw(SkCanvas* canvas) { // The image is not animating, and never was. Draw directly from // mSkAnimatedImage. if (lazyPaint) { - canvas->saveLayer(mSkAnimatedImage->getBounds(), &*lazyPaint); + SkMatrix inverse; + (void) matrix.invert(&inverse); + SkRect r = mProperties.mBounds; + inverse.mapRect(&r); + canvas->saveLayer(r, &*lazyPaint); } std::unique_lock lock{mImageLock}; @@ -211,17 +234,31 @@ void AnimatedImageDrawable::onDraw(SkCanvas* canvas) { } int AnimatedImageDrawable::drawStaging(SkCanvas* canvas) { - SkAutoCanvasRestore acr(canvas, false); + // Store the matrix used to handle bounds and mirroring separate from the + // canvas. We may need to invert the matrix to determine the proper bounds + // to pass to saveLayer, and this matrix (as opposed to, potentially, the + // canvas' matrix) only uses scale and translate, so it must be invertible. + SkMatrix matrix; + SkAutoCanvasRestore acr(canvas, true); + handleBounds(&matrix, mSkAnimatedImage->getBounds(), mStagingProperties.mBounds); + + if (mStagingProperties.mMirrored) { + matrix.preTranslate(mSkAnimatedImage->getBounds().width(), 0); + matrix.preScale(-1, 1); + } + + canvas->concat(matrix); + if (mStagingProperties.mAlpha != SK_AlphaOPAQUE || mStagingProperties.mColorFilter.get()) { SkPaint paint; paint.setAlpha(mStagingProperties.mAlpha); paint.setColorFilter(mStagingProperties.mColorFilter); - canvas->saveLayer(mSkAnimatedImage->getBounds(), &paint); - } - if (mStagingProperties.mMirrored) { - canvas->save(); - canvas->translate(mSkAnimatedImage->getBounds().width(), 0); - canvas->scale(-1, 1); + + SkMatrix inverse; + (void) matrix.invert(&inverse); + SkRect r = mStagingProperties.mBounds; + inverse.mapRect(&r); + canvas->saveLayer(r, &paint); } if (!mRunning) { @@ -294,4 +331,10 @@ int AnimatedImageDrawable::drawStaging(SkCanvas* canvas) { return ns2ms(mTimeToShowNextSnapshot - mCurrentTime); } +SkRect AnimatedImageDrawable::onGetBounds() { + // This must return a bounds that is valid for all possible states, + // including if e.g. the client calls setBounds. + return SkRectMakeLargest(); +} + } // namespace android diff --git a/libs/hwui/hwui/AnimatedImageDrawable.h b/libs/hwui/hwui/AnimatedImageDrawable.h index f81a5a40b44e..8ca3c7e125f1 100644 --- a/libs/hwui/hwui/AnimatedImageDrawable.h +++ b/libs/hwui/hwui/AnimatedImageDrawable.h @@ -67,9 +67,10 @@ public: mStagingProperties.mColorFilter = filter; } void setStagingMirrored(bool mirrored) { mStagingProperties.mMirrored = mirrored; } + void setStagingBounds(const SkRect& bounds) { mStagingProperties.mBounds = bounds; } void syncProperties(); - virtual SkRect onGetBounds() override { return mSkAnimatedImage->getBounds(); } + SkRect onGetBounds() override; // Draw to software canvas, and return time to next draw. // 0 means the animation is not running. @@ -109,7 +110,7 @@ public: size_t byteSize() const { return sizeof(*this) + mBytesUsed; } protected: - virtual void onDraw(SkCanvas* canvas) override; + void onDraw(SkCanvas* canvas) override; private: sk_sp<SkAnimatedImage> mSkAnimatedImage; @@ -145,6 +146,7 @@ private: int mAlpha = SK_AlphaOPAQUE; sk_sp<SkColorFilter> mColorFilter; bool mMirrored = false; + SkRect mBounds; Properties() = default; Properties(Properties&) = default; diff --git a/libs/hwui/jni/AnimatedImageDrawable.cpp b/libs/hwui/jni/AnimatedImageDrawable.cpp index 1ff156593c41..c9433ec8a9da 100644 --- a/libs/hwui/jni/AnimatedImageDrawable.cpp +++ b/libs/hwui/jni/AnimatedImageDrawable.cpp @@ -244,6 +244,14 @@ static void AnimatedImageDrawable_nSetMirrored(JNIEnv* env, jobject /*clazz*/, j drawable->setStagingMirrored(mirrored); } +static void AnimatedImageDrawable_nSetBounds(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, + jobject jrect) { + auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); + SkRect rect; + GraphicsJNI::jrect_to_rect(env, jrect, &rect); + drawable->setStagingBounds(rect); +} + static const JNINativeMethod gAnimatedImageDrawableMethods[] = { { "nCreate", "(JLandroid/graphics/ImageDecoder;IIJZLandroid/graphics/Rect;)J",(void*) AnimatedImageDrawable_nCreate }, { "nGetNativeFinalizer", "()J", (void*) AnimatedImageDrawable_nGetNativeFinalizer }, @@ -259,6 +267,7 @@ static const JNINativeMethod gAnimatedImageDrawableMethods[] = { { "nSetOnAnimationEndListener", "(JLandroid/graphics/drawable/AnimatedImageDrawable;)V", (void*) AnimatedImageDrawable_nSetOnAnimationEndListener }, { "nNativeByteSize", "(J)J", (void*) AnimatedImageDrawable_nNativeByteSize }, { "nSetMirrored", "(JZ)V", (void*) AnimatedImageDrawable_nSetMirrored }, + { "nSetBounds", "(JLandroid/graphics/Rect;)V", (void*) AnimatedImageDrawable_nSetBounds }, }; int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv* env) { diff --git a/libs/hwui/jni/Typeface.cpp b/libs/hwui/jni/Typeface.cpp index 10c80774fd16..8f455fe4ab43 100644 --- a/libs/hwui/jni/Typeface.cpp +++ b/libs/hwui/jni/Typeface.cpp @@ -25,12 +25,17 @@ #include <hwui/Typeface.h> #include <minikin/FontCollection.h> #include <minikin/FontFamily.h> +#include <minikin/FontFileParser.h> #include <minikin/SystemFonts.h> #include <utils/TraceUtils.h> #include <mutex> #include <unordered_map> +#ifdef __ANDROID__ +#include <sys/stat.h> +#endif + using namespace android; using android::uirenderer::TraceUtils; @@ -155,12 +160,43 @@ static void Typeface_registerGenericFamily(JNIEnv *env, jobject, jstring familyN toTypeface(ptr)->fFontCollection); } -static sk_sp<SkData> makeSkDataCached(const std::string& path) { +#ifdef __ANDROID__ + +static bool getVerity(const std::string& path) { + struct statx out = {}; + if (statx(AT_FDCWD, path.c_str(), 0 /* flags */, STATX_ALL, &out) != 0) { + ALOGE("statx failed for %s, errno = %d", path.c_str(), errno); + return false; + } + + // Validity check. + if ((out.stx_attributes_mask & STATX_ATTR_VERITY) == 0) { + // STATX_ATTR_VERITY not supported by kernel. + return false; + } + + return (out.stx_attributes & STATX_ATTR_VERITY) != 0; +} + +#else + +static bool getVerity(const std::string&) { + // verity check is not enabled on desktop. + return false; +} + +#endif // __ANDROID__ + +static sk_sp<SkData> makeSkDataCached(const std::string& path, bool hasVerity) { // We don't clear cache as Typeface objects created by Typeface_readTypefaces() will be stored // in a static field and will not be garbage collected. static std::unordered_map<std::string, sk_sp<SkData>> cache; static std::mutex mutex; ALOG_ASSERT(!path.empty()); + if (hasVerity && !getVerity(path)) { + LOG_ALWAYS_FATAL("verity bit was removed from %s", path.c_str()); + return nullptr; + } std::lock_guard lock{mutex}; sk_sp<SkData>& entry = cache[path]; if (entry.get() == nullptr) { @@ -171,15 +207,34 @@ static sk_sp<SkData> makeSkDataCached(const std::string& path) { static std::function<std::shared_ptr<minikin::MinikinFont>()> readMinikinFontSkia( minikin::BufferReader* reader) { - std::string_view fontPath = reader->readString(); - int fontIndex = reader->read<int>(); - const minikin::FontVariation* axesPtr; - uint32_t axesCount; - std::tie(axesPtr, axesCount) = reader->readArray<minikin::FontVariation>(); - return [fontPath, fontIndex, axesPtr, axesCount]() -> std::shared_ptr<minikin::MinikinFont> { + const void* buffer = reader->data(); + size_t pos = reader->pos(); + // Advance reader's position. + reader->skipString(); // fontPath + reader->skip<int>(); // fontIndex + reader->skipArray<minikin::FontVariation>(); // axesPtr, axesCount + bool hasVerity = static_cast<bool>(reader->read<int8_t>()); + if (hasVerity) { + reader->skip<uint32_t>(); // expectedFontRevision + reader->skipString(); // expectedPostScriptName + } + return [buffer, pos]() -> std::shared_ptr<minikin::MinikinFont> { + minikin::BufferReader fontReader(buffer, pos); + std::string_view fontPath = fontReader.readString(); std::string path(fontPath.data(), fontPath.size()); ATRACE_FORMAT("Loading font %s", path.c_str()); - sk_sp<SkData> data = makeSkDataCached(path); + int fontIndex = fontReader.read<int>(); + const minikin::FontVariation* axesPtr; + uint32_t axesCount; + std::tie(axesPtr, axesCount) = fontReader.readArray<minikin::FontVariation>(); + bool hasVerity = static_cast<bool>(fontReader.read<int8_t>()); + uint32_t expectedFontRevision; + std::string_view expectedPostScriptName; + if (hasVerity) { + expectedFontRevision = fontReader.read<uint32_t>(); + expectedPostScriptName = fontReader.readString(); + } + sk_sp<SkData> data = makeSkDataCached(path, hasVerity); if (data.get() == nullptr) { // This may happen if: // 1. When the process failed to open the file (e.g. invalid path or permission). @@ -189,6 +244,20 @@ static std::function<std::shared_ptr<minikin::MinikinFont>()> readMinikinFontSki } const void* fontPtr = data->data(); size_t fontSize = data->size(); + if (hasVerity) { + // Verify font metadata if verity is enabled. + minikin::FontFileParser parser(fontPtr, fontSize, fontIndex); + std::optional<uint32_t> revision = parser.getFontRevision(); + if (!revision.has_value() || revision.value() != expectedFontRevision) { + LOG_ALWAYS_FATAL("Wrong font revision: %s", path.c_str()); + return nullptr; + } + std::optional<std::string> psName = parser.getPostScriptName(); + if (!psName.has_value() || psName.value() != expectedPostScriptName) { + LOG_ALWAYS_FATAL("Wrong PostScript name: %s", path.c_str()); + return nullptr; + } + } std::vector<minikin::FontVariation> axes(axesPtr, axesPtr + axesCount); std::shared_ptr<minikin::MinikinFont> minikinFont = fonts::createMinikinFontSkia(std::move(data), fontPath, fontPtr, fontSize, @@ -203,10 +272,24 @@ static std::function<std::shared_ptr<minikin::MinikinFont>()> readMinikinFontSki static void writeMinikinFontSkia(minikin::BufferWriter* writer, const minikin::MinikinFont* typeface) { - writer->writeString(typeface->GetFontPath()); + const std::string& path = typeface->GetFontPath(); + writer->writeString(path); writer->write<int>(typeface->GetFontIndex()); const std::vector<minikin::FontVariation>& axes = typeface->GetAxes(); writer->writeArray<minikin::FontVariation>(axes.data(), axes.size()); + bool hasVerity = getVerity(path); + writer->write<int8_t>(static_cast<int8_t>(hasVerity)); + if (hasVerity) { + // Write font metadata for verification only when verity is enabled. + minikin::FontFileParser parser(typeface->GetFontData(), typeface->GetFontSize(), + typeface->GetFontIndex()); + std::optional<uint32_t> revision = parser.getFontRevision(); + LOG_ALWAYS_FATAL_IF(!revision.has_value()); + writer->write<uint32_t>(revision.value()); + std::optional<std::string> psName = parser.getPostScriptName(); + LOG_ALWAYS_FATAL_IF(!psName.has_value()); + writer->writeString(psName.value()); + } } static jint Typeface_writeTypefaces(JNIEnv *env, jobject, jobject buffer, jlongArray faceHandles) { diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java index 96371e505a77..9f327c93f63b 100644 --- a/media/java/android/media/tv/tuner/filter/Filter.java +++ b/media/java/android/media/tv/tuner/filter/Filter.java @@ -209,7 +209,7 @@ public class Filter implements AutoCloseable { prefix = "MONITOR_EVENT_", value = {MONITOR_EVENT_SCRAMBLING_STATUS, MONITOR_EVENT_IP_CID_CHANGE}) @Retention(RetentionPolicy.SOURCE) - public @interface MonitorEventTypeMask {} + public @interface MonitorEventMask {} /** * Monitor scrambling status change. @@ -239,7 +239,7 @@ public class Filter implements AutoCloseable { int type, int subType, FilterConfiguration settings); private native int nativeGetId(); private native long nativeGetId64Bit(); - private native int nativeConfigureMonitorEvent(int monitorEventTypesMask); + private native int nativeConfigureMonitorEvent(int monitorEventMask); private native int nativeSetDataSource(Filter source); private native int nativeStartFilter(); private native int nativeStopFilter(); @@ -344,19 +344,19 @@ public class Filter implements AutoCloseable { * will cause no-op. Use {@link TunerVersionChecker#getTunerVersion()} to get the version * information. * - * @param monitorEventTypesMask Types of event to be monitored. Set corresponding bit to - * monitor it. Reset to stop monitoring. + * @param monitorEventMask Types of event to be monitored. Set corresponding bit to + * monitor it. Reset to stop monitoring. * @return result status of the operation. */ @Result - public int configureMonitorEvent(@MonitorEventTypeMask int monitorEventTypesMask) { + public int setMonitorEventMask(@MonitorEventMask int monitorEventMask) { synchronized (mLock) { TunerUtils.checkResourceState(TAG, mIsClosed); if (!TunerVersionChecker.checkHigherOrEqualVersionTo( - TunerVersionChecker.TUNER_VERSION_1_1, "configureMonitorEvent")) { + TunerVersionChecker.TUNER_VERSION_1_1, "setMonitorEventMask")) { return Tuner.RESULT_UNAVAILABLE; } - return nativeConfigureMonitorEvent(monitorEventTypesMask); + return nativeConfigureMonitorEvent(monitorEventMask); } } diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index 6f4f72aae8ca..3837743a0ff8 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Sal nie outomaties koppel nie"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Geen internettoegang nie"</string> <string name="saved_network" msgid="7143698034077223645">"Gestoor deur <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Outomaties deur %1$s gekoppel"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Outomaties deur netwerkgraderingverskaffer gekoppel"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Gekoppel via %1$s"</string> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index 3f5df34b07b0..4ce01d68a5ae 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"በራስ-ሰር አይገናኝም"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"ምንም የበይነመረብ መዳረሻ ያለም"</string> <string name="saved_network" msgid="7143698034077223645">"የተቀመጠው በ<xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"በ%1$s በኩል በራስ-ሰር ተገናኝቷል"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"በአውታረ መረብ ደረጃ ሰጪ አቅራቢ በኩል በራስ-ሰር ተገናኝቷል"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"በ%1$s በኩል መገናኘት"</string> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index 0351d946c05c..2580d0fc82cc 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"لن يتم الاتصال تلقائيًا"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"لا يتوفّر اتصال بالإنترنت"</string> <string name="saved_network" msgid="7143698034077223645">"تم الحفظ بواسطة <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"تم الاتصال تلقائيًا عبر %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"تم الاتصال تلقائيًا عبر مقدم خدمة تقييم الشبكة"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"تم الاتصال عبر %1$s"</string> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index 0f7db8f1e966..b61ff508cfe7 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"স্বয়ংক্ৰিয়ভাৱে সংযোগ নহ’ব"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"ইণ্টাৰনেট সংযোগ নাই"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g>এ ছেভ কৰিছে"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s মাধ্যমেদি স্বয়ংক্ৰিয়ভাৱে সংযোগ কৰা হৈছে"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"নেটৱৰ্ক ৰেটিং প্ৰদানকাৰীৰ জৰিয়তে স্বয়ং সংয়োগ কৰা হ’ল"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s-ৰ মাধ্যমেদি সংযোগ কৰা হৈছে"</string> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index 0855d17e8c70..d06377677a50 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Avtomatik qoşulmayacaq"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"İnternet girişi yoxdur"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> tərəfindən saxlandı"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s üzərindən avtomatik qoşuldu"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Avtomatik olaraq şəbəkə reytinq provayderi ilə qoşuludur"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s vasitəsilə qoşuludur"</string> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index 3386fe8649ea..2976bb5893b6 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Automatsko povezivanje nije uspelo"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Nema pristupa internetu"</string> <string name="saved_network" msgid="7143698034077223645">"Sačuvao/la je <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatski povezano preko %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatski povezano preko dobavljača ocene mreže"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Veza je uspostavljena preko pristupne tačke %1$s"</string> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index 6ac2172496c4..7af9e9323e05 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Не будзе аўтаматычна падключацца"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Няма доступу да інтэрнэту"</string> <string name="saved_network" msgid="7143698034077223645">"Захавана праз: <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Аўтаматычна падключана праз %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Аўтаматычна падключана праз пастаўшчыка паслугі ацэнкі сеткі"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Падключана праз %1$s"</string> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index 19ed5bdd5688..77b6493931f7 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Няма да се свърже автоматично"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Няма достъп до интернет"</string> <string name="saved_network" msgid="7143698034077223645">"Запазено от <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Автоматично е установена връзка чрез %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Автоматично е установена връзка чрез доставчик на услуги за оценяване на мрежите"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Установена е връзка през „%1$s“"</string> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index b0e9342c19c6..819625b67725 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"অটোমেটিক কানেক্ট করবে না"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"ইন্টারনেট অ্যাক্সেস নেই"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> দ্বারা সেভ করা"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"স্বয়ংক্রিয়ভাবে %1$s এর মাধ্যমে কানেক্ট হয়েছে"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"নেটওয়ার্কের রেটিং প্রদানকারীর মাধ্যমে অটোমেটিক কানেক্ট"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s মাধ্যমে কানেক্ট হয়েছে"</string> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index 9377624d6d26..193ac6066e40 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Neće se automatski povezati"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Nema pristupa internetu"</string> <string name="saved_network" msgid="7143698034077223645">"Sačuvano: <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatski povezano koristeći %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatski povezano putem ocjenjivača mreže"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Povezani preko %1$s"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index a2a7770e7db7..fef9bfb5c8c2 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"No es connectarà automàticament"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"No hi ha accés a Internet"</string> <string name="saved_network" msgid="7143698034077223645">"Desada per <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Connectada automàticament a través de: %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Connectada automàticament a través d\'un proveïdor de valoració de xarxes"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Connectada mitjançant %1$s"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index 4db4d5c81e3f..281a78850e00 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Připojení nebude automaticky navázáno"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Nebyl zjištěn žádný přístup k internetu"</string> <string name="saved_network" msgid="7143698034077223645">"Uloženo uživatelem <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automaticky připojeno přes poskytovatele %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automaticky připojeno přes poskytovatele hodnocení sítí"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Připojeno prostřednictvím %1$s"</string> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index b76afa5c42d6..69cc8d805091 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Der oprettes ikke automatisk forbindelse"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Ingen internetadgang"</string> <string name="saved_network" msgid="7143698034077223645">"Gemt af <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatisk tilsluttet via %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisk forbundet via udbyder af netværksvurdering"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Tilsluttet via %1$s"</string> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index ba760dd54020..737ea167a8c9 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Kein automatischer Verbindungsaufbau"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Kein Internetzugriff"</string> <string name="saved_network" msgid="7143698034077223645">"Gespeichert von <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatisch über %1$s verbunden"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisch über Anbieter von Netzwerkbewertungen verbunden"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Über %1$s verbunden"</string> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index c608a629a80d..8e1d5e3d1b18 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Δεν θα συνδεθεί αυτόματα"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Δεν υπάρχει πρόσβαση στο διαδίκτυο"</string> <string name="saved_network" msgid="7143698034077223645">"Αποθηκεύτηκε από <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Συνδέθηκε αυτόματα μέσω %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Συνδέθηκε αυτόματα μέσω παρόχου αξιολόγησης δικτύου"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Συνδέθηκε μέσω %1$s"</string> diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index e67c3d1ddc5a..b98c4b8968e9 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -36,6 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Won\'t automatically connect"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"No Internet access"</string> <string name="saved_network" msgid="7143698034077223645">"Saved by <xliff:g id="NAME">%1$s</xliff:g>"</string> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Connected to metered network"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatically connected via %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatically connected via network rating provider"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Connected via %1$s"</string> diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml index b0830fc40e2e..aa0d3f1ddf18 100644 --- a/packages/SettingsLib/res/values-en-rCA/strings.xml +++ b/packages/SettingsLib/res/values-en-rCA/strings.xml @@ -36,6 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Won\'t automatically connect"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"No Internet access"</string> <string name="saved_network" msgid="7143698034077223645">"Saved by <xliff:g id="NAME">%1$s</xliff:g>"</string> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Connected to metered network"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatically connected via %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatically connected via network rating provider"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Connected via %1$s"</string> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index e67c3d1ddc5a..b98c4b8968e9 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -36,6 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Won\'t automatically connect"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"No Internet access"</string> <string name="saved_network" msgid="7143698034077223645">"Saved by <xliff:g id="NAME">%1$s</xliff:g>"</string> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Connected to metered network"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatically connected via %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatically connected via network rating provider"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Connected via %1$s"</string> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index e67c3d1ddc5a..b98c4b8968e9 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -36,6 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Won\'t automatically connect"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"No Internet access"</string> <string name="saved_network" msgid="7143698034077223645">"Saved by <xliff:g id="NAME">%1$s</xliff:g>"</string> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Connected to metered network"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatically connected via %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatically connected via network rating provider"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Connected via %1$s"</string> diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml index 4c0af75fada6..c01f3a05645e 100644 --- a/packages/SettingsLib/res/values-en-rXC/strings.xml +++ b/packages/SettingsLib/res/values-en-rXC/strings.xml @@ -36,6 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Won\'t automatically connect"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"No internet access"</string> <string name="saved_network" msgid="7143698034077223645">"Saved by <xliff:g id="NAME">%1$s</xliff:g>"</string> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Connected to metered network"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatically connected via %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatically connected via network rating provider"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Connected via %1$s"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index 9ae77819029b..f79072fff1e9 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"No se conectará automáticamente"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"No hay acceso a Internet"</string> <string name="saved_network" msgid="7143698034077223645">"Guardada por <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Conexión automática mediante %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectado automáticamente mediante proveedor de calificación de red"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Conexión a través de %1$s"</string> @@ -116,8 +118,8 @@ <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"SINCRONIZAR"</string> <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Cancelar"</string> <string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"La sincronización te permite acceder a los contactos y al historial de llamadas cuando el dispositivo está conectado."</string> - <string name="bluetooth_pairing_error_message" msgid="6626399020672335565">"No se pudo sincronizar con <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> - <string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"No se pudo sincronizar con <xliff:g id="DEVICE_NAME">%1$s</xliff:g> debido a que el PIN o la clave de acceso son incorrectos."</string> + <string name="bluetooth_pairing_error_message" msgid="6626399020672335565">"No se pudo vincular con <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"No se pudo vincular con <xliff:g id="DEVICE_NAME">%1$s</xliff:g> debido a que el PIN o la clave de acceso son incorrectos."</string> <string name="bluetooth_pairing_device_down_error_message" msgid="2554424863101358857">"No se puede establecer la comunicación con <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> <string name="bluetooth_pairing_rejected_error_message" msgid="5943444352777314442">"Vínculo rechazado por <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_talkback_computer" msgid="3736623135703893773">"Computadora"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index 203726a5d557..f99a3f12307b 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"No se establecerá conexión automáticamente"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"No se ha detectado ningún acceso a Internet"</string> <string name="saved_network" msgid="7143698034077223645">"Guardada por <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Conectada automáticamente a través de %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectado automáticamente a través de un proveedor de valoración de redes"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Conectado a través de %1$s"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index 379ed6cb9065..fa2aa467d0c5 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Automaatselt ei ühendata"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Juurdepääs Internetile puudub"</string> <string name="saved_network" msgid="7143698034077223645">"Salvestas: <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Ühendus loodi automaatselt teenusega %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Ühendus loodi automaatselt võrgukvaliteedi hinnangute pakkuja kaudu"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Ühendatud üksuse %1$s kaudu"</string> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index 2582854924fe..2c4a8ee09f5e 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Ez da konektatuko automatikoki"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Ezin da konektatu Internetera"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> aplikazioak gorde du"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s bidez automatikoki konektatuta"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatikoki konektatuta sare-balorazioen hornitzailearen bidez"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s bidez konektatuta"</string> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index cb598e523100..1c6ca763197a 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"اتصال بهصورت خودکار انجام نمیشود"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"دسترسی به اینترنت ندارد"</string> <string name="saved_network" msgid="7143698034077223645">"ذخیرهشده توسط <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"اتصال خودکار ازطریق %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"اتصال خودکار ازطریق ارائهدهنده رتبهبندی شبکه"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"متصل از طریق %1$s"</string> @@ -263,8 +265,8 @@ <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"انتخاب نسخه MAP بلوتوث"</string> <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"کدک بلوتوث صوتی"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"راهاندازی کدک صوتی بلوتوثی\nانتخاب"</string> - <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"سرعت نمونه بلوتوث صوتی"</string> - <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5876305103137067798">"راهاندازی کدک صوتی بلوتوثی\nانتخاب: سرعت نمونه"</string> + <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"بسامد نمونه صوتی بلوتوث"</string> + <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5876305103137067798">"راهاندازی کدک صوتی بلوتوثی\nانتخاب: بسامد نمونه"</string> <string name="bluetooth_select_a2dp_codec_type_help_info" msgid="8647200416514412338">"«خاکستری» به این معناست که تلفن یا هدست از آن پشتیبانی نمیکند"</string> <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="6253965294594390806">"بیتهای بلوتوث صوتی در هر نمونه"</string> <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4898693684282596143">"راهاندازی کدک صوتی بلوتوثی\nانتخاب: تعداد بیت در نمونه"</string> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index 31dfe1829133..04c9130ec31f 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Yhteyttä ei muodosteta automaattisesti"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Ei internetyhteyttä"</string> <string name="saved_network" msgid="7143698034077223645">"Tallentaja: <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automaattinen yhteys muodostettu palvelun %1$s kautta"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Yhdistetty automaattisesti verkon arviointipalvelun kautta"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Yhdistetty seuraavan kautta: %1$s"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index a8476dd212d1..aa0cd2a684c7 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Reconnexion automatique impossible"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Aucun accès à Internet"</string> <string name="saved_network" msgid="7143698034077223645">"Enregistrés par <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatiquement connecté par %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Connecté automatiquement par le fournisseur d\'avis sur le réseau"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Connecté par %1$s"</string> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index a79ed0c51ef7..dbdc160030fa 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Reconnexion automatique impossible"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Aucun accès à Internet"</string> <string name="saved_network" msgid="7143698034077223645">"Enregistré lors de : <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Connecté automatiquement via %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Connecté automatiquement via un fournisseur d\'évaluation de l\'état du réseau"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Connecté via %1$s"</string> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index 97662a65fafe..90c130301a8f 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Non se conectará automaticamente"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Sen acceso a Internet"</string> <string name="saved_network" msgid="7143698034077223645">"Gardada por <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Conectouse automaticamente a través de %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectada automaticamente a través dun provedor de valoración de redes"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Conectado a través de %1$s"</string> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index 46bd71b9b382..4caeda279849 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ઑટોમૅટિક રીતે કનેક્ટ કરશે નહીં"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"કોઈ ઇન્ટરનેટ ઍક્સેસ નથી"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> દ્વારા સચવાયું"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s દ્વારા સ્વત: કનેક્ટ થયેલ"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"નેટવર્ક રેટિંગ પ્રદાતા દ્વારા ઑટોમૅટિક રીતે કનેક્ટ થયું"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s દ્વારા કનેક્ટ થયેલ"</string> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 9cae3112cd48..468808be918a 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"अपने आप कनेक्ट नहीं होगा"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"इंटरनेट नहीं है"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> के द्वारा सहेजा गया"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s के ज़रिए ऑटोमैटिक रूप से कनेक्ट है"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"नेटवर्क रेटिंग कंपनी के ज़रिए अपने आप कनेक्ट है"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s के द्वारा उपलब्ध"</string> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index 83bb2d1e7732..932c2560f2d9 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Neće se povezati automatski"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Nema pristupa internetu"</string> <string name="saved_network" msgid="7143698034077223645">"Spremila aplik. <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatski povezan putem %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatski povezan putem ocjenjivača mreže"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Povezano putem %1$s"</string> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index 9f184c7052f5..fbaffac6b083 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nem csatlakozik automatikusan"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Nincs internet-hozzáférés"</string> <string name="saved_network" msgid="7143698034077223645">"Mentette: <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatikusan csatlakozott a következőn keresztül: %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatikusan csatlakozott a hálózatértékelés szolgáltatóján keresztül"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Csatlakozva a következőn keresztül: %1$s"</string> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index cd6cbf3bc396..224d6418b5d5 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Չի միանա ավտոմատ"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Ինտերնետ կապ չկա"</string> <string name="saved_network" msgid="7143698034077223645">"Ով է պահել՝ <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Ավտոմատ կերպով կապակցվել է %1$s-ի միջոցով"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Ավտոմատ միացել է ցանցերի վարկանիշի մատակարարի միջոցով"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Միացված է %1$s-ի միջոցով"</string> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index 3b8091872562..8cf13cde6a42 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Tidak akan tersambung otomatis"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Tidak ada akses internet"</string> <string name="saved_network" msgid="7143698034077223645">"Disimpan oleh <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Tersambung otomatis melalui %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Otomatis tersambung melalui penyedia rating jaringan"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Terhubung melalui %1$s"</string> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index ce9e66522aaf..aa8893e4783a 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Mun ekki tengjast sjálfkrafa"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Enginn netaðgangur"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> vistaði"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Sjálfkrafa tengt um %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Sjálfkrafa tengt um netgæðaveitu"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Tengt í gegnum %1$s"</string> @@ -226,12 +228,12 @@ <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Wi-Fi pörunarkóði"</string> <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"Pörun mistókst"</string> <string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"Gakktu úr skugga um að tækið sé tengt sama neti."</string> - <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Tengja tæki með Wi-Fi með því að skanna QR-kóða"</string> + <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Para tæki gegnum Wi-Fi með því að skanna QR-kóða"</string> <string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"Parar tæki…"</string> <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Ekki tókst að para við tækið. Annað hvort var QR-kóðinn rangur eða tækið ekki tengt sama neti."</string> <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"IP-tala og gátt"</string> <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Skanna QR-kóða"</string> - <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Tengja tæki með Wi-Fi með því að skanna QR-kóða"</string> + <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Para tæki gegnum Wi-Fi með því að skanna QR-kóða"</string> <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Tengstu Wi-Fi neti"</string> <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, villuleit, dev"</string> <string name="bugreport_in_power" msgid="8664089072534638709">"Flýtileið í villutilkynningu"</string> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index 93b24a0bf066..0199f549d099 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Non verrà eseguita la connessione automatica"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Nessun accesso a Internet"</string> <string name="saved_network" msgid="7143698034077223645">"Salvata da <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Collegato automaticamente tramite %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Collegato automaticamente tramite fornitore di servizi di valutazione rete"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Collegato tramite %1$s"</string> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index ab678092dd9c..a50a22d2d08d 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"לא יתבצע חיבור באופן אוטומטי"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"אין גישה לאינטרנט"</string> <string name="saved_network" msgid="7143698034077223645">"נשמר על ידי <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"מחובר אוטומטית דרך %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"מחובר אוטומטית דרך ספק של דירוג רשת"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"מחובר דרך %1$s"</string> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index 711a22f8a41e..a1d1b70d607e 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"自動的に接続されません"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"インターネット接続なし"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g>で保存"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s 経由で自動的に接続しています"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ネットワーク評価プロバイダ経由で自動的に接続しています"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s経由で接続"</string> diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml index 08133056c408..77fd4b156024 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ავტომატურად დაკავშირება ვერ მოხერხდება"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"ინტერნეტ-კავშირი არ არის"</string> <string name="saved_network" msgid="7143698034077223645">"შენახული <xliff:g id="NAME">%1$s</xliff:g>-ის მიერ"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"ავტომატურად დაკავშირდა %1$s-ის მეშვეობით"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ავტომატურად დაკავშირდა ქსელის ხარისხის შეფასების პროვაიდერის მეშვეობით"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s-ით დაკავშირებული"</string> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index 16ee9c00fa18..eb5cd54e4f19 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Автоматты қосылмайды"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Интернетпен байланыс жоқ"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> сақтаған"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s арқылы автоматты қосылды"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Желі рейтингі провайдері арқылы автоматты түрде қосылған"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s арқылы қосылған"</string> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index 5b47381758f7..38abb805cb10 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"នឹងមិនភ្ជាប់ដោយស្វ័យប្រវត្តិទេ"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"មិនមានការតភ្ជាប់អ៊ីនធឺណិតទេ"</string> <string name="saved_network" msgid="7143698034077223645">"បានរក្សាទុកដោយ <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"បានភ្ជាប់ដោយស្វ័យប្រវត្តិតាមរយៈ %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"បានភ្ជាប់ដោយស្វ័យប្រវត្តិតាមរយៈក្រុមហ៊ុនផ្តល់ការវាយតម្លៃលើបណ្តាញ"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"បានភ្ជាប់តាមរយៈ %1$s"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index fd63dcb343ae..560fba15bbad 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸಂಪರ್ಕಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಪ್ರವೇಶವಿಲ್ಲ"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> ನಿಂದ ಉಳಿಸಲಾಗಿದೆ"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s ಮೂಲಕ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸಂಪರ್ಕಿಸಲಾಗಿದೆ"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ನೆಟ್ವರ್ಕ್ ರೇಟಿಂಗ್ ಒದಗಿಸುವವರ ಮೂಲಕ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸಂಪರ್ಕಿಸಲಾಗಿದೆ"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s ಮೂಲಕ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index a9c3f316f4c2..f43ce16368cb 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"자동으로 연결되지 않습니다."</string> <string name="wifi_no_internet" msgid="1774198889176926299">"인터넷에 연결되어 있지 않음"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g>(으)로 저장됨"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s을(를) 통해 자동으로 연결됨"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"네트워크 평가 제공업체를 통해 자동으로 연결됨"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s을(를) 통해 연결됨"</string> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index bc3656f0846f..35b1ecacd505 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Автоматтык түрдө туташпайт"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Интернетке туташпай турат"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> тарабынан сакталды"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s аркылуу автоматтык түрдө туташты"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Тармактар рейтингинин булагы аркылуу автоматтык түрдө туташты"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s аркылуу жеткиликтүү"</string> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index 8408c93459a6..f60fe7f8affc 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ຈະບໍ່ເຊື່ອມຕໍ່ອັດຕະໂນມັດ"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"ບໍ່ມີການເຊື່ອມຕໍ່ອິນເຕີເນັດ"</string> <string name="saved_network" msgid="7143698034077223645">"ບັນທຶກໂດຍ <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"ເຊື່ອມຕໍ່ຜ່ານທາງ %1$s ໂດຍອັດຕະໂນມັດ"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ເຊື່ອມຕໍ່ກັບອັດຕະໂນມັດແລ້ວຜ່ານຜູ້ໃຫ້ບໍລິການຄະແນນເຄືອຂ່າຍ"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"ເຊື່ອມຕໍ່ຜ່ານ %1$s ແລ້ວ"</string> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index 1dcfcf7bbb36..e66e3c5a9944 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nebus automatiškai prisijungiama"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Nėra interneto ryšio"</string> <string name="saved_network" msgid="7143698034077223645">"Išsaugojo <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatiškai prisijungta naudojant „%1$s“"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatiškai prisijungta naudojant tinklo įvertinimo paslaugos teikėjo paslaugomis"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Prisijungta naudojant „%1$s“"</string> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index 3d9b78acf6ed..a8bd2cc34a51 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -36,6 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Savienojums netiks izveidots automātiski"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Nav piekļuves internetam"</string> <string name="saved_network" msgid="7143698034077223645">"Saglabāja: <xliff:g id="NAME">%1$s</xliff:g>"</string> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Izveidots savienojums ar maksas tīklu"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automātiski savienots, izmantojot %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automātiski izveidots savienojums, izmantojot tīkla vērtējuma sniedzēju"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Savienots, izmantojot %1$s"</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index 0674f118c04b..90bcc04d1212 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Не може да се поврзе автоматски"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Нема пристап до интернет"</string> <string name="saved_network" msgid="7143698034077223645">"Зачувано од <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Автоматски поврзано преку %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Автоматски поврзано преку оценувач на мрежа"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Поврзано преку %1$s"</string> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index 1455669fbc85..ba1987bc1d88 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"സ്വയമേവ കണക്റ്റുചെയ്യില്ല"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"ഇന്റർനെറ്റ് ആക്സസ് ഇല്ല"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> സംരക്ഷിച്ചത്"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s വഴി സ്വയമേവ ബന്ധിപ്പിച്ചു"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"നെറ്റ്വർക്ക് റേറ്റിംഗ് ദാതാവുമായി സ്വയം കണക്റ്റുചെയ്തു"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s വഴി ബന്ധിപ്പിച്ചു"</string> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index f13cb0b03884..45a831daee14 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Автоматаар холбогдохгүй"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Интернет хандалт алга"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> хадгалсан"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s-р автоматаар холбогдсон"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Сүлжээний үнэлгээ үзүүлэгчээр автоматаар холбогдох"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s-р холбогдсон"</string> diff --git a/packages/SettingsLib/res/values-mr/arrays.xml b/packages/SettingsLib/res/values-mr/arrays.xml index aaf51b3d8a68..6cc013071223 100644 --- a/packages/SettingsLib/res/values-mr/arrays.xml +++ b/packages/SettingsLib/res/values-mr/arrays.xml @@ -155,14 +155,28 @@ <item msgid="253388653486517049">", अॅक्टिव्ह (मीडिया)"</item> <item msgid="5001852592115448348">", अॅक्टिव्ह (फोन)"</item> </string-array> - <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> + <string-array name="select_logd_size_titles"> + <item msgid="1191094707770726722">"बंद"</item> + <item msgid="7839165897132179888">"64K"</item> + <item msgid="2715700596495505626">"256K"</item> + <item msgid="7099386891713159947">"1M"</item> + <item msgid="6069075827077845520">"4M"</item> + <item msgid="6078203297886482480">"८MB"</item> + </string-array> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"बंद"</item> <item msgid="4064786181089783077">"64K"</item> <item msgid="3052710745383602630">"256K"</item> <item msgid="3691785423374588514">"1M"</item> </string-array> - <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> + <string-array name="select_logd_size_summaries"> + <item msgid="409235464399258501">"बंद"</item> + <item msgid="4195153527464162486">"प्रति लॉग बफर 64K"</item> + <item msgid="7464037639415220106">"प्रति लॉग बफर 256K"</item> + <item msgid="8539423820514360724">"प्रति लॉग बफर 1M"</item> + <item msgid="1984761927103140651">"प्रति लॉग बफर 4M"</item> + <item msgid="2983219471251787208">"८MB प्रति लॉग बफर"</item> + </string-array> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"बंद"</item> <item msgid="6014837961827347618">"सर्व"</item> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index 72ddc8f9b024..1e69b28fa4de 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"स्वयंचलितपणे कनेक्ट करणार नाही"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"इंटरनेट अॅक्सेस नाही"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> द्वारे सेव्ह केले"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s द्वारे स्वयंचलितपणे कनेक्ट केले"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"नेटवर्क रेटिंग प्रदात्याद्वारे स्वयंचलितपणे कनेक्ट केले"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s द्वारे कनेक्ट केले"</string> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index 9527793b179e..71c9eeafc594 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Tidak akan menyambung secara automatik"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Tiada akses Internet"</string> <string name="saved_network" msgid="7143698034077223645">"Diselamatkan oleh <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Disambungkan secara automatik melalui %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Disambungkan secara automatik melalui pembekal penilaian rangkaian"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Disambungkan melalui %1$s"</string> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index 1dcb3cfd349d..3b3983e5164e 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"အလိုအလျောက်ချိတ်ဆက်မည်မဟုတ်ပါ"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"အင်တာနက် ချိတ်ဆက်မှု မရှိပါ"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> က သိမ်းဆည်းခဲ့သည်"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s မှတစ်ဆင့် အလိုအလျောက် ချိတ်ဆက်ထားပါသည်"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ကွန်ရက်အဆင့်သတ်မှတ်ပေးသူ မှတစ်ဆင့် အလိုအလျောက် ချိတ်ဆက်ထားပါသည်"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s မှတစ်ဆင့် ချိတ်ဆက်ထားသည်"</string> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index 13f69c1b793e..a8c01b3db73c 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Kobler ikke til automatisk"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Ingen internettilgang"</string> <string name="saved_network" msgid="7143698034077223645">"Lagret av <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatisk tilkoblet via %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisk tilkoblet via leverandør av nettverksvurdering"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Tilkoblet via %1$s"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index 5795cc9b6d92..c28563764a2d 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"स्वतः जडान हुने छैन"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"इन्टरनेटमाथिको पहुँच छैन"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> द्वारा सुरक्षित गरियो"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s मार्फत् स्वतः जडान गरिएको"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"नेटवर्कको दर्जा प्रदायक मार्फत स्वत: जडान गरिएको"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s मार्फत जडित"</string> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index 718ce5de62f8..9f4ea68f4c64 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Er wordt niet automatisch verbinding gemaakt"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Geen internettoegang"</string> <string name="saved_network" msgid="7143698034077223645">"Opgeslagen door \'<xliff:g id="NAME">%1$s</xliff:g>\'"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatisch verbonden via %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisch verbonden via provider van netwerkbeoordelingen"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Verbonden via %1$s"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index 41e84f9e1a84..31bd7af804e0 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ସ୍ୱଚାଳିତ ଭାବେ ସଂଯୁକ୍ତ ହେବନାହିଁ"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"ଇଣ୍ଟରନେଟ୍ର କୌଣସି ଆକ୍ସେସ୍ ନାହିଁ"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> ଦ୍ୱାରା ସେଭ କରାଯାଇଛି"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s ମାଧ୍ୟମରେ ଅଟୋମେଟିକାଲୀ ସଂଯୁକ୍ତ"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ନେଟୱର୍କ ମୂଲ୍ୟାୟନ ପ୍ରଦାତାଙ୍କ ମାଧ୍ୟମରେ ଅଟୋମେଟିକାଲ୍ୟ ସଂଯୁକ୍ତ"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s ମାଧ୍ୟମରେ ସଂଯୁକ୍ତ"</string> diff --git a/packages/SettingsLib/res/values-pa/arrays.xml b/packages/SettingsLib/res/values-pa/arrays.xml index c64ee7604058..c6116aa48bdb 100644 --- a/packages/SettingsLib/res/values-pa/arrays.xml +++ b/packages/SettingsLib/res/values-pa/arrays.xml @@ -155,14 +155,28 @@ <item msgid="253388653486517049">", ਕਿਰਿਆਸ਼ੀਲ (ਮੀਡੀਆ)"</item> <item msgid="5001852592115448348">", ਕਿਰਿਆਸ਼ੀਲ (ਫ਼ੋਨ)"</item> </string-array> - <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) --> + <string-array name="select_logd_size_titles"> + <item msgid="1191094707770726722">"ਬੰਦ"</item> + <item msgid="7839165897132179888">"64K"</item> + <item msgid="2715700596495505626">"256K"</item> + <item msgid="7099386891713159947">"1M"</item> + <item msgid="6069075827077845520">"4M"</item> + <item msgid="6078203297886482480">"8M"</item> + </string-array> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"ਬੰਦ"</item> <item msgid="4064786181089783077">"64K"</item> <item msgid="3052710745383602630">"256K"</item> <item msgid="3691785423374588514">"1M"</item> </string-array> - <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) --> + <string-array name="select_logd_size_summaries"> + <item msgid="409235464399258501">"ਬੰਦ"</item> + <item msgid="4195153527464162486">"64K ਪ੍ਰਤੀ ਲੌਗ ਬਫ਼ਰ"</item> + <item msgid="7464037639415220106">"256K ਪ੍ਰਤੀ ਲੌਗ ਬਫ਼ਰ"</item> + <item msgid="8539423820514360724">"1M ਪ੍ਰਤੀ ਲੌਗ ਬਫ਼ਰ"</item> + <item msgid="1984761927103140651">"4M ਪ੍ਰਤੀ ਲੌਗ ਬਫ਼ਰ"</item> + <item msgid="2983219471251787208">"8M ਪ੍ਰਤੀ ਲੌਗ ਬਫ਼ਰ"</item> + </string-array> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"ਬੰਦ"</item> <item msgid="6014837961827347618">"ਸਭ"</item> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index e04e2017265f..f21c4cedfb71 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ ਜਾਵੇਗਾ"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"ਕੋਈ ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ ਨਹੀਂ"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> ਵੱਲੋਂ ਰੱਖਿਅਤ ਕੀਤਾ ਗਿਆ"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s ਰਾਹੀਂ ਆਪਣੇ-ਆਪ ਕਨੈਕਟ ਹੋਇਆ"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ਨੈੱਟਵਰਕ ਰੇਟਿੰਗ ਪ੍ਰਦਾਨਕ ਰਾਹੀਂ ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਕਨੈਕਟ ਹੋਇਆ"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s ਰਾਹੀਂ ਕਨੈਕਟ ਕੀਤਾ"</string> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index a57e5413e9aa..c9c4a6c31f49 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nie można połączyć automatycznie"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Brak dostępu do internetu"</string> <string name="saved_network" msgid="7143698034077223645">"Zapisane przez: <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatycznie połączono przez: %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatycznie połączono przez dostawcę ocen jakości sieci"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Połączono przez %1$s"</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index f24b52bb409c..881bccb90062 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -36,6 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Não se conectará automaticamente"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Sem acesso à Internet"</string> <string name="saved_network" msgid="7143698034077223645">"Salva por <xliff:g id="NAME">%1$s</xliff:g>"</string> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Conectado a uma rede limitada"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Conectado automaticamente via %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectado automaticamente via provedor de avaliação de rede"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Conectado via %1$s"</string> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index 42ad0fece020..94cad918cab8 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -36,6 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Não é efetuada uma ligação automaticamente"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Sem acesso à Internet."</string> <string name="saved_network" msgid="7143698034077223645">"Guardada por <xliff:g id="NAME">%1$s</xliff:g>"</string> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Ligação estabelecida a uma rede de acesso limitado."</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Ligado automaticamente através de %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Ligado automaticamente através do fornecedor de classificação de rede"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Ligado através de %1$s"</string> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index f24b52bb409c..881bccb90062 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -36,6 +36,7 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Não se conectará automaticamente"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Sem acesso à Internet"</string> <string name="saved_network" msgid="7143698034077223645">"Salva por <xliff:g id="NAME">%1$s</xliff:g>"</string> + <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Conectado a uma rede limitada"</string> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Conectado automaticamente via %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectado automaticamente via provedor de avaliação de rede"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Conectado via %1$s"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index 0743fe99056c..4b710853f329 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nu se va conecta automat"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Nu există acces la internet"</string> <string name="saved_network" msgid="7143698034077223645">"Salvată de <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Conectată automat prin %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectată automat prin furnizor de evaluări ale rețelei"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Conectată prin %1$s"</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index d19438aceb16..4aeb9855ff2b 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Подключение не будет выполняться автоматически"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Без доступа к Интернету"</string> <string name="saved_network" msgid="7143698034077223645">"Кто сохранил: <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Автоматически подключено к %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Автоматически подключено через автора рейтинга сетей"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Подключено к %1$s"</string> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index 15fe8c8370e8..525d4231eb4c 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ස්වයංක්රිය නැවත සම්බන්ධ නොවනු ඇත"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"අන්තර්ජාල ප්රවේශය නැත"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> විසින් සුරකින ලදී"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s හරහා ස්වයංක්රියව සම්බන්ධ විය"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ජාල ශ්රේණිගත සපයන්නා හරහා ස්වයංක්රියව සම්බන්ධ විය"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s හරහා සම්බන්ධ විය"</string> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index ee53b7cfbcec..ee9ae6a47d2f 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nedôjde k automatickému pripojeniu"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Žiadny prístup k internetu"</string> <string name="saved_network" msgid="7143698034077223645">"Uložila aplikácia <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automaticky pripojené prostredníctvom %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automaticky pripojené prostredníctvom poskytovateľa hodnotenia siete"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Pripojené prostredníctvom %1$s"</string> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index 66d33a7bf51a..ff60a322dc1d 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Samodejna vnovična vzpostavitev povezave se ne bo izvedla"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Ni dostopa do interneta"</string> <string name="saved_network" msgid="7143698034077223645">"Shranil(-a): <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Samodejno vzpostavljena povezava prek: %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Samodejno vzpostavljena povezava prek ponudnika ocenjevanja omrežij"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Vzpostavljena povezava prek: %1$s"</string> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index d5c0231f7652..78e6ed60c176 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nuk do të lidhet automatikisht"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Nuk ka qasje në internet"</string> <string name="saved_network" msgid="7143698034077223645">"E ruajtur nga <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Lidhur automatikisht përmes %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Lidhur automatikisht nëpërmjet ofruesit të vlerësimit të rrjetit"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"E lidhur përmes %1$s"</string> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index ce74b84aa1e4..8bb9277ba42b 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Аутоматско повезивање није успело"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Нема приступа интернету"</string> <string name="saved_network" msgid="7143698034077223645">"Сачувао/ла је <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Аутоматски повезано преко %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Аутоматски повезано преко добављача оцене мреже"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Веза је успостављена преко приступне тачке %1$s"</string> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index eacb7a80cdb8..03fb2238989c 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Det går inte att ansluta automatiskt"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Ingen internetåtkomst"</string> <string name="saved_network" msgid="7143698034077223645">"Sparades av <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatiskt ansluten via %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatiskt ansluten via leverantör av nätverksbetyg"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Anslutet via %1$s"</string> diff --git a/packages/SettingsLib/res/values-sw/arrays.xml b/packages/SettingsLib/res/values-sw/arrays.xml index 62ae06b55152..b95d69c95fc7 100644 --- a/packages/SettingsLib/res/values-sw/arrays.xml +++ b/packages/SettingsLib/res/values-sw/arrays.xml @@ -161,7 +161,7 @@ <item msgid="2715700596495505626">"K256"</item> <item msgid="7099386891713159947">"M1"</item> <item msgid="6069075827077845520">"M4"</item> - <item msgid="6078203297886482480">"M8"</item> + <item msgid="6078203297886482480">"MB 8"</item> </string-array> <string-array name="select_logd_size_lowram_titles"> <item msgid="1145807928339101085">"Imezimwa"</item> @@ -175,7 +175,7 @@ <item msgid="7464037639415220106">"K256 kwa kila akiba ya kumbukumbu"</item> <item msgid="8539423820514360724">"M1 kwa kila akiba ya kumbukumbu"</item> <item msgid="1984761927103140651">"M4 kwa kila akiba ya kumbukumbu"</item> - <item msgid="2983219471251787208">"M8 kwa kila akiba ya kumbukumbu"</item> + <item msgid="2983219471251787208">"MB 8 kwa kila akiba ya kumbukumbu"</item> </string-array> <string-array name="select_logpersist_titles"> <item msgid="704720725704372366">"Yamezimwa"</item> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index e7045a72b51b..631a41346282 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Haiwezi kuunganisha kiotomatiki"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Hakuna muunganisho wa intaneti"</string> <string name="saved_network" msgid="7143698034077223645">"Ilihifadhiwa na <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Imeunganishwa kiotomatiki kupitia %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Imeunganishwa kiotomatiki kupitia mtoa huduma wa ukadiriaji wa mtandao"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Imeunganishwa kupitia %1$s"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index 06c7ccb98120..5796603a761a 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"தானாக இணைக்கப்படாது"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"இண்டர்நெட் அணுகல் இல்லை"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> சேமித்தது"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s மூலம் தானாக இணைக்கப்பட்டது"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"நெட்வொர்க் மதிப்பீடு வழங்குநரால் தானாக இணைக்கப்பட்டது"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s வழியாக இணைக்கப்பட்டது"</string> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index 5e880a95c2de..9027ca18fc79 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"స్వయంచాలకంగా కనెక్ట్ కాదు"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"ఇంటర్నెట్ యాక్సెస్ లేదు"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> ద్వారా సేవ్ చేయబడింది"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s ద్వారా స్వయంచాలకంగా కనెక్ట్ చేయబడింది"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"నెట్వర్క్ రేటింగ్ ప్రదాత ద్వారా స్వయంచాలకంగా కనెక్ట్ చేయబడింది"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s ద్వారా కనెక్ట్ చేయబడింది"</string> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index 6316452511b4..8358dbbfafa5 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"จะไม่เชื่อมต่อโดยอัตโนมัติ"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"เข้าถึงอินเทอร์เน็ตไม่ได้"</string> <string name="saved_network" msgid="7143698034077223645">"บันทึกโดย<xliff:g id="NAME">%1$s</xliff:g> แล้ว"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"เชื่อมต่ออัตโนมัติผ่าน %1$s แล้ว"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"เชื่อมต่ออัตโนมัติผ่านผู้ให้บริการการจัดอันดับเครือข่าย"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"เชื่อมต่อผ่าน %1$s แล้ว"</string> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index 0aabbe5f96ab..d5250a4581db 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Hindi awtomatikong kokonekta"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Walang access sa internet"</string> <string name="saved_network" msgid="7143698034077223645">"Na-save ng <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Awtomatikong nakakonekta sa pamamagitan ng %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Awtomatikong nakakonekta sa pamamagitan ng provider ng rating ng network"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Nakakonekta sa pamamagitan ng %1$s"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index 44c8f13acb96..8e0b889f5191 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Otomatik olarak bağlanma"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"İnternet erişimi yok"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> tarafından kaydedildi"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s üzerinden otomatik olarak bağlı"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Ağ derecelendirme sağlayıcı aracılığıyla otomatik olarak bağlandı"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s üzerinden bağlı"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index 7851111910d2..c2f71c38d83e 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Не під’єднуватиметься автоматично"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Немає доступу до Інтернету"</string> <string name="saved_network" msgid="7143698034077223645">"Збережено додатком <xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Автоматично під’єднано через %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Автоматично під’єднано через постачальника оцінки якості мережі"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Під’єднано через %1$s"</string> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index 82783b3a172a..a0c5f94a2845 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"خودکار طور پر منسلک نہیں ہو گا"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"انٹرنیٹ تک کوئی رسائی نہیں"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> کی جانب سے محفوظ کردہ"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s کے ذریعے از خود منسلک کردہ"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"نیٹ ورک درجہ بندی کے فراہم کنندہ کے ذریعے از خود منسلک"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"منسلک بذریعہ %1$s"</string> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index c2a96c6a810e..1fb610bad36e 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Avtomatik ravishda ulanilmaydi"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Internet aloqasi yo‘q"</string> <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> tomonidan saqlangan"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s orqali avtomatik ulandi"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Tarmoqlar reytingi muallifi orqali avtomatik ulandi"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s orqali ulangan"</string> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index 259642466187..1f3ea486bd08 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Sẽ không tự động kết nối"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Không có quyền truy cập Internet"</string> <string name="saved_network" msgid="7143698034077223645">"Do <xliff:g id="NAME">%1$s</xliff:g> lưu"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Tự động được kết nối qua %1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Tự động được kết nối qua nhà cung cấp dịch vụ xếp hạng mạng"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Được kết nối qua %1$s"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index 60afd6d8352f..828d25fbe71b 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"无法自动连接"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"无法访问互联网"</string> <string name="saved_network" msgid="7143698034077223645">"由“<xliff:g id="NAME">%1$s</xliff:g>”保存"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"已通过%1$s自动连接"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"已自动连接(通过网络评分服务提供方)"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"已通过%1$s连接"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index 925f73870ea3..54e1f41e9ed8 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"不會自動連線"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"無法連接互聯網"</string> <string name="saved_network" msgid="7143698034077223645">"由「<xliff:g id="NAME">%1$s</xliff:g>」儲存"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"已透過 %1$s 自動連線"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"已透過網絡評分供應商自動連線"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"已透過 %1$s 連線"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index e40d3519788d..b8f1f58b5691 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"無法自動連線"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"沒有可用的網際網路連線"</string> <string name="saved_network" msgid="7143698034077223645">"由「<xliff:g id="NAME">%1$s</xliff:g>」儲存"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"已透過 %1$s 自動連線"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"已透過網路評分供應商自動連線"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"已透過 %1$s 連線"</string> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index c304c14f6073..d680b66b56fa 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -36,6 +36,8 @@ <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Ngeke ize ixhumeke ngokuzenzakalela"</string> <string name="wifi_no_internet" msgid="1774198889176926299">"Akukho ukufinyelela kwe-inthanethi"</string> <string name="saved_network" msgid="7143698034077223645">"Kulondolozwe ngu-<xliff:g id="NAME">%1$s</xliff:g>"</string> + <!-- no translation found for connected_to_metered_access_point (9179693207918156341) --> + <skip /> <string name="connected_via_network_scorer" msgid="7665725527352893558">"Ixhumeke ngokuzenzakalela nge-%1$s"</string> <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Kuxhunywe ngokuzenzakalelayo ngomhlinzeki wesilinganiso wenethiwekhi"</string> <string name="connected_via_passpoint" msgid="7735442932429075684">"Kuxhumeke nge-%1$s"</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java index 6d7e86f64944..34da30555fb3 100644 --- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java @@ -108,7 +108,7 @@ public class BatterySaverUtils { * - If it's the first time and needFirstTimeWarning, show the first time dialog. * - If it's 4th time through 8th time, show the schedule suggestion notification. * - * @param enable true to disable battery saver. + * @param enable true to enable battery saver. * * @return true if the request succeeded. */ diff --git a/packages/SystemUI/res/layout/long_screenshot.xml b/packages/SystemUI/res/layout/long_screenshot.xml new file mode 100644 index 000000000000..fc68a64ac9bd --- /dev/null +++ b/packages/SystemUI/res/layout/long_screenshot.xml @@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 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. + --> +<androidx.constraintlayout.widget.ConstraintLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:background="?android:colorBackgroundFloating" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <ImageView + android:id="@+id/preview" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginVertical="8dp" + android:layout_marginHorizontal="48dp" + android:adjustViewBounds="true" + app:layout_constrainedHeight="true" + app:layout_constrainedWidth="true" + app:layout_constraintBottom_toBottomOf="@id/guideline" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + tools:background="?android:colorBackground" + tools:minHeight="100dp" + tools:minWidth="100dp" /> + + <androidx.constraintlayout.widget.Guideline + android:id="@+id/guideline" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + app:layout_constraintGuide_percent="0.9" /> + + <Button + android:id="@+id/close" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_margin="8dp" + android:text="Close" + app:layout_constraintEnd_toStartOf="@+id/edit" + app:layout_constraintHorizontal_bias="0.5" + app:layout_constraintHorizontal_chainStyle="packed" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="@+id/guideline" /> + + <Button + android:id="@+id/edit" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_margin="8dp" + android:text="Edit" + app:layout_constraintEnd_toStartOf="@+id/share" + app:layout_constraintHorizontal_bias="0.5" + app:layout_constraintStart_toEndOf="@+id/close" + app:layout_constraintTop_toTopOf="@+id/guideline" /> + + <Button + android:id="@+id/share" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_margin="8dp" + android:text="Share" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="0.5" + app:layout_constraintStart_toEndOf="@+id/edit" + app:layout_constraintTop_toTopOf="@+id/guideline" /> + +</androidx.constraintlayout.widget.ConstraintLayout> + diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java index c3815e4cee78..42bc1d0ea0ff 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java @@ -37,4 +37,9 @@ public interface PluginInitializer { * Called from {@link PluginManagerImpl#handleWtfs()}. */ void handleWtfs(); + + /** + * Returns if pluging manager should run in debug mode. + */ + boolean isDebuggable(); } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java index ee7030a8cf5f..1a4e2d1665f6 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java @@ -28,7 +28,6 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.net.Uri; -import android.os.Build; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -72,7 +71,7 @@ public class PluginInstanceManager<T extends Plugin> { PluginInstanceManager(Context context, String action, PluginListener<T> listener, boolean allowMultiple, Looper looper, VersionInfo version, PluginManagerImpl manager) { this(context, context.getPackageManager(), action, listener, allowMultiple, looper, version, - manager, Build.IS_DEBUGGABLE, manager.getWhitelistedPlugins()); + manager, manager.isDebuggable(), manager.getWhitelistedPlugins()); } @VisibleForTesting diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java index 6d67f2147d37..f5ed9da15fa3 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java @@ -64,8 +64,6 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage private static final String TAG = PluginManagerImpl.class.getSimpleName(); static final String DISABLE_PLUGIN = "com.android.systemui.action.DISABLE_PLUGIN"; - private static PluginManager sInstance; - private final ArrayMap<PluginListener<?>, PluginInstanceManager> mPluginMap = new ArrayMap<>(); private final Map<String, ClassLoader> mClassLoaders = new ArrayMap<>(); @@ -73,7 +71,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage private final ArraySet<String> mWhitelistedPlugins = new ArraySet<>(); private final Context mContext; private final PluginInstanceManagerFactory mFactory; - private final boolean isDebuggable; + private final boolean mIsDebuggable; private final PluginPrefs mPluginPrefs; private final PluginEnabler mPluginEnabler; private final PluginInitializer mPluginInitializer; @@ -83,7 +81,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage private Looper mLooper; public PluginManagerImpl(Context context, PluginInitializer initializer) { - this(context, new PluginInstanceManagerFactory(), Build.IS_DEBUGGABLE, + this(context, new PluginInstanceManagerFactory(), initializer.isDebuggable(), Thread.getUncaughtExceptionPreHandler(), initializer); } @@ -93,7 +91,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage mContext = context; mFactory = factory; mLooper = initializer.getBgLooper(); - isDebuggable = debuggable; + mIsDebuggable = debuggable; mWhitelistedPlugins.addAll(Arrays.asList(initializer.getWhitelistedPlugins(mContext))); mPluginPrefs = new PluginPrefs(mContext); mPluginEnabler = initializer.getPluginEnabler(mContext); @@ -111,6 +109,10 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage }); } + public boolean isDebuggable() { + return mIsDebuggable; + } + public String[] getWhitelistedPlugins() { return mWhitelistedPlugins.toArray(new String[0]); } @@ -297,7 +299,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage /** Returns class loader specific for the given plugin. */ public ClassLoader getClassLoader(ApplicationInfo appInfo) { - if (!isDebuggable && !isPluginPackageWhitelisted(appInfo.packageName)) { + if (!mIsDebuggable && !isPluginPackageWhitelisted(appInfo.packageName)) { Log.w(TAG, "Cannot get class loader for non-whitelisted plugin. Src:" + appInfo.sourceDir + ", pkg: " + appInfo.packageName); return null; diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java index 70021b6f3d45..fbabaa489d74 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java @@ -114,10 +114,13 @@ public class SyncRtSurfaceTransactionApplierCompat { for (int i = params.length - 1; i >= 0; i--) { SyncRtSurfaceTransactionApplierCompat.SurfaceParams surfaceParams = params[i]; - t.deferTransactionUntil(surfaceParams.surface, mBarrierSurfaceControl, frame); surfaceParams.applyTo(t); } - t.apply(); + if (mTargetViewRootImpl != null) { + mTargetViewRootImpl.mergeWithNextTransaction(t, frame); + } else { + t.apply(); + } Trace.traceEnd(Trace.TRACE_TAG_VIEW); Message.obtain(mApplyHandler, MSG_UPDATE_SEQUENCE_NUMBER, toApplySeqNo, 0) .sendToTarget(); diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java index 4a28d56a41e1..89c60f1d3f06 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java @@ -56,4 +56,12 @@ public class ViewRootImplCompat { }); } } + + public void mergeWithNextTransaction(SurfaceControl.Transaction t, long frame) { + if (mViewRoot != null) { + mViewRoot.mergeWithNextTransaction(t, frame); + } else { + t.apply(); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java index 93a8df41c673..cd3d6a84a352 100644 --- a/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java +++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java @@ -26,8 +26,7 @@ public class AppOpItem { private String mPackageName; private long mTimeStarted; private StringBuilder mState; - // This is only used for items with mCode == AppOpsManager.OP_RECORD_AUDIO - private boolean mSilenced; + private boolean mIsDisabled; public AppOpItem(int code, int uid, String packageName, long timeStarted) { this.mCode = code; @@ -58,16 +57,16 @@ public class AppOpItem { return mTimeStarted; } - public void setSilenced(boolean silenced) { - mSilenced = silenced; + public void setDisabled(boolean misDisabled) { + this.mIsDisabled = misDisabled; } - public boolean isSilenced() { - return mSilenced; + public boolean isDisabled() { + return mIsDisabled; } @Override public String toString() { - return mState.append(mSilenced).append(")").toString(); + return mState.append(mIsDisabled).append(")").toString(); } } diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java index 1036c9916c8b..d8ca63960b30 100644 --- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java @@ -16,6 +16,8 @@ package com.android.systemui.appops; +import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_CAMERA; +import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE; import static android.media.AudioManager.ACTION_MICROPHONE_MUTE_CHANGED; import android.Manifest; @@ -45,6 +47,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dump.DumpManager; +import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController; import com.android.systemui.util.Assert; import java.io.FileDescriptor; @@ -64,7 +67,8 @@ import javax.inject.Inject; @SysUISingleton public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsController, AppOpsManager.OnOpActiveChangedInternalListener, - AppOpsManager.OnOpNotedListener, Dumpable { + AppOpsManager.OnOpNotedListener, IndividualSensorPrivacyController.Callback, + Dumpable { // This is the minimum time that we will keep AppOps that are noted on record. If multiple // occurrences of the same (op, package, uid) happen in a shorter interval, they will not be @@ -77,8 +81,8 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon private final AppOpsManager mAppOps; private final AudioManager mAudioManager; private final LocationManager mLocationManager; - // TODO ntmyren: remove t private final PackageManager mPackageManager; + private final IndividualSensorPrivacyController mSensorPrivacyController; // mLocationProviderPackages are cached and updated only occasionally private static final long LOCATION_PROVIDER_UPDATE_FREQUENCY_MS = 30000; @@ -91,6 +95,7 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon private final PermissionFlagsCache mFlagsCache; private boolean mListening; private boolean mMicMuted; + private boolean mCameraDisabled; @GuardedBy("mActiveItems") private final List<AppOpItem> mActiveItems = new ArrayList<>(); @@ -118,6 +123,7 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon DumpManager dumpManager, PermissionFlagsCache cache, AudioManager audioManager, + IndividualSensorPrivacyController sensorPrivacyController, BroadcastDispatcher dispatcher ) { mDispatcher = dispatcher; @@ -129,7 +135,10 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon mCallbacksByCode.put(OPS[i], new ArraySet<>()); } mAudioManager = audioManager; - mMicMuted = audioManager.isMicrophoneMute(); + mSensorPrivacyController = sensorPrivacyController; + mMicMuted = audioManager.isMicrophoneMute() + || mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_MICROPHONE); + mCameraDisabled = mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_CAMERA); mLocationManager = context.getSystemService(LocationManager.class); mPackageManager = context.getPackageManager(); dumpManager.registerDumpable(TAG, this); @@ -147,6 +156,12 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon mAppOps.startWatchingActive(OPS, this); mAppOps.startWatchingNoted(OPS, this); mAudioManager.registerAudioRecordingCallback(mAudioRecordingCallback, mBGHandler); + mSensorPrivacyController.addCallback(this); + + mMicMuted = mAudioManager.isMicrophoneMute() + || mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_MICROPHONE); + mCameraDisabled = mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_CAMERA); + mBGHandler.post(() -> mAudioRecordingCallback.onRecordingConfigChanged( mAudioManager.getActiveRecordingConfigurations())); mDispatcher.registerReceiverWithHandler(this, @@ -156,6 +171,7 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon mAppOps.stopWatchingActive(this); mAppOps.stopWatchingNoted(this); mAudioManager.unregisterAudioRecordingCallback(mAudioRecordingCallback); + mSensorPrivacyController.removeCallback(this); mBGHandler.removeCallbacksAndMessages(null); // null removes all mDispatcher.unregisterReceiver(this); @@ -235,11 +251,13 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon if (item == null && active) { item = new AppOpItem(code, uid, packageName, System.currentTimeMillis()); if (code == AppOpsManager.OP_RECORD_AUDIO) { - item.setSilenced(isAnyRecordingPausedLocked(uid)); + item.setDisabled(isAnyRecordingPausedLocked(uid)); + } else if (code == AppOpsManager.OP_CAMERA) { + item.setDisabled(mCameraDisabled); } mActiveItems.add(item); if (DEBUG) Log.w(TAG, "Added item: " + item.toString()); - return !item.isSilenced(); + return !item.isDisabled(); } else if (item != null && !active) { mActiveItems.remove(item); if (DEBUG) Log.w(TAG, "Removed item: " + item.toString()); @@ -409,7 +427,7 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon AppOpItem item = mActiveItems.get(i); if ((userId == UserHandle.USER_ALL || UserHandle.getUserId(item.getUid()) == userId) - && isUserVisible(item) && !item.isSilenced()) { + && isUserVisible(item) && !item.isDisabled()) { list.add(item); } } @@ -512,22 +530,27 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon return false; } - private void updateRecordingPausedStatus() { + private void updateSensorDisabledStatus() { synchronized (mActiveItems) { int size = mActiveItems.size(); for (int i = 0; i < size; i++) { AppOpItem item = mActiveItems.get(i); + + boolean paused = false; if (item.getCode() == AppOpsManager.OP_RECORD_AUDIO) { - boolean paused = isAnyRecordingPausedLocked(item.getUid()); - if (item.isSilenced() != paused) { - item.setSilenced(paused); - notifySuscribers( - item.getCode(), - item.getUid(), - item.getPackageName(), - !item.isSilenced() - ); - } + paused = isAnyRecordingPausedLocked(item.getUid()); + } else if (item.getCode() == AppOpsManager.OP_CAMERA) { + paused = mCameraDisabled; + } + + if (item.isDisabled() != paused) { + item.setDisabled(paused); + notifySuscribers( + item.getCode(), + item.getUid(), + item.getPackageName(), + !item.isDisabled() + ); } } } @@ -552,14 +575,27 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon recordings.add(recording); } } - updateRecordingPausedStatus(); + updateSensorDisabledStatus(); } }; @Override public void onReceive(Context context, Intent intent) { - mMicMuted = mAudioManager.isMicrophoneMute(); - updateRecordingPausedStatus(); + mMicMuted = mAudioManager.isMicrophoneMute() + || mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_MICROPHONE); + updateSensorDisabledStatus(); + } + + @Override + public void onSensorBlockedChanged(int sensor, boolean blocked) { + mBGHandler.post(() -> { + if (sensor == INDIVIDUAL_SENSOR_CAMERA) { + mCameraDisabled = blocked; + } else if (sensor == INDIVIDUAL_SENSOR_MICROPHONE) { + mMicMuted = mAudioManager.isMicrophoneMute() || blocked; + } + updateSensorDisabledStatus(); + }); } protected class H extends Handler { diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java index 95029c013ab6..7f01d6f1ffa3 100644 --- a/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java @@ -15,6 +15,7 @@ package com.android.systemui.plugins; import android.content.Context; +import android.os.Build; import android.os.Looper; import android.util.Log; @@ -67,4 +68,9 @@ public class PluginInitializerImpl implements PluginInitializer { }); } } + + @Override + public boolean isDebuggable() { + return Build.IS_DEBUGGABLE; + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java index fb281169b2f1..63e27796f441 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java @@ -47,6 +47,7 @@ import javax.inject.Inject; /** Quick settings tile: Enable/Disable NFC **/ public class NfcTile extends QSTileImpl<BooleanState> { + private static final String NFC = "nfc"; private final Icon mIcon = ResourceIcon.get(R.drawable.ic_qs_nfc); private NfcAdapter mAdapter; @@ -89,7 +90,13 @@ public class NfcTile extends QSTileImpl<BooleanState> { @Override public boolean isAvailable() { - return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC); + String stockTiles = mContext.getString(R.string.quick_settings_tiles_stock); + // For the restore from backup case + // Return false when "nfc" is not listed in quick_settings_tiles_stock. + if (stockTiles.contains(NFC)) { + return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC); + } + return false; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTile.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTile.java index 143121af9f2c..212e6c86e9da 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTile.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTile.java @@ -19,6 +19,7 @@ import static android.graphics.ColorSpace.Named.SRGB; import static java.util.Objects.requireNonNull; +import android.annotation.NonNull; import android.graphics.Bitmap; import android.graphics.ColorSpace; import android.graphics.RecordingCanvas; @@ -50,14 +51,13 @@ class ImageTile implements AutoCloseable { * @param image an image containing a hardware buffer * @param location the captured area represented by image tile (virtual coordinates) */ - ImageTile(Image image, Rect location) { + ImageTile(@NonNull Image image, @NonNull Rect location) { mImage = requireNonNull(image, "image"); - mLocation = location; - + mLocation = requireNonNull(location); requireNonNull(mImage.getHardwareBuffer(), "image must be a hardware image"); } - RenderNode getDisplayList() { + synchronized RenderNode getDisplayList() { if (mNode == null) { mNode = new RenderNode("Tile{" + Integer.toHexString(mImage.hashCode()) + "}"); } @@ -69,7 +69,6 @@ class ImageTile implements AutoCloseable { mNode.setPosition(0, 0, w, h); RecordingCanvas canvas = mNode.beginRecording(w, h); - Rect rect = new Rect(0, 0, w, h); canvas.save(); canvas.clipRect(0, 0, mLocation.right, mLocation.bottom); canvas.drawBitmap(Bitmap.wrapHardwareBuffer(mImage.getHardwareBuffer(), COLOR_SPACE), @@ -100,9 +99,11 @@ class ImageTile implements AutoCloseable { } @Override - public void close() { + public synchronized void close() { mImage.close(); - mNode.discardDisplayList(); + if (mNode != null) { + mNode.discardDisplayList(); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index 4431b6974b7b..d6413ed63e6e 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -603,7 +603,18 @@ public class ScreenshotController { cancelTimeout(); ScrollCaptureController controller = new ScrollCaptureController(mContext, connection, mMainExecutor, mBgExecutor, mImageExporter); - controller.start(/* onDismiss */ () -> dismissScreenshot(false)); + controller.attach(mWindow); + controller.start(new TakeScreenshotService.RequestCallback() { + @Override + public void reportError() { + } + + @Override + public void onFinish() { + Log.d(TAG, "onFinish from ScrollCaptureController"); + finishDismiss(); + } + }); } /** diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java index 825c85769e03..9be3566e1f63 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java @@ -16,16 +16,23 @@ package com.android.systemui.screenshot; +import android.annotation.IdRes; +import android.annotation.UiThread; import android.content.Context; import android.content.Intent; -import android.graphics.Bitmap; import android.net.Uri; import android.os.UserHandle; import android.util.Log; -import android.widget.Toast; +import android.view.View; +import android.view.ViewTreeObserver.InternalInsetsInfo; +import android.view.ViewTreeObserver.OnComputeInternalInsetsListener; +import android.view.Window; +import android.widget.ImageView; +import com.android.systemui.R; import com.android.systemui.screenshot.ScrollCaptureClient.Connection; import com.android.systemui.screenshot.ScrollCaptureClient.Session; +import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback; import com.google.common.util.concurrent.ListenableFuture; @@ -38,11 +45,9 @@ import java.util.function.Consumer; /** * Interaction controller between the UI and ScrollCaptureClient. */ -public class ScrollCaptureController { +public class ScrollCaptureController implements OnComputeInternalInsetsListener { private static final String TAG = "ScrollCaptureController"; - private static final boolean USE_TILED_IMAGE = false; - public static final int MAX_PAGES = 5; public static final int MAX_HEIGHT = 12000; @@ -56,6 +61,15 @@ public class ScrollCaptureController { private ZonedDateTime mCaptureTime; private UUID mRequestId; + private RequestCallback mCallback; + private Window mWindow; + private ImageView mPreview; + private View mClose; + private View mEdit; + private View mShare; + + private ListenableFuture<ImageExporter.Result> mExportFuture; + private Runnable mPendingAction; public ScrollCaptureController(Context context, Connection connection, Executor uiExecutor, Executor bgExecutor, ImageExporter exporter) { @@ -68,17 +82,122 @@ public class ScrollCaptureController { } /** + * @param window the window to display the preview + */ + public void attach(Window window) { + mWindow = window; + } + + /** * Run scroll capture! * - * @param after action to take after the flow is complete + * @param callback request callback to report back to the service */ - public void start(final Runnable after) { + public void start(RequestCallback callback) { mCaptureTime = ZonedDateTime.now(); mRequestId = UUID.randomUUID(); - mConnection.start((session) -> startCapture(session, after)); + mCallback = callback; + + setContentView(R.layout.long_screenshot); + mWindow.getDecorView().getViewTreeObserver() + .addOnComputeInternalInsetsListener(this); + mPreview = findViewById(R.id.preview); + + mClose = findViewById(R.id.close); + mEdit = findViewById(R.id.edit); + mShare = findViewById(R.id.share); + + mClose.setOnClickListener(this::onClicked); + mEdit.setOnClickListener(this::onClicked); + mShare.setOnClickListener(this::onClicked); + + //mPreview.setImageDrawable(mImageTileSet.getDrawable()); + mConnection.start(this::startCapture); + } + + + /** Ensure the entire window is touchable */ + public void onComputeInternalInsets(InternalInsetsInfo inoutInfo) { + inoutInfo.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME); + } + + void disableButtons() { + mClose.setEnabled(false); + mEdit.setEnabled(false); + mShare.setEnabled(false); + } + + private void onClicked(View v) { + Log.d(TAG, "button clicked!"); + + int id = v.getId(); + if (id == R.id.close) { + v.setPressed(true); + disableButtons(); + finish(); + } else if (id == R.id.edit) { + v.setPressed(true); + disableButtons(); + edit(); + } else if (id == R.id.share) { + v.setPressed(true); + disableButtons(); + share(); + } } - private void startCapture(Session session, final Runnable onDismiss) { + private void finish() { + if (mExportFuture == null) { + doFinish(); + } else { + mExportFuture.addListener(this::doFinish, mUiExecutor); + } + } + + private void doFinish() { + mPreview.setImageDrawable(null); + mImageTileSet.clear(); + mCallback.onFinish(); + mWindow.getDecorView().getViewTreeObserver() + .removeOnComputeInternalInsetsListener(this); + } + + private void edit() { + sendIntentWhenReady(Intent.ACTION_EDIT); + } + + private void share() { + sendIntentWhenReady(Intent.ACTION_SEND); + } + + void sendIntentWhenReady(String action) { + if (mExportFuture != null) { + mExportFuture.addListener(() -> { + try { + ImageExporter.Result result = mExportFuture.get(); + sendIntent(action, result.uri); + mCallback.onFinish(); + } catch (InterruptedException | ExecutionException e) { + Log.e(TAG, "failed to export", e); + mCallback.onFinish(); + } + + }, mUiExecutor); + } else { + mPendingAction = this::edit; + } + } + + private void setContentView(@IdRes int id) { + mWindow.setContentView(id); + } + + <T extends View> T findViewById(@IdRes int res) { + return mWindow.findViewById(res); + } + + private void startCapture(Session session) { + Log.d(TAG, "startCapture"); Consumer<ScrollCaptureClient.CaptureResult> consumer = new Consumer<ScrollCaptureClient.CaptureResult>() { @@ -91,17 +210,17 @@ public class ScrollCaptureController { boolean emptyFrame = result.captured.height() == 0; if (!emptyFrame) { - mImageTileSet.addTile(new ImageTile(result.image, result.captured)); + ImageTile tile = new ImageTile(result.image, result.captured); + Log.d(TAG, "Adding tile: " + tile); + mImageTileSet.addTile(tile); + Log.d(TAG, "New dimens: w=" + mImageTileSet.getWidth() + ", " + + "h=" + mImageTileSet.getHeight()); } if (emptyFrame || mFrameCount >= MAX_PAGES || mTop + session.getTileHeight() > MAX_HEIGHT) { - if (!mImageTileSet.isEmpty()) { - exportToFile(mImageTileSet.toBitmap(), session, onDismiss); - mImageTileSet.clear(); - } else { - session.end(onDismiss); - } + + mUiExecutor.execute(() -> afterCaptureComplete(session)); return; } mTop += result.captured.height(); @@ -113,25 +232,25 @@ public class ScrollCaptureController { session.requestTile(0, consumer); }; - void exportToFile(Bitmap bitmap, Session session, Runnable afterEnd) { - mImageExporter.setFormat(Bitmap.CompressFormat.PNG); - mImageExporter.setQuality(6); - ListenableFuture<ImageExporter.Result> future = - mImageExporter.export(mBgExecutor, mRequestId, bitmap, mCaptureTime); - future.addListener(() -> { - try { - ImageExporter.Result result = future.get(); - launchViewer(result.uri); - } catch (InterruptedException | ExecutionException e) { - Toast.makeText(mContext, "Failed to write image", Toast.LENGTH_SHORT).show(); - Log.e(TAG, "Error storing screenshot to media store", e.getCause()); + @UiThread + void afterCaptureComplete(Session session) { + Log.d(TAG, "afterCaptureComplete"); + + if (mImageTileSet.isEmpty()) { + session.end(mCallback::onFinish); + } else { + mPreview.setImageDrawable(mImageTileSet.getDrawable()); + mExportFuture = mImageExporter.export( + mBgExecutor, mRequestId, mImageTileSet.toBitmap(), mCaptureTime); + // The user chose an action already, link it to the result + if (mPendingAction != null) { + mExportFuture.addListener(mPendingAction, mUiExecutor); } - session.end(afterEnd); // end session, close connection, afterEnd.run() - }, mUiExecutor); + } } - void launchViewer(Uri uri) { - Intent editIntent = new Intent(Intent.ACTION_VIEW); + void sendIntent(String action, Uri uri) { + Intent editIntent = new Intent(action); editIntent.setType("image/png"); editIntent.setData(uri); editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java index 231fe08e6a99..32d15ed41648 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java @@ -16,8 +16,8 @@ package com.android.systemui.statusbar.policy; -import static android.service.SensorPrivacyIndividualEnabledSensorProto.CAMERA; -import static android.service.SensorPrivacyIndividualEnabledSensorProto.MICROPHONE; +import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_CAMERA; +import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE; import android.hardware.SensorPrivacyManager; import android.hardware.SensorPrivacyManager.IndividualSensor; @@ -30,7 +30,8 @@ import java.util.Set; public class IndividualSensorPrivacyControllerImpl implements IndividualSensorPrivacyController { - private static final int[] SENSORS = new int[] {CAMERA, MICROPHONE}; + private static final int[] SENSORS = new int[] {INDIVIDUAL_SENSOR_CAMERA, + INDIVIDUAL_SENSOR_MICROPHONE}; private final @NonNull SensorPrivacyManager mSensorPrivacyManager; private final SparseBooleanArray mState = new SparseBooleanArray(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java index 02143a750cae..bc322f7f18fc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java @@ -16,6 +16,9 @@ package com.android.systemui.appops; +import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_CAMERA; +import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE; + import static junit.framework.TestCase.assertFalse; import static org.junit.Assert.assertEquals; @@ -49,6 +52,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dump.DumpManager; +import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController; import org.junit.Before; import org.junit.Test; @@ -81,6 +85,8 @@ public class AppOpsControllerTest extends SysuiTestCase { private PermissionFlagsCache mFlagsCache; @Mock private PackageManager mPackageManager; + @Mock + private IndividualSensorPrivacyController mSensorPrivacyController; @Mock(stubOnly = true) private AudioManager mAudioManager; @Mock() @@ -118,12 +124,18 @@ public class AppOpsControllerTest extends SysuiTestCase { when(mAudioManager.getActiveRecordingConfigurations()) .thenReturn(List.of(mPausedMockRecording)); + when(mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_CAMERA)) + .thenReturn(false); + when(mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_CAMERA)) + .thenReturn(false); + mController = new AppOpsControllerImpl( mContext, mTestableLooper.getLooper(), mDumpManager, mFlagsCache, mAudioManager, + mSensorPrivacyController, mDispatcher ); } @@ -133,6 +145,7 @@ public class AppOpsControllerTest extends SysuiTestCase { mController.setListening(true); verify(mAppOpsManager, times(1)).startWatchingActive(AppOpsControllerImpl.OPS, mController); verify(mDispatcher, times(1)).registerReceiverWithHandler(eq(mController), any(), any()); + verify(mSensorPrivacyController, times(1)).addCallback(mController); } @Test @@ -140,6 +153,7 @@ public class AppOpsControllerTest extends SysuiTestCase { mController.setListening(false); verify(mAppOpsManager, times(1)).stopWatchingActive(mController); verify(mDispatcher, times(1)).unregisterReceiver(mController); + verify(mSensorPrivacyController, times(1)).removeCallback(mController); } @Test @@ -476,6 +490,71 @@ public class AppOpsControllerTest extends SysuiTestCase { AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, false); } + @Test + public void testAudioFilteredWhenMicDisabled() { + mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_CAMERA}, + mCallback); + mTestableLooper.processAllMessages(); + mController.onOpActiveChanged( + AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true); + mTestableLooper.processAllMessages(); + List<AppOpItem> list = mController.getActiveAppOps(); + assertEquals(1, list.size()); + assertEquals(AppOpsManager.OP_RECORD_AUDIO, list.get(0).getCode()); + assertFalse(list.get(0).isDisabled()); + + // Add a camera op, and disable the microphone. The camera op should be the only op returned + mController.onSensorBlockedChanged(INDIVIDUAL_SENSOR_MICROPHONE, true); + mController.onOpActiveChanged( + AppOpsManager.OP_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true); + mTestableLooper.processAllMessages(); + list = mController.getActiveAppOps(); + assertEquals(1, list.size()); + assertEquals(AppOpsManager.OP_CAMERA, list.get(0).getCode()); + + + // Re enable the microphone, and verify the op returns + mController.onSensorBlockedChanged(INDIVIDUAL_SENSOR_MICROPHONE, false); + mTestableLooper.processAllMessages(); + + list = mController.getActiveAppOps(); + assertEquals(2, list.size()); + int micIdx = list.get(0).getCode() == AppOpsManager.OP_CAMERA ? 1 : 0; + assertEquals(AppOpsManager.OP_RECORD_AUDIO, list.get(micIdx).getCode()); + } + + @Test + public void testCameraFilteredWhenCameraDisabled() { + mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_CAMERA}, + mCallback); + mTestableLooper.processAllMessages(); + mController.onOpActiveChanged( + AppOpsManager.OP_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true); + mTestableLooper.processAllMessages(); + List<AppOpItem> list = mController.getActiveAppOps(); + assertEquals(1, list.size()); + assertEquals(AppOpsManager.OP_CAMERA, list.get(0).getCode()); + assertFalse(list.get(0).isDisabled()); + + // Add an audio op, and disable the camera. The audio op should be the only op returned + mController.onSensorBlockedChanged(INDIVIDUAL_SENSOR_CAMERA, true); + mController.onOpActiveChanged( + AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true); + mTestableLooper.processAllMessages(); + list = mController.getActiveAppOps(); + assertEquals(1, list.size()); + assertEquals(AppOpsManager.OP_RECORD_AUDIO, list.get(0).getCode()); + + // Re enable the camera, and verify the op returns + mController.onSensorBlockedChanged(INDIVIDUAL_SENSOR_CAMERA, false); + mTestableLooper.processAllMessages(); + + list = mController.getActiveAppOps(); + assertEquals(2, list.size()); + int cameraIdx = list.get(0).getCode() == AppOpsManager.OP_CAMERA ? 0 : 1; + assertEquals(AppOpsManager.OP_CAMERA, list.get(cameraIdx).getCode()); + } + private class TestHandler extends AppOpsControllerImpl.H { TestHandler(Looper looper) { mController.super(looper); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java new file mode 100644 index 000000000000..b37ac4a2b759 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2021 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.qs.tiles; + +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Handler; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.test.filters.SmallTest; + +import com.android.internal.logging.MetricsLogger; +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.qs.QSTileHost; +import com.android.systemui.qs.logging.QSLogger; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +@SmallTest +public class NfcTileTest extends SysuiTestCase { + + private static final String TILES_STOCK_WITHOUT_NFC = "wifi,cell,battery,dnd,flashlight,bt"; + private static final String TILES_STOCK_WITH_NFC = "wifi,cell,battery,dnd,nfc,flashlight,bt"; + + @Mock + private Context mMockContext; + @Mock + private PackageManager mPackageManager; + @Mock + private ActivityStarter mActivityStarter; + @Mock + private QSTileHost mHost; + @Mock + private MetricsLogger mMetricsLogger; + @Mock + private StatusBarStateController mStatusBarStateController; + @Mock + private QSLogger mQSLogger; + @Mock + private BroadcastDispatcher mBroadcastDispatcher; + + private TestableLooper mTestableLooper; + private NfcTile mNfcTile; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mTestableLooper = TestableLooper.get(this); + + when(mHost.getContext()).thenReturn(mMockContext); + when(mMockContext.getPackageManager()).thenReturn(mPackageManager); + + mNfcTile = new NfcTile( + mHost, + mTestableLooper.getLooper(), + new Handler(mTestableLooper.getLooper()), + mMetricsLogger, + mStatusBarStateController, + mActivityStarter, + mQSLogger, + mBroadcastDispatcher + ); + } + + @Test + public void testIsAvailable_stockWithoutNfc_returnsFalse() { + when(mMockContext.getString(R.string.quick_settings_tiles_stock)).thenReturn( + TILES_STOCK_WITHOUT_NFC); + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_NFC)).thenReturn(true); + assertFalse(mNfcTile.isAvailable()); + } + + @Test + public void testIsAvailable_stockWithNfc_returnsTrue() { + when(mMockContext.getString(R.string.quick_settings_tiles_stock)).thenReturn( + TILES_STOCK_WITH_NFC); + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_NFC)).thenReturn(true); + assertTrue(mNfcTile.isAvailable()); + } +} diff --git a/services/core/java/com/android/server/am/PhantomProcessList.java b/services/core/java/com/android/server/am/PhantomProcessList.java index 5167c5719955..37b17411dac5 100644 --- a/services/core/java/com/android/server/am/PhantomProcessList.java +++ b/services/core/java/com/android/server/am/PhantomProcessList.java @@ -38,6 +38,7 @@ import com.android.internal.os.ProcessCpuTracker; import libcore.io.IoUtils; +import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -109,10 +110,23 @@ public final class PhantomProcessList { private final ActivityManagerService mService; private final Handler mKillHandler; + private static final int CGROUP_V1 = 0; + private static final int CGROUP_V2 = 1; + private static final String[] CGROUP_PATH_PREFIXES = { + "/acct/uid_" /* cgroup v1 */, + "/sys/fs/cgroup/uid_" /* cgroup v2 */ + }; + private static final String CGROUP_PID_PREFIX = "/pid_"; + private static final String CGROUP_PROCS = "/cgroup.procs"; + + @VisibleForTesting + int mCgroupVersion = CGROUP_V1; + PhantomProcessList(final ActivityManagerService service) { mService = service; mKillHandler = service.mProcessList.sKillHandler; mInjector = new Injector(); + probeCgroupVersion(); } @VisibleForTesting @@ -190,9 +204,18 @@ public final class PhantomProcessList { } } + private void probeCgroupVersion() { + for (int i = CGROUP_PATH_PREFIXES.length - 1; i >= 0; i--) { + if ((new File(CGROUP_PATH_PREFIXES[i] + Process.SYSTEM_UID)).exists()) { + mCgroupVersion = i; + break; + } + } + } + @VisibleForTesting - static String getCgroupFilePath(int uid, int pid) { - return "/acct/uid_" + uid + "/pid_" + pid + "/cgroup.procs"; + String getCgroupFilePath(int uid, int pid) { + return CGROUP_PATH_PREFIXES[mCgroupVersion] + uid + CGROUP_PID_PREFIX + pid + CGROUP_PROCS; } static String getProcessName(int pid) { diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java index 1a63dded4298..825392762c8c 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java @@ -218,7 +218,7 @@ public class FaceService extends SystemService implements BiometricServiceCallba @Override // Binder call public void enroll(int userId, final IBinder token, final byte[] hardwareAuthToken, final IFaceServiceReceiver receiver, final String opPackageName, - final int[] disabledFeatures, Surface surface) { + final int[] disabledFeatures, Surface surface, boolean debugConsent) { Utils.checkPermission(getContext(), MANAGE_BIOMETRIC); final Pair<Integer, ServiceProvider> provider = getSingleProvider(); @@ -229,7 +229,7 @@ public class FaceService extends SystemService implements BiometricServiceCallba provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId, receiver, opPackageName, disabledFeatures, - convertSurfaceToNativeHandle(surface)); + convertSurfaceToNativeHandle(surface), debugConsent); } @Override // Binder call diff --git a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java index 32428ac13114..cc24b8960e75 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java @@ -94,7 +94,8 @@ public interface ServiceProvider { void scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName, - @NonNull int[] disabledFeatures, @Nullable NativeHandle surfaceHandle); + @NonNull int[] disabledFeatures, @Nullable NativeHandle surfaceHandle, + boolean debugConsent); void cancelEnrollment(int sensorId, @NonNull IBinder token); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java index 211d79c6a263..d2673d2969c9 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java @@ -142,7 +142,8 @@ public class BiometricTestSessionImpl extends ITestSession.Stub { Utils.checkPermission(mContext, TEST_BIOMETRIC); mProvider.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver, - mContext.getOpPackageName(), new int[0] /* disabledFeatures */, null /* surface */); + mContext.getOpPackageName(), new int[0] /* disabledFeatures */, null /* surface */, + false /* debugConsent */); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java index d60bb79de2d7..afc7f6485bc9 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java @@ -23,6 +23,7 @@ import android.hardware.biometrics.BiometricFaceConstants; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.common.ICancellationSignal; import android.hardware.biometrics.face.EnrollmentType; +import android.hardware.biometrics.face.Feature; import android.hardware.biometrics.face.IFace; import android.hardware.biometrics.face.ISession; import android.hardware.face.Face; @@ -55,12 +56,14 @@ public class FaceEnrollClient extends EnrollClient<ISession> { @Nullable private ICancellationSignal mCancellationSignal; @Nullable private android.hardware.common.NativeHandle mPreviewSurface; private final int mMaxTemplatesPerUser; + private final boolean mDebugConsent; FaceEnrollClient(@NonNull Context context, @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull byte[] hardwareAuthToken, @NonNull String opPackageName, @NonNull BiometricUtils<Face> utils, @NonNull int[] disabledFeatures, int timeoutSec, - @Nullable NativeHandle previewSurface, int sensorId, int maxTemplatesPerUser) { + @Nullable NativeHandle previewSurface, int sensorId, int maxTemplatesPerUser, + boolean debugConsent) { super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, opPackageName, utils, timeoutSec, BiometricsProtoEnums.MODALITY_FACE, sensorId, false /* shouldVibrate */); @@ -69,6 +72,7 @@ public class FaceEnrollClient extends EnrollClient<ISession> { mEnrollIgnoreListVendor = getContext().getResources() .getIntArray(R.array.config_face_acquire_vendor_enroll_ignorelist); mMaxTemplatesPerUser = maxTemplatesPerUser; + mDebugConsent = debugConsent; try { // We must manually close the duplicate handle after it's no longer needed. // The caller is responsible for closing the original handle. @@ -116,9 +120,17 @@ public class FaceEnrollClient extends EnrollClient<ISession> { try { // TODO(b/172593978): Pass features. // TODO(b/174619156): Handle accessibility enrollment. + byte[] features; + if (mDebugConsent) { + features = new byte[1]; + features[0] = Feature.DEBUG; + } else { + features = new byte[0]; + } + mCancellationSignal = getFreshDaemon().enroll(mSequentialId, HardwareAuthTokenUtils.toHardwareAuthToken(mHardwareAuthToken), - EnrollmentType.DEFAULT, new byte[0], mPreviewSurface); + EnrollmentType.DEFAULT, features, mPreviewSurface); } catch (RemoteException e) { Slog.e(TAG, "Remote exception when requesting enroll", e); onError(BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS, 0 /* vendorCode */); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java index f7feffddb343..e685ee2899af 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java @@ -382,7 +382,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { public void scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName, @NonNull int[] disabledFeatures, - @Nullable NativeHandle previewSurface) { + @Nullable NativeHandle previewSurface, boolean debugConsent) { mHandler.post(() -> { final IFace daemon = getHalInstance(); if (daemon == null) { @@ -404,7 +404,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { mSensors.get(sensorId).getLazySession(), token, new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken, opPackageName, FaceUtils.getInstance(sensorId), disabledFeatures, - ENROLL_TIMEOUT_SEC, previewSurface, sensorId, maxTemplatesPerUser); + ENROLL_TIMEOUT_SEC, previewSurface, sensorId, maxTemplatesPerUser, + debugConsent); scheduleForSensor(sensorId, client, new BaseClientMonitor.Callback() { @Override public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java index 9ed8f789aaa2..4142a52c9253 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java @@ -131,7 +131,7 @@ public class BiometricTestSessionImpl extends ITestSession.Stub { mFace10.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver, mContext.getOpPackageName(), new int[0] /* disabledFeatures */, - null /* surfaceHandle */); + null /* surfaceHandle */, false /* debugConsent */); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java index 775d8d417dda..e46661a5e985 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java @@ -602,7 +602,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { public void scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName, @NonNull int[] disabledFeatures, - @Nullable NativeHandle surfaceHandle) { + @Nullable NativeHandle surfaceHandle, boolean debugConsent) { mHandler.post(() -> { scheduleUpdateActiveUserWithoutHandler(userId); diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index d9ee9a306f07..13dc0b9be21f 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -710,9 +710,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } } - private void initialize() { + private void initialize(int displayState) { mPowerState = new DisplayPowerState(mBlanker, - mColorFadeEnabled ? new ColorFade(mDisplayId) : null, mDisplayId); + mColorFadeEnabled ? new ColorFade(mDisplayId) : null, mDisplayId, displayState); if (mColorFadeEnabled) { mColorFadeOnAnimator = ObjectAnimator.ofFloat( @@ -812,11 +812,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mustNotify = !mDisplayReadyLocked; } - // Initialize things the first time the power state is changed. - if (mustInitialize) { - initialize(); - } - // Compute the basic display state using the policy. // We might override this below based on other factors. // Initialise brightness as invalid. @@ -850,6 +845,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } assert(state != Display.STATE_UNKNOWN); + // Initialize things the first time the power state is changed. + if (mustInitialize) { + initialize(state); + } + // Apply the proximity sensor. if (mProximitySensor != null) { if (mPowerRequest.useProximitySensor && state != Display.STATE_OFF) { diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java index 54f30a954c33..173adce00cd9 100644 --- a/services/core/java/com/android/server/display/DisplayPowerState.java +++ b/services/core/java/com/android/server/display/DisplayPowerState.java @@ -72,7 +72,8 @@ final class DisplayPowerState { private Runnable mCleanListener; - public DisplayPowerState(DisplayBlanker blanker, ColorFade colorFade, int displayId) { + DisplayPowerState( + DisplayBlanker blanker, ColorFade colorFade, int displayId, int displayState) { mHandler = new Handler(true /*async*/); mChoreographer = Choreographer.getInstance(); mBlanker = blanker; @@ -81,14 +82,14 @@ final class DisplayPowerState { mPhotonicModulator.start(); mDisplayId = displayId; - // At boot time, we know that the screen is on and the electron beam - // animation is not playing. We don't know the screen's brightness though, + // At boot time, we don't know the screen's brightness, // so prepare to set it to a known state when the state is next applied. - // Although we set the brightness to full on here, the display power controller + // Although we set the brightness here, the display power controller // will reset the brightness to a new level immediately before the changes // actually have a chance to be applied. - mScreenState = Display.STATE_ON; - mScreenBrightness = PowerManager.BRIGHTNESS_MAX; + mScreenState = displayState; + mScreenBrightness = (displayState != Display.STATE_OFF) ? PowerManager.BRIGHTNESS_MAX + : PowerManager.BRIGHTNESS_OFF_FLOAT; scheduleScreenUpdate(); mColorFadePrepared = false; diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java index 5b3db011b427..5f7d93867331 100644 --- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java +++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java @@ -63,7 +63,7 @@ public final class FontManagerService extends IFontManager.Stub { @Override public FontConfig getFontConfig() throws RemoteException { - return getCurrentFontSettings().getSystemFontConfig(); + return getSystemFontConfig(); } /* package */ static class SystemFontException extends AndroidException { @@ -103,7 +103,7 @@ public final class FontManagerService extends IFontManager.Stub { if (!Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) { return null; } - return mService.getCurrentFontSettings().getSerializedSystemFontMap(); + return mService.getCurrentFontMap(); } }); publishBinderService(Context.FONT_SERVICE, mService); @@ -162,7 +162,7 @@ public final class FontManagerService extends IFontManager.Stub { @GuardedBy("FontManagerService.this") @Nullable - private SystemFontSettings mCurrentFontSettings = null; + private SharedMemory mSerializedFontMap = null; private FontManagerService(Context context) { mContext = context; @@ -188,12 +188,12 @@ public final class FontManagerService extends IFontManager.Stub { return mContext; } - @NonNull /* package */ SystemFontSettings getCurrentFontSettings() { + @NonNull /* package */ SharedMemory getCurrentFontMap() { synchronized (FontManagerService.this) { - if (mCurrentFontSettings == null) { - mCurrentFontSettings = SystemFontSettings.create(mUpdatableFontDir); + if (mSerializedFontMap == null) { + mSerializedFontMap = buildNewSerializedFontMap(); } - return mCurrentFontSettings; + return mSerializedFontMap; } } @@ -207,7 +207,7 @@ public final class FontManagerService extends IFontManager.Stub { synchronized (FontManagerService.this) { mUpdatableFontDir.installFontFile(fd, pkcs7Signature); // Create updated font map in the next getSerializedSystemFontMap() call. - mCurrentFontSettings = null; + mSerializedFontMap = null; } } @@ -245,69 +245,44 @@ public final class FontManagerService extends IFontManager.Stub { new FontManagerShellCommand(this).exec(this, in, out, err, args, callback, result); } - /* package */ static class SystemFontSettings { - private final @NonNull SharedMemory mSerializedSystemFontMap; - private final @NonNull FontConfig mSystemFontConfig; - private final @NonNull Map<String, FontFamily[]> mSystemFallbackMap; - private final @NonNull Map<String, Typeface> mSystemTypefaceMap; - - SystemFontSettings( - @NonNull SharedMemory serializedSystemFontMap, - @NonNull FontConfig systemFontConfig, - @NonNull Map<String, FontFamily[]> systemFallbackMap, - @NonNull Map<String, Typeface> systemTypefaceMap) { - mSerializedSystemFontMap = serializedSystemFontMap; - mSystemFontConfig = systemFontConfig; - mSystemFallbackMap = systemFallbackMap; - mSystemTypefaceMap = systemTypefaceMap; - } - - public @NonNull SharedMemory getSerializedSystemFontMap() { - return mSerializedSystemFontMap; - } - - public @NonNull FontConfig getSystemFontConfig() { - return mSystemFontConfig; + /** + * Returns an active system font configuration. + */ + public @NonNull FontConfig getSystemFontConfig() { + if (mUpdatableFontDir != null) { + return mUpdatableFontDir.getSystemFontConfig(); + } else { + return SystemFonts.getSystemPreinstalledFontConfig(); } + } - public @NonNull Map<String, FontFamily[]> getSystemFallbackMap() { - return mSystemFallbackMap; - } + /** + * Make new serialized font map data. + */ + public @Nullable SharedMemory buildNewSerializedFontMap() { + try { + final FontConfig fontConfig = getSystemFontConfig(); + final Map<String, FontFamily[]> fallback = SystemFonts.buildSystemFallback(fontConfig); + final Map<String, Typeface> typefaceMap = + SystemFonts.buildSystemTypefaces(fontConfig, fallback); - public @NonNull Map<String, Typeface> getSystemTypefaceMap() { - return mSystemTypefaceMap; + return Typeface.serializeFontMap(typefaceMap); + } catch (IOException | ErrnoException e) { + Slog.w(TAG, "Failed to serialize updatable font map. " + + "Retrying with system image fonts.", e); } - public static @Nullable SystemFontSettings create( - @Nullable UpdatableFontDir updatableFontDir) { - if (updatableFontDir != null) { - final FontConfig fontConfig = updatableFontDir.getSystemFontConfig(); - final Map<String, FontFamily[]> fallback = - SystemFonts.buildSystemFallback(fontConfig); - final Map<String, Typeface> typefaceMap = - SystemFonts.buildSystemTypefaces(fontConfig, fallback); - - try { - final SharedMemory shm = Typeface.serializeFontMap(typefaceMap); - return new SystemFontSettings(shm, fontConfig, fallback, typefaceMap); - } catch (IOException | ErrnoException e) { - Slog.w(TAG, "Failed to serialize updatable font map. " - + "Retrying with system image fonts.", e); - } - } - + try { final FontConfig fontConfig = SystemFonts.getSystemPreinstalledFontConfig(); final Map<String, FontFamily[]> fallback = SystemFonts.buildSystemFallback(fontConfig); final Map<String, Typeface> typefaceMap = SystemFonts.buildSystemTypefaces(fontConfig, fallback); - try { - final SharedMemory shm = Typeface.serializeFontMap(typefaceMap); - return new SystemFontSettings(shm, fontConfig, fallback, typefaceMap); - } catch (IOException | ErrnoException e) { - Slog.e(TAG, "Failed to serialize SystemServer system font map", e); - } - return null; + + return Typeface.serializeFontMap(typefaceMap); + } catch (IOException | ErrnoException e) { + Slog.e(TAG, "Failed to serialize SystemServer system font map", e); } + return null; } } diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java b/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java index fd5c020b1a15..5a01a97dff1a 100644 --- a/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java +++ b/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java @@ -25,6 +25,7 @@ import android.graphics.fonts.Font; import android.graphics.fonts.FontFamily; import android.graphics.fonts.FontManager; import android.graphics.fonts.FontVariationAxis; +import android.graphics.fonts.SystemFonts; import android.os.Binder; import android.os.ParcelFileDescriptor; import android.os.Process; @@ -95,8 +96,8 @@ public class FontManagerShellCommand extends ShellCommand { } /* package */ void dumpAll(@NonNull IndentingPrintWriter w) { - final FontManagerService.SystemFontSettings settings = mService.getCurrentFontSettings(); - dumpFontConfig(w, settings.getSystemFontConfig()); + FontConfig fontConfig = mService.getSystemFontConfig(); + dumpFontConfig(w, fontConfig); } private void dumpSingleFontConfig( @@ -276,19 +277,19 @@ public class FontManagerShellCommand extends ShellCommand { private int dump(ShellCommand shell) { final Context ctx = mService.getContext(); - final FontManagerService.SystemFontSettings settings = - mService.getCurrentFontSettings(); + if (!DumpUtils.checkDumpPermission(ctx, TAG, shell.getErrPrintWriter())) { return 1; } final IndentingPrintWriter writer = new IndentingPrintWriter(shell.getOutPrintWriter(), " "); String nextArg = shell.getNextArg(); + FontConfig fontConfig = mService.getSystemFontConfig(); if (nextArg == null) { - dumpFontConfig(writer, settings.getSystemFontConfig()); + dumpFontConfig(writer, fontConfig); } else { final Map<String, FontFamily[]> fallbackMap = - settings.getSystemFallbackMap(); + SystemFonts.buildSystemFallback(fontConfig); FontFamily[] families = fallbackMap.get(nextArg); if (families == null) { writer.println("Font Family \"" + nextArg + "\" not found"); @@ -364,10 +365,9 @@ public class FontManagerShellCommand extends ShellCommand { } private int status(ShellCommand shell) throws SystemFontException { - final FontManagerService.SystemFontSettings settings = mService.getCurrentFontSettings(); final IndentingPrintWriter writer = new IndentingPrintWriter(shell.getOutPrintWriter(), " "); - FontConfig config = settings.getSystemFontConfig(); + FontConfig config = mService.getSystemFontConfig(); writer.println("Current Version: " + config.getConfigVersion()); LocalDateTime dt = LocalDateTime.ofEpochSecond(config.getLastModifiedDate(), 0, diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java index b5b93d6d8ecd..142f64f0a510 100644 --- a/services/core/java/com/android/server/location/LocationManagerService.java +++ b/services/core/java/com/android/server/location/LocationManagerService.java @@ -74,7 +74,6 @@ import android.os.ICancellationSignal; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteException; -import android.os.SystemClock; import android.os.UserHandle; import android.os.WorkSource; import android.os.WorkSource.WorkChain; @@ -1297,10 +1296,7 @@ public class LocationManagerService extends ILocationManager.Stub { return null; } - long currentNanos = SystemClock.elapsedRealtimeNanos(); - long deltaMs = NANOSECONDS.toMillis( - location.getElapsedRealtimeAgeNanos(currentNanos)); - return new LocationTime(location.getTime() + deltaMs, currentNanos); + return new LocationTime(location.getTime(), location.getElapsedRealtimeNanos()); } } diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index e218dc174ae8..4eaac2e44da8 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -70,7 +70,6 @@ import android.content.pm.IDataLoaderStatusListener; import android.content.pm.IPackageInstallObserver2; import android.content.pm.IPackageInstallerSession; import android.content.pm.IPackageInstallerSessionFileSystemConnector; -import android.content.pm.IPackageLoadingProgressCallback; import android.content.pm.InstallationFile; import android.content.pm.InstallationFileParcel; import android.content.pm.PackageInfo; @@ -322,8 +321,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private float mProgress = 0; @GuardedBy("mLock") private float mReportedProgress = -1; - @GuardedBy("mLock") - private float mIncrementalProgress = 0; /** State of the session. */ @GuardedBy("mLock") @@ -1202,12 +1199,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @GuardedBy("mLock") private void computeProgressLocked(boolean forcePublish) { - // This method is triggered when the client progress is updated or the incremental progress - // is updated. For incremental installs, ignore the progress values reported from client. - // Instead, only use the progress reported by IncFs as the percentage of loading completion. - final float loadingProgress = - isIncrementalInstallation() ? mIncrementalProgress : mClientProgress; - mProgress = MathUtils.constrain(loadingProgress * 0.8f, 0f, 0.8f) + mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f) + MathUtils.constrain(mInternalProgress * 0.2f, 0f, 0.2f); // Only publish when meaningful change @@ -3767,16 +3759,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { try { mIncrementalFileStorages = IncrementalFileStorages.initialize(mContext, stageDir, params, statusListener, healthCheckParams, healthListener, addedFiles, - perUidReadTimeouts, - new IPackageLoadingProgressCallback.Stub() { - @Override - public void onPackageLoadingProgressChanged(float progress) { - synchronized (mLock) { - mIncrementalProgress = progress; - computeProgressLocked(true); - } - } - }); + perUidReadTimeouts); return false; } catch (IOException e) { throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, e.getMessage(), diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index c27e670c4c99..d2fc5b4c0967 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -2153,6 +2153,10 @@ public class PackageManagerService extends IPackageManager.Stub void enforceCrossUserPermission(int callingUid, @UserIdInt int userId, boolean requireFullPermission, boolean checkShell, boolean requirePermissionWhenSameUser, String message); + SigningDetails getSigningDetails(@NonNull String packageName); + SigningDetails getSigningDetails(int uid); + boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId); + boolean filterAppAccess(String packageName, int callingUid, int userId); } /** @@ -4578,6 +4582,40 @@ public class PackageManagerService extends IPackageManager.Stub throw new SecurityException(errorMessage); } + public SigningDetails getSigningDetails(@NonNull String packageName) { + AndroidPackage p = mPackages.get(packageName); + if (p == null) { + return null; + } + return p.getSigningDetails(); + } + + public SigningDetails getSigningDetails(int uid) { + final int appId = UserHandle.getAppId(uid); + final Object obj = mSettings.getSettingLPr(appId); + if (obj != null) { + if (obj instanceof SharedUserSetting) { + return ((SharedUserSetting) obj).signatures.mSigningDetails; + } else if (obj instanceof PackageSetting) { + final PackageSetting ps = (PackageSetting) obj; + return ps.signatures.mSigningDetails; + } + } + return SigningDetails.UNKNOWN; + } + + public boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId) { + PackageSetting ps = getPackageSetting(pkg.getPackageName()); + return shouldFilterApplicationLocked(ps, callingUid, + userId); + } + + public boolean filterAppAccess(String packageName, int callingUid, int userId) { + PackageSetting ps = getPackageSetting(packageName); + return shouldFilterApplicationLocked(ps, callingUid, + userId); + } + } /** @@ -4728,6 +4766,26 @@ public class PackageManagerService extends IPackageManager.Stub return super.getPackageUidInternal(packageName, flags, userId, callingUid); } } + public SigningDetails getSigningDetails(@NonNull String packageName) { + synchronized (mLock) { + return super.getSigningDetails(packageName); + } + } + public SigningDetails getSigningDetails(int uid) { + synchronized (mLock) { + return super.getSigningDetails(uid); + } + } + public boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId) { + synchronized (mLock) { + return super.filterAppAccess(pkg, callingUid, userId); + } + } + public boolean filterAppAccess(String packageName, int callingUid, int userId) { + synchronized (mLock) { + return super.filterAppAccess(packageName, callingUid, userId); + } + } } @@ -26560,6 +26618,22 @@ public class PackageManagerService extends IPackageManager.Stub return snapshotComputer().getPackage(uid); } + private SigningDetails getSigningDetails(@NonNull String packageName) { + return snapshotComputer().getSigningDetails(packageName); + } + + private SigningDetails getSigningDetails(int uid) { + return snapshotComputer().getSigningDetails(uid); + } + + private boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId) { + return snapshotComputer().filterAppAccess(pkg, callingUid, userId); + } + + private boolean filterAppAccess(String packageName, int callingUid, int userId) { + return snapshotComputer().filterAppAccess(packageName, callingUid, userId); + } + private class PackageManagerInternalImpl extends PackageManagerInternal { @Override public List<ApplicationInfo> getInstalledApplications(int flags, int userId, @@ -26615,29 +26689,11 @@ public class PackageManagerService extends IPackageManager.Stub } private SigningDetails getSigningDetails(@NonNull String packageName) { - synchronized (mLock) { - AndroidPackage p = mPackages.get(packageName); - if (p == null) { - return null; - } - return p.getSigningDetails(); - } + return PackageManagerService.this.getSigningDetails(packageName); } private SigningDetails getSigningDetails(int uid) { - synchronized (mLock) { - final int appId = UserHandle.getAppId(uid); - final Object obj = mSettings.getSettingLPr(appId); - if (obj != null) { - if (obj instanceof SharedUserSetting) { - return ((SharedUserSetting) obj).signatures.mSigningDetails; - } else if (obj instanceof PackageSetting) { - final PackageSetting ps = (PackageSetting) obj; - return ps.signatures.mSigningDetails; - } - } - return SigningDetails.UNKNOWN; - } + return PackageManagerService.this.getSigningDetails(uid); } @Override @@ -26652,20 +26708,12 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId) { - synchronized (mLock) { - PackageSetting ps = getPackageSetting(pkg.getPackageName()); - return PackageManagerService.this.shouldFilterApplicationLocked(ps, callingUid, - userId); - } + return PackageManagerService.this.filterAppAccess(pkg, callingUid, userId); } @Override public boolean filterAppAccess(String packageName, int callingUid, int userId) { - synchronized (mLock) { - PackageSetting ps = getPackageSetting(packageName); - return PackageManagerService.this.shouldFilterApplicationLocked(ps, callingUid, - userId); - } + return PackageManagerService.this.filterAppAccess(packageName, callingUid, userId); } @Override @@ -28304,6 +28352,13 @@ public class PackageManagerService extends IPackageManager.Stub } continue; } + if (ps.appId < Process.FIRST_APPLICATION_UID) { + if (DEBUG_PER_UID_READ_TIMEOUTS) { + Slog.i(TAG, "PerUidReadTimeouts: package is system, appId=" + ps.appId); + } + continue; + } + final AndroidPackage pkg = ps.getPkg(); if (pkg.getLongVersionCode() < perPackage.versionCodes.minVersionCode || pkg.getLongVersionCode() > perPackage.versionCodes.maxVersionCode) { 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 8c31d88c6f15..aff871118a34 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -3356,11 +3356,12 @@ public class PermissionManagerService extends IPermissionManager.Stub { // - or its signing certificate was rotated from the source package's certificate // - or its signing certificate is a previous signing certificate of the defining // package, and the defining package still trusts the old certificate for permissions + // - or it shares a common signing certificate in its lineage with the defining package, + // and the defining package still trusts the old certificate for permissions // - or it shares the above relationships with the system package final PackageParser.SigningDetails sourceSigningDetails = getSourcePackageSigningDetails(bp); - return pkg.getSigningDetails().hasAncestorOrSelf(sourceSigningDetails) - || sourceSigningDetails.checkCapability( + return sourceSigningDetails.hasCommonSignerWithCapability( pkg.getSigningDetails(), PackageParser.SigningDetails.CertCapabilities.PERMISSION) || pkg.getSigningDetails().hasAncestorOrSelf(systemPackage.getSigningDetails()) diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index c073b430c8df..89e7986fc4bc 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -3491,15 +3491,27 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** {@inheritDoc} */ @Override public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) { + final int keyCode = event.getKeyCode(); + final boolean down = event.getAction() == KeyEvent.ACTION_DOWN; + boolean isWakeKey = (policyFlags & WindowManagerPolicy.FLAG_WAKE) != 0 + || event.isWakeKey(); + if (!mSystemBooted) { // If we have not yet booted, don't let key events do anything. + // Exception: Wake and power key events are forwarded to PowerManager to allow it to + // wake from quiescent mode during boot. + if (down && (keyCode == KeyEvent.KEYCODE_POWER + || keyCode == KeyEvent.KEYCODE_TV_POWER)) { + wakeUpFromPowerKey(event.getDownTime()); + } else if (down && (isWakeKey || keyCode == KeyEvent.KEYCODE_WAKEUP) + && isWakeKeyWhenScreenOff(keyCode)) { + wakeUpFromWakeKey(event); + } return 0; } final boolean interactive = (policyFlags & FLAG_INTERACTIVE) != 0; - final boolean down = event.getAction() == KeyEvent.ACTION_DOWN; final boolean canceled = event.isCanceled(); - final int keyCode = event.getKeyCode(); final int displayId = event.getDisplayId(); final boolean isInjected = (policyFlags & WindowManagerPolicy.FLAG_INJECTED) != 0; @@ -3518,8 +3530,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Basic policy based on interactive state. int result; - boolean isWakeKey = (policyFlags & WindowManagerPolicy.FLAG_WAKE) != 0 - || event.isWakeKey(); if (interactive || (isInjected && !isWakeKey)) { // When the device is interactive or the key is injected pass the // key to the application. @@ -4740,7 +4750,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } startedWakingUp(PowerManager.WAKE_REASON_UNKNOWN); finishedWakingUp(PowerManager.WAKE_REASON_UNKNOWN); - screenTurningOn(DEFAULT_DISPLAY, null); + screenTurningOn(DEFAULT_DISPLAY, mDefaultDisplayPolicy.getScreenOnListener()); screenTurnedOn(DEFAULT_DISPLAY); } diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 54d05124c114..db4b6d0a3005 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -1036,12 +1036,12 @@ public final class PowerManagerService extends SystemService userActivityNoUpdateLocked( now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID); + updatePowerStateLocked(); if (sQuiescent) { goToSleepNoUpdateLocked(mClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_QUIESCENT, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID); } - updatePowerStateLocked(); } } } @@ -1679,8 +1679,15 @@ public final class PowerManagerService extends SystemService Slog.d(TAG, "wakeUpNoUpdateLocked: eventTime=" + eventTime + ", uid=" + reasonUid); } - if (eventTime < mLastSleepTime || getWakefulnessLocked() == WAKEFULNESS_AWAKE - || mForceSuspendActive || !mSystemReady) { + if (eventTime < mLastSleepTime || mForceSuspendActive || !mSystemReady) { + return false; + } + + if (getWakefulnessLocked() == WAKEFULNESS_AWAKE) { + if (!mBootCompleted && sQuiescent) { + mDirty |= DIRTY_QUIESCENT; + return true; + } return false; } @@ -2821,7 +2828,7 @@ public final class PowerManagerService extends SystemService * * This function recalculates the display power state each time. * - * @return True if the display became ready. + * @return true if the display became ready. */ private boolean updateDisplayPowerStateLocked(int dirty) { final boolean oldDisplayReady = mDisplayReady; @@ -2830,7 +2837,11 @@ public final class PowerManagerService extends SystemService | DIRTY_SETTINGS | DIRTY_SCREEN_BRIGHTNESS_BOOST | DIRTY_VR_MODE_CHANGED | DIRTY_QUIESCENT)) != 0) { if ((dirty & DIRTY_QUIESCENT) != 0) { - sQuiescent = false; + if (mDisplayReady) { + sQuiescent = false; + } else { + mDirty |= DIRTY_QUIESCENT; + } } final DisplayPowerRequest displayPowerRequest = mDisplayPowerRequestMapper.get( @@ -5605,7 +5616,7 @@ public final class PowerManagerService extends SystemService * 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 + * @return true if the proximity sensor was successfully ignored and we should * consume the key event. */ private boolean interceptPowerKeyDownInternal(KeyEvent event) { diff --git a/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java b/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java index 3e39b4bbe1d0..eb9df756dd0e 100644 --- a/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java +++ b/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java @@ -295,14 +295,14 @@ public final class PowerStatsHALWrapper { @Override public android.hardware.power.stats.EnergyConsumer[] getEnergyConsumerInfo() { if (DEBUG) Slog.d(TAG, "Energy consumer info is not supported"); - return null; + return new android.hardware.power.stats.EnergyConsumer[0]; } @Override public android.hardware.power.stats.EnergyConsumerResult[] getEnergyConsumed( int[] energyConsumerIds) { if (DEBUG) Slog.d(TAG, "Energy consumer results are not supported"); - return null; + return new android.hardware.power.stats.EnergyConsumerResult[0]; } @Override diff --git a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java index e71b962baef7..766cf9c1b678 100644 --- a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java +++ b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java @@ -266,6 +266,7 @@ public class ProtoStreamUtils { long token = pos.start(PowerStatsServiceMeterProto.CHANNEL); pos.write(ChannelProto.ID, channel[i].id); pos.write(ChannelProto.NAME, channel[i].name); + pos.write(ChannelProto.SUBSYSTEM, channel[i].subsystem); pos.end(token); } } @@ -275,7 +276,8 @@ public class ProtoStreamUtils { for (int i = 0; i < channel.length; i++) { Slog.d(TAG, "ChannelId: " + channel[i].id - + ", ChannelName: " + channel[i].name); + + ", ChannelName: " + channel[i].name + + ", ChannelSubsystem: " + channel[i].subsystem); } } @@ -284,7 +286,8 @@ public class ProtoStreamUtils { for (int i = 0; i < channel.length; i++) { pw.println("ChannelId: " + channel[i].id - + ", ChannelName: " + channel[i].name); + + ", ChannelName: " + channel[i].name + + ", ChannelSubsystem: " + channel[i].subsystem); } } } diff --git a/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java b/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java index 43f8a3a131d2..57e39b6c6829 100644 --- a/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java +++ b/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java @@ -21,6 +21,9 @@ import static android.content.Context.BIND_INCLUDE_CAPABILITIES; import static android.service.rotationresolver.RotationResolverService.ROTATION_RESULT_FAILURE_CANCELLED; import static android.service.rotationresolver.RotationResolverService.ROTATION_RESULT_FAILURE_TIMED_OUT; +import static com.android.server.rotationresolver.RotationResolverManagerService.RESOLUTION_FAILURE; +import static com.android.server.rotationresolver.RotationResolverManagerService.logRotationStats; + import android.annotation.NonNull; import android.content.ComponentName; import android.content.Context; @@ -29,6 +32,7 @@ import android.os.CancellationSignal; import android.os.Handler; import android.os.ICancellationSignal; import android.os.RemoteException; +import android.os.SystemClock; import android.rotationresolver.RotationResolverInternal; import android.service.rotationresolver.IRotationResolverCallback; import android.service.rotationresolver.IRotationResolverService; @@ -112,6 +116,7 @@ class RemoteRotationResolverService extends ServiceConnector.Impl<IRotationResol boolean mIsDispatched; private final Object mLock = new Object(); + private final long mRequestStartTimeMillis; RotationRequest( @NonNull RotationResolverInternal.RotationResolverCallbackInternal @@ -125,6 +130,7 @@ class RemoteRotationResolverService extends ServiceConnector.Impl<IRotationResol mPackageName = packageName; mIRotationResolverCallback = new RotationResolverCallback(); mCancellationSignalInternal = cancellationSignal; + mRequestStartTimeMillis = SystemClock.elapsedRealtime(); } @@ -164,7 +170,10 @@ class RemoteRotationResolverService extends ServiceConnector.Impl<IRotationResol } mIsFulfilled = true; mCallbackInternal.onSuccess(rotation); - logStats(rotation); + final long timeToCalculate = + SystemClock.elapsedRealtime() - mRequestStartTimeMillis; + logRotationStats(mProposedRotation, mCurrentRotation, rotation, + timeToCalculate); } } @@ -177,7 +186,10 @@ class RemoteRotationResolverService extends ServiceConnector.Impl<IRotationResol } mIsFulfilled = true; mCallbackInternal.onFailure(error); - logStats(error); + final long timeToCalculate = + SystemClock.elapsedRealtime() - mRequestStartTimeMillis; + logRotationStats(mProposedRotation, mCurrentRotation, RESOLUTION_FAILURE, + timeToCalculate); } } @@ -196,10 +208,6 @@ class RemoteRotationResolverService extends ServiceConnector.Impl<IRotationResol } } - - private void logStats(int result) { - // TODO FrameworkStatsLog - } } } } diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java index f0e2d79685b9..8a1c7785fecd 100644 --- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java +++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java @@ -18,7 +18,9 @@ package com.android.server.rotationresolver; import static android.service.rotationresolver.RotationResolverService.ROTATION_RESULT_FAILURE_CANCELLED; +import static com.android.server.rotationresolver.RotationResolverManagerService.RESOLUTION_UNAVAILABLE; import static com.android.server.rotationresolver.RotationResolverManagerService.getServiceConfigPackage; +import static com.android.server.rotationresolver.RotationResolverManagerService.logRotationStats; import android.Manifest; import android.annotation.NonNull; @@ -98,6 +100,7 @@ final class RotationResolverManagerPerUserService extends if (!isServiceAvailableLocked()) { Slog.w(TAG, "Service is not available at this moment."); callbackInternal.onFailure(ROTATION_RESULT_FAILURE_CANCELLED); + logRotationStats(proposedRotation, currentRotation, RESOLUTION_UNAVAILABLE); return; } diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java index 0377d2310426..4a37e7960912 100644 --- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java +++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java @@ -19,6 +19,11 @@ package com.android.server.rotationresolver; import static android.provider.DeviceConfig.NAMESPACE_ROTATION_RESOLVER; import static android.service.rotationresolver.RotationResolverService.ROTATION_RESULT_FAILURE_CANCELLED; +import static com.android.internal.util.FrameworkStatsLog.AUTO_ROTATE_REPORTED__PROPOSED_ORIENTATION__ROTATION_0; +import static com.android.internal.util.FrameworkStatsLog.AUTO_ROTATE_REPORTED__PROPOSED_ORIENTATION__ROTATION_180; +import static com.android.internal.util.FrameworkStatsLog.AUTO_ROTATE_REPORTED__PROPOSED_ORIENTATION__ROTATION_270; +import static com.android.internal.util.FrameworkStatsLog.AUTO_ROTATE_REPORTED__PROPOSED_ORIENTATION__ROTATION_90; + import android.Manifest; import android.annotation.NonNull; import android.annotation.UserIdInt; @@ -33,9 +38,11 @@ import android.rotationresolver.RotationResolverInternal; import android.text.TextUtils; import android.util.IndentingPrintWriter; import android.util.Slog; +import android.view.Surface; import com.android.internal.R; import com.android.internal.util.DumpUtils; +import com.android.internal.util.FrameworkStatsLog; import com.android.server.SystemService; import com.android.server.infra.AbstractMasterSystemService; import com.android.server.infra.FrameworkResourcesServiceNameResolver; @@ -61,6 +68,15 @@ public class RotationResolverManagerService extends /** Default value in absence of {@link DeviceConfig} override. */ private static final boolean DEFAULT_SERVICE_ENABLED = false; + static final int ORIENTATION_UNKNOWN = + FrameworkStatsLog.AUTO_ROTATE_REPORTED__PROPOSED_ORIENTATION__UNKNOWN; + static final int RESOLUTION_DISABLED = + FrameworkStatsLog.AUTO_ROTATE_REPORTED__PROPOSED_ORIENTATION__DISABLED; + static final int RESOLUTION_UNAVAILABLE = + FrameworkStatsLog.AUTO_ROTATE_REPORTED__PROPOSED_ORIENTATION__UNAVAILABLE; + static final int RESOLUTION_FAILURE = + FrameworkStatsLog.AUTO_ROTATE_REPORTED__PROPOSED_ORIENTATION__FAILURE; + private final Context mContext; boolean mIsServiceEnabled; @@ -147,6 +163,7 @@ public class RotationResolverManagerService extends } else { Slog.w(TAG, "Rotation Resolver service is disabled."); callbackInternal.onFailure(ROTATION_RESULT_FAILURE_CANCELLED); + logRotationStats(proposedRotation, currentRotation, RESOLUTION_DISABLED); } } } @@ -178,4 +195,36 @@ public class RotationResolverManagerService extends resultReceiver); } } + + static void logRotationStats(int proposedRotation, int currentRotation, + int resolvedRotation, long timeToCalculate) { + FrameworkStatsLog.write(FrameworkStatsLog.AUTO_ROTATE_REPORTED, + /* previous_orientation= */ surfaceRotationToProto(currentRotation), + /* proposed_orientation= */ surfaceRotationToProto(proposedRotation), + /* resolved_orientation= */ surfaceRotationToProto(resolvedRotation), + /* process_duration_millis= */ timeToCalculate); + } + + static void logRotationStats(int proposedRotation, int currentRotation, + int resolvedRotation) { + FrameworkStatsLog.write(FrameworkStatsLog.AUTO_ROTATE_REPORTED, + /* previous_orientation= */ surfaceRotationToProto(currentRotation), + /* proposed_orientation= */ surfaceRotationToProto(proposedRotation), + /* resolved_orientation= */ surfaceRotationToProto(resolvedRotation)); + } + + private static int surfaceRotationToProto(@Surface.Rotation int rotationPoseResult) { + switch (rotationPoseResult) { + case Surface.ROTATION_0: + return AUTO_ROTATE_REPORTED__PROPOSED_ORIENTATION__ROTATION_0; + case Surface.ROTATION_90: + return AUTO_ROTATE_REPORTED__PROPOSED_ORIENTATION__ROTATION_90; + case Surface.ROTATION_180: + return AUTO_ROTATE_REPORTED__PROPOSED_ORIENTATION__ROTATION_180; + case Surface.ROTATION_270: + return AUTO_ROTATE_REPORTED__PROPOSED_ORIENTATION__ROTATION_270; + default: + return ORIENTATION_UNKNOWN; + } + } } diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java index 8805fa2f4dbb..703bfab6d868 100644 --- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java +++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java @@ -638,6 +638,22 @@ public class VcnGatewayConnection extends StateMachine { protected abstract void processStateMsg(Message msg) throws Exception; + @Override + public void exit() { + try { + exitState(); + } catch (Exception e) { + Slog.wtf(TAG, "Uncaught exception", e); + sendMessage( + EVENT_DISCONNECT_REQUESTED, + TOKEN_ALL, + new EventDisconnectRequestedInfo( + DISCONNECT_REASON_INTERNAL_ERROR + e.toString())); + } + } + + protected void exitState() throws Exception {} + protected void logUnhandledMessage(Message msg) { // Log as unexpected all known messages, and log all else as unknown. switch (msg.what) { @@ -664,18 +680,11 @@ public class VcnGatewayConnection extends StateMachine { } } - protected void teardownIke() { - if (mIkeSession != null) { - mIkeSession.close(); - } - } - protected void handleDisconnectRequested(String msg) { Slog.v(TAG, "Tearing down. Cause: " + msg); mIsRunning = false; teardownNetwork(); - teardownIke(); if (mIkeSession == null) { // Already disconnected, go straight to DisconnectedState @@ -768,6 +777,20 @@ public class VcnGatewayConnection extends StateMachine { * does not complete teardown in a timely fashion, it will be killed (forcibly closed). */ private class DisconnectingState extends ActiveBaseState { + /** + * Whether to skip the RetryTimeoutState and go straight to the ConnectingState. + * + * <p>This is used when an underlying network change triggered a restart on a new network. + * + * <p>Reset (to false) upon exit of the DisconnectingState. + */ + private boolean mSkipRetryTimeout = false; + + // TODO(b/178441390): Remove this in favor of resetting retry timers on UND_NET change. + public void setSkipRetryTimeout(boolean shouldSkip) { + mSkipRetryTimeout = shouldSkip; + } + @Override protected void enterState() throws Exception { if (mIkeSession == null) { @@ -783,6 +806,7 @@ public class VcnGatewayConnection extends StateMachine { return; } + mIkeSession.close(); sendMessageDelayed( EVENT_TEARDOWN_TIMEOUT_EXPIRED, mCurrentToken, @@ -822,7 +846,7 @@ public class VcnGatewayConnection extends StateMachine { mIkeSession = null; if (mIsRunning && mUnderlying != null) { - transitionTo(mRetryTimeoutState); + transitionTo(mSkipRetryTimeout ? mConnectingState : mRetryTimeoutState); } else { teardownNetwork(); transitionTo(mDisconnectedState); @@ -833,6 +857,11 @@ public class VcnGatewayConnection extends StateMachine { break; } } + + @Override + protected void exitState() throws Exception { + mSkipRetryTimeout = false; + } } /** @@ -843,7 +872,69 @@ public class VcnGatewayConnection extends StateMachine { */ private class ConnectingState extends ActiveBaseState { @Override - protected void processStateMsg(Message msg) {} + protected void enterState() { + if (mIkeSession != null) { + Slog.wtf(TAG, "ConnectingState entered with active session"); + + // Attempt to recover. + mIkeSession.kill(); + mIkeSession = null; + } + + mIkeSession = buildIkeSession(); + } + + @Override + protected void processStateMsg(Message msg) { + switch (msg.what) { + case EVENT_UNDERLYING_NETWORK_CHANGED: + final UnderlyingNetworkRecord oldUnderlying = mUnderlying; + mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying; + + if (oldUnderlying == null) { + // This should never happen, but if it does, there's likely a nasty bug. + Slog.wtf(TAG, "Old underlying network was null in connected state. Bug?"); + } + + // If new underlying is null, all underlying networks have been lost; disconnect + if (mUnderlying == null) { + transitionTo(mDisconnectingState); + break; + } + + if (oldUnderlying != null + && mUnderlying.network.equals(oldUnderlying.network)) { + break; // Only network properties have changed; continue connecting. + } + // Else, retry on the new network. + + // Immediately come back to the ConnectingState (skip RetryTimeout, since this + // isn't a failure) + mDisconnectingState.setSkipRetryTimeout(true); + + // fallthrough - disconnect, and retry on new network. + case EVENT_SESSION_LOST: + transitionTo(mDisconnectingState); + break; + case EVENT_SESSION_CLOSED: + deferMessage(msg); + + transitionTo(mDisconnectingState); + break; + case EVENT_SETUP_COMPLETED: // fallthrough + case EVENT_TRANSFORM_CREATED: + // Child setup complete; move to ConnectedState for NetworkAgent registration + deferMessage(msg); + transitionTo(mConnectedState); + break; + case EVENT_DISCONNECT_REQUESTED: + handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason); + break; + default: + logUnhandledMessage(msg); + break; + } + } } private abstract class ConnectedStateBase extends ActiveBaseState {} @@ -1015,12 +1106,12 @@ public class VcnGatewayConnection extends StateMachine { } private IkeSessionParams buildIkeParams() { - // TODO: Implement this with ConnectingState + // TODO: Implement this once IkeSessionParams is persisted return null; } private ChildSessionParams buildChildParams() { - // TODO: Implement this with ConnectingState + // TODO: Implement this once IkeSessionParams is persisted return null; } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 509cbdef41ff..3bb4c74b7dc0 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -1355,11 +1355,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final boolean surfaceReady = w.isDrawn() // Regular case || w.mWinAnimator.mSurfaceDestroyDeferred // The preserved surface is still ready. || w.isDragResizeChanged(); // Waiting for relayoutWindow to call preserveSurface. - final boolean needsLetterbox = surfaceReady && w.isLetterboxedAppWindow() && fillsParent(); + final boolean needsLetterbox = surfaceReady && isLetterboxed(w); + updateRoundedCorners(w); if (needsLetterbox) { if (mLetterbox == null) { mLetterbox = new Letterbox(() -> makeChildSurface(null), - mWmService.mTransactionFactory); + mWmService.mTransactionFactory, + mWmService::isLetterboxActivityCornersRounded); mLetterbox.attachInput(w); } getPosition(mTmpPoint); @@ -1371,7 +1373,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final Rect spaceToFill = transformedBounds != null ? transformedBounds : inMultiWindowMode() - ? task.getBounds() + ? getRootTask().getBounds() : getRootTask().getParent().getBounds(); mLetterbox.layout(spaceToFill, w.getFrame(), mTmpPoint); } else if (mLetterbox != null) { @@ -1379,6 +1381,27 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } + /** @return {@code true} when main window is letterboxed and activity isn't transparent. */ + private boolean isLetterboxed(WindowState mainWindow) { + return mainWindow.isLetterboxedAppWindow() && fillsParent(); + } + + private void updateRoundedCorners(WindowState mainWindow) { + int cornersRadius = + // Don't round corners if letterboxed only for display cutout. + isLetterboxed(mainWindow) && !mainWindow.isLetterboxedForDisplayCutout() + ? Math.max(0, mWmService.getLetterboxActivityCornersRadius()) : 0; + setCornersRadius(mainWindow, cornersRadius); + } + + private void setCornersRadius(WindowState mainWindow, int cornersRadius) { + final SurfaceControl windowSurface = mainWindow.getClientViewRootSurface(); + if (windowSurface != null && windowSurface.isValid()) { + Transaction transaction = getPendingTransaction(); + transaction.setCornerRadius(windowSurface, cornersRadius); + } + } + void updateLetterboxSurface(WindowState winHint) { final WindowState w = findMainWindow(); if (w != winHint && winHint != null && w != null) { @@ -1408,10 +1431,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } /** - * @see Letterbox#notIntersectsOrFullyContains(Rect) + * @return {@code true} if bar shown within a given rectangle is allowed to be transparent + * when the current activity is displayed. */ - boolean letterboxNotIntersectsOrFullyContains(Rect rect) { - return mLetterbox == null || mLetterbox.notIntersectsOrFullyContains(rect); + boolean isTransparentBarAllowed(Rect rect) { + // TODO(b/175482966): Allow status and navigation bars to be semi-transparent black + // in letterbox mode. + return mLetterbox == null || mLetterbox.notIntersectsOrFullyContains(rect) + || mWmService.isLetterboxActivityCornersRounded(); } /** @@ -6589,8 +6616,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // which point, the activity type is still undefined if it will be standard. // For other non-standard types, the type is set in the constructor, so this should // not be a problem. - && isActivityTypeStandardOrUndefined() - && !mAtmService.mForceResizableActivities; + && isActivityTypeStandardOrUndefined(); } @Override diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index f0db3f9855df..404773d1b257 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -35,6 +35,7 @@ import static android.app.ActivityManagerInternal.ALLOW_NON_FULL; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.ActivityTaskManager.RESIZE_MODE_PRESERVE_WINDOW; import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; @@ -1877,6 +1878,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { final long ident = Binder.clearCallingIdentity(); try { + if (isInLockTaskMode() && windowingMode != WINDOWING_MODE_FULLSCREEN) { + Slog.w(TAG, "setTaskWindowingMode: Is in lock task mode=" + + getLockTaskModeState()); + return false; + } + if (WindowConfiguration.isSplitScreenWindowingMode(windowingMode)) { return setTaskWindowingModeSplitScreen(taskId, windowingMode, toTop); } @@ -2141,11 +2148,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { throw new IllegalArgumentException("Calling setTaskWindowingModeSplitScreen with non" + "split-screen mode: " + windowingMode); } - if (isInLockTaskMode()) { - Slog.w(TAG, "setTaskWindowingModeSplitScreen: Is in lock task mode=" - + getLockTaskModeState()); - return false; - } final Task task = mRootWindowContainer.anyTaskForId(taskId, MATCH_ATTACHED_TASK_ONLY); diff --git a/services/core/java/com/android/server/wm/BarController.java b/services/core/java/com/android/server/wm/BarController.java index 4a90bbcc6623..eee27c72e583 100644 --- a/services/core/java/com/android/server/wm/BarController.java +++ b/services/core/java/com/android/server/wm/BarController.java @@ -56,6 +56,6 @@ public class BarController { if (win == null) { return true; } - return win.letterboxNotIntersectsOrFullyContains(getContentFrame(win)); + return win.isTransparentBarAllowed(getContentFrame(win)); } } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 2a40500258c9..0aaa1a1752bf 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -190,6 +190,7 @@ import android.util.SparseBooleanArray; import android.util.proto.ProtoOutputStream; import android.view.Display; import android.view.DisplayCutout; +import android.view.DisplayCutout.CutoutPathParserInfo; import android.view.DisplayInfo; import android.view.Gravity; import android.view.IDisplayWindowInsetsController; @@ -1934,18 +1935,22 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp if (cutout == null || cutout == DisplayCutout.NO_CUTOUT) { return WmDisplayCutout.NO_CUTOUT; } - final Insets waterfallInsets = - RotationUtils.rotateInsets(cutout.getWaterfallInsets(), rotation); if (rotation == ROTATION_0) { return WmDisplayCutout.computeSafeInsets( cutout, mInitialDisplayWidth, mInitialDisplayHeight); } + final Insets waterfallInsets = + RotationUtils.rotateInsets(cutout.getWaterfallInsets(), rotation); final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270); final Rect[] newBounds = mRotationUtil.getRotatedBounds( cutout.getBoundingRectsAll(), rotation, mInitialDisplayWidth, mInitialDisplayHeight); + final CutoutPathParserInfo info = cutout.getCutoutPathParserInfo(); + final CutoutPathParserInfo newInfo = new CutoutPathParserInfo( + info.getDisplayWidth(), info.getDisplayHeight(), info.getDensity(), + info.getCutoutSpec(), rotation, info.getScale()); return WmDisplayCutout.computeSafeInsets( - DisplayCutout.fromBoundsAndWaterfall(newBounds, waterfallInsets), + DisplayCutout.constructDisplayCutout(newBounds, waterfallInsets, newInfo), rotated ? mInitialDisplayHeight : mInitialDisplayWidth, rotated ? mInitialDisplayWidth : mInitialDisplayHeight); } @@ -3604,7 +3609,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp && mImeLayeringTarget.mActivityRecord.matchParentBounds() // IME is attached to non-Letterboxed app windows, other than windows with // LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER flag. (Refer to WS.isLetterboxedAppWindow()) - && mImeLayeringTarget.matchesRootDisplayAreaBounds(); + && mImeLayeringTarget.matchesDisplayAreaBounds(); } /** @@ -4102,8 +4107,15 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp * Callbacks when the given type of {@link WindowContainer} animation finished running in the * hierarchy. */ - void onWindowAnimationFinished(int type) { + void onWindowAnimationFinished(@NonNull WindowContainer wc, int type) { if (type == ANIMATION_TYPE_APP_TRANSITION || type == ANIMATION_TYPE_RECENTS) { + // Unfreeze the insets state of the frozen target when the animation finished if exists. + final Task task = wc.asTask(); + if (task != null) { + task.forAllWindows(w -> { + w.clearFrozenInsetsState(); + }, true /* traverseTopToBottom */); + } removeImeSurfaceImmediately(); } } diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index 398049fb97c3..267f67759a24 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -74,7 +74,7 @@ class InsetsStateController { private final ArraySet<InsetsControlTarget> mPendingControlChanged = new ArraySet<>(); private final Consumer<WindowState> mDispatchInsetsChanged = w -> { - if (w.isVisible()) { + if (w.isReadyToDispatchInsetsState()) { w.notifyInsetsChanged(); } }; @@ -117,7 +117,8 @@ class InsetsStateController { final @InternalInsetsType int type = provider != null ? provider.getSource().getType() : ITYPE_INVALID; return getInsetsForTarget(type, target.getWindowingMode(), target.isAlwaysOnTop(), - isAboveIme(target)); + isAboveIme(target), + target.getFrozenInsetsState() != null ? target.getFrozenInsetsState() : mState); } InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) { @@ -132,7 +133,7 @@ class InsetsStateController { final @WindowingMode int windowingMode = token != null ? token.getWindowingMode() : WINDOWING_MODE_UNDEFINED; final boolean alwaysOnTop = token != null && token.isAlwaysOnTop(); - return getInsetsForTarget(type, windowingMode, alwaysOnTop, isAboveIme(token)); + return getInsetsForTarget(type, windowingMode, alwaysOnTop, isAboveIme(token), mState); } private boolean isAboveIme(WindowContainer target) { @@ -180,9 +181,8 @@ class InsetsStateController { * @see #getInsetsForWindowMetrics */ private InsetsState getInsetsForTarget(@InternalInsetsType int type, - @WindowingMode int windowingMode, boolean isAlwaysOnTop, boolean aboveIme) { - InsetsState state = mState; - + @WindowingMode int windowingMode, boolean isAlwaysOnTop, boolean aboveIme, + @NonNull InsetsState state) { if (type != ITYPE_INVALID) { state = new InsetsState(state); state.removeSource(type); diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java index 44ce4de529b0..02a43b74aa33 100644 --- a/services/core/java/com/android/server/wm/Letterbox.java +++ b/services/core/java/com/android/server/wm/Letterbox.java @@ -44,12 +44,17 @@ public class Letterbox { private final Supplier<SurfaceControl.Builder> mSurfaceControlFactory; private final Supplier<SurfaceControl.Transaction> mTransactionFactory; + private final Supplier<Boolean> mAreCornersRounded; private final Rect mOuter = new Rect(); private final Rect mInner = new Rect(); private final LetterboxSurface mTop = new LetterboxSurface("top"); private final LetterboxSurface mLeft = new LetterboxSurface("left"); private final LetterboxSurface mBottom = new LetterboxSurface("bottom"); private final LetterboxSurface mRight = new LetterboxSurface("right"); + // Prevents wallpaper from peeking through near rounded corners. It's not included in + // mSurfaces array since it isn't needed in methods like notIntersectsOrFullyContains + // or attachInput. + private final LetterboxSurface mBehind = new LetterboxSurface("behind"); private final LetterboxSurface[] mSurfaces = { mLeft, mTop, mRight, mBottom }; /** @@ -58,9 +63,11 @@ public class Letterbox { * @param surfaceControlFactory a factory for creating the managed {@link SurfaceControl}s */ public Letterbox(Supplier<SurfaceControl.Builder> surfaceControlFactory, - Supplier<SurfaceControl.Transaction> transactionFactory) { + Supplier<SurfaceControl.Transaction> transactionFactory, + Supplier<Boolean> areCornersRounded) { mSurfaceControlFactory = surfaceControlFactory; mTransactionFactory = transactionFactory; + mAreCornersRounded = areCornersRounded; } /** @@ -82,6 +89,7 @@ public class Letterbox { mLeft.layout(outer.left, outer.top, inner.left, outer.bottom, surfaceOrigin); mBottom.layout(outer.left, inner.bottom, outer.right, outer.bottom, surfaceOrigin); mRight.layout(inner.right, outer.top, outer.right, outer.bottom, surfaceOrigin); + mBehind.layout(inner.left, inner.top, inner.right, inner.bottom, surfaceOrigin); } @@ -157,6 +165,7 @@ public class Letterbox { for (LetterboxSurface surface : mSurfaces) { surface.remove(); } + mBehind.remove(); } /** Returns whether a call to {@link #applySurfaceChanges} would change the surface. */ @@ -166,6 +175,9 @@ public class Letterbox { return true; } } + if (mBehind.needsApplySurfaceChanges()) { + return true; + } return false; } @@ -173,6 +185,11 @@ public class Letterbox { for (LetterboxSurface surface : mSurfaces) { surface.applySurfaceChanges(t); } + if (mAreCornersRounded.get()) { + mBehind.applySurfaceChanges(t); + } else { + mBehind.remove(); + } } /** Enables touches to slide into other neighboring surfaces. */ diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index ec1588d15320..6a3110f52c91 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -35,6 +35,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMAR import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.app.WindowConfiguration.activityTypeToString; +import static android.app.WindowConfiguration.isSplitScreenWindowingMode; import static android.app.WindowConfiguration.windowingModeToString; import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; @@ -2859,41 +2860,61 @@ class Task extends WindowContainer<WindowContainer> { adjustForMinimalTaskDimensions(outOverrideBounds, previousBounds, newParentConfig); if (windowingMode == WINDOWING_MODE_FREEFORM) { - // by policy, make sure the window remains within parent somewhere - final float density = - ((float) newParentConfig.densityDpi) / DisplayMetrics.DENSITY_DEFAULT; - final Rect parentBounds = - new Rect(newParentConfig.windowConfiguration.getBounds()); - final DisplayContent display = getDisplayContent(); - if (display != null) { - // If a freeform window moves below system bar, there is no way to move it again - // by touch. Because its caption is covered by system bar. So we exclude them - // from root task bounds. and then caption will be shown inside stable area. - final Rect stableBounds = new Rect(); - display.getStableRect(stableBounds); - parentBounds.intersect(stableBounds); - } - - fitWithinBounds(outOverrideBounds, parentBounds, - (int) (density * WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP), - (int) (density * WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP)); + computeFreeformBounds(outOverrideBounds, newParentConfig); + return; + } - // Prevent to overlap caption with stable insets. - final int offsetTop = parentBounds.top - outOverrideBounds.top; - if (offsetTop > 0) { - outOverrideBounds.offset(0, offsetTop); - } + if (isSplitScreenWindowingMode(windowingMode) + || windowingMode == WINDOWING_MODE_MULTI_WINDOW) { + // This is to compute whether the task should be letterboxed to handle non-resizable app + // in multi window. There is no split screen only logic. + computeLetterboxBounds(outOverrideBounds, newParentConfig); } } - /** - * Compute bounds (letterbox or pillarbox) for - * {@link WindowConfiguration#WINDOWING_MODE_FULLSCREEN} when the parent doesn't handle the - * orientation change and the requested orientation is different from the parent. - */ + /** Computes bounds for {@link WindowConfiguration#WINDOWING_MODE_FULLSCREEN}. */ + @VisibleForTesting void computeFullscreenBounds(@NonNull Rect outBounds, @NonNull Configuration newParentConfig) { // In FULLSCREEN mode, always start with empty bounds to indicate "fill parent". outBounds.setEmpty(); + computeLetterboxBounds(outBounds, newParentConfig); + } + + /** Computes bounds for {@link WindowConfiguration#WINDOWING_MODE_FREEFORM}. */ + private void computeFreeformBounds(@NonNull Rect outBounds, + @NonNull Configuration newParentConfig) { + // by policy, make sure the window remains within parent somewhere + final float density = + ((float) newParentConfig.densityDpi) / DisplayMetrics.DENSITY_DEFAULT; + final Rect parentBounds = + new Rect(newParentConfig.windowConfiguration.getBounds()); + final DisplayContent display = getDisplayContent(); + if (display != null) { + // If a freeform window moves below system bar, there is no way to move it again + // by touch. Because its caption is covered by system bar. So we exclude them + // from root task bounds. and then caption will be shown inside stable area. + final Rect stableBounds = new Rect(); + display.getStableRect(stableBounds); + parentBounds.intersect(stableBounds); + } + + fitWithinBounds(outBounds, parentBounds, + (int) (density * WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP), + (int) (density * WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP)); + + // Prevent to overlap caption with stable insets. + final int offsetTop = parentBounds.top - outBounds.top; + if (offsetTop > 0) { + outBounds.offset(0, offsetTop); + } + } + + /** + * Computes bounds (letterbox or pillarbox) when the parent doesn't handle the orientation + * change and the requested orientation is different from the parent. + */ + private void computeLetterboxBounds(@NonNull Rect outBounds, + @NonNull Configuration newParentConfig) { if (handlesOrientationChangeFromDescendant()) { // No need to letterbox at task level. Display will handle fixed-orientation requests. return; @@ -2951,6 +2972,8 @@ class Task extends WindowContainer<WindowContainer> { aspect = letterboxAspectRatioOverride > MIN_TASK_LETTERBOX_ASPECT_RATIO ? letterboxAspectRatioOverride : aspect; + // Store the current bounds to be able to revert to size compat mode values below if needed. + mTmpFullBounds.set(outBounds); if (forcedOrientation == ORIENTATION_LANDSCAPE) { final int height = (int) Math.rint(parentWidth / aspect); final int top = parentBounds.centerY() - height / 2; @@ -2969,7 +2992,7 @@ class Task extends WindowContainer<WindowContainer> { // The app shouldn't be resized, we only do task letterboxing if the compat bounds // is also from the same task letterbox. Otherwise, clear the task bounds to show // app in size compat mode. - outBounds.setEmpty(); + outBounds.set(mTmpFullBounds); } } } @@ -3355,8 +3378,9 @@ class Task extends WindowContainer<WindowContainer> { @Override boolean handlesOrientationChangeFromDescendant() { return super.handlesOrientationChangeFromDescendant() - // Display won't rotate for the orientation request if the TaskDisplayArea can't - // specify orientation. + // Display won't rotate for the orientation request if the Task/TaskDisplayArea + // can't specify orientation. + && canSpecifyOrientation() && getDisplayArea().canSpecifyOrientation(); } @@ -3869,7 +3893,9 @@ class Task extends WindowContainer<WindowContainer> { } boolean isTaskLetterboxed() { - return getWindowingMode() == WINDOWING_MODE_FULLSCREEN && !matchParentBounds(); + // No letterbox for multi window root task + return !matchParentBounds() + && (getWindowingMode() == WINDOWING_MODE_FULLSCREEN || !isRootTask()); } @Override @@ -6041,7 +6067,9 @@ class Task extends WindowContainer<WindowContainer> { mInResumeTopActivity = true; if (isLeafTask()) { - someActivityResumed = resumeTopActivityInnerLocked(prev, options); + if (isFocusableAndVisible()) { + someActivityResumed = resumeTopActivityInnerLocked(prev, options); + } } else { int idx = mChildren.size() - 1; while (idx >= 0) { diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 03fca1137e47..dd4ee877c05b 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -2684,6 +2684,14 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< @Nullable ArrayList<WindowContainer> sources) { final Task task = asTask(); if (task != null && !enter && !task.isHomeOrRecentsRootTask()) { + if (AppTransition.isClosingTransitOld(transit)) { + // Freezes the insets state when the window is in app exiting transition, to + // ensure the exiting window won't receive unexpected insets changes from the + // next window. + task.forAllWindows(w -> { + w.freezeInsetsState(); + }, true /* traverseTopToBottom */); + } mDisplayContent.showImeScreenshot(); } final Pair<AnimationAdapter, AnimationAdapter> adapters = getAnimationAdapter(lp, @@ -2831,7 +2839,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } mSurfaceAnimationSources.clear(); if (mDisplayContent != null) { - mDisplayContent.onWindowAnimationFinished(type); + mDisplayContent.onWindowAnimationFinished(this, type); } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index b6fabee33d60..8e6a778c351d 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -1012,6 +1012,9 @@ public class WindowManagerService extends IWindowManager.Stub // ignored. private float mTaskLetterboxAspectRatio; + // Corners radius for activities presented in the letterbox mode, values < 0 will be ignored. + private int mLetterboxActivityCornersRadius; + final InputManagerService mInputManager; final DisplayManagerInternal mDisplayManagerInternal; final DisplayManager mDisplayManager; @@ -1239,6 +1242,8 @@ public class WindowManagerService extends IWindowManager.Stub com.android.internal.R.bool.config_assistantOnTopOfDream); mTaskLetterboxAspectRatio = context.getResources().getFloat( com.android.internal.R.dimen.config_taskLetterboxAspectRatio); + mLetterboxActivityCornersRadius = context.getResources().getInteger( + com.android.internal.R.integer.config_letterboxActivityCornersRadius); mInputManager = inputManager; // Must be before createDisplayContentLocked. mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); @@ -3936,6 +3941,60 @@ public class WindowManagerService extends IWindowManager.Stub } } + /** + * Overrides corners raidus for activities presented in the letterbox mode. If given value < 0, + * both it and a value of {@link + * com.android.internal.R.integer.config_letterboxActivityCornersRadius} will be ignored and + * and corners of the activity won't be rounded. + */ + void setLetterboxActivityCornersRadius(int cornersRadius) { + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + mLetterboxActivityCornersRadius = cornersRadius; + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + /** + * Resets corners raidus for activities presented in the letterbox mode to {@link + * com.android.internal.R.integer.config_letterboxActivityCornersRadius}. + */ + void resetLetterboxActivityCornersRadius() { + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + mLetterboxActivityCornersRadius = mContext.getResources().getInteger( + com.android.internal.R.integer.config_letterboxActivityCornersRadius); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + /** + * Whether corners of letterboxed activities are rounded. + */ + boolean isLetterboxActivityCornersRounded() { + return getLetterboxActivityCornersRadius() > 0; + } + + /** + * Gets corners raidus for activities presented in the letterbox mode. + */ + int getLetterboxActivityCornersRadius() { + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + return mLetterboxActivityCornersRadius; + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + @Override public void setIgnoreOrientationRequest(int displayId, boolean ignoreOrientationRequest) { mAtmInternal.enforceCallerIsRecentsOrHasPermission( diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java index a3a9c1ce9219..badd29aba968 100644 --- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java +++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java @@ -115,6 +115,10 @@ public class WindowManagerShellCommand extends ShellCommand { return runSetTaskLetterboxAspectRatio(pw); case "get-task-letterbox-aspect-ratio": return runGetTaskLetterboxAspectRatio(pw); + case "set-letterbox-activity-corners-radius": + return runSetLetterboxActivityCornersRadius(pw); + case "get-letterbox-activity-corners-radius": + return runGetLetterboxActivityCornersRadius(pw); case "reset": return runReset(pw); default: @@ -545,6 +549,38 @@ public class WindowManagerShellCommand extends ShellCommand { return 0; } + private int runSetLetterboxActivityCornersRadius(PrintWriter pw) throws RemoteException { + final int cornersRadius; + try { + String arg = getNextArgRequired(); + if ("reset".equals(arg)) { + mInternal.resetLetterboxActivityCornersRadius(); + return 0; + } + cornersRadius = Integer.parseInt(arg); + } catch (NumberFormatException e) { + getErrPrintWriter().println("Error: bad corners radius format " + e); + return -1; + } catch (IllegalArgumentException e) { + getErrPrintWriter().println( + "Error: 'reset' or corners radius should be provided as an argument " + e); + return -1; + } + + mInternal.setLetterboxActivityCornersRadius(cornersRadius); + return 0; + } + + private int runGetLetterboxActivityCornersRadius(PrintWriter pw) throws RemoteException { + final int cornersRadius = mInternal.getLetterboxActivityCornersRadius(); + if (cornersRadius < 0) { + pw.println("Letterbox corners radius is not set"); + } else { + pw.println("Letterbox corners radius is " + cornersRadius); + } + return 0; + } + private int runReset(PrintWriter pw) throws RemoteException { int displayId = getDisplayId(getNextArg()); @@ -572,6 +608,9 @@ public class WindowManagerShellCommand extends ShellCommand { // set-task-letterbox-aspect-ratio mInternal.resetTaskLetterboxAspectRatio(); + // set-letterbox-activity-corners-radius + mInternal.resetLetterboxActivityCornersRadius(); + pw.println("Reset all settings for displayId=" + displayId); return 0; } @@ -608,6 +647,11 @@ public class WindowManagerShellCommand extends ShellCommand { + WindowManagerService.MIN_TASK_LETTERBOX_ASPECT_RATIO); pw.println(" both it and R.dimen.config_taskLetterboxAspectRatio will be ignored"); pw.println(" and framework implementation will be used to determine aspect ratio."); + pw.println(" set-letterbox-activity-corners-radius [reset|cornersRadius]"); + pw.println(" get-letterbox-activity-corners-radius"); + pw.println(" Corners radius for activities in the letterbox mode. If radius < 0,"); + pw.println(" both it and R.integer.config_letterboxActivityCornersRadius will be"); + pw.println(" ignored and corners of the activity won't be rounded."); pw.println(" reset [-d DISPLAY_ID]"); pw.println(" Reset all override settings."); if (!IS_USER) { diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 043844b2e66b..1b81914bbe7c 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.Manifest.permission.READ_FRAME_BUFFER; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT; @@ -264,57 +265,63 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } // Hierarchy changes final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps(); - for (int i = 0, n = hops.size(); i < n; ++i) { - final WindowContainerTransaction.HierarchyOp hop = hops.get(i); - switch (hop.getType()) { - case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: { - final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer()); - final Task task = wc != null ? wc.asTask() : null; - if (task != null) { - task.getDisplayArea().setLaunchRootTask(task, - hop.getWindowingModes(), hop.getActivityTypes()); - } else { - throw new IllegalArgumentException( - "Cannot set non-task as launch root: " + wc); - } - break; - } - case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: - effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId); - break; - case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS: - effects |= setAdjacentRootsHierarchyOp(hop); - break; - case HIERARCHY_OP_TYPE_REORDER: - case HIERARCHY_OP_TYPE_REPARENT: - final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer()); - if (wc == null || !wc.isAttached()) { - Slog.e(TAG, "Attempt to operate on detached container: " + wc); - continue; - } - if (syncId >= 0) { - addToSyncSet(syncId, wc); + if (!hops.isEmpty() && mService.isInLockTaskMode()) { + Slog.w(TAG, "Attempt to perform hierarchy operations while in lock task mode..."); + } else { + for (int i = 0, n = hops.size(); i < n; ++i) { + final WindowContainerTransaction.HierarchyOp hop = hops.get(i); + switch (hop.getType()) { + case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: { + final WindowContainer wc = WindowContainer.fromBinder( + hop.getContainer()); + final Task task = wc != null ? wc.asTask() : null; + if (task != null) { + task.getDisplayArea().setLaunchRootTask(task, + hop.getWindowingModes(), hop.getActivityTypes()); + } else { + throw new IllegalArgumentException( + "Cannot set non-task as launch root: " + wc); + } + break; } - if (transition != null) { - transition.collect(wc); - if (hop.isReparent()) { - if (wc.getParent() != null) { - // Collect the current parent. It's visibility may change as - // a result of this reparenting. - transition.collect(wc.getParent()); - } - if (hop.getNewParent() != null) { - final WindowContainer parentWc = - WindowContainer.fromBinder(hop.getNewParent()); - if (parentWc == null) { - Slog.e(TAG, "Can't resolve parent window from token"); - continue; + case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: + effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId); + break; + case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS: + effects |= setAdjacentRootsHierarchyOp(hop); + break; + case HIERARCHY_OP_TYPE_REORDER: + case HIERARCHY_OP_TYPE_REPARENT: + final WindowContainer wc = WindowContainer.fromBinder( + hop.getContainer()); + if (wc == null || !wc.isAttached()) { + Slog.e(TAG, "Attempt to operate on detached container: " + wc); + continue; + } + if (syncId >= 0) { + addToSyncSet(syncId, wc); + } + if (transition != null) { + transition.collect(wc); + if (hop.isReparent()) { + if (wc.getParent() != null) { + // Collect the current parent. It's visibility may change as + // a result of this reparenting. + transition.collect(wc.getParent()); + } + if (hop.getNewParent() != null) { + final WindowContainer parentWc = + WindowContainer.fromBinder(hop.getNewParent()); + if (parentWc == null) { + Slog.e(TAG, "Can't resolve parent window from token"); + continue; + } + transition.collect(parentWc); } - transition.collect(parentWc); } } - } - effects |= sanitizeAndApplyHierarchyOp(wc, hop); + effects |= sanitizeAndApplyHierarchyOp(wc, hop); + } } } // Queue-up bounds-change transactions for tasks which are now organized. Do @@ -412,6 +419,10 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } if (windowingMode > -1) { + if (mService.isInLockTaskMode() && windowingMode != WINDOWING_MODE_FULLSCREEN) { + throw new UnsupportedOperationException("Not supported to set non-fullscreen" + + " windowing mode during locked task mode."); + } container.setWindowingMode(windowingMode); } return effects; diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 093106f123e5..9a7823e35a01 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -713,6 +713,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP private @Nullable InsetsSourceProvider mControllableInsetProvider; private final InsetsState mRequestedInsetsState = new InsetsState(); + /** + * Freeze the insets state in some cases that not necessarily keeps up-to-date to the client. + * (e.g app exiting transition) + */ + private InsetsState mFrozenInsetsState; + @Nullable InsetsSourceProvider mPendingPositionChanged; private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f; @@ -758,6 +764,33 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } + /** + * Set a freeze state for the window to ignore dispatching its insets state to the client. + * + * Used to keep the insets state for some use cases. (e.g. app exiting transition) + */ + void freezeInsetsState() { + if (mFrozenInsetsState == null) { + mFrozenInsetsState = new InsetsState(getInsetsState(), true /* copySources */); + } + } + + void clearFrozenInsetsState() { + mFrozenInsetsState = null; + } + + InsetsState getFrozenInsetsState() { + return mFrozenInsetsState; + } + + /** + * Check if the insets state of the window is ready to dispatch to the client when invoking + * {@link InsetsStateController#notifyInsetsChanged}. + */ + boolean isReadyToDispatchInsetsState() { + return isVisible() && mFrozenInsetsState == null; + } + void seamlesslyRotateIfAllowed(Transaction transaction, @Rotation int oldRotation, @Rotation int rotation, boolean requested) { // Invisible windows and the wallpaper do not participate in the seamless rotation animation @@ -2110,12 +2143,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return getDisplayContent().getBounds().equals(getBounds()); } - boolean matchesRootDisplayAreaBounds() { - RootDisplayArea root = getRootDisplayArea(); - if (root == null || root == getDisplayContent()) { + boolean matchesDisplayAreaBounds() { + final DisplayArea displayArea = getDisplayArea(); + if (displayArea == null) { return matchesDisplayBounds(); } - return root.getBounds().equals(getBounds()); + return displayArea.getBounds().equals(getBounds()); } /** @@ -3762,16 +3795,20 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return getDisplayContent().mCurrentFocus == this; } - /** Is this window in a container that takes up the entire screen space? */ private boolean inAppWindowThatMatchesParentBounds() { return mActivityRecord == null || (mActivityRecord.matchParentBounds() && !inMultiWindowMode()); } - /** @return true when the window is in fullscreen mode, but has non-fullscreen bounds set, or - * is transitioning into/out-of fullscreen. */ + /** @return true when the window should be letterboxed. */ boolean isLetterboxedAppWindow() { - return !inMultiWindowMode() && !matchesRootDisplayAreaBounds() + // Fullscreen mode but doesn't fill display area. + return (!inMultiWindowMode() && !matchesDisplayAreaBounds()) + // Activity in size compat. + || (mActivityRecord != null && mActivityRecord.inSizeCompatMode()) + // Task letterboxed. + || (getTask() != null && getTask().isTaskLetterboxed()) + // Letterboxed for display cutout. || isLetterboxedForDisplayCutout(); } @@ -3809,11 +3846,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } /** - * @see Letterbox#notIntersectsOrFullyContains(Rect) + * @return {@code true} if bar shown within a given frame is allowed to be transparent + * when the current window is displayed. */ - boolean letterboxNotIntersectsOrFullyContains(Rect rect) { - return mActivityRecord == null - || mActivityRecord.letterboxNotIntersectsOrFullyContains(rect); + boolean isTransparentBarAllowed(Rect frame) { + return mActivityRecord == null || mActivityRecord.isTransparentBarAllowed(frame); } public boolean isLetterboxedOverlappingWith(Rect rect) { diff --git a/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp b/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp index ec2549c5991c..1208354899d5 100644 --- a/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp +++ b/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp @@ -33,6 +33,7 @@ static jclass class_C; static jmethodID method_C_init; static jfieldID field_C_id; static jfieldID field_C_name; +static jfieldID field_C_subsystem; // EnergyMeasurement static jclass class_EM; @@ -277,11 +278,14 @@ static jobjectArray nativeGetEnergyMeterInfo(JNIEnv *env, jclass clazz) { channelArray = env->NewObjectArray(railInfo.size(), class_C, nullptr); for (int i = 0; i < railInfo.size(); i++) { jstring name = env->NewStringUTF(railInfo[i].railName.c_str()); + jstring subsystem = env->NewStringUTF(railInfo[i].subsysName.c_str()); jobject channel = env->NewObject(class_C, method_C_init); env->SetIntField(channel, field_C_id, railInfo[i].index); env->SetObjectField(channel, field_C_name, name); + env->SetObjectField(channel, field_C_subsystem, subsystem); env->SetObjectArrayElement(channelArray, i, channel); env->DeleteLocalRef(name); + env->DeleteLocalRef(subsystem); env->DeleteLocalRef(channel); } } @@ -359,6 +363,7 @@ static jboolean nativeInit(JNIEnv *env, jclass clazz) { method_C_init = env->GetMethodID(class_C, "<init>", "()V"); field_C_id = env->GetFieldID(class_C, "id", "I"); field_C_name = env->GetFieldID(class_C, "name", "Ljava/lang/String;"); + field_C_subsystem = env->GetFieldID(class_C, "subsystem", "Ljava/lang/String;"); // EnergyMeasurement temp = env->FindClass("android/hardware/power/stats/EnergyMeasurement"); diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp index 6fabc589cf95..dfa6083691a9 100644 --- a/services/incremental/ServiceWrappers.cpp +++ b/services/incremental/ServiceWrappers.cpp @@ -210,7 +210,17 @@ public: ErrorCode setUidReadTimeouts(const Control& control, const std::vector<android::os::incremental::PerUidReadTimeouts>& perUidReadTimeouts) const final { - return -ENOTSUP; + std::vector<incfs::UidReadTimeouts> timeouts; + timeouts.resize(perUidReadTimeouts.size()); + for (int i = 0, size = perUidReadTimeouts.size(); i < size; ++i) { + auto&& timeout = timeouts[i]; + const auto& perUidTimeout = perUidReadTimeouts[i]; + timeout.uid = perUidTimeout.uid; + timeout.minTimeUs = perUidTimeout.minTimeUs; + timeout.minPendingTimeUs = perUidTimeout.minPendingTimeUs; + timeout.maxPendingTimeUs = perUidTimeout.maxPendingTimeUs; + } + return incfs::setUidReadTimeouts(control, timeouts); } }; diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index fd8dfc32e5d3..d28c3cc8e2b8 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -1705,15 +1705,6 @@ public final class SystemServer implements Dumpable { } t.traceEnd(); - t.traceBegin("StartVcnManagementService"); - try { - vcnManagement = VcnManagementService.create(context); - ServiceManager.addService(Context.VCN_MANAGEMENT_SERVICE, vcnManagement); - } catch (Throwable e) { - reportWtf("starting VCN Management Service", e); - } - t.traceEnd(); - t.traceBegin("StartFontManagerService"); mSystemServiceManager.startService(FontManagerService.Lifecycle.class); t.traceEnd(); @@ -1815,6 +1806,15 @@ public final class SystemServer implements Dumpable { networkPolicy.bindConnectivityManager(connectivity); t.traceEnd(); + t.traceBegin("StartVcnManagementService"); + try { + vcnManagement = VcnManagementService.create(context); + ServiceManager.addService(Context.VCN_MANAGEMENT_SERVICE, vcnManagement); + } catch (Throwable e) { + reportWtf("starting VCN Management Service", e); + } + t.traceEnd(); + t.traceBegin("StartNsdService"); try { serviceDiscovery = NsdService.create(context); @@ -2632,15 +2632,6 @@ public final class SystemServer implements Dumpable { reportWtf("making IpSec Service ready", e); } t.traceEnd(); - t.traceBegin("MakeVcnManagementServiceReady"); - try { - if (vcnManagementF != null) { - vcnManagementF.systemReady(); - } - } catch (Throwable e) { - reportWtf("making VcnManagementService ready", e); - } - t.traceEnd(); t.traceBegin("MakeNetworkStatsServiceReady"); try { if (networkStatsF != null) { @@ -2659,6 +2650,15 @@ public final class SystemServer implements Dumpable { reportWtf("making Connectivity Service ready", e); } t.traceEnd(); + t.traceBegin("MakeVcnManagementServiceReady"); + try { + if (vcnManagementF != null) { + vcnManagementF.systemReady(); + } + } catch (Throwable e) { + reportWtf("making VcnManagementService ready", e); + } + t.traceEnd(); t.traceBegin("MakeNetworkPolicyServiceReady"); try { if (networkPolicyF != null) { diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java index 091e688743dd..5453de14a5f1 100644 --- a/services/people/java/com/android/server/people/PeopleService.java +++ b/services/people/java/com/android/server/people/PeopleService.java @@ -45,7 +45,6 @@ import com.android.server.LocalServices; 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; @@ -156,6 +155,13 @@ public class PeopleService extends SystemService { final IBinder mService = new IPeopleManager.Stub() { @Override + public ConversationChannel getConversation( + String packageName, int userId, String shortcutId) { + enforceSystemRootOrSystemUI(getContext(), "get conversation"); + return mDataManager.getConversation(packageName, userId, shortcutId); + } + + @Override public ParceledListSlice<ConversationChannel> getRecentConversations() { enforceSystemRootOrSystemUI(getContext(), "get recent conversations"); return new ParceledListSlice<>( diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java index 752141598c9d..9a9a17112245 100644 --- a/services/people/java/com/android/server/people/data/DataManager.java +++ b/services/people/java/com/android/server/people/data/DataManager.java @@ -222,33 +222,65 @@ public class DataManager { mContext.getPackageName(), intentFilter, callingUserId); } + /** + * Returns a {@link ConversationChannel} with the associated {@code shortcutId} if existent. + * Otherwise, returns null. + */ + @Nullable + public ConversationChannel getConversation(String packageName, int userId, String shortcutId) { + UserData userData = getUnlockedUserData(userId); + if (userData != null) { + PackageData packageData = userData.getPackageData(packageName); + // App may have been uninstalled. + if (packageData != null) { + return getConversationChannel(packageData, shortcutId); + } + } + return null; + } + + @Nullable + private ConversationChannel getConversationChannel(PackageData packageData, String shortcutId) { + ConversationInfo conversationInfo = packageData.getConversationInfo(shortcutId); + if (conversationInfo == null) { + return null; + } + int userId = packageData.getUserId(); + String packageName = packageData.getPackageName(); + ShortcutInfo shortcutInfo = getShortcut(packageName, userId, shortcutId); + if (shortcutInfo == null) { + return null; + } + int uid = mPackageManagerInternal.getPackageUid(packageName, 0, userId); + NotificationChannel parentChannel = + mNotificationManagerInternal.getNotificationChannel(packageName, uid, + conversationInfo.getParentNotificationChannelId()); + NotificationChannelGroup parentChannelGroup = null; + if (parentChannel != null) { + parentChannelGroup = + mNotificationManagerInternal.getNotificationChannelGroup(packageName, + uid, parentChannel.getId()); + } + return new ConversationChannel(shortcutInfo, uid, parentChannel, + parentChannelGroup, + conversationInfo.getLastEventTimestamp(), + hasActiveNotifications(packageName, userId, shortcutId)); + } + /** Returns the cached non-customized recent conversations. */ public List<ConversationChannel> getRecentConversations(@UserIdInt int callingUserId) { List<ConversationChannel> conversationChannels = new ArrayList<>(); forPackagesInProfile(callingUserId, packageData -> { - String packageName = packageData.getPackageName(); - int userId = packageData.getUserId(); packageData.forAllConversations(conversationInfo -> { if (!isCachedRecentConversation(conversationInfo)) { return; } String shortcutId = conversationInfo.getShortcutId(); - ShortcutInfo shortcutInfo = getShortcut(packageName, userId, shortcutId); - int uid = mPackageManagerInternal.getPackageUid(packageName, 0, userId); - NotificationChannel parentChannel = - mNotificationManagerInternal.getNotificationChannel(packageName, uid, - conversationInfo.getParentNotificationChannelId()); - if (shortcutInfo == null || parentChannel == null) { + ConversationChannel channel = getConversationChannel(packageData, shortcutId); + if (channel == null || channel.getParentNotificationChannel() == null) { return; } - NotificationChannelGroup parentChannelGroup = - mNotificationManagerInternal.getNotificationChannelGroup(packageName, - uid, parentChannel.getId()); - conversationChannels.add( - new ConversationChannel(shortcutInfo, uid, parentChannel, - parentChannelGroup, - conversationInfo.getLastEventTimestamp(), - hasActiveNotifications(packageName, userId, shortcutId))); + conversationChannels.add(channel); }); }); return conversationChannels; diff --git a/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java b/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java index b85da9460476..17f326fbdbce 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java @@ -298,7 +298,7 @@ public class AppChildProcessTest { } void addToProcess(int uid, int pid, int newPid) { - final String path = PhantomProcessList.getCgroupFilePath(uid, pid); + final String path = mPhantomProcessList.getCgroupFilePath(uid, pid); StringBuffer sb = mPathToData.get(path); if (sb == null) { sb = new StringBuffer(); diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java index 63330d518297..161d3163c1cf 100644 --- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java @@ -515,6 +515,85 @@ public final class DataManagerTest { } @Test + public void testGetConversationReturnsCustomizedConversation() { + mDataManager.onUserUnlocked(USER_ID_PRIMARY); + + ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, + buildPerson()); + mDataManager.addOrUpdateConversationInfo(shortcut); + + NotificationListenerService listenerService = + mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY); + + listenerService.onNotificationPosted(mStatusBarNotification); + shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS); + mDataManager.addOrUpdateConversationInfo(shortcut); + + assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY, + TEST_SHORTCUT_ID)).isNotNull(); + + listenerService.onNotificationChannelModified(TEST_PKG_NAME, UserHandle.of(USER_ID_PRIMARY), + mNotificationChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED); + + assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY, + TEST_SHORTCUT_ID)).isNotNull(); + } + + @Test + public void testGetConversation() { + mDataManager.onUserUnlocked(USER_ID_PRIMARY); + assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY, + TEST_SHORTCUT_ID)).isNull(); + + ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, + buildPerson()); + shortcut.setCached(ShortcutInfo.FLAG_PINNED); + mDataManager.addOrUpdateConversationInfo(shortcut); + assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY, + TEST_SHORTCUT_ID)).isNotNull(); + assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY, + TEST_SHORTCUT_ID + "1")).isNull(); + + NotificationListenerService listenerService = + mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY); + listenerService.onNotificationPosted(mStatusBarNotification); + + ConversationChannel result = mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY, + TEST_SHORTCUT_ID); + assertThat(result).isNotNull(); + assertEquals(shortcut.getId(), result.getShortcutInfo().getId()); + assertEquals(1, result.getShortcutInfo().getPersons().length); + assertEquals(CONTACT_URI, result.getShortcutInfo().getPersons()[0].getUri()); + assertEquals(mParentNotificationChannel.getId(), + result.getParentNotificationChannel().getId()); + assertEquals(mStatusBarNotification.getPostTime(), result.getLastEventTimestamp()); + assertTrue(result.hasActiveNotifications()); + } + + @Test + public void testGetConversationGetsPersonsData() { + mDataManager.onUserUnlocked(USER_ID_PRIMARY); + + ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, + buildPerson()); + shortcut.setCached(ShortcutInfo.FLAG_PINNED); + mDataManager.addOrUpdateConversationInfo(shortcut); + + NotificationListenerService listenerService = + mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY); + listenerService.onNotificationPosted(mStatusBarNotification); + + ConversationChannel result = mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY, + TEST_SHORTCUT_ID); + + verify(mShortcutServiceInternal).getShortcuts( + anyInt(), anyString(), anyLong(), anyString(), anyList(), any(), any(), + mQueryFlagsCaptor.capture(), anyInt(), anyInt(), anyInt()); + Integer queryFlags = mQueryFlagsCaptor.getValue(); + assertThat(hasFlag(queryFlags, ShortcutQuery.FLAG_GET_PERSONS_DATA)).isTrue(); + } + + @Test public void testNotificationChannelCreated() { mDataManager.onUserUnlocked(USER_ID_PRIMARY); mDataManager.onUserUnlocked(USER_ID_SECONDARY); diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java index 533dc1708896..1d0b595d8fc1 100644 --- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java @@ -877,6 +877,22 @@ public class PowerManagerServiceTest { } @Test + public void testQuiescentBoot_WakeKeyBeforeBootCompleted_AwakeAfterBootCompleted() + throws Exception { + when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), any())).thenReturn("1"); + createService(); + mService.systemReady(null); + + mService.getBinderServiceInstance().wakeUp(mClock.now(), + PowerManager.WAKE_REASON_UNKNOWN, "testing IPowerManager.wakeUp()", "pkg.name"); + + mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); + assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); + assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo( + DisplayPowerRequest.POLICY_BRIGHT); + } + + @Test public void testIsAmbientDisplayAvailable_available() throws Exception { createService(); when(mAmbientDisplayConfigurationMock.ambientDisplayAvailable()).thenReturn(true); diff --git a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java index b6ae8555b02e..84b690f01b02 100644 --- a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java @@ -67,6 +67,7 @@ public class PowerStatsServiceTest { private static final String RESIDENCY_FILENAME = "residencytest"; private static final String PROTO_OUTPUT_FILENAME = "powerstats.proto"; private static final String CHANNEL_NAME = "channelname"; + private static final String CHANNEL_SUBSYSTEM = "channelsubsystem"; private static final String POWER_ENTITY_NAME = "powerentityinfo"; private static final String STATE_NAME = "stateinfo"; private static final String ENERGY_CONSUMER_NAME = "energyconsumer"; @@ -214,6 +215,7 @@ public class PowerStatsServiceTest { energyMeterList[i] = new Channel(); energyMeterList[i].id = i; energyMeterList[i].name = new String(CHANNEL_NAME + i); + energyMeterList[i].subsystem = new String(CHANNEL_SUBSYSTEM + i); } return energyMeterList; } @@ -272,6 +274,7 @@ public class PowerStatsServiceTest { for (int i = 0; i < pssProto.channel.length; i++) { assertTrue(pssProto.channel[i].id == i); assertTrue(pssProto.channel[i].name.equals(CHANNEL_NAME + i)); + assertTrue(pssProto.channel[i].subsystem.equals(CHANNEL_SUBSYSTEM + i)); } // Validate the energyMeasurement array matches what was written to on-device storage. @@ -414,6 +417,7 @@ public class PowerStatsServiceTest { for (int i = 0; i < pssProto.channel.length; i++) { assertTrue(pssProto.channel[i].id == i); assertTrue(pssProto.channel[i].name.equals(CHANNEL_NAME + i)); + assertTrue(pssProto.channel[i].subsystem.equals(CHANNEL_SUBSYSTEM + i)); } // No energyMeasurements should be written to the incident report since it @@ -547,6 +551,7 @@ public class PowerStatsServiceTest { for (int i = 0; i < pssProto.channel.length; i++) { assertTrue(pssProto.channel[i].id == i); assertTrue(pssProto.channel[i].name.equals(CHANNEL_NAME + i)); + assertTrue(pssProto.channel[i].subsystem.equals(CHANNEL_SUBSYSTEM + i)); } // No energyMeasurements should be written to the incident report since the diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index 83282a5b8e5a..4bea9a2eea45 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -27,7 +27,6 @@ import static android.os.Build.VERSION_CODES.P; import static android.os.Build.VERSION_CODES.Q; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.FLAG_PRIVATE; -import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT; import static android.view.DisplayCutout.BOUNDS_POSITION_TOP; import static android.view.DisplayCutout.fromBoundingRect; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; @@ -96,6 +95,7 @@ import android.app.ActivityTaskManager; import android.app.WindowConfiguration; import android.app.servertransaction.FixedRotationAdjustmentsItem; import android.content.res.Configuration; +import android.graphics.Insets; import android.graphics.Rect; import android.graphics.Region; import android.metrics.LogMaker; @@ -558,7 +558,7 @@ public class DisplayContentTests extends WindowTestsBase { // hence isLetterboxedAppWindow() returns true. ws.mActivityRecord.getConfiguration().windowConfiguration.setBounds(new Rect(1, 1, 1, 1)); assertFalse("matchesRootDisplayAreaBounds() should return false", - ws.matchesRootDisplayAreaBounds()); + ws.matchesDisplayAreaBounds()); assertTrue("isLetterboxedAppWindow() should return true", ws.isLetterboxedAppWindow()); assertTrue("IME shouldn't be attached to app", dc.computeImeParent() != dc.getImeTarget(IME_TARGET_LAYERING).getWindow() @@ -707,6 +707,7 @@ public class DisplayContentTests extends WindowTestsBase { // same width and height. final int displayWidth = dc.mInitialDisplayWidth; final int displayHeight = dc.mInitialDisplayHeight; + final float density = dc.mInitialDisplayDensity; final int cutoutWidth = 40; final int cutoutHeight = 10; final int left = (displayWidth - cutoutWidth) / 2; @@ -714,9 +715,13 @@ public class DisplayContentTests extends WindowTestsBase { final int right = (displayWidth + cutoutWidth) / 2; final int bottom = cutoutHeight; - final Rect r1 = new Rect(left, top, right, bottom); + final Rect zeroRect = new Rect(); + final Rect[] bounds = new Rect[]{zeroRect, new Rect(left, top, right, bottom), zeroRect, + zeroRect}; + final DisplayCutout.CutoutPathParserInfo info = new DisplayCutout.CutoutPathParserInfo( + displayWidth, displayHeight, density, "", Surface.ROTATION_0, 1f); final DisplayCutout cutout = new WmDisplayCutout( - fromBoundingRect(r1.left, r1.top, r1.right, r1.bottom, BOUNDS_POSITION_TOP), null) + DisplayCutout.constructDisplayCutout(bounds, Insets.NONE, info), null) .computeSafeInsets(displayWidth, displayHeight).getDisplayCutout(); dc.mInitialDisplayCutout = cutout; @@ -731,9 +736,12 @@ public class DisplayContentTests extends WindowTestsBase { // | | ---o // | | | // | | ------------- - final Rect r = new Rect(top, left, bottom, right); + final Rect[] bounds90 = new Rect[]{new Rect(top, left, bottom, right), zeroRect, zeroRect, + zeroRect}; + final DisplayCutout.CutoutPathParserInfo info90 = new DisplayCutout.CutoutPathParserInfo( + displayWidth, displayHeight, density, "", Surface.ROTATION_90, 1f); assertEquals(new WmDisplayCutout( - fromBoundingRect(r.left, r.top, r.right, r.bottom, BOUNDS_POSITION_LEFT), null) + DisplayCutout.constructDisplayCutout(bounds90, Insets.NONE, info90), null) .computeSafeInsets(displayHeight, displayWidth).getDisplayCutout(), dc.getDisplayInfo().displayCutout); } diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java index 2f3004bf6832..a045100c4cd8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java @@ -17,6 +17,9 @@ package com.android.server.wm; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.clearInvocations; @@ -47,10 +50,12 @@ public class LetterboxTest { SurfaceControlMocker mSurfaces; SurfaceControl.Transaction mTransaction; + private boolean mAreCornersRounded = false; + @Before public void setUp() throws Exception { mSurfaces = new SurfaceControlMocker(); - mLetterbox = new Letterbox(mSurfaces, StubTransaction::new); + mLetterbox = new Letterbox(mSurfaces, StubTransaction::new, () -> mAreCornersRounded); mTransaction = spy(StubTransaction.class); } @@ -64,6 +69,7 @@ public class LetterboxTest { private static final int BOTTOM_BAR = 0x2; private static final int LEFT_BAR = 0x4; private static final int RIGHT_BAR = 0x8; + @Test public void testNotIntersectsOrFullyContains_usesGlobalCoordinates() { final Rect outer = new Rect(0, 0, 10, 50); @@ -165,6 +171,41 @@ public class LetterboxTest { } @Test + public void testApplySurfaceChanges_cornersNotRounded_surfaceBehindNotCreated() { + mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000)); + mLetterbox.applySurfaceChanges(mTransaction); + + assertNull(mSurfaces.behind); + } + + @Test + public void testApplySurfaceChanges_cornersRounded_surfaceBehindCreated() { + mAreCornersRounded = true; + mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000)); + mLetterbox.applySurfaceChanges(mTransaction); + + assertNotNull(mSurfaces.behind); + } + + @Test + public void testIsOverlappingWith_cornersRounded_doesNotCheckSurfaceBehind() { + mAreCornersRounded = true; + mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(0, 0)); + mLetterbox.applySurfaceChanges(mTransaction); + + assertFalse(mLetterbox.isOverlappingWith(new Rect(1, 2, 9, 9))); + } + + @Test + public void testNotIntersectsOrFullyContains_cornersRounded_doesNotCheckSurfaceBehind() { + mAreCornersRounded = true; + mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(0, 0)); + mLetterbox.applySurfaceChanges(mTransaction); + + assertTrue(mLetterbox.notIntersectsOrFullyContains(new Rect(1, 2, 9, 9))); + } + + @Test public void testSurfaceOrigin_changeCausesReapply() { mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000)); mLetterbox.applySurfaceChanges(mTransaction); @@ -184,6 +225,8 @@ public class LetterboxTest { public SurfaceControl right; private SurfaceControl.Builder mBottomBuilder; public SurfaceControl bottom; + private SurfaceControl.Builder mBehindBuilder; + public SurfaceControl behind; @Override public SurfaceControl.Builder get() { @@ -198,6 +241,8 @@ public class LetterboxTest { mRightBuilder = (SurfaceControl.Builder) i.getMock(); } else if (((String) i.getArgument(0)).contains("bottom")) { mBottomBuilder = (SurfaceControl.Builder) i.getMock(); + } else if (((String) i.getArgument(0)).contains("behind")) { + mBehindBuilder = (SurfaceControl.Builder) i.getMock(); } return i.getMock(); }); @@ -212,6 +257,8 @@ public class LetterboxTest { right = control; } else if (i.getMock() == mBottomBuilder) { bottom = control; + } else if (i.getMock() == mBehindBuilder) { + behind = control; } return control; }).when(builder).build(); diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index 371e6802ced7..942e1c91989c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -16,15 +16,16 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; +import static android.view.Surface.ROTATION_180; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; -import static android.view.SurfaceProto.ROTATION_180; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; @@ -35,6 +36,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING; import static com.android.server.wm.Task.ActivityState.STOPPED; +import static com.android.server.wm.WindowContainer.POSITION_TOP; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -905,6 +907,57 @@ public class SizeCompatTests extends WindowTestsBase { assertEquals(1000, activityBounds.height()); } + @Test + public void testSupportsNonResizableInSplitScreen() { + // Support non resizable in multi window + mAtm.mSupportsNonResizableMultiWindow = true; + setUpDisplaySizeWithApp(1000, 2800); + final TestSplitOrganizer organizer = + new TestSplitOrganizer(mAtm, mActivity.getDisplayContent()); + + // Non-resizable landscape activity + prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE); + final Rect originalBounds = new Rect(mActivity.getBounds()); + + // Move activity to split screen + mTask.reparent(organizer.mPrimary, POSITION_TOP, + false /*moveParents*/, "test"); + assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, mTask.getWindowingMode()); + assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, mActivity.getWindowingMode()); + + // Non-resizable activity in size compat mode + assertScaled(); + assertEquals(originalBounds, + mActivity.getConfiguration().windowConfiguration.getBounds()); + + // Recompute the natural configuration of the non-resizable activity and the split screen. + mActivity.clearSizeCompatMode(); + + // Draw letterbox. + mActivity.setVisible(false); + mActivity.mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_OPEN); + mActivity.mDisplayContent.mOpeningApps.add(mActivity); + addWindowToActivity(mActivity); + mActivity.mRootWindowContainer.performSurfacePlacement(); + + // Split screen is also in portrait [1000,1400], so Task should be in letterbox, and + // activity fills task. + assertEquals(ORIENTATION_LANDSCAPE, mTask.getConfiguration().orientation); + assertEquals(ORIENTATION_LANDSCAPE, mActivity.getConfiguration().orientation); + assertFitted(); + assertTrue(mTask.isTaskLetterboxed()); + + // Letterbox should fill the gap between the split screen and the letterboxed task. + final Rect primarySplitBounds = new Rect(organizer.mPrimary.getBounds()); + final Rect letterboxedTaskBounds = new Rect(mTask.getBounds()); + assertTrue(primarySplitBounds.contains(letterboxedTaskBounds)); + assertEquals(new Rect(letterboxedTaskBounds.left - primarySplitBounds.left, + letterboxedTaskBounds.top - primarySplitBounds.top, + primarySplitBounds.right - letterboxedTaskBounds.right, + primarySplitBounds.bottom - letterboxedTaskBounds.bottom), + mActivity.getLetterboxInsets()); + } + private static WindowState addWindowToActivity(ActivityRecord activity) { final WindowManager.LayoutParams params = new WindowManager.LayoutParams(); params.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java index b0b8afd6c3a4..df5b48a038f3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java @@ -22,6 +22,8 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static android.view.WindowManager.TRANSIT_CLOSE; +import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE; import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN; import static android.view.WindowManager.TRANSIT_OPEN; import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER; @@ -978,6 +980,31 @@ public class WindowContainerTests extends WindowTestsBase { assertEquals(200, listener.mConfiguration.densityDpi); } + @Test + public void testFreezeInsetsStateWhenAppTransition() { + final Task stack = createTaskStackOnDisplay(mDisplayContent); + final Task task = createTaskInStack(stack, 0 /* userId */); + final ActivityRecord activity = createActivityRecord(mDisplayContent, task); + final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win"); + task.getDisplayContent().prepareAppTransition(TRANSIT_CLOSE); + spyOn(win); + doReturn(true).when(task).okToAnimate(); + ArrayList<WindowContainer> sources = new ArrayList<>(); + sources.add(activity); + + // Simulate the task applying the exit transition, verify the main window of the task + // will be set the frozen insets state. + task.applyAnimation(null, TRANSIT_OLD_TASK_CLOSE, false /* enter */, + false /* isVoiceInteraction */, sources); + verify(win).freezeInsetsState(); + + // Simulate the task transition finished, verify the frozen insets state of the window + // will be reset. + task.onAnimationFinished(ANIMATION_TYPE_APP_TRANSITION, + task.mSurfaceAnimator.getAnimation()); + verify(win).clearFrozenInsetsState(); + } + /* Used so we can gain access to some protected members of the {@link WindowContainer} class */ private static class TestWindowContainer extends WindowContainer<TestWindowContainer> { private final int mLayer; diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index 263aa194a108..3231f8b6551a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -810,4 +810,27 @@ public class WindowStateTests extends WindowTestsBase { WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); assertFalse(sameTokenWindow.needsRelativeLayeringToIme()); } + + @Test + public void testSetFreezeInsetsState() { + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + spyOn(app); + doReturn(true).when(app).isVisible(); + + // Set freezing the insets state to make the window ignore to dispatch insets changed. + final InsetsState expectedState = new InsetsState(app.getInsetsState(), + true /* copySources */); + app.freezeInsetsState(); + assertEquals(expectedState, app.getFrozenInsetsState()); + assertFalse(app.isReadyToDispatchInsetsState()); + assertEquals(expectedState, app.getInsetsState()); + mDisplayContent.getInsetsStateController().notifyInsetsChanged(); + verify(app, never()).notifyInsetsChanged(); + + // Unfreeze the insets state to make the window can dispatch insets changed. + app.clearFrozenInsetsState(); + assertTrue(app.isReadyToDispatchInsetsState()); + mDisplayContent.getInsetsStateController().notifyInsetsChanged(); + verify(app).notifyInsetsChanged(); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java index 39976a5a2af1..b2646f281eb9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java @@ -150,9 +150,9 @@ public class WmDisplayCutoutTest { @Test public void computeSafeInsets_waterfall() { WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets( - DisplayCutout.fromBoundsAndWaterfall( + DisplayCutout.constructDisplayCutout( new Rect[] {ZERO_RECT, ZERO_RECT, ZERO_RECT, ZERO_RECT}, - Insets.of(1, 2, 3, 4)), + Insets.of(1, 2, 3, 4), null), 200, 400); assertEquals(new Rect(1, 2, 3, 4), cutout.getDisplayCutout().getSafeInsets()); @@ -161,9 +161,9 @@ public class WmDisplayCutoutTest { @Test public void computeSafeInsets_cutoutTop_greaterThan_waterfallTop() { WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets( - DisplayCutout.fromBoundsAndWaterfall( + DisplayCutout.constructDisplayCutout( new Rect[] {ZERO_RECT, new Rect(80, 0, 120, 30), ZERO_RECT, ZERO_RECT}, - Insets.of(0, 20, 0, 0)), + Insets.of(0, 20, 0, 0), null), 200, 400); assertEquals(new Rect(0, 30, 0, 0), cutout.getDisplayCutout().getSafeInsets()); @@ -172,9 +172,9 @@ public class WmDisplayCutoutTest { @Test public void computeSafeInsets_cutoutTop_lessThan_waterfallTop() { WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets( - DisplayCutout.fromBoundsAndWaterfall( + DisplayCutout.constructDisplayCutout( new Rect[] {ZERO_RECT, new Rect(80, 0, 120, 30), ZERO_RECT, ZERO_RECT}, - Insets.of(0, 40, 0, 0)), + Insets.of(0, 40, 0, 0), null), 200, 400); assertEquals(new Rect(0, 40, 0, 0), cutout.getDisplayCutout().getSafeInsets()); @@ -183,9 +183,9 @@ public class WmDisplayCutoutTest { @Test public void computeSafeInsets_cutoutLeft_greaterThan_waterfallLeft() { WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets( - DisplayCutout.fromBoundsAndWaterfall( + DisplayCutout.constructDisplayCutout( new Rect[] {new Rect(0, 180, 30, 220), ZERO_RECT, ZERO_RECT, ZERO_RECT}, - Insets.of(20, 0, 0, 0)), + Insets.of(20, 0, 0, 0), null), 200, 400); assertEquals(new Rect(30, 0, 0, 0), cutout.getDisplayCutout().getSafeInsets()); @@ -194,9 +194,9 @@ public class WmDisplayCutoutTest { @Test public void computeSafeInsets_cutoutLeft_lessThan_waterfallLeft() { WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets( - DisplayCutout.fromBoundsAndWaterfall( + DisplayCutout.constructDisplayCutout( new Rect[] {new Rect(0, 180, 30, 220), ZERO_RECT, ZERO_RECT, ZERO_RECT}, - Insets.of(40, 0, 0, 0)), + Insets.of(40, 0, 0, 0), null), 200, 400); assertEquals(new Rect(40, 0, 0, 0), cutout.getDisplayCutout().getSafeInsets()); @@ -205,9 +205,9 @@ public class WmDisplayCutoutTest { @Test public void computeSafeInsets_cutoutBottom_greaterThan_waterfallBottom() { WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets( - DisplayCutout.fromBoundsAndWaterfall( + DisplayCutout.constructDisplayCutout( new Rect[] {ZERO_RECT, ZERO_RECT, ZERO_RECT, new Rect(80, 370, 120, 400)}, - Insets.of(0, 0, 0, 20)), + Insets.of(0, 0, 0, 20), null), 200, 400); assertEquals(new Rect(0, 0, 0, 30), cutout.getDisplayCutout().getSafeInsets()); @@ -216,9 +216,9 @@ public class WmDisplayCutoutTest { @Test public void computeSafeInsets_cutoutBottom_lessThan_waterfallBottom() { WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets( - DisplayCutout.fromBoundsAndWaterfall( + DisplayCutout.constructDisplayCutout( new Rect[] {ZERO_RECT, ZERO_RECT, ZERO_RECT, new Rect(80, 370, 120, 400)}, - Insets.of(0, 0, 0, 40)), + Insets.of(0, 0, 0, 40), null), 200, 400); assertEquals(new Rect(0, 0, 0, 40), cutout.getDisplayCutout().getSafeInsets()); @@ -227,9 +227,9 @@ public class WmDisplayCutoutTest { @Test public void computeSafeInsets_cutoutRight_greaterThan_waterfallRight() { WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets( - DisplayCutout.fromBoundsAndWaterfall( + DisplayCutout.constructDisplayCutout( new Rect[] {ZERO_RECT, ZERO_RECT, new Rect(170, 180, 200, 220), ZERO_RECT}, - Insets.of(0, 0, 20, 0)), + Insets.of(0, 0, 20, 0), null), 200, 400); assertEquals(new Rect(0, 0, 30, 0), cutout.getDisplayCutout().getSafeInsets()); @@ -238,9 +238,9 @@ public class WmDisplayCutoutTest { @Test public void computeSafeInsets_cutoutRight_lessThan_waterfallRight() { WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets( - DisplayCutout.fromBoundsAndWaterfall( + DisplayCutout.constructDisplayCutout( new Rect[] {ZERO_RECT, ZERO_RECT, new Rect(170, 180, 200, 220), ZERO_RECT}, - Insets.of(0, 0, 40, 0)), + Insets.of(0, 0, 40, 0), null), 200, 400); assertEquals(new Rect(0, 0, 40, 0), cutout.getDisplayCutout().getSafeInsets()); diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index 5b03863efc7d..472d63946ebc 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -314,20 +314,22 @@ public class TelecomManager { public static final String EXTRA_HAS_PICTURE = "android.telecom.extra.HAS_PICTURE"; /** - * A URI representing the picture that was downloaded when a call is received. + * A URI representing the picture that was downloaded when a call is received or uploaded + * when a call is placed. + * * This is a content URI within the call log provider which can be used to open a file * descriptor. This could be set a short time after a call is added to the Dialer app if the - * download is delayed for some reason. The Dialer app will receive a callback via + * download/upload is delayed for some reason. The Dialer app will receive a callback via * {@link Call.Callback#onDetailsChanged} when this value has changed. * * Reference: RCC.20 Section 2.4.3.2 */ - public static final String EXTRA_INCOMING_PICTURE = "android.telecom.extra.INCOMING_PICTURE"; + public static final String EXTRA_PICTURE_URI = "android.telecom.extra.PICTURE_URI"; - // TODO(hallliu), This UUID is obtained from TelephonyManager#uploadCallComposerPicture. /** * A ParcelUuid used as a token to represent a picture that was uploaded prior to the call - * being placed. + * being placed. The value of this extra should be set using the {@link android.os.ParcelUuid} + * obtained from the callback in {@link TelephonyManager#uploadCallComposerPicture}. */ public static final String EXTRA_OUTGOING_PICTURE = "android.telecom.extra.OUTGOING_PICTURE"; diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java index b359ebed2683..fadf0e1b84d9 100644 --- a/telephony/java/android/telephony/PhysicalChannelConfig.java +++ b/telephony/java/android/telephony/PhysicalChannelConfig.java @@ -554,7 +554,8 @@ public final class PhysicalChannelConfig implements Parcelable { } public @NonNull Builder setFrequencyRange(int frequencyRange) { - if (!ServiceState.isFrequencyRangeValid(frequencyRange)) { + if (!ServiceState.isFrequencyRangeValid(frequencyRange) + && frequencyRange != ServiceState.FREQUENCY_RANGE_UNKNOWN) { throw new IllegalArgumentException("Frequency range: " + frequencyRange + " is invalid."); } diff --git a/telephony/java/android/telephony/TelephonyLocalConnection.java b/telephony/java/android/telephony/TelephonyLocalConnection.java new file mode 100644 index 000000000000..1cab267cc817 --- /dev/null +++ b/telephony/java/android/telephony/TelephonyLocalConnection.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2021 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.telephony; + +import java.util.UUID; + +/** + * Shim used for code in frameworks/opt/telephony to be able to call code in + * packages/services/Telephony. A singleton instance of this class is set when the phone process + * is brought up. + * @hide + */ +public class TelephonyLocalConnection { + public interface ConnectionImpl { + String getCallComposerServerUrlForHandle(int subscriptionId, UUID uuid); + } + private static ConnectionImpl sInstance; + + public static String getCallComposerServerUrlForHandle(int subscriptionId, UUID uuid) { + checkInstance(); + return sInstance.getCallComposerServerUrlForHandle(subscriptionId, uuid); + } + + private static void checkInstance() { + if (sInstance == null) { + throw new IllegalStateException("Connection impl is null!"); + } + } + + public static void setInstance(ConnectionImpl impl) { + sInstance = impl; + } +} diff --git a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java index 519d0164b0d6..5eb75e762fc9 100644 --- a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java +++ b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java @@ -19,6 +19,7 @@ package android.telephony.ims; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringDef; +import android.annotation.SystemApi; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; @@ -34,14 +35,135 @@ import java.util.List; * network during a SUBSCRIBE request. See RFC3863 for more information. * @hide */ +@SystemApi public final class RcsContactPresenceTuple implements Parcelable { - /** The service id of the MMTEL */ + /** + * The service ID used to indicate that MMTEL service is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ public static final String SERVICE_ID_MMTEL = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.mmtel"; - /** The service id of the Call Composer */ + /** + * The service ID used to indicate that the chat(v1.0) is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_CHAT_V1 = "org.openmobilealliance:IM-session"; + + /** + * The service ID used to indicate that the chat(v2.0) is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_CHAT_V2 = "org.openmobilealliance:ChatSession"; + + /** + * The service ID used to indicate that the File Transfer is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_FT = "org.openmobilealliance:File-Transfer-HTTP"; + + /** + * The service ID used to indicate that the File Transfer over SMS is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_FT_OVER_SMS = + "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.ftsms"; + + /** + * The service ID used to indicate that the Geolocation Push is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_GEO_PUSH = + "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.geopush"; + + /** + * The service ID used to indicate that the Geolocation Push via SMS is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_GEO_PUSH_VIA_SMS = + "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.geosms"; + + /** + * The service ID used to indicate that the Call Composer is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ public static final String SERVICE_ID_CALL_COMPOSER = - "org.3gpp.urn:urn-7:3gppservice.ims.icsi.gsma.callcomposer"; + "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.callcomposer"; + + /** + * The service ID used to indicate that the Post Call is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_POST_CALL = + "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.callunanswered"; + + /** + * The service ID used to indicate that the Shared Map is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_SHARED_MAP = + "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.sharedmap"; + + /** + * The service ID used to indicate that the Shared Sketch is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_SHARED_SKETCH = + "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.sharedsketch"; + + /** + * The service ID used to indicate that the Chatbot using Session is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_CHATBOT = + "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.chatbot"; + + /** + * The service ID used to indicate that the Standalone Messaging is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_CHATBOT_STANDALONE = + " org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.chatbot.sa"; + + /** + * The service ID used to indicate that the Chatbot Role is available. + * <p> + * See the GSMA RCC.07 specification for more information. + */ + public static final String SERVICE_ID_CHATBOT_ROLE = "org.gsma.rcs.isbot"; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @StringDef(prefix = "SERVICE_ID_", value = { + SERVICE_ID_MMTEL, + SERVICE_ID_CHAT_V1, + SERVICE_ID_CHAT_V2, + SERVICE_ID_FT, + SERVICE_ID_FT_OVER_SMS, + SERVICE_ID_GEO_PUSH, + SERVICE_ID_GEO_PUSH_VIA_SMS, + SERVICE_ID_CALL_COMPOSER, + SERVICE_ID_POST_CALL, + SERVICE_ID_SHARED_MAP, + SERVICE_ID_SHARED_SKETCH, + SERVICE_ID_CHATBOT, + SERVICE_ID_CHATBOT_STANDALONE, + SERVICE_ID_CHATBOT_ROLE + }) + public @interface ServiceId {} /** The service capabilities is available. */ public static final String TUPLE_BASIC_STATUS_OPEN = "open"; @@ -149,6 +271,7 @@ public final class RcsContactPresenceTuple implements Parcelable { in.readStringList(mSupportedDuplexModeList); in.readStringList(mUnsupportedDuplexModeList); } + @Override public void writeToParcel(@NonNull Parcel out, int flags) { out.writeBoolean(mIsAudioCapable); @@ -217,12 +340,14 @@ public final class RcsContactPresenceTuple implements Parcelable { /** * Builds a RcsContactPresenceTuple instance. + * @param status The status associated with the service capability. See RFC3865 for more + * information. * @param serviceId The OMA Presence service-id associated with this capability. See the * OMA Presence SIMPLE specification v1.1, section 10.5.1. * @param serviceVersion The OMA Presence version associated with the service capability. * See the OMA Presence SIMPLE specification v1.1, section 10.5.1. */ - public Builder(@NonNull @BasicStatus String status, @NonNull String serviceId, + public Builder(@NonNull @BasicStatus String status, @NonNull @ServiceId String serviceId, @NonNull String serviceVersion) { mPresenceTuple = new RcsContactPresenceTuple(status, serviceId, serviceVersion); } @@ -230,16 +355,17 @@ public final class RcsContactPresenceTuple implements Parcelable { /** * The optional SIP Contact URI associated with the PIDF tuple element. */ - public @NonNull Builder addContactUri(@NonNull Uri contactUri) { + public @NonNull Builder setContactUri(@NonNull Uri contactUri) { mPresenceTuple.mContactUri = contactUri; return this; } /** * The optional timestamp indicating the data and time of the status change of this tuple. - * See RFC3863, section 4.1.7 for more information on the expected format. + * Per RFC3863 section 4.1.7, the timestamp is formatted as an IMPP datetime format + * string per RFC3339. */ - public @NonNull Builder addTimeStamp(@NonNull String timestamp) { + public @NonNull Builder setTimestamp(@NonNull String timestamp) { mPresenceTuple.mTimestamp = timestamp; return this; } @@ -248,7 +374,7 @@ public final class RcsContactPresenceTuple implements Parcelable { * An optional parameter containing the description element of the service-description. See * OMA Presence SIMPLE specification v1.1 */ - public @NonNull Builder addDescription(@NonNull String description) { + public @NonNull Builder setServiceDescription(@NonNull String description) { mPresenceTuple.mServiceDescription = description; return this; } @@ -257,7 +383,7 @@ public final class RcsContactPresenceTuple implements Parcelable { * An optional parameter containing the service capabilities of the presence tuple if they * are present in the servcaps element. */ - public @NonNull Builder addServiceCapabilities(@NonNull ServiceCapabilities caps) { + public @NonNull Builder setServiceCapabilities(@NonNull ServiceCapabilities caps) { mPresenceTuple.mServiceCapabilities = caps; return this; } diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java index d4715bfeeb3e..fe855023f5d0 100644 --- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java +++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java @@ -19,6 +19,7 @@ package android.telephony.ims; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; @@ -33,6 +34,7 @@ import java.util.List; * Contains the User Capability Exchange capabilities corresponding to a contact's URI. * @hide */ +@SystemApi public final class RcsContactUceCapability implements Parcelable { /** Contains presence information associated with the contact */ @@ -70,52 +72,46 @@ public final class RcsContactUceCapability implements Parcelable { public @interface SourceType {} /** + * Capability information for the requested contact has expired and can not be refreshed due to + * a temporary network error. This is a temporary error and the capabilities of the contact + * should be queried again at a later time. + */ + public static final int REQUEST_RESULT_UNKNOWN = 0; + + /** * The requested contact was found to be offline when queried. This is only applicable to * contact capabilities that were queried via OPTIONS requests and the network returned a * 408/480 response. */ - public static final int REQUEST_RESULT_NOT_ONLINE = 0; + public static final int REQUEST_RESULT_NOT_ONLINE = 1; /** * Capability information for the requested contact was not found. The contact should not be * considered an RCS user. */ - public static final int REQUEST_RESULT_NOT_FOUND = 1; + public static final int REQUEST_RESULT_NOT_FOUND = 2; /** * Capability information for the requested contact was found successfully. */ - public static final int REQUEST_RESULT_FOUND = 2; - - /** - * Capability information for the requested contact has expired and can not be refreshed due to - * a temporary network error. This is a temporary error and the capabilities of the contact - * should be queried again at a later time. - */ - public static final int REQUEST_RESULT_UNKNOWN = 3; + public static final int REQUEST_RESULT_FOUND = 3; /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = "REQUEST_RESULT_", value = { + REQUEST_RESULT_UNKNOWN, REQUEST_RESULT_NOT_ONLINE, REQUEST_RESULT_NOT_FOUND, - REQUEST_RESULT_FOUND, - REQUEST_RESULT_UNKNOWN + REQUEST_RESULT_FOUND }) public @interface RequestResult {} /** - * The base class of {@link OptionsBuilder} and {@link PresenceBuilder} - */ - public static abstract class RcsUcsCapabilityBuilder { - public abstract @NonNull RcsContactUceCapability build(); - } - - /** * Builder to help construct {@link RcsContactUceCapability} instances when capabilities were * queried through SIP OPTIONS. + * @hide */ - public static class OptionsBuilder extends RcsUcsCapabilityBuilder { + public static final class OptionsBuilder { private final RcsContactUceCapability mCapabilities; @@ -162,7 +158,6 @@ public final class RcsContactUceCapability implements Parcelable { /** * @return the constructed instance. */ - @Override public @NonNull RcsContactUceCapability build() { return mCapabilities; } @@ -172,7 +167,7 @@ public final class RcsContactUceCapability implements Parcelable { * Builder to help construct {@link RcsContactUceCapability} instances when capabilities were * queried through a presence server. */ - public static class PresenceBuilder extends RcsUcsCapabilityBuilder { + public static final class PresenceBuilder { private final RcsContactUceCapability mCapabilities; @@ -214,7 +209,6 @@ public final class RcsContactUceCapability implements Parcelable { /** * @return the RcsContactUceCapability instance. */ - @Override public @NonNull RcsContactUceCapability build() { return mCapabilities; } @@ -284,6 +278,7 @@ public final class RcsContactUceCapability implements Parcelable { * <p> * Note: this is only populated if {@link #getCapabilityMechanism} is * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_OPTIONS} + * @hide */ public @NonNull List<String> getOptionsFeatureTags() { if (mCapabilityMechanism != CAPABILITY_MECHANISM_OPTIONS) { @@ -299,7 +294,7 @@ public final class RcsContactUceCapability implements Parcelable { * Note: this is only populated if {@link #getCapabilityMechanism} is * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_PRESENCE} */ - public @NonNull List<RcsContactPresenceTuple> getPresenceTuples() { + public @NonNull List<RcsContactPresenceTuple> getCapabilityTuples() { if (mCapabilityMechanism != CAPABILITY_MECHANISM_PRESENCE) { return Collections.emptyList(); } @@ -309,13 +304,14 @@ public final class RcsContactUceCapability implements Parcelable { /** * Get the RcsContactPresenceTuple associated with the given service id. * @param serviceId The service id to get the presence tuple. - * @return The RcsContactPresenceTuple which has the given service id. + * @return The RcsContactPresenceTuple which has the given service id or {@code null} if the + * service id does not exist in the list of presence tuples returned from the network. * * <p> * Note: this is only populated if {@link #getCapabilityMechanism} is * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_PRESENCE} */ - public @Nullable RcsContactPresenceTuple getPresenceTuple(@NonNull String serviceId) { + public @Nullable RcsContactPresenceTuple getCapabilityTuple(@NonNull String serviceId) { if (mCapabilityMechanism != CAPABILITY_MECHANISM_PRESENCE) { return null; } diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java index 6c31466c2a89..070fd799d6cc 100644 --- a/telephony/java/android/telephony/ims/RcsUceAdapter.java +++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java @@ -63,6 +63,7 @@ public class RcsUceAdapter { * RcsFeature should not publish capabilities or service capability requests. * @hide */ + @SystemApi public static final int CAPABILITY_TYPE_PRESENCE_UCE = 1 << 1; /**@hide*/ @@ -77,12 +78,14 @@ public class RcsUceAdapter { * An unknown error has caused the request to fail. * @hide */ + @SystemApi public static final int ERROR_GENERIC_FAILURE = 1; /** * The carrier network does not have UCE support enabled for this subscriber. * @hide */ + @SystemApi public static final int ERROR_NOT_ENABLED = 2; /** @@ -90,12 +93,14 @@ public class RcsUceAdapter { * 1x only currently). * @hide */ + @SystemApi public static final int ERROR_NOT_AVAILABLE = 3; /** * The network has responded with SIP 403 error and a reason "User not registered." * @hide */ + @SystemApi public static final int ERROR_NOT_REGISTERED = 4; /** @@ -103,12 +108,14 @@ public class RcsUceAdapter { * presence" for this subscriber. * @hide */ + @SystemApi public static final int ERROR_NOT_AUTHORIZED = 5; /** * The network has responded to this request with a SIP 403 error and no reason. * @hide */ + @SystemApi public static final int ERROR_FORBIDDEN = 6; /** @@ -116,6 +123,7 @@ public class RcsUceAdapter { * subscriber to the carrier network. * @hide */ + @SystemApi public static final int ERROR_NOT_FOUND = 7; /** @@ -123,6 +131,7 @@ public class RcsUceAdapter { * with a lower number of contact numbers. The number varies per carrier. * @hide */ + @SystemApi // TODO: Try to integrate this into the API so that the service will split based on carrier. public static final int ERROR_REQUEST_TOO_LARGE = 8; @@ -130,18 +139,21 @@ public class RcsUceAdapter { * The network did not respond to the capabilities request before the request timed out. * @hide */ + @SystemApi public static final int ERROR_REQUEST_TIMEOUT = 9; /** * The request failed due to the service having insufficient memory. * @hide */ + @SystemApi public static final int ERROR_INSUFFICIENT_MEMORY = 10; /** * The network was lost while trying to complete the request. * @hide */ + @SystemApi public static final int ERROR_LOST_NETWORK = 11; /** @@ -149,6 +161,7 @@ public class RcsUceAdapter { * time returned in {@link CapabilitiesCallback#onError} has elapsed. * @hide */ + @SystemApi public static final int ERROR_SERVER_UNAVAILABLE = 12; /**@hide*/ @@ -405,6 +418,7 @@ public class RcsUceAdapter { * @see #requestCapabilities(Executor, List, CapabilitiesCallback) * @hide */ + @SystemApi public interface CapabilitiesCallback { /** @@ -424,10 +438,10 @@ public class RcsUceAdapter { * The pending request has resulted in an error and may need to be retried, depending on the * error code. * @param errorCode The reason for the framework being unable to process the request. - * @param retryAfterMilliseconds The time in milliseconds the requesting application should + * @param retryIntervalMillis The time in milliseconds the requesting application should * wait before retrying, if non-zero. */ - void onError(@ErrorCode int errorCode, long retryAfterMilliseconds); + void onError(@ErrorCode int errorCode, long retryIntervalMillis); } private final Context mContext; @@ -458,9 +472,9 @@ public class RcsUceAdapter { * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} is enabled or else * this operation will fail with {@link #ERROR_NOT_AVAILABLE} or {@link #ERROR_NOT_ENABLED}. * + * @param contactNumbers A list of numbers that the capabilities are being requested for. * @param executor The executor that will be used when the request is completed and the * {@link CapabilitiesCallback} is called. - * @param contactNumbers A list of numbers that the capabilities are being requested for. * @param c A one-time callback for when the request for capabilities completes or there is an * error processing the request. * @throws ImsException if the subscription associated with this instance of @@ -469,9 +483,10 @@ public class RcsUceAdapter { * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public void requestCapabilities(@NonNull @CallbackExecutor Executor executor, - @NonNull List<Uri> contactNumbers, + public void requestCapabilities(@NonNull List<Uri> contactNumbers, + @NonNull @CallbackExecutor Executor executor, @NonNull CapabilitiesCallback c) throws ImsException { if (c == null) { throw new IllegalArgumentException("Must include a non-null CapabilitiesCallback."); @@ -495,8 +510,7 @@ public class RcsUceAdapter { public void onCapabilitiesReceived(List<RcsContactUceCapability> contactCapabilities) { final long callingIdentity = Binder.clearCallingIdentity(); try { - executor.execute(() -> - c.onCapabilitiesReceived(contactCapabilities)); + executor.execute(() -> c.onCapabilitiesReceived(contactCapabilities)); } finally { restoreCallingIdentity(callingIdentity); } @@ -550,13 +564,17 @@ public class RcsUceAdapter { * {@link #ERROR_NOT_AVAILABLE} or {@link #ERROR_NOT_ENABLED}. * * @param contactNumber The contact of the capabilities is being requested for. + * @param executor The executor that will be used when the request is completed and the + * {@link CapabilitiesCallback} is called. * @param c A one-time callback for when the request for capabilities completes or there is * an error processing the request. * @hide */ + @SystemApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) - public void requestNetworkAvailability(@NonNull @CallbackExecutor Executor executor, - @NonNull Uri contactNumber, @NonNull CapabilitiesCallback c) throws ImsException { + public void requestAvailability(@NonNull Uri contactNumber, + @NonNull @CallbackExecutor Executor executor, + @NonNull CapabilitiesCallback c) throws ImsException { if (executor == null) { throw new IllegalArgumentException("Must include a non-null Executor."); } @@ -569,7 +587,7 @@ public class RcsUceAdapter { IImsRcsController imsRcsController = getIImsRcsController(); if (imsRcsController == null) { - Log.e(TAG, "requestNetworkAvailability: IImsRcsController is null"); + Log.e(TAG, "requestAvailability: IImsRcsController is null"); throw new ImsException("Cannot find remote IMS service", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } @@ -579,8 +597,7 @@ public class RcsUceAdapter { public void onCapabilitiesReceived(List<RcsContactUceCapability> contactCapabilities) { final long callingIdentity = Binder.clearCallingIdentity(); try { - executor.execute(() -> - c.onCapabilitiesReceived(contactCapabilities)); + executor.execute(() -> c.onCapabilitiesReceived(contactCapabilities)); } finally { restoreCallingIdentity(callingIdentity); } @@ -606,12 +623,12 @@ public class RcsUceAdapter { }; try { - imsRcsController.requestNetworkAvailability(mSubId, mContext.getOpPackageName(), + imsRcsController.requestAvailability(mSubId, mContext.getOpPackageName(), mContext.getAttributionTag(), contactNumber, internalCallback); } catch (ServiceSpecificException e) { throw new ImsException(e.toString(), e.errorCode); } catch (RemoteException e) { - Log.e(TAG, "Error calling IImsRcsController#requestNetworkAvailability", e); + Log.e(TAG, "Error calling IImsRcsController#requestAvailability", e); throw new ImsException("Remote IMS Service is not available", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } @@ -683,7 +700,7 @@ public class RcsUceAdapter { if (imsRcsController == null) { Log.e(TAG, "addOnPublishStateChangedListener : IImsRcsController is null"); throw new ImsException("Cannot find remote IMS service", - ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } PublishStateCallbackAdapter stateCallback = addPublishStateCallback(executor, listener); @@ -694,7 +711,7 @@ public class RcsUceAdapter { } catch (RemoteException e) { Log.e(TAG, "Error calling IImsRcsController#registerUcePublishStateCallback", e); throw new ImsException("Remote IMS Service is not available", - ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } } diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl index 36349895c35b..7a6c28bddd09 100644 --- a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl +++ b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl @@ -52,7 +52,7 @@ interface IImsRcsController { // ImsUceAdapter specific void requestCapabilities(int subId, String callingPackage, String callingFeatureId, in List<Uri> contactNumbers, IRcsUceControllerCallback c); - void requestNetworkAvailability(int subId, String callingPackage, + void requestAvailability(int subId, String callingPackage, String callingFeatureId, in Uri contactNumber, IRcsUceControllerCallback c); int getUcePublishState(int subId); diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java index c84e23c38e97..7eba709a11da 100644 --- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java +++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java @@ -24,6 +24,7 @@ import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.net.Uri; import android.telephony.ims.ImsException; +import android.telephony.ims.RcsUceAdapter; import android.telephony.ims.feature.ImsFeature; import android.telephony.ims.feature.RcsFeature; import android.util.Log; @@ -139,18 +140,19 @@ public class RcsCapabilityExchangeImplBase { * Provide the framework with a subsequent network response update to * {@link #publishCapabilities(String, PublishResponseCallback)}. * - * @param code The SIP response code sent from the network for the operation + * @param sipCode The SIP response code sent from the network for the operation * token specified. * @param reason The optional reason response from the network. If there is a reason header * included in the response, that should take precedence over the reason provided in the - * status line. If the network provided no reason with the code, the string should be empty. + * status line. If the network provided no reason with the sip code, the string should be + * empty. * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is * not currently connected to the framework. This can happen if the {@link RcsFeature} * is not {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received * the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases * when the Telephony stack has crashed. */ - void onNetworkResponse(@IntRange(from = 100, to = 699) int code, + void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode, @NonNull String reason) throws ImsException; } @@ -173,7 +175,7 @@ public class RcsCapabilityExchangeImplBase { /** * Send the response of a SIP OPTIONS capability exchange to the framework. - * @param code The SIP response code that was sent by the network in response + * @param sipCode The SIP response code that was sent by the network in response * to the request sent by {@link #sendOptionsCapabilityRequest}. * @param reason The optional SIP response reason sent by the network. * If none was sent, this should be an empty string. @@ -186,17 +188,20 @@ public class RcsCapabilityExchangeImplBase { * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare * cases when the Telephony stack has crashed. */ - void onNetworkResponse(int code, @NonNull String reason, + void onNetworkResponse(int sipCode, @NonNull String reason, @Nullable List<String> theirCaps) throws ImsException; } /** * Interface used by the framework to receive the response of the subscribe request. - * @hide */ public interface SubscribeResponseCallback { /** * Notify the framework that the command associated with this callback has failed. + * <p> + * Must only be called when there was an error generating a SUBSCRIBE request due to an + * IMS stack error. This is a terminating event, so no other callback event will be + * expected after this callback. * * @param code The reason why the associated command has failed. * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is @@ -211,27 +216,38 @@ public class RcsCapabilityExchangeImplBase { /** * Notify the framework of the response to the SUBSCRIBE request from * {@link #subscribeForCapabilities(List<Uri>, SubscribeResponseCallback)}. + * <p> + * If the carrier network responds to the SUBSCRIBE request with a 2XX response, then the + * framework will expect the IMS stack to call {@link #onNotifyCapabilitiesUpdate}, + * {@link #onResourceTerminated}, and {@link #onTerminated} as required for the + * subsequent NOTIFY responses to the subscription. * - * @param code The SIP response code sent from the network for the operation + * @param sipCode The SIP response code sent from the network for the operation * token specified. * @param reason The optional reason response from the network. If the network - * provided no reason with the code, the string should be empty. + * provided no reason with the sip code, the string should be empty. * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is * not currently connected to the framework. This can happen if the * {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the * {@link RcsFeature} has not received the {@link ImsFeature#onFeatureReady()} callback. * This may also happen in rare cases when the Telephony stack has crashed. */ - void onNetworkResponse(@IntRange(from = 100, to = 699) int code, + void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode, @NonNull String reason) throws ImsException; /** - * Provides the framework with latest XML PIDF documents included in the - * network response for the requested contacts' capabilities requested by the - * Framework using {@link #requestCapabilities(List, int)}. This should be - * called every time a new NOTIFY event is received with new capability - * information. + * Notify the framework of the latest XML PIDF documents included in the network response + * for the requested contacts' capabilities requested by the Framework using + * {@link RcsUceAdapter#requestCapabilities(Executor, List<Uri>, CapabilitiesCallback)}. + * <p> + * The expected format for the PIDF XML is defined in RFC3861. Each XML document must be a + * "application/pidf+xml" object and start with a root <presence> element. For NOTIFY + * responses that contain RLMI information and potentially multiple PIDF XMLs, each + * PIDF XML should be separated and added as a separate item in the List. This should be + * called every time a new NOTIFY event is received with new capability information. * + * @param pidfXmls The list of the PIDF XML data for the contact URIs that it subscribed + * for. * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is * not currently connected to the framework. * This can happen if the {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the @@ -242,21 +258,42 @@ public class RcsCapabilityExchangeImplBase { void onNotifyCapabilitiesUpdate(@NonNull List<String> pidfXmls) throws ImsException; /** - * A resource in the resource list for the presence subscribe event has been terminated. + * Notify the framework that a resource in the RLMI XML contained in the NOTIFY response + * for the ongoing SUBSCRIBE dialog has been terminated. * <p> - * This allows the framework to know that there will not be any capability information for - * a specific contact URI that they subscribed for. + * This will be used to notify the framework that a contact URI that the IMS stack has + * subscribed to on the Resource List Server has been terminated as well as the reason why. + * Usually this means that there will not be any capability information for the contact URI + * that they subscribed for. See RFC 4662 for more information. + * + * @param uriTerminatedReason The contact URIs which have been terminated. Each pair in the + * list is the contact URI and its terminated reason. + * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is + * not currently connected to the framework. + * This can happen if the {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the + * {@link RcsFeature} {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not + * received the {@link ImsFeature#onFeatureReady()} callback. This may also happen in + * rare cases when the Telephony stack has crashed. */ void onResourceTerminated( @NonNull List<Pair<Uri, String>> uriTerminatedReason) throws ImsException; /** - * The subscription associated with a previous #requestCapabilities operation - * has been terminated. This will mostly be due to the subscription expiring, - * but may also happen due to an error. - * <p> - * This allows the framework to know that there will no longer be any - * capability updates for the requested operationToken. + * The subscription associated with a previous + * {@link RcsUceAdapter#requestCapabilities(Executor, List<Uri>, CapabilitiesCallback)} + * operation has been terminated. This will mostly be due to the network sending a final + * NOTIFY response due to the subscription expiring, but this may also happen due to a + * network error. + * + * @param reason The reason for the request being unable to process. + * @param retryAfterMilliseconds The time in milliseconds the requesting application should + * wait before retrying, if non-zero. + * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is + * not currently connected to the framework. + * This can happen if the {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the + * {@link RcsFeature} {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not + * received the {@link ImsFeature#onFeatureReady()} callback. This may also happen in + * rare cases when the Telephony stack has crashed. */ void onTerminated(@NonNull String reason, long retryAfterMilliseconds) throws ImsException; } @@ -278,18 +315,23 @@ public class RcsCapabilityExchangeImplBase { /** * The user capabilities of one or multiple contacts have been requested by the framework. * <p> + * The implementer must follow up this call with an + * {@link SubscribeResponseCallback#onCommandError} call to indicate this operation has failed. * The response from the network to the SUBSCRIBE request must be sent back to the framework - * using {@link #onSubscribeNetworkResponse(int, String, int)}. As NOTIFY requests come in from - * the network, the requested contact’s capabilities should be sent back to the framework using - * {@link #onSubscribeNotifyRequest} and {@link onSubscribeResourceTerminated} + * using {@link SubscribeResponseCallback#onNetworkResponse(int, String)}. + * As NOTIFY requests come in from the network, the requested contact’s capabilities should be + * sent back to the framework using + * {@link SubscribeResponseCallback#onNotifyCapabilitiesUpdate(List<String>}) and + * {@link SubscribeResponseCallback#onResourceTerminated(List<Pair<Uri, String>>)} * should be called with the presence information for the contacts specified. * <p> - * Once the subscription is terminated, {@link #onSubscriptionTerminated} must be called for - * the framework to finish listening for NOTIFY responses. + * Once the subscription is terminated, + * {@link SubscribeResponseCallback#onTerminated(String, long)} must be called for the + * framework to finish listening for NOTIFY responses. + * * @param uris A {@link List} of the {@link Uri}s that the framework is requesting the UCE * capabilities for. * @param cb The callback of the subscribe request. - * @hide */ // executor used is defined in the constructor. @SuppressLint("ExecutorRegistration") diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 2d3c8f29ec9c..ee6c36ca6b76 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -2350,6 +2350,11 @@ interface ITelephony { */ String getMobileProvisioningUrl(); + /* + * Remove the EAB contacts from the EAB database. + */ + int removeContactFromEab(int subId, String contacts); + /** * Set a SignalStrengthUpdateRequest to receive notification when Signal Strength breach the * specified thresholds. 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 59e375fa3bd6..f474ec2c389c 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 @@ -114,7 +114,8 @@ class CloseImeWindowToHomeTest( enabled = !configuration.startRotation.isRotated()) navBarLayerIsAlwaysVisible() statusBarLayerIsAlwaysVisible() - visibleLayersShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE)) + visibleLayersShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE), + enabled = false) imeLayerBecomesInvisible() imeAppLayerBecomesInvisible(testApp) diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java new file mode 100644 index 000000000000..d936183e5a0b --- /dev/null +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java @@ -0,0 +1,105 @@ +/* + * 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.vcn; + +import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** Tests for VcnGatewayConnection.ConnectingState */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class VcnGatewayConnectionConnectingStateTest extends VcnGatewayConnectionTestBase { + private VcnIkeSession mIkeSession; + + @Before + public void setUp() throws Exception { + super.setUp(); + + mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1); + mGatewayConnection.transitionTo(mGatewayConnection.mConnectingState); + mTestLooper.dispatchAll(); + + mIkeSession = mGatewayConnection.getIkeSession(); + } + + @Test + public void testEnterStateCreatesNewIkeSession() throws Exception { + verify(mDeps).newIkeSession(any(), any(), any(), any(), any()); + } + + @Test + public void testNullNetworkTriggersDisconnect() throws Exception { + mGatewayConnection + .getUnderlyingNetworkTrackerCallback() + .onSelectedUnderlyingNetworkChanged(null); + mTestLooper.dispatchAll(); + + assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); + verify(mIkeSession).kill(); + } + + @Test + public void testNewNetworkTriggersReconnect() throws Exception { + mGatewayConnection + .getUnderlyingNetworkTrackerCallback() + .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_2); + mTestLooper.dispatchAll(); + + assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); + verify(mIkeSession).close(); + verify(mIkeSession, never()).kill(); + } + + @Test + public void testSameNetworkDoesNotTriggerReconnect() throws Exception { + mGatewayConnection + .getUnderlyingNetworkTrackerCallback() + .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1); + mTestLooper.dispatchAll(); + + assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState()); + } + + @Test + public void testChildSessionClosedTriggersDisconnect() throws Exception { + getChildSessionCallback().onClosed(); + mTestLooper.dispatchAll(); + + assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); + verify(mIkeSession).close(); + } + + @Test + public void testIkeSessionClosedTriggersDisconnect() throws Exception { + getIkeSessionCallback().onClosed(); + mTestLooper.dispatchAll(); + + assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState()); + verify(mIkeSession).close(); + } +} diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java index 346785907fcf..b4d39bf74a4b 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java @@ -32,6 +32,7 @@ import android.net.IpSecTunnelInterfaceResponse; import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; +import android.net.ipsec.ike.ChildSessionCallback; import android.net.ipsec.ike.IkeSessionCallback; import android.net.vcn.VcnGatewayConnectionConfig; import android.net.vcn.VcnGatewayConnectionConfigTest; @@ -117,4 +118,11 @@ public class VcnGatewayConnectionTestBase { verify(mDeps).newIkeSession(any(), any(), any(), captor.capture(), any()); return captor.getValue(); } + + protected ChildSessionCallback getChildSessionCallback() { + ArgumentCaptor<ChildSessionCallback> captor = + ArgumentCaptor.forClass(ChildSessionCallback.class); + verify(mDeps).newIkeSession(any(), any(), any(), any(), captor.capture()); + return captor.getValue(); + } } diff --git a/tools/powerstats/PowerStatsServiceProtoParser.java b/tools/powerstats/PowerStatsServiceProtoParser.java index e4135b5584e4..21409542714d 100644 --- a/tools/powerstats/PowerStatsServiceProtoParser.java +++ b/tools/powerstats/PowerStatsServiceProtoParser.java @@ -30,7 +30,7 @@ public class PowerStatsServiceProtoParser { for (int i = 0; i < proto.getChannelCount(); i++) { ChannelProto energyMeterInfo = proto.getChannel(i); csvHeader += "Index,Timestamp,Duration," + energyMeterInfo.getId() - + "/" + energyMeterInfo.getName() + ","; + + "/" + energyMeterInfo.getName() + "/" + energyMeterInfo.getSubsystem() + ","; } System.out.println(csvHeader); } diff --git a/wifi/java/src/android/net/wifi/nl80211/SingleScanSettings.java b/wifi/java/src/android/net/wifi/nl80211/SingleScanSettings.java index 24b1854fbf6c..1d479fc14d29 100644 --- a/wifi/java/src/android/net/wifi/nl80211/SingleScanSettings.java +++ b/wifi/java/src/android/net/wifi/nl80211/SingleScanSettings.java @@ -32,6 +32,7 @@ public class SingleScanSettings implements Parcelable { private static final String TAG = "SingleScanSettings"; public int scanType; + public boolean enable6GhzRnr; public ArrayList<ChannelSettings> channelSettings; public ArrayList<HiddenNetwork> hiddenNetworks; @@ -50,6 +51,7 @@ public class SingleScanSettings implements Parcelable { return false; } return scanType == settings.scanType + && enable6GhzRnr == settings.enable6GhzRnr && channelSettings.equals(settings.channelSettings) && hiddenNetworks.equals(settings.hiddenNetworks); } @@ -57,7 +59,7 @@ public class SingleScanSettings implements Parcelable { /** override hash code */ @Override public int hashCode() { - return Objects.hash(scanType, channelSettings, hiddenNetworks); + return Objects.hash(scanType, channelSettings, hiddenNetworks, enable6GhzRnr); } @@ -83,6 +85,7 @@ public class SingleScanSettings implements Parcelable { Log.wtf(TAG, "Invalid scan type " + scanType); } out.writeInt(scanType); + out.writeBoolean(enable6GhzRnr); out.writeTypedList(channelSettings); out.writeTypedList(hiddenNetworks); } @@ -100,6 +103,7 @@ public class SingleScanSettings implements Parcelable { if (!isValidScanType(result.scanType)) { Log.wtf(TAG, "Invalid scan type " + result.scanType); } + result.enable6GhzRnr = in.readBoolean(); result.channelSettings = new ArrayList<ChannelSettings>(); in.readTypedList(result.channelSettings, ChannelSettings.CREATOR); result.hiddenNetworks = new ArrayList<HiddenNetwork>(); diff --git a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java index db2eb99297c2..ef2653204af5 100644 --- a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java +++ b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java @@ -28,6 +28,7 @@ import android.net.wifi.SoftApInfo; import android.net.wifi.WifiAnnotations; import android.net.wifi.WifiScanner; import android.os.Binder; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; @@ -90,6 +91,10 @@ public class WifiNl80211Manager { */ public static final int SCAN_TYPE_PNO_SCAN = 1; + // Extra scanning parameter used to enable 6Ghz RNR (Reduced Neighbour Support). + public static final String SCANNING_PARAM_ENABLE_6GHZ_RNR = + "android.net.wifi.nl80211.SCANNING_PARAM_ENABLE_6GHZ_RNR"; + private AlarmManager mAlarmManager; private Handler mEventHandler; @@ -911,6 +916,15 @@ public class WifiNl80211Manager { } /** + * @deprecated replaced by {@link #startScan(String, int, Set, List, Bundle)} + **/ + @Deprecated + public boolean startScan(@NonNull String ifaceName, @WifiAnnotations.ScanType int scanType, + @Nullable Set<Integer> freqs, @Nullable List<byte[]> hiddenNetworkSSIDs) { + return startScan(ifaceName, scanType, freqs, hiddenNetworkSSIDs, null); + } + + /** * Start a scan using the specified parameters. A scan is an asynchronous operation. The * result of the operation is returned in the {@link ScanEventCallback} registered when * setting up an interface using @@ -929,11 +943,13 @@ public class WifiNl80211Manager { * @param freqs list of frequencies to scan for, if null scan all supported channels. * @param hiddenNetworkSSIDs List of hidden networks to be scanned for, a null indicates that * no hidden frequencies will be scanned for. + * @param extraScanningParams bundle of extra scanning parameters. * @return Returns true on success, false on failure (e.g. when called before the interface * has been set up). */ public boolean startScan(@NonNull String ifaceName, @WifiAnnotations.ScanType int scanType, - @Nullable Set<Integer> freqs, @Nullable List<byte[]> hiddenNetworkSSIDs) { + @Nullable Set<Integer> freqs, @Nullable List<byte[]> hiddenNetworkSSIDs, + @Nullable Bundle extraScanningParams) { IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName); if (scannerImpl == null) { Log.e(TAG, "No valid wificond scanner interface handler for iface=" + ifaceName); @@ -948,6 +964,9 @@ public class WifiNl80211Manager { } settings.channelSettings = new ArrayList<>(); settings.hiddenNetworks = new ArrayList<>(); + if (extraScanningParams != null) { + settings.enable6GhzRnr = extraScanningParams.getBoolean(SCANNING_PARAM_ENABLE_6GHZ_RNR); + } if (freqs != null) { for (Integer freq : freqs) { diff --git a/wifi/tests/src/android/net/wifi/nl80211/SingleScanSettingsTest.java b/wifi/tests/src/android/net/wifi/nl80211/SingleScanSettingsTest.java index 905920888012..fd595fa5660c 100644 --- a/wifi/tests/src/android/net/wifi/nl80211/SingleScanSettingsTest.java +++ b/wifi/tests/src/android/net/wifi/nl80211/SingleScanSettingsTest.java @@ -74,6 +74,7 @@ public class SingleScanSettingsTest { new ArrayList<>(Arrays.asList(mChannelSettings1, mChannelSettings2)); scanSettings.hiddenNetworks = new ArrayList<>(Arrays.asList(mHiddenNetwork1, mHiddenNetwork2)); + scanSettings.enable6GhzRnr = true; Parcel parcel = Parcel.obtain(); scanSettings.writeToParcel(parcel, 0); diff --git a/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java b/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java index 9ee0acbfbaa2..4b03a49e40bb 100644 --- a/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java +++ b/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java @@ -44,6 +44,7 @@ import android.net.wifi.SoftApInfo; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiScanner; import android.net.wifi.util.HexEncoding; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; @@ -506,7 +507,51 @@ public class WifiNl80211ManagerTest { SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST)); verify(mWifiScannerImpl).scan(argThat(new ScanMatcher( IWifiScannerImpl.SCAN_TYPE_LOW_POWER, - SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST))); + SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST, false))); + } + + /** + * Verify the new startScan() API can convert input parameters to SingleScanSettings correctly. + */ + @Test + public void testScanWithBundle() throws Exception { + when(mWifiScannerImpl.scan(any(SingleScanSettings.class))).thenReturn(true); + Bundle bundle = new Bundle(); + bundle.putBoolean(WifiNl80211Manager.SCANNING_PARAM_ENABLE_6GHZ_RNR, true); + assertTrue(mWificondControl.startScan( + TEST_INTERFACE_NAME, WifiScanner.SCAN_TYPE_LOW_POWER, + SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST, bundle)); + verify(mWifiScannerImpl).scan(argThat(new ScanMatcher( + IWifiScannerImpl.SCAN_TYPE_LOW_POWER, + SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST, true))); + } + + /** + * Verify default values in SingleScanSettings when the input Bundle to startScan is null. + */ + @Test + public void testScanWithNullBundle() throws Exception { + when(mWifiScannerImpl.scan(any(SingleScanSettings.class))).thenReturn(true); + assertTrue(mWificondControl.startScan( + TEST_INTERFACE_NAME, WifiScanner.SCAN_TYPE_LOW_POWER, + SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST, null)); + verify(mWifiScannerImpl).scan(argThat(new ScanMatcher( + IWifiScannerImpl.SCAN_TYPE_LOW_POWER, + SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST, false))); + } + + /** + * Verify default values in SingleScanSettings when the input Bundle to startScan is empty. + */ + @Test + public void testScanWithEmptyBundle() throws Exception { + when(mWifiScannerImpl.scan(any(SingleScanSettings.class))).thenReturn(true); + assertTrue(mWificondControl.startScan( + TEST_INTERFACE_NAME, WifiScanner.SCAN_TYPE_LOW_POWER, + SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST, new Bundle())); + verify(mWifiScannerImpl).scan(argThat(new ScanMatcher( + IWifiScannerImpl.SCAN_TYPE_LOW_POWER, + SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST, false))); } /** @@ -527,7 +572,7 @@ public class WifiNl80211ManagerTest { // But the argument passed down should have the duplicate removed. verify(mWifiScannerImpl).scan(argThat(new ScanMatcher( IWifiScannerImpl.SCAN_TYPE_LOW_POWER, - SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST))); + SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST, false))); } /** @@ -539,7 +584,7 @@ public class WifiNl80211ManagerTest { assertTrue(mWificondControl.startScan( TEST_INTERFACE_NAME, WifiScanner.SCAN_TYPE_HIGH_ACCURACY, null, null)); verify(mWifiScannerImpl).scan(argThat(new ScanMatcher( - IWifiScannerImpl.SCAN_TYPE_HIGH_ACCURACY, null, null))); + IWifiScannerImpl.SCAN_TYPE_HIGH_ACCURACY, null, null, false))); } /** @@ -1068,11 +1113,14 @@ public class WifiNl80211ManagerTest { int mExpectedScanType; private final Set<Integer> mExpectedFreqs; private final List<byte[]> mExpectedSsids; + private final boolean mExpectedEnable6GhzRnr; - ScanMatcher(int expectedScanType, Set<Integer> expectedFreqs, List<byte[]> expectedSsids) { + ScanMatcher(int expectedScanType, Set<Integer> expectedFreqs, List<byte[]> expectedSsids, + boolean expectedEnable6GhzRnr) { this.mExpectedScanType = expectedScanType; this.mExpectedFreqs = expectedFreqs; this.mExpectedSsids = expectedSsids; + this.mExpectedEnable6GhzRnr = expectedEnable6GhzRnr; } @Override @@ -1080,6 +1128,9 @@ public class WifiNl80211ManagerTest { if (settings.scanType != mExpectedScanType) { return false; } + if (settings.enable6GhzRnr != mExpectedEnable6GhzRnr) { + return false; + } ArrayList<ChannelSettings> channelSettings = settings.channelSettings; ArrayList<HiddenNetwork> hiddenNetworks = settings.hiddenNetworks; if (mExpectedFreqs != null) { |