diff options
60 files changed, 790 insertions, 656 deletions
diff --git a/apex/Android.bp b/apex/Android.bp index 51e030bd174d..e8afa1dabb25 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -63,6 +63,60 @@ mainline_service_stubs_args = "--hide-annotation android.annotation.Hide " + "--hide InternalClasses " // com.android.* classes are okay in this interface +// Defaults for mainline module provided java_sdk_library instances. +java_defaults { + name: "framework-module-defaults", + + // Additional annotations used for compiling both the implementation and the + // stubs libraries. + libs: ["framework-annotations-lib"], + + // Enable api lint. This will eventually become the default for java_sdk_library + // but it cannot yet be turned on because some usages have not been cleaned up. + // TODO(b/156126315) - Remove when no longer needed. + api_lint: { + enabled: true, + }, + + // The API scope specific properties. + public: { + enabled: true, + sdk_version: "module_current", + }, + system: { + enabled: true, + sdk_version: "module_current", + }, + module_lib: { + enabled: true, + sdk_version: "module_current", + }, + + // The stub libraries must be visible to frameworks/base so they can be combined + // into API specific libraries. + stubs_library_visibility: [ + "//frameworks/base", // Framework + ], + + // Set the visibility of the modules creating the stubs source. + stubs_source_visibility: [ + // Ignore any visibility rules specified on the java_sdk_library when + // setting the visibility of the stubs source modules. + "//visibility:override", + + // Currently, the stub source is not required for anything other than building + // the stubs library so is private to avoid misuse. + "//visibility:private", + ], + + // Collates API usages from each module for further analysis. + plugins: ["java_api_finder"], + + // Mainline modules should only rely on 'module_lib' APIs provided by other modules + // and the non updatable parts of the platform. + sdk_version: "module_current", +} + stubs_defaults { name: "framework-module-stubs-defaults-publicapi", args: mainline_framework_stubs_args, diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java index 46d449a9257c..372ec981df02 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java @@ -79,6 +79,12 @@ public class AppIdleHistory { private static final int STANDBY_BUCKET_UNKNOWN = -1; + /** + * The bucket beyond which apps are considered idle. Any apps in this bucket or lower are + * considered idle while those in higher buckets are not considered idle. + */ + static final int IDLE_BUCKET_CUTOFF = STANDBY_BUCKET_RARE; + @VisibleForTesting static final String APP_IDLE_FILENAME = "app_idle_stats.xml"; private static final String TAG_PACKAGES = "packages"; @@ -350,7 +356,7 @@ public class AppIdleHistory { ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, elapsedRealtime, true); - return appUsageHistory.currentBucket >= STANDBY_BUCKET_RARE; + return appUsageHistory.currentBucket >= IDLE_BUCKET_CUTOFF; } public AppUsageHistory getAppUsageHistory(String packageName, int userId, @@ -487,7 +493,7 @@ public class AppIdleHistory { final int newBucket; final int reason; if (idle) { - newBucket = STANDBY_BUCKET_RARE; + newBucket = IDLE_BUCKET_CUTOFF; reason = REASON_MAIN_FORCED_BY_USER; } else { newBucket = STANDBY_BUCKET_ACTIVE; diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index 980372d58f33..2834ab14f28d 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -54,6 +54,7 @@ import static com.android.server.SystemService.PHASE_BOOT_COMPLETED; import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.AppGlobals; @@ -92,6 +93,7 @@ import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings.Global; import android.telephony.TelephonyManager; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.KeyValueListParser; import android.util.Slog; @@ -227,6 +229,13 @@ public class AppStandbyController implements AppStandbyInternal { @GuardedBy("mActiveAdminApps") private final SparseArray<Set<String>> mActiveAdminApps = new SparseArray<>(); + /** + * Set of system apps that are headless (don't have any declared activities, enabled or + * disabled). Presence in this map indicates that the app is a headless system app. + */ + @GuardedBy("mAppIdleLock") + private final ArrayMap<String, Boolean> mHeadlessSystemApps = new ArrayMap<>(); + private final CountDownLatch mAdminDataAvailableLatch = new CountDownLatch(1); // Messages for the handler @@ -667,20 +676,22 @@ public class AppStandbyController implements AppStandbyInternal { return; } } - final boolean isSpecial = isAppSpecial(packageName, + final int minBucket = getAppMinBucket(packageName, UserHandle.getAppId(uid), userId); if (DEBUG) { - Slog.d(TAG, " Checking idle state for " + packageName + " special=" + - isSpecial); + Slog.d(TAG, " Checking idle state for " + packageName + + " minBucket=" + minBucket); } - if (isSpecial) { + if (minBucket <= STANDBY_BUCKET_ACTIVE) { + // No extra processing needed for ACTIVE or higher since apps can't drop into lower + // buckets. synchronized (mAppIdleLock) { mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, - STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT); + minBucket, REASON_MAIN_DEFAULT); } maybeInformListeners(packageName, userId, elapsedRealtime, - STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT, false); + minBucket, REASON_MAIN_DEFAULT, false); } else { synchronized (mAppIdleLock) { final AppIdleHistory.AppUsageHistory app = @@ -761,6 +772,14 @@ public class AppStandbyController implements AppStandbyInternal { Slog.d(TAG, "Bringing up from RESTRICTED to RARE due to off switch"); } } + if (newBucket > minBucket) { + newBucket = minBucket; + // Leave the reason alone. + if (DEBUG) { + Slog.d(TAG, "Bringing up from " + newBucket + " to " + minBucket + + " due to min bucketing"); + } + } if (DEBUG) { Slog.d(TAG, " Old bucket=" + oldBucket + ", newBucket=" + newBucket); @@ -1027,20 +1046,35 @@ public class AppStandbyController implements AppStandbyInternal { return isAppIdleFiltered(packageName, getAppId(packageName), userId, elapsedRealtime); } - private boolean isAppSpecial(String packageName, int appId, int userId) { - if (packageName == null) return false; + @StandbyBuckets + private int getAppMinBucket(String packageName, int userId) { + try { + final int uid = mPackageManager.getPackageUidAsUser(packageName, userId); + return getAppMinBucket(packageName, UserHandle.getAppId(uid), userId); + } catch (PackageManager.NameNotFoundException e) { + // Not a valid package for this user, nothing to do + return STANDBY_BUCKET_NEVER; + } + } + + /** + * Return the lowest bucket this app should ever enter. + */ + @StandbyBuckets + private int getAppMinBucket(String packageName, int appId, int userId) { + if (packageName == null) return STANDBY_BUCKET_NEVER; // If not enabled at all, of course nobody is ever idle. if (!mAppIdleEnabled) { - return true; + return STANDBY_BUCKET_EXEMPTED; } if (appId < Process.FIRST_APPLICATION_UID) { // System uids never go idle. - return true; + return STANDBY_BUCKET_EXEMPTED; } if (packageName.equals("android")) { // Nor does the framework (which should be redundant with the above, but for MR1 we will // retain this for safety). - return true; + return STANDBY_BUCKET_EXEMPTED; } if (mSystemServicesReady) { try { @@ -1048,42 +1082,51 @@ public class AppStandbyController implements AppStandbyInternal { // for idle mode, because app idle (aka app standby) is really not as big an issue // for controlling who participates vs. doze mode. if (mInjector.isNonIdleWhitelisted(packageName)) { - return true; + return STANDBY_BUCKET_EXEMPTED; } } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } if (isActiveDeviceAdmin(packageName, userId)) { - return true; + return STANDBY_BUCKET_EXEMPTED; } if (isActiveNetworkScorer(packageName)) { - return true; + return STANDBY_BUCKET_EXEMPTED; } if (mAppWidgetManager != null && mInjector.isBoundWidgetPackage(mAppWidgetManager, packageName, userId)) { - return true; + // TODO: consider lowering to ACTIVE + return STANDBY_BUCKET_EXEMPTED; } if (isDeviceProvisioningPackage(packageName)) { - return true; + return STANDBY_BUCKET_EXEMPTED; } } // Check this last, as it can be the most expensive check if (isCarrierApp(packageName)) { - return true; + return STANDBY_BUCKET_EXEMPTED; } - return false; + if (isHeadlessSystemApp(packageName)) { + return STANDBY_BUCKET_ACTIVE; + } + + return STANDBY_BUCKET_NEVER; + } + + private boolean isHeadlessSystemApp(String packageName) { + return mHeadlessSystemApps.containsKey(packageName); } @Override public boolean isAppIdleFiltered(String packageName, int appId, int userId, long elapsedRealtime) { - if (isAppSpecial(packageName, appId, userId)) { + if (getAppMinBucket(packageName, appId, userId) < AppIdleHistory.IDLE_BUCKET_CUTOFF) { return false; } else { synchronized (mAppIdleLock) { @@ -1423,6 +1466,8 @@ public class AppStandbyController implements AppStandbyInternal { } } + // Make sure we don't put the app in a lower bucket than it's supposed to be in. + newBucket = Math.min(newBucket, getAppMinBucket(packageName, userId)); mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket, reason, resetTimeout); } @@ -1617,14 +1662,16 @@ public class AppStandbyController implements AppStandbyInternal { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); + final String pkgName = intent.getData().getSchemeSpecificPart(); + final int userId = getSendingUserId(); if (Intent.ACTION_PACKAGE_ADDED.equals(action) || Intent.ACTION_PACKAGE_CHANGED.equals(action)) { clearCarrierPrivilegedApps(); + // ACTION_PACKAGE_ADDED is called even for system app downgrades. + evaluateSystemAppException(pkgName, userId); } if ((Intent.ACTION_PACKAGE_REMOVED.equals(action) || Intent.ACTION_PACKAGE_ADDED.equals(action))) { - final String pkgName = intent.getData().getSchemeSpecificPart(); - final int userId = getSendingUserId(); if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { maybeUnrestrictBuggyApp(pkgName, userId); } else { @@ -1634,6 +1681,34 @@ public class AppStandbyController implements AppStandbyInternal { } } + private void evaluateSystemAppException(String packageName, int userId) { + if (!mSystemServicesReady) { + // The app will be evaluated in initializeDefaultsForSystemApps() when possible. + return; + } + try { + PackageInfo pi = mPackageManager.getPackageInfoAsUser(packageName, + PackageManager.GET_ACTIVITIES | PackageManager.MATCH_DISABLED_COMPONENTS, + userId); + evaluateSystemAppException(pi); + } catch (PackageManager.NameNotFoundException e) { + mHeadlessSystemApps.remove(packageName); + } + } + + private void evaluateSystemAppException(@Nullable PackageInfo pkgInfo) { + if (pkgInfo.applicationInfo != null && pkgInfo.applicationInfo.isSystemApp()) { + synchronized (mAppIdleLock) { + if (pkgInfo.activities == null || pkgInfo.activities.length == 0) { + // Headless system app. + mHeadlessSystemApps.put(pkgInfo.packageName, true); + } else { + mHeadlessSystemApps.remove(pkgInfo.packageName); + } + } + } + } + @Override public void initializeDefaultsForSystemApps(int userId) { if (!mSystemServicesReady) { @@ -1645,7 +1720,7 @@ public class AppStandbyController implements AppStandbyInternal { + "appIdleEnabled=" + mAppIdleEnabled); final long elapsedRealtime = mInjector.elapsedRealtime(); List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser( - PackageManager.MATCH_DISABLED_COMPONENTS, + PackageManager.GET_ACTIVITIES | PackageManager.MATCH_DISABLED_COMPONENTS, userId); final int packageCount = packages.size(); synchronized (mAppIdleLock) { @@ -1658,6 +1733,8 @@ public class AppStandbyController implements AppStandbyInternal { mAppIdleHistory.reportUsage(packageName, userId, STANDBY_BUCKET_ACTIVE, REASON_SUB_USAGE_SYSTEM_UPDATE, 0, elapsedRealtime + mSystemUpdateUsageTimeoutMillis); + + evaluateSystemAppException(pi); } } // Immediately persist defaults to disk diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java index c3adf6044655..ddecfed68d9b 100644 --- a/apex/media/framework/java/android/media/MediaParser.java +++ b/apex/media/framework/java/android/media/MediaParser.java @@ -21,6 +21,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringDef; import android.media.MediaCodec.CryptoInfo; +import android.os.Build; import android.text.TextUtils; import android.util.Log; import android.util.Pair; @@ -1099,6 +1100,9 @@ public final class MediaParser { // Private methods. private MediaParser(OutputConsumer outputConsumer, boolean sniff, String... parserNamesPool) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { + throw new UnsupportedOperationException("Android version must be R or greater."); + } mParserParameters = new HashMap<>(); mOutputConsumer = outputConsumer; mParserNamesPool = parserNamesPool; diff --git a/apex/sdkextensions/Android.bp b/apex/sdkextensions/Android.bp index dbb5bd3d660f..fdb078e00d92 100644 --- a/apex/sdkextensions/Android.bp +++ b/apex/sdkextensions/Android.bp @@ -39,7 +39,7 @@ apex_defaults { sdk { name: "sdkextensions-sdk", - java_header_libs: [ "framework-sdkextensions-stubs-systemapi" ], + java_sdk_libs: [ "framework-sdkextensions" ], } apex_key { diff --git a/apex/sdkextensions/framework/Android.bp b/apex/sdkextensions/framework/Android.bp index 14e23ed9a8a1..b8aad7d8204f 100644 --- a/apex/sdkextensions/framework/Android.bp +++ b/apex/sdkextensions/framework/Android.bp @@ -25,14 +25,18 @@ filegroup { visibility: [ "//frameworks/base" ] // For the "global" stubs. } -java_library { +java_sdk_library { name: "framework-sdkextensions", srcs: [ ":framework-sdkextensions-sources" ], - sdk_version: "system_current", - libs: [ "framework-annotations-lib" ], + defaults: ["framework-module-defaults"], + + // TODO(b/155480189) - Remove naming_scheme once references have been resolved. + // Temporary java_sdk_library component naming scheme to use to ease the transition from separate + // modules to java_sdk_library. + naming_scheme: "framework-modules", + permitted_packages: [ "android.os.ext" ], installable: true, - plugins: ["java_api_finder"], visibility: [ "//frameworks/base/apex/sdkextensions", "//frameworks/base/apex/sdkextensions/testing", @@ -43,102 +47,3 @@ java_library { "test_com.android.sdkext", ], } - -stubs_defaults { - name: "framework-sdkextensions-stubs-defaults", - srcs: [ ":framework-sdkextensions-sources" ], - libs: [ "framework-annotations-lib" ], - dist: { dest: "framework-sdkextensions.txt" }, -} - -droidstubs { - name: "framework-sdkextensions-stubs-srcs-publicapi", - defaults: [ - "framework-module-stubs-defaults-publicapi", - "framework-sdkextensions-stubs-defaults", - ], - check_api: { - last_released: { - api_file: ":framework-sdkextensions.api.public.latest", - removed_api_file: ":framework-sdkextensions-removed.api.public.latest", - }, - api_lint: { - new_since: ":framework-sdkextensions.api.public.latest", - }, - }, -} - -droidstubs { - name: "framework-sdkextensions-stubs-srcs-systemapi", - defaults: [ - "framework-module-stubs-defaults-systemapi", - "framework-sdkextensions-stubs-defaults", - ], - check_api: { - last_released: { - api_file: ":framework-sdkextensions.api.system.latest", - removed_api_file: ":framework-sdkextensions-removed.api.system.latest", - }, - api_lint: { - new_since: ":framework-sdkextensions.api.system.latest", - }, - }, -} - -droidstubs { - name: "framework-sdkextensions-api-module_libs_api", - defaults: [ - "framework-module-api-defaults-module_libs_api", - "framework-sdkextensions-stubs-defaults", - ], - check_api: { - last_released: { - api_file: ":framework-sdkextensions.api.module-lib.latest", - removed_api_file: ":framework-sdkextensions-removed.api.module-lib.latest", - }, - api_lint: { - new_since: ":framework-sdkextensions.api.module-lib.latest", - }, - }, -} - -droidstubs { - name: "framework-sdkextensions-stubs-srcs-module_libs_api", - defaults: [ - "framework-module-stubs-defaults-module_libs_api", - "framework-sdkextensions-stubs-defaults", - ], -} - -java_library { - name: "framework-sdkextensions-stubs-publicapi", - srcs: [":framework-sdkextensions-stubs-srcs-publicapi"], - defaults: ["framework-module-stubs-lib-defaults-publicapi"], - visibility: [ - "//frameworks/base", // Framework - "//frameworks/base/apex/sdkextensions", // sdkextensions SDK - ], - dist: { dest: "framework-sdkextensions.jar" }, -} - -java_library { - name: "framework-sdkextensions-stubs-systemapi", - srcs: [":framework-sdkextensions-stubs-srcs-systemapi"], - defaults: ["framework-module-stubs-lib-defaults-systemapi"], - visibility: [ - "//frameworks/base", // Framework - "//frameworks/base/apex/sdkextensions", // sdkextensions SDK - ], - dist: { dest: "framework-sdkextensions.jar" }, -} - -java_library { - name: "framework-sdkextensions-stubs-module_libs_api", - srcs: [":framework-sdkextensions-stubs-srcs-module_libs_api"], - defaults: ["framework-module-stubs-lib-defaults-module_libs_api"], - visibility: [ - "//frameworks/base", // Framework - "//frameworks/base/apex/sdkextensions", // sdkextensions SDK - ], - dist: { dest: "framework-sdkextensions.jar" }, -} diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 926d8fc5a368..0410c9024dcb 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -336,7 +336,7 @@ interface IWindowSession { * an input channel where the client can receive input. */ void grantInputChannel(int displayId, in SurfaceControl surface, in IWindow window, - in IBinder hostInputToken, int flags, out InputChannel outInputChannel); + in IBinder hostInputToken, int flags, int type, out InputChannel outInputChannel); /** * Update the flags on an input channel associated with a particular surface. diff --git a/core/java/android/view/ImeFocusController.java b/core/java/android/view/ImeFocusController.java index dbbe4b61c81c..a4800726bbe8 100644 --- a/core/java/android/view/ImeFocusController.java +++ b/core/java/android/view/ImeFocusController.java @@ -208,26 +208,6 @@ public final class ImeFocusController { } /** - * Called by {@link ViewRootImpl} to feedback the state of the screen for this view. - * @param newScreenState The new state of the screen. Can be either - * {@link View#SCREEN_STATE_ON} or {@link View#SCREEN_STATE_OFF} - */ - @UiThread - void onScreenStateChanged(int newScreenState) { - if (!getImmDelegate().isCurrentRootView(mViewRootImpl)) { - return; - } - // Close input connection and IME when the screen is turn off for security concern. - if (newScreenState == View.SCREEN_STATE_OFF && mServedView != null) { - if (DEBUG) { - Log.d(TAG, "onScreenStateChanged, disconnect input when screen turned off"); - } - mNextServedView = null; - mViewRootImpl.dispatchCheckFocus(); - } - } - - /** * @param windowAttribute {@link WindowManager.LayoutParams} to be checked. * @return Whether the window is in local focus mode or not. */ diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java index 7086dc09c8a3..385078165e84 100644 --- a/core/java/android/view/SurfaceControlViewHost.java +++ b/core/java/android/view/SurfaceControlViewHost.java @@ -275,12 +275,4 @@ public class SurfaceControlViewHost { // ViewRoot will release mSurfaceControl for us. mViewRoot.die(false /* immediate */); } - - /** - * Tell this viewroot to clean itself up. - * @hide - */ - public void die() { - mViewRoot.die(false /* immediate */); - } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 5b9cd7786a39..d36a2381f159 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1496,7 +1496,6 @@ public final class ViewRootImpl implements ViewParent, final int newScreenState = toViewScreenState(newDisplayState); if (oldScreenState != newScreenState) { mView.dispatchScreenStateChanged(newScreenState); - mImeFocusController.onScreenStateChanged(newScreenState); } if (oldDisplayState == Display.STATE_OFF) { // Draw was suppressed so we need to for it to happen here. diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java index cd954c41f469..d20ffb3a6ec1 100644 --- a/core/java/android/view/WindowlessWindowManager.java +++ b/core/java/android/view/WindowlessWindowManager.java @@ -143,7 +143,7 @@ public class WindowlessWindowManager implements IWindowSession { WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0)) { try { mRealWm.grantInputChannel(displayId, sc, window, mHostInputToken, attrs.flags, - outInputChannel); + attrs.type, outInputChannel); } catch (RemoteException e) { Log.e(TAG, "Failed to grant input to surface: ", e); } @@ -432,7 +432,7 @@ public class WindowlessWindowManager implements IWindowSession { @Override public void grantInputChannel(int displayId, SurfaceControl surface, IWindow window, - IBinder hostInputToken, int flags, InputChannel outInputChannel) { + IBinder hostInputToken, int flags, int type, InputChannel outInputChannel) { } @Override diff --git a/core/res/res/layout/resolver_empty_states.xml b/core/res/res/layout/resolver_empty_states.xml index fe11769e8613..196a0e865405 100644 --- a/core/res/res/layout/resolver_empty_states.xml +++ b/core/res/res/layout/resolver_empty_states.xml @@ -59,7 +59,7 @@ <Button android:id="@+id/resolver_empty_state_button" android:layout_below="@+id/resolver_empty_state_subtitle" - android:layout_marginTop="16dp" + android:layout_marginTop="8dp" android:text="@string/resolver_switch_on_work" android:layout_width="wrap_content" android:layout_height="wrap_content" diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java index 1d06df08bd3a..008a433bb7a4 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java @@ -475,5 +475,10 @@ public class InfoMediaManager extends MediaManager { public void onRequestFailed(int reason) { dispatchOnRequestFailed(reason); } + + @Override + public void onSessionUpdated(RoutingSessionInfo sessionInfo) { + dispatchDataChanged(); + } } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java index 99c568a4707b..734866f49a53 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java @@ -625,6 +625,15 @@ public class InfoMediaManagerTest { } @Test + public void onSessionUpdated_shouldDispatchDataChanged() { + mInfoMediaManager.registerCallback(mCallback); + + mInfoMediaManager.mMediaRouterCallback.onSessionUpdated(mock(RoutingSessionInfo.class)); + + verify(mCallback).onDeviceAttributesChanged(); + } + + @Test public void addMediaDevice_verifyDeviceTypeCanCorrespondToMediaDevice() { final MediaRoute2Info route2Info = mock(MediaRoute2Info.class); final CachedBluetoothDeviceManager cachedBluetoothDeviceManager = diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestReceiver.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestReceiver.java index 8bd7c790682d..30156a0cd6f1 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestReceiver.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestReceiver.java @@ -58,7 +58,7 @@ public class SurfaceViewRequestReceiver { */ public void onReceive(Context context, Bundle bundle, View view, Size viewSize) { if (mSurfaceControlViewHost != null) { - mSurfaceControlViewHost.die(); + mSurfaceControlViewHost.release(); } SurfaceControl surfaceControl = SurfaceViewRequestUtils.getSurfaceControl(bundle); diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java index fa45ea1acb95..1a53c28c0fc5 100644 --- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java @@ -134,7 +134,7 @@ class ImageGLWallpaper { private void setupTexture(Bitmap bitmap) { final int[] tids = new int[1]; - if (bitmap == null) { + if (bitmap == null || bitmap.isRecycled()) { Log.w(TAG, "setupTexture: invalid bitmap"); return; } @@ -146,16 +146,20 @@ class ImageGLWallpaper { return; } - // Bind a named texture to a target. - glBindTexture(GL_TEXTURE_2D, tids[0]); - // Load the bitmap data and copy it over into the texture object that is currently bound. - GLUtils.texImage2D(GL_TEXTURE_2D, 0, bitmap, 0); - // Use bilinear texture filtering when minification. - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - // Use bilinear texture filtering when magnification. - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - mTextureId = tids[0]; + try { + // Bind a named texture to a target. + glBindTexture(GL_TEXTURE_2D, tids[0]); + // Load the bitmap data and copy it over into the texture object + // that is currently bound. + GLUtils.texImage2D(GL_TEXTURE_2D, 0, bitmap, 0); + // Use bilinear texture filtering when minification. + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + // Use bilinear texture filtering when magnification. + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + mTextureId = tids[0]; + } catch (IllegalArgumentException e) { + Log.w(TAG, "Failed uploading texture: " + e.getLocalizedMessage()); + } } void useTexture() { diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java index 046837346d3a..e302fd73d785 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -815,6 +815,7 @@ public class PipTouchHandler { private class DefaultPipTouchGesture extends PipTouchGesture { private final Point mStartPosition = new Point(); private final PointF mDelta = new PointF(); + private boolean mShouldHideMenuAfterFling; @Override public void onDown(PipTouchState touchState) { @@ -892,21 +893,17 @@ public class PipTouchHandler { final float velocity = PointF.length(vel.x, vel.y); if (touchState.isDragging()) { - Runnable endAction = null; if (mMenuState != MENU_STATE_NONE) { // If the menu is still visible, then just poke the menu so that // it will timeout after the user stops touching it mMenuController.showMenu(mMenuState, mMotionHelper.getBounds(), true /* allowMenuTimeout */, willResizeMenu()); - } else { - // If the menu is not visible, then we can still be showing the activity for the - // dismiss overlay, so just finish it after the animation completes - endAction = mMenuController::hideMenu; } + mShouldHideMenuAfterFling = mMenuState == MENU_STATE_NONE; mMotionHelper.flingToSnapTarget(vel.x, vel.y, PipTouchHandler.this::updateDismissFraction /* updateAction */, - endAction /* endAction */); + this::flingEndAction /* endAction */); } else if (mTouchState.isDoubleTap()) { // Expand to fullscreen if this is a double tap // the PiP should be frozen until the transition ends @@ -927,6 +924,15 @@ public class PipTouchHandler { } return true; } + + private void flingEndAction() { + mTouchState.setAllowTouches(true); + if (mShouldHideMenuAfterFling) { + // If the menu is not visible, then we can still be showing the activity for the + // dismiss overlay, so just finish it after the animation completes + mMenuController.hideMenu(); + } + } }; /** diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java index 02a7aca38abe..db33c79be773 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java @@ -592,13 +592,11 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, removeDivider(); addDivider(configuration); - if (mView != null) { - if (mMinimized) { - mView.setMinimizedDockStack(true, mHomeStackResizable); - updateTouchable(); - } - mView.setHidden(isDividerHidden); + if (mMinimized) { + mView.setMinimizedDockStack(true, mHomeStackResizable); + updateTouchable(); } + mView.setHidden(isDividerHidden); } void onTaskVanished() { @@ -610,7 +608,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, mContext.getDisplayId()).getResources().getConfiguration())); } - void updateVisibility(final boolean visible) { + private void updateVisibility(final boolean visible) { if (DEBUG) Slog.d(TAG, "Updating visibility " + mVisible + "->" + visible); if (mVisible != visible) { mVisible = visible; @@ -639,6 +637,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, void onSplitDismissed() { mMinimized = false; updateVisibility(false /* visible */); + removeDivider(); } /** Switch to minimized state if appropriate */ @@ -788,6 +787,8 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, } void startEnterSplit() { + update(mDisplayController.getDisplayContext( + mContext.getDisplayId()).getResources().getConfiguration()); // Set resizable directly here because applyEnterSplit already resizes home stack. mHomeStackResizable = WindowManagerProxy.applyEnterSplit(mSplits, mSplitLayout); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java index 2e4a929ded5b..926c1bd7c271 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java @@ -321,8 +321,7 @@ public class NotificationShadeWindowController implements Callback, Dumpable, || state.mPanelVisible || state.mKeyguardFadingAway || state.mBouncerShowing || state.mHeadsUpShowing || state.mScrimsVisibility != ScrimController.TRANSPARENT) - || state.mBackgroundBlurRadius > 0 - || state.mLaunchingActivity; + || state.mBackgroundBlurRadius > 0; } private void applyFitsSystemWindows(State state) { @@ -486,11 +485,6 @@ public class NotificationShadeWindowController implements Callback, Dumpable, apply(mCurrentState); } - void setLaunchingActivity(boolean launching) { - mCurrentState.mLaunchingActivity = launching; - apply(mCurrentState); - } - public void setScrimsVisibility(int scrimsVisibility) { mCurrentState.mScrimsVisibility = scrimsVisibility; apply(mCurrentState); @@ -651,7 +645,6 @@ public class NotificationShadeWindowController implements Callback, Dumpable, boolean mForceCollapsed; boolean mForceDozeBrightness; boolean mForceUserActivity; - boolean mLaunchingActivity; boolean mBackdropShowing; boolean mWallpaperSupportsAmbientMode; boolean mNotTouchable; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java index 0d2589847bcb..596a607bb8ad 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java @@ -93,7 +93,6 @@ public class NotificationShadeWindowViewController { private PhoneStatusBarView mStatusBarView; private PhoneStatusBarTransitions mBarTransitions; private StatusBar mService; - private NotificationShadeWindowController mNotificationShadeWindowController; private DragDownHelper mDragDownHelper; private boolean mDoubleTapEnabled; private boolean mSingleTapEnabled; @@ -431,14 +430,10 @@ public class NotificationShadeWindowViewController { public void setExpandAnimationPending(boolean pending) { mExpandAnimationPending = pending; - mNotificationShadeWindowController - .setLaunchingActivity(mExpandAnimationPending | mExpandAnimationRunning); } public void setExpandAnimationRunning(boolean running) { mExpandAnimationRunning = running; - mNotificationShadeWindowController - .setLaunchingActivity(mExpandAnimationPending | mExpandAnimationRunning); } public void cancelExpandHelper() { @@ -461,9 +456,8 @@ public class NotificationShadeWindowViewController { } } - public void setService(StatusBar statusBar, NotificationShadeWindowController controller) { + public void setService(StatusBar statusBar) { mService = statusBar; - mNotificationShadeWindowController = controller; } @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index dd54a3d800fb..bbf83bc2057a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -1001,7 +1001,7 @@ public class StatusBar extends SystemUI implements DemoMode, updateTheme(); inflateStatusBarWindow(); - mNotificationShadeWindowViewController.setService(this, mNotificationShadeWindowController); + mNotificationShadeWindowViewController.setService(this); mNotificationShadeWindowView.setOnTouchListener(getStatusBarWindowTouchListener()); // TODO: Deal with the ugliness that comes from having some of the statusbar broken out diff --git a/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java b/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java index 93f45c51f0eb..21f67aef5604 100644 --- a/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java +++ b/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java @@ -125,7 +125,7 @@ public class SystemWindows { */ public void removeView(View view) { SurfaceControlViewHost root = mViewRoots.remove(view); - root.die(); + root.release(); } /** diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt index ca7a5dbdc36d..8948fd07379b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt @@ -74,7 +74,7 @@ class NotificationSectionsFeatureManagerTest : SysuiTestCase() { DeviceConfig.NAMESPACE_SYSTEMUI, NOTIFICATIONS_USE_PEOPLE_FILTERING, "true", false) assertTrue("People filtering should be enabled", manager!!.isFilteringEnabled()) - assertTrue("Expecting 4 buckets when people filtering is enabled", - manager!!.getNumberOfBuckets() == 4) + assertTrue("Expecting 5 buckets when people filtering is enabled", + manager!!.getNumberOfBuckets() == 5) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java index e04d25b17c71..cc2d1c25de38 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java @@ -83,7 +83,6 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { @Mock private NotificationStackScrollLayout mNotificationStackScrollLayout; @Mock private NotificationShadeDepthController mNotificationShadeDepthController; @Mock private SuperStatusBarViewFactory mStatusBarViewFactory; - @Mock private NotificationShadeWindowController mNotificationShadeWindowController; @Before public void setUp() { @@ -122,7 +121,7 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { mNotificationPanelViewController, mStatusBarViewFactory); mController.setupExpandedStatusBar(); - mController.setService(mStatusBar, mNotificationShadeWindowController); + mController.setService(mStatusBar); mController.setDragDownHelper(mDragDownHelper); } diff --git a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java index 2a5e6200e5db..04ad43f6e2d8 100644 --- a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -109,8 +109,10 @@ import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceSpecificException; +import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; +import android.provider.Settings; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; import android.text.TextUtils; @@ -228,6 +230,7 @@ public class Tethering { private final ConnectedClientsTracker mConnectedClientsTracker; private final TetheringThreadExecutor mExecutor; private final TetheringNotificationUpdater mNotificationUpdater; + private final UserManager mUserManager; private int mActiveDataSubId = INVALID_SUBSCRIPTION_ID; // All the usage of mTetheringEventCallback should run in the same thread. private ITetheringEventCallback mTetheringEventCallback = null; @@ -305,23 +308,24 @@ public class Tethering { mStateReceiver = new StateReceiver(); - final UserManager userManager = (UserManager) mContext.getSystemService( - Context.USER_SERVICE); + mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); mTetheringRestriction = new UserRestrictionActionListener( - userManager, this, mNotificationUpdater); + mUserManager, this, mNotificationUpdater); mExecutor = new TetheringThreadExecutor(mHandler); mActiveDataSubIdListener = new ActiveDataSubIdListener(mExecutor); mNetdCallback = new NetdCallback(); // Load tethering configuration. updateConfiguration(); + + startStateMachineUpdaters(); } /** * Start to register callbacks. * Call this function when tethering is ready to handle callback events. */ - public void startStateMachineUpdaters() { + private void startStateMachineUpdaters() { try { mNetd.registerUnsolicitedEventListener(mNetdCallback); } catch (RemoteException e) { @@ -779,7 +783,7 @@ public class Tethering { // TODO: Figure out how to update for local hotspot mode interfaces. private void sendTetherStateChangedBroadcast() { - if (!mDeps.isTetheringSupported()) return; + if (!isTetheringSupported()) return; final ArrayList<String> availableList = new ArrayList<>(); final ArrayList<String> tetherList = new ArrayList<>(); @@ -1020,14 +1024,14 @@ public class Tethering { @VisibleForTesting protected static class UserRestrictionActionListener { - private final UserManager mUserManager; + private final UserManager mUserMgr; private final Tethering mWrapper; private final TetheringNotificationUpdater mNotificationUpdater; public boolean mDisallowTethering; public UserRestrictionActionListener(@NonNull UserManager um, @NonNull Tethering wrapper, @NonNull TetheringNotificationUpdater updater) { - mUserManager = um; + mUserMgr = um; mWrapper = wrapper; mNotificationUpdater = updater; mDisallowTethering = false; @@ -1037,7 +1041,7 @@ public class Tethering { // getUserRestrictions gets restriction for this process' user, which is the primary // user. This is fine because DISALLOW_CONFIG_TETHERING can only be set on the primary // user. See UserManager.DISALLOW_CONFIG_TETHERING. - final Bundle restrictions = mUserManager.getUserRestrictions(); + final Bundle restrictions = mUserMgr.getUserRestrictions(); final boolean newlyDisallowed = restrictions.getBoolean(UserManager.DISALLOW_CONFIG_TETHERING); final boolean prevDisallowed = mDisallowTethering; @@ -1988,7 +1992,7 @@ public class Tethering { mHandler.post(() -> { mTetheringEventCallbacks.register(callback, new CallbackCookie(hasListPermission)); final TetheringCallbackStartedParcel parcel = new TetheringCallbackStartedParcel(); - parcel.tetheringSupported = mDeps.isTetheringSupported(); + parcel.tetheringSupported = isTetheringSupported(); parcel.upstreamNetwork = mTetherUpstream; parcel.config = mConfig.toStableParcelable(); parcel.states = @@ -2111,6 +2115,20 @@ public class Tethering { } } + // if ro.tether.denied = true we default to no tethering + // gservices could set the secure setting to 1 though to enable it on a build where it + // had previously been turned off. + boolean isTetheringSupported() { + final int defaultVal = + SystemProperties.get("ro.tether.denied").equals("true") ? 0 : 1; + final boolean tetherSupported = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.TETHER_SUPPORTED, defaultVal) != 0; + final boolean tetherEnabledInSettings = tetherSupported + && !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING); + + return tetherEnabledInSettings && hasTetherableConfiguration(); + } + void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer, @Nullable String[] args) { // Binder.java closes the resource for us. @SuppressWarnings("resource") diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringService.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringService.java index d07c555f66cd..bf7fb042ed3f 100644 --- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringService.java +++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringService.java @@ -40,15 +40,12 @@ import android.net.TetheringRequestParcel; import android.net.dhcp.DhcpServerCallbacks; import android.net.dhcp.DhcpServingParamsParcel; import android.net.ip.IpServer; -import android.net.util.SharedLog; import android.os.Binder; import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.os.ResultReceiver; -import android.os.SystemProperties; -import android.os.UserManager; import android.provider.Settings; import android.util.Log; @@ -68,21 +65,14 @@ import java.io.PrintWriter; public class TetheringService extends Service { private static final String TAG = TetheringService.class.getSimpleName(); - private final SharedLog mLog = new SharedLog(TAG); private TetheringConnector mConnector; - private Context mContext; - private TetheringDependencies mDeps; - private Tethering mTethering; - private UserManager mUserManager; @Override public void onCreate() { - mLog.mark("onCreate"); - mDeps = getTetheringDependencies(); - mContext = mDeps.getContext(); - mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - mTethering = makeTethering(mDeps); - mTethering.startStateMachineUpdaters(); + final TetheringDependencies deps = makeTetheringDependencies(); + // The Tethering object needs a fully functional context to start, so this can't be done + // in the constructor. + mConnector = new TetheringConnector(makeTethering(deps), TetheringService.this); } /** @@ -94,21 +84,10 @@ public class TetheringService extends Service { return new Tethering(deps); } - /** - * Create a binder connector for the system server to communicate with the tethering. - */ - private synchronized IBinder makeConnector() { - if (mConnector == null) { - mConnector = new TetheringConnector(mTethering, TetheringService.this); - } - return mConnector; - } - @NonNull @Override public IBinder onBind(Intent intent) { - mLog.mark("onBind"); - return makeConnector(); + return mConnector; } private static class TetheringConnector extends ITetheringConnector.Stub { @@ -237,7 +216,7 @@ public class TetheringService extends Service { listener.onResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); return true; } - if (!mService.isTetheringSupported()) { + if (!mTethering.isTetheringSupported()) { listener.onResult(TETHER_ERROR_UNSUPPORTED); return true; } @@ -253,7 +232,7 @@ public class TetheringService extends Service { receiver.send(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION, null); return true; } - if (!mService.isTetheringSupported()) { + if (!mTethering.isTetheringSupported()) { receiver.send(TETHER_ERROR_UNSUPPORTED, null); return true; } @@ -287,105 +266,83 @@ public class TetheringService extends Service { } } - // if ro.tether.denied = true we default to no tethering - // gservices could set the secure setting to 1 though to enable it on a build where it - // had previously been turned off. - private boolean isTetheringSupported() { - final int defaultVal = - SystemProperties.get("ro.tether.denied").equals("true") ? 0 : 1; - final boolean tetherSupported = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.TETHER_SUPPORTED, defaultVal) != 0; - final boolean tetherEnabledInSettings = tetherSupported - && !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING); - - return tetherEnabledInSettings && mTethering.hasTetherableConfiguration(); - } - /** * An injection method for testing. */ @VisibleForTesting - public TetheringDependencies getTetheringDependencies() { - if (mDeps == null) { - mDeps = new TetheringDependencies() { - @Override - public NetworkRequest getDefaultNetworkRequest() { - // TODO: b/147280869, add a proper system API to replace this. - final NetworkRequest trackDefaultRequest = new NetworkRequest.Builder() - .clearCapabilities() - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) - .addCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) - .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) - .build(); - return trackDefaultRequest; - } - - @Override - public Looper getTetheringLooper() { - final HandlerThread tetherThread = new HandlerThread("android.tethering"); - tetherThread.start(); - return tetherThread.getLooper(); - } + public TetheringDependencies makeTetheringDependencies() { + return new TetheringDependencies() { + @Override + public NetworkRequest getDefaultNetworkRequest() { + // TODO: b/147280869, add a proper system API to replace this. + final NetworkRequest trackDefaultRequest = new NetworkRequest.Builder() + .clearCapabilities() + .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) + .addCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED) + .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) + .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .build(); + return trackDefaultRequest; + } - @Override - public boolean isTetheringSupported() { - return TetheringService.this.isTetheringSupported(); - } + @Override + public Looper getTetheringLooper() { + final HandlerThread tetherThread = new HandlerThread("android.tethering"); + tetherThread.start(); + return tetherThread.getLooper(); + } - @Override - public Context getContext() { - return TetheringService.this; - } + @Override + public Context getContext() { + return TetheringService.this; + } - @Override - public IpServer.Dependencies getIpServerDependencies() { - return new IpServer.Dependencies() { - @Override - public void makeDhcpServer(String ifName, DhcpServingParamsParcel params, - DhcpServerCallbacks cb) { + @Override + public IpServer.Dependencies getIpServerDependencies() { + return new IpServer.Dependencies() { + @Override + public void makeDhcpServer(String ifName, DhcpServingParamsParcel params, + DhcpServerCallbacks cb) { + try { + final INetworkStackConnector service = getNetworkStackConnector(); + if (service == null) return; + + service.makeDhcpServer(ifName, params, cb); + } catch (RemoteException e) { + Log.e(TAG, "Fail to make dhcp server"); try { - final INetworkStackConnector service = getNetworkStackConnector(); - if (service == null) return; - - service.makeDhcpServer(ifName, params, cb); - } catch (RemoteException e) { - Log.e(TAG, "Fail to make dhcp server"); - try { - cb.onDhcpServerCreated(STATUS_UNKNOWN_ERROR, null); - } catch (RemoteException re) { } - } + cb.onDhcpServerCreated(STATUS_UNKNOWN_ERROR, null); + } catch (RemoteException re) { } } - }; - } + } + }; + } - // TODO: replace this by NetworkStackClient#getRemoteConnector after refactoring - // networkStackClient. - static final int NETWORKSTACK_TIMEOUT_MS = 60_000; - private INetworkStackConnector getNetworkStackConnector() { - IBinder connector; - try { - final long before = System.currentTimeMillis(); - while ((connector = NetworkStack.getService()) == null) { - if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) { - Log.wtf(TAG, "Timeout, fail to get INetworkStackConnector"); - return null; - } - Thread.sleep(200); + // TODO: replace this by NetworkStackClient#getRemoteConnector after refactoring + // networkStackClient. + static final int NETWORKSTACK_TIMEOUT_MS = 60_000; + private INetworkStackConnector getNetworkStackConnector() { + IBinder connector; + try { + final long before = System.currentTimeMillis(); + while ((connector = NetworkStack.getService()) == null) { + if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) { + Log.wtf(TAG, "Timeout, fail to get INetworkStackConnector"); + return null; } - } catch (InterruptedException e) { - Log.wtf(TAG, "Interrupted, fail to get INetworkStackConnector"); - return null; + Thread.sleep(200); } - return INetworkStackConnector.Stub.asInterface(connector); + } catch (InterruptedException e) { + Log.wtf(TAG, "Interrupted, fail to get INetworkStackConnector"); + return null; } + return INetworkStackConnector.Stub.asInterface(connector); + } - @Override - public BluetoothAdapter getBluetoothAdapter() { - return BluetoothAdapter.getDefaultAdapter(); - } - }; - } - return mDeps; + @Override + public BluetoothAdapter getBluetoothAdapter() { + return BluetoothAdapter.getDefaultAdapter(); + } + }; } } diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java index 51bad9af23ef..4a667b1bdc41 100644 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java +++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java @@ -82,8 +82,7 @@ public final class TetheringServiceTest { mTetheringConnector = mockConnector.getTetheringConnector(); final MockTetheringService service = mockConnector.getService(); mTethering = service.getTethering(); - verify(mTethering).startStateMachineUpdaters(); - when(mTethering.hasTetherableConfiguration()).thenReturn(true); + when(mTethering.isTetheringSupported()).thenReturn(true); } @After @@ -96,7 +95,7 @@ public final class TetheringServiceTest { when(mTethering.tether(TEST_IFACE_NAME)).thenReturn(TETHER_ERROR_NO_ERROR); final TestTetheringResult result = new TestTetheringResult(); mTetheringConnector.tether(TEST_IFACE_NAME, TEST_CALLER_PKG, result); - verify(mTethering).hasTetherableConfiguration(); + verify(mTethering).isTetheringSupported(); verify(mTethering).tether(TEST_IFACE_NAME); verifyNoMoreInteractions(mTethering); result.assertResult(TETHER_ERROR_NO_ERROR); @@ -107,7 +106,7 @@ public final class TetheringServiceTest { when(mTethering.untether(TEST_IFACE_NAME)).thenReturn(TETHER_ERROR_NO_ERROR); final TestTetheringResult result = new TestTetheringResult(); mTetheringConnector.untether(TEST_IFACE_NAME, TEST_CALLER_PKG, result); - verify(mTethering).hasTetherableConfiguration(); + verify(mTethering).isTetheringSupported(); verify(mTethering).untether(TEST_IFACE_NAME); verifyNoMoreInteractions(mTethering); result.assertResult(TETHER_ERROR_NO_ERROR); @@ -118,7 +117,7 @@ public final class TetheringServiceTest { when(mTethering.setUsbTethering(true /* enable */)).thenReturn(TETHER_ERROR_NO_ERROR); final TestTetheringResult result = new TestTetheringResult(); mTetheringConnector.setUsbTethering(true /* enable */, TEST_CALLER_PKG, result); - verify(mTethering).hasTetherableConfiguration(); + verify(mTethering).isTetheringSupported(); verify(mTethering).setUsbTethering(true /* enable */); verifyNoMoreInteractions(mTethering); result.assertResult(TETHER_ERROR_NO_ERROR); @@ -130,7 +129,7 @@ public final class TetheringServiceTest { final TetheringRequestParcel request = new TetheringRequestParcel(); request.tetheringType = TETHERING_WIFI; mTetheringConnector.startTethering(request, TEST_CALLER_PKG, result); - verify(mTethering).hasTetherableConfiguration(); + verify(mTethering).isTetheringSupported(); verify(mTethering).startTethering(eq(request), eq(result)); verifyNoMoreInteractions(mTethering); } @@ -139,7 +138,7 @@ public final class TetheringServiceTest { public void testStopTethering() throws Exception { final TestTetheringResult result = new TestTetheringResult(); mTetheringConnector.stopTethering(TETHERING_WIFI, TEST_CALLER_PKG, result); - verify(mTethering).hasTetherableConfiguration(); + verify(mTethering).isTetheringSupported(); verify(mTethering).stopTethering(TETHERING_WIFI); verifyNoMoreInteractions(mTethering); result.assertResult(TETHER_ERROR_NO_ERROR); @@ -150,7 +149,7 @@ public final class TetheringServiceTest { final ResultReceiver result = new ResultReceiver(null); mTetheringConnector.requestLatestTetheringEntitlementResult(TETHERING_WIFI, result, true /* showEntitlementUi */, TEST_CALLER_PKG); - verify(mTethering).hasTetherableConfiguration(); + verify(mTethering).isTetheringSupported(); verify(mTethering).requestLatestTetheringEntitlementResult(eq(TETHERING_WIFI), eq(result), eq(true) /* showEntitlementUi */); verifyNoMoreInteractions(mTethering); @@ -177,7 +176,7 @@ public final class TetheringServiceTest { public void testStopAllTethering() throws Exception { final TestTetheringResult result = new TestTetheringResult(); mTetheringConnector.stopAllTethering(TEST_CALLER_PKG, result); - verify(mTethering).hasTetherableConfiguration(); + verify(mTethering).isTetheringSupported(); verify(mTethering).untetherAll(); verifyNoMoreInteractions(mTethering); result.assertResult(TETHER_ERROR_NO_ERROR); @@ -187,7 +186,7 @@ public final class TetheringServiceTest { public void testIsTetheringSupported() throws Exception { final TestTetheringResult result = new TestTetheringResult(); mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, result); - verify(mTethering).hasTetherableConfiguration(); + verify(mTethering).isTetheringSupported(); verifyNoMoreInteractions(mTethering); result.assertResult(TETHER_ERROR_NO_ERROR); } diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java index 2bd8ae028814..00174e6d8b5c 100644 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java @@ -485,18 +485,6 @@ public class TetheringTest { MockitoAnnotations.initMocks(this); when(mResources.getStringArray(R.array.config_tether_dhcp_range)) .thenReturn(new String[0]); - when(mResources.getStringArray(R.array.config_tether_usb_regexs)) - .thenReturn(new String[] { "test_rndis\\d" }); - when(mResources.getStringArray(R.array.config_tether_wifi_regexs)) - .thenReturn(new String[]{ "test_wlan\\d" }); - when(mResources.getStringArray(R.array.config_tether_wifi_p2p_regexs)) - .thenReturn(new String[]{ "test_p2p-p2p\\d-.*" }); - when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs)) - .thenReturn(new String[0]); - when(mResources.getStringArray(R.array.config_tether_ncm_regexs)) - .thenReturn(new String[] { "test_ncm\\d" }); - when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[0]); - when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(false); when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( false); when(mNetd.interfaceGetList()) @@ -515,6 +503,7 @@ public class TetheringTest { mServiceContext = new TestContext(mContext); mContentResolver = new MockContentResolver(mServiceContext); mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); + setTetheringSupported(true /* supported */); mIntents = new Vector<>(); mBroadcastReceiver = new BroadcastReceiver() { @Override @@ -525,7 +514,6 @@ public class TetheringTest { mServiceContext.registerReceiver(mBroadcastReceiver, new IntentFilter(ACTION_TETHER_STATE_CHANGED)); mTethering = makeTethering(); - mTethering.startStateMachineUpdaters(); verify(mStatsManager, times(1)).registerNetworkStatsProvider(anyString(), any()); verify(mNetd).registerUnsolicitedEventListener(any()); final ArgumentCaptor<PhoneStateListener> phoneListenerCaptor = @@ -536,6 +524,31 @@ public class TetheringTest { mPhoneStateListener = phoneListenerCaptor.getValue(); } + private void setTetheringSupported(final boolean supported) { + Settings.Global.putInt(mContentResolver, Settings.Global.TETHER_SUPPORTED, + supported ? 1 : 0); + when(mUserManager.hasUserRestriction( + UserManager.DISALLOW_CONFIG_TETHERING)).thenReturn(!supported); + // Setup tetherable configuration. + when(mResources.getStringArray(R.array.config_tether_usb_regexs)) + .thenReturn(new String[] { "test_rndis\\d" }); + when(mResources.getStringArray(R.array.config_tether_wifi_regexs)) + .thenReturn(new String[]{ "test_wlan\\d" }); + when(mResources.getStringArray(R.array.config_tether_wifi_p2p_regexs)) + .thenReturn(new String[]{ "test_p2p-p2p\\d-.*" }); + when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs)) + .thenReturn(new String[0]); + when(mResources.getStringArray(R.array.config_tether_ncm_regexs)) + .thenReturn(new String[] { "test_ncm\\d" }); + when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[0]); + when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(true); + } + + private void initTetheringUpstream(UpstreamNetworkState upstreamState) { + when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState); + when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())).thenReturn(upstreamState); + } + private Tethering makeTethering() { mTetheringDependencies.reset(); return new Tethering(mTetheringDependencies); @@ -672,9 +685,7 @@ public class TetheringTest { } private void prepareUsbTethering(UpstreamNetworkState upstreamState) { - when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState); - when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())) - .thenReturn(upstreamState); + initTetheringUpstream(upstreamState); // Emulate pressing the USB tethering button in Settings UI. mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB), null); @@ -700,7 +711,7 @@ public class TetheringTest { verify(mNetd, times(1)).interfaceGetList(); // UpstreamNetworkMonitor should receive selected upstream - verify(mUpstreamNetworkMonitor, times(1)).selectPreferredUpstreamType(any()); + verify(mUpstreamNetworkMonitor, times(1)).getCurrentPreferredUpstream(); verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(upstreamState.network); } @@ -872,8 +883,7 @@ public class TetheringTest { // Then 464xlat comes up upstreamState = buildMobile464xlatUpstreamState(); - when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())) - .thenReturn(upstreamState); + initTetheringUpstream(upstreamState); // Upstream LinkProperties changed: UpstreamNetworkMonitor sends EVENT_ON_LINKPROPERTIES. mTetheringDependencies.mUpstreamNetworkMonitorMasterSM.sendMessage( @@ -1344,9 +1354,7 @@ public class TetheringTest { callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED); // 2. Enable wifi tethering. UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState(); - when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState); - when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())) - .thenReturn(upstreamState); + initTetheringUpstream(upstreamState); when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true); mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); mLooper.dispatchAll(); @@ -1723,7 +1731,7 @@ public class TetheringTest { final Tethering.TetherMasterSM stateMachine = (Tethering.TetherMasterSM) mTetheringDependencies.mUpstreamNetworkMonitorMasterSM; final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState(); - when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())).thenReturn(upstreamState); + initTetheringUpstream(upstreamState); stateMachine.chooseUpstreamType(true); verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(eq(upstreamState.network)); @@ -1735,7 +1743,7 @@ public class TetheringTest { final Tethering.TetherMasterSM stateMachine = (Tethering.TetherMasterSM) mTetheringDependencies.mUpstreamNetworkMonitorMasterSM; final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState(); - when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())).thenReturn(upstreamState); + initTetheringUpstream(upstreamState); stateMachine.chooseUpstreamType(true); stateMachine.handleUpstreamNetworkMonitorCallback(EVENT_ON_CAPABILITIES, upstreamState); diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java index a14584a63bb2..4b89731b75b6 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java @@ -20,9 +20,9 @@ import static android.view.MotionEvent.INVALID_POINTER_ID; import static com.android.server.accessibility.gestures.GestureUtils.MM_PER_CM; import static com.android.server.accessibility.gestures.GestureUtils.getActionIndex; -import static com.android.server.accessibility.gestures.Swipe.CANCEL_ON_PAUSE_THRESHOLD_NOT_STARTED_MS; -import static com.android.server.accessibility.gestures.Swipe.CANCEL_ON_PAUSE_THRESHOLD_STARTED_MS; import static com.android.server.accessibility.gestures.Swipe.GESTURE_CONFIRM_CM; +import static com.android.server.accessibility.gestures.Swipe.MAX_TIME_TO_CONTINUE_SWIPE_MS; +import static com.android.server.accessibility.gestures.Swipe.MAX_TIME_TO_START_SWIPE_MS; import static com.android.server.accessibility.gestures.TouchExplorer.DEBUG; import android.content.Context; @@ -387,10 +387,10 @@ class MultiFingerSwipe extends GestureMatcher { cancelPendingTransitions(); switch (getState()) { case STATE_CLEAR: - cancelAfter(CANCEL_ON_PAUSE_THRESHOLD_NOT_STARTED_MS, event, rawEvent, policyFlags); + cancelAfter(MAX_TIME_TO_START_SWIPE_MS, event, rawEvent, policyFlags); break; case STATE_GESTURE_STARTED: - cancelAfter(CANCEL_ON_PAUSE_THRESHOLD_STARTED_MS, event, rawEvent, policyFlags); + cancelAfter(MAX_TIME_TO_CONTINUE_SWIPE_MS, event, rawEvent, policyFlags); break; default: break; diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/Swipe.java b/services/accessibility/java/com/android/server/accessibility/gestures/Swipe.java index 9108c69ce1f6..041b4243293e 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/Swipe.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/Swipe.java @@ -49,11 +49,10 @@ class Swipe extends GestureMatcher { // Buffer for storing points for gesture detection. private final ArrayList<PointF> mStrokeBuffer = new ArrayList<>(100); - // The minimal delta between moves to add a gesture point. - private static final int TOUCH_TOLERANCE_PIX = 3; - - // The minimal score for accepting a predicted gesture. - private static final float MIN_PREDICTION_SCORE = 2.0f; + // Constants for sampling motion event points. + // We sample based on a minimum distance between points, primarily to improve accuracy by + // reducing noisy minor changes in direction. + private static final float MIN_CM_BETWEEN_SAMPLES = 0.25f; // Distance a finger must travel before we decide if it is a gesture or not. public static final int GESTURE_CONFIRM_CM = 1; @@ -67,22 +66,19 @@ class Swipe extends GestureMatcher { // all gestures started with the initial movement taking less than 100ms. // When touch exploring, the first movement almost always takes longer than // 200ms. - public static final long CANCEL_ON_PAUSE_THRESHOLD_NOT_STARTED_MS = 150; + public static final long MAX_TIME_TO_START_SWIPE_MS = 150 * GESTURE_CONFIRM_CM; // Time threshold used to determine if a gesture should be cancelled. If - // the finger takes more than this time to move 1cm, the ongoing gesture is - // cancelled. - public static final long CANCEL_ON_PAUSE_THRESHOLD_STARTED_MS = 300; + // the finger takes more than this time to move to the next sample point, the ongoing gesture + // is cancelled. + public static final long MAX_TIME_TO_CONTINUE_SWIPE_MS = 350 * GESTURE_CONFIRM_CM; private int[] mDirections; private float mBaseX; private float mBaseY; + private long mBaseTime; private float mPreviousGestureX; private float mPreviousGestureY; - // Constants for sampling motion event points. - // We sample based on a minimum distance between points, primarily to improve accuracy by - // reducing noisy minor changes in direction. - private static final float MIN_CM_BETWEEN_SAMPLES = 0.25f; private final float mMinPixelsBetweenSamplesX; private final float mMinPixelsBetweenSamplesY; // The minmimum distance the finger must travel before we evaluate the initial direction of the @@ -134,16 +130,19 @@ class Swipe extends GestureMatcher { protected void clear() { mBaseX = Float.NaN; mBaseY = Float.NaN; + mBaseTime = 0; + mPreviousGestureX = Float.NaN; + mPreviousGestureY = Float.NaN; mStrokeBuffer.clear(); super.clear(); } @Override protected void onDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) { - cancelAfterPauseThreshold(event, rawEvent, policyFlags); if (Float.isNaN(mBaseX) && Float.isNaN(mBaseY)) { mBaseX = rawEvent.getX(); mBaseY = rawEvent.getY(); + mBaseTime = rawEvent.getEventTime(); mPreviousGestureX = mBaseX; mPreviousGestureY = mBaseY; } @@ -154,9 +153,11 @@ class Swipe extends GestureMatcher { protected void onMove(MotionEvent event, MotionEvent rawEvent, int policyFlags) { final float x = rawEvent.getX(); final float y = rawEvent.getY(); + final long time = rawEvent.getEventTime(); final float dX = Math.abs(x - mPreviousGestureX); final float dY = Math.abs(y - mPreviousGestureY); final double moveDelta = Math.hypot(Math.abs(x - mBaseX), Math.abs(y - mBaseY)); + final long timeDelta = time - mBaseTime; if (DEBUG) { Slog.d( getGestureName(), @@ -171,34 +172,38 @@ class Swipe extends GestureMatcher { return; } else if (mStrokeBuffer.size() == 0) { // First, make sure the pointer is going in the right direction. - cancelAfterPauseThreshold(event, rawEvent, policyFlags); int direction = toDirection(x - mBaseX, y - mBaseY); if (direction != mDirections[0]) { cancelGesture(event, rawEvent, policyFlags); return; - } else { - // This is confirmed to be some kind of swipe so start tracking points. - mStrokeBuffer.add(new PointF(mBaseX, mBaseY)); - } - } - if (moveDelta > mGestureDetectionThresholdPixels) { - // If the pointer has moved more than the threshold, - // update the stored values. - mBaseX = x; - mBaseY = y; - if (getState() == STATE_CLEAR) { - startGesture(event, rawEvent, policyFlags); - cancelAfterPauseThreshold(event, rawEvent, policyFlags); } + // This is confirmed to be some kind of swipe so start tracking points. + mStrokeBuffer.add(new PointF(mBaseX, mBaseY)); } } - if (getState() == STATE_GESTURE_STARTED) { - if (dX >= mMinPixelsBetweenSamplesX || dY >= mMinPixelsBetweenSamplesY) { - mPreviousGestureX = x; - mPreviousGestureY = y; - mStrokeBuffer.add(new PointF(x, y)); - cancelAfterPauseThreshold(event, rawEvent, policyFlags); + if (moveDelta > mGestureDetectionThresholdPixels) { + // This is a gesture, not touch exploration. + mBaseX = x; + mBaseY = y; + mBaseTime = time; + startGesture(event, rawEvent, policyFlags); + } else if (getState() == STATE_CLEAR) { + if (timeDelta > MAX_TIME_TO_START_SWIPE_MS) { + // The user isn't moving fast enough. + cancelGesture(event, rawEvent, policyFlags); + return; } + } else if (getState() == STATE_GESTURE_STARTED) { + if (timeDelta > MAX_TIME_TO_CONTINUE_SWIPE_MS) { + cancelGesture(event, rawEvent, policyFlags); + return; + } + } + if (dX >= mMinPixelsBetweenSamplesX || dY >= mMinPixelsBetweenSamplesY) { + // At this point gesture detection has started and we are sampling points. + mPreviousGestureX = x; + mPreviousGestureY = y; + mStrokeBuffer.add(new PointF(x, y)); } } @@ -230,25 +235,6 @@ class Swipe extends GestureMatcher { } /** - * queues a transition to STATE_GESTURE_CANCEL based on the current state. If we have - * transitioned to STATE_GESTURE_STARTED the delay is longer. - */ - private void cancelAfterPauseThreshold( - MotionEvent event, MotionEvent rawEvent, int policyFlags) { - cancelPendingTransitions(); - switch (getState()) { - case STATE_CLEAR: - cancelAfter(CANCEL_ON_PAUSE_THRESHOLD_NOT_STARTED_MS, event, rawEvent, policyFlags); - break; - case STATE_GESTURE_STARTED: - cancelAfter(CANCEL_ON_PAUSE_THRESHOLD_STARTED_MS, event, rawEvent, policyFlags); - break; - default: - break; - } - } - - /** * Looks at the sequence of motions in mStrokeBuffer, classifies the gesture, then calls * Listener callbacks for success or failure. * diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java index 3282870fe281..3dd2433ac2bd 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java +++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java @@ -67,8 +67,8 @@ final class AutofillInlineSessionController { * Requests the IME to create an {@link InlineSuggestionsRequest} for {@code autofillId}. * * @param autofillId the Id of the field for which the request is for. - * @param requestConsumer the callback which will be invoked when IME responded or if it times - * out waiting for IME response. + * @param requestConsumer the callback to be invoked when the IME responds. Note that this is + * never invoked if the IME doesn't respond. */ @GuardedBy("mLock") void onCreateInlineSuggestionsRequestLocked(@NonNull AutofillId autofillId, diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java index 0bf89936f2ce..1a3baba1ff19 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java +++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java @@ -55,16 +55,6 @@ final class AutofillInlineSuggestionsRequestSession { private static final String TAG = AutofillInlineSuggestionsRequestSession.class.getSimpleName(); - // This timeout controls how long Autofill should wait for the IME to respond either - // unsupported or an {@link InlineSuggestionsRequest}. The timeout is needed to take into - // account the latency between the two events after a field is focused, 1) an Autofill - // request is triggered on framework; 2) the InputMethodService#onStartInput() event is - // triggered on the IME side. When 1) happens, Autofill may call the IME to return an {@link - // InlineSuggestionsRequest}, but the IME will only return it after 2) happens (or return - // immediately if the IME doesn't support inline suggestions). Also there is IPC latency - // between the framework and the IME but that should be small compare to that. - private static final int CREATE_INLINE_SUGGESTIONS_REQUEST_TIMEOUT_MS = 1000; - @NonNull private final InputMethodManagerInternal mInputMethodManagerInternal; private final int mUserId; @@ -92,9 +82,6 @@ final class AutofillInlineSuggestionsRequestSession { @GuardedBy("mLock") @Nullable private IInlineSuggestionsResponseCallback mResponseCallback; - @GuardedBy("mLock") - @Nullable - private Runnable mTimeoutCallback; @GuardedBy("mLock") @Nullable @@ -174,12 +161,17 @@ final class AutofillInlineSuggestionsRequestSession { } /** - * This method must be called when the session is destroyed, to avoid further callbacks from/to - * the IME. + * Prevents further interaction with the IME. Must be called before starting a new request + * session to avoid unwanted behavior from two overlapping requests. */ @GuardedBy("mLock") void destroySessionLocked() { mDestroyed = true; + + if (!mImeRequestReceived) { + Slog.w(TAG, + "Never received an InlineSuggestionsRequest from the IME for " + mAutofillId); + } } /** @@ -196,11 +188,6 @@ final class AutofillInlineSuggestionsRequestSession { mInputMethodManagerInternal.onCreateInlineSuggestionsRequest(mUserId, new InlineSuggestionsRequestInfo(mComponentName, mAutofillId, mUiExtras), new InlineSuggestionsRequestCallbackImpl(this)); - mTimeoutCallback = () -> { - Slog.w(TAG, "Timed out waiting for IME callback InlineSuggestionsRequest."); - handleOnReceiveImeRequest(null, null); - }; - mHandler.postDelayed(mTimeoutCallback, CREATE_INLINE_SUGGESTIONS_REQUEST_TIMEOUT_MS); } /** @@ -212,12 +199,12 @@ final class AutofillInlineSuggestionsRequestSession { if (mDestroyed || mResponseCallback == null) { return; } - if (!mImeInputViewStarted && mPreviousResponseIsNotEmpty) { - // 1. if previous response is not empty, and IME just become invisible, then send - // empty response to make sure existing responses don't stick around on the IME. + if (!mImeInputStarted && mPreviousResponseIsNotEmpty) { + // 1. if previous response is not empty, and IME is just disconnected from the view, + // then send empty response to make sure existing responses don't stick around. // Although the inline suggestions should disappear when IME hides which removes them - // from the view hierarchy, but we still send an empty response to be extra safe. - + // from the view hierarchy, but we still send an empty response to indicate that the + // previous suggestions are invalid now. if (sVerbose) Slog.v(TAG, "Send empty inline response"); updateResponseToImeUncheckLocked(new InlineSuggestionsResponse(Collections.EMPTY_LIST)); mPreviousResponseIsNotEmpty = false; @@ -264,11 +251,6 @@ final class AutofillInlineSuggestionsRequestSession { } mImeRequestReceived = true; - if (mTimeoutCallback != null) { - if (sVerbose) Slog.v(TAG, "removing timeout callback"); - mHandler.removeCallbacks(mTimeoutCallback); - mTimeoutCallback = null; - } if (request != null && callback != null) { mImeRequest = request; mResponseCallback = callback; diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index ef17d1331a1e..1d3ab9b7e24b 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -2581,7 +2581,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (sVerbose) Slog.v(TAG, "Exiting view " + id); mUi.hideFillUi(this); hideAugmentedAutofillLocked(viewState); - mInlineSessionController.hideInlineSuggestionsUiLocked(mCurrentViewId); mCurrentViewId = null; } break; @@ -2655,6 +2654,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (sVerbose) { Slog.v(TAG, "ignoring autofilled change on id " + id); } + // TODO(b/156099633): remove this once framework gets out of business of resending + // inline suggestions when IME visibility changes. + mInlineSessionController.hideInlineSuggestionsUiLocked(viewState.id); viewState.resetState(ViewState.STATE_CHANGED); return; } else if ((viewState.id.equals(this.mCurrentViewId)) @@ -3348,7 +3350,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (generateEvent) { mService.logDatasetSelected(dataset.getId(), id, mClientState); } - + if (mCurrentViewId != null) { + mInlineSessionController.hideInlineSuggestionsUiLocked(mCurrentViewId); + } autoFillApp(dataset); return; } diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java index 089eeb2aa086..8cc0de473328 100644 --- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java +++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java @@ -57,7 +57,6 @@ final class InlineSuggestionFactory { @NonNull AutoFillUI.AutoFillUiCallback client, @NonNull Runnable onErrorCallback, @Nullable RemoteInlineSuggestionRenderService remoteRenderService) { final BiConsumer<Dataset, Integer> onClickFactory = (dataset, datasetIndex) -> { - client.requestHideFillUi(autofillId); client.authenticate(response.getRequestId(), datasetIndex, response.getAuthentication(), response.getClientState(), /* authenticateInline= */ true); @@ -85,7 +84,6 @@ final class InlineSuggestionFactory { final Consumer<IntentSender> intentSenderConsumer = (intentSender) -> client.startIntentSender(intentSender, new Intent()); final BiConsumer<Dataset, Integer> onClickFactory = (dataset, datasetIndex) -> { - client.requestHideFillUi(autofillId); client.fill(requestId, datasetIndex, dataset); }; diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java index d49d4b2c3278..de13bd86a415 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java @@ -45,6 +45,12 @@ public abstract class InputMethodManagerInternal { } /** + * Called by the power manager to tell the input method manager whether it + * should start watching for wake events. + */ + public abstract void setInteractive(boolean interactive); + + /** * Hides the current input method, if visible. */ public abstract void hideCurrentInputMethod(@SoftInputShowHideReason int reason); @@ -108,6 +114,10 @@ public abstract class InputMethodManagerInternal { private static final InputMethodManagerInternal NOP = new InputMethodManagerInternal() { @Override + public void setInteractive(boolean interactive) { + } + + @Override public void hideCurrentInputMethod(@SoftInputShowHideReason int reason) { } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index b949d6bcf2e2..6efc88e017af 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -4202,6 +4202,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub + ((ClientState)msg.obj).uid); } return true; + case MSG_SET_INTERACTIVE: + handleSetInteractive(msg.arg1 != 0); + return true; case MSG_REPORT_FULLSCREEN_MODE: { final boolean fullscreen = msg.arg1 != 0; final ClientState clientState = (ClientState)msg.obj; @@ -4276,6 +4279,20 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return false; } + private void handleSetInteractive(final boolean interactive) { + synchronized (mMethodMap) { + mIsInteractive = interactive; + updateSystemUiLocked(interactive ? mImeWindowVis : 0, mBackDisposition); + + // Inform the current client of the change in active status + if (mCurClient != null && mCurClient.client != null) { + executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIIO( + MSG_SET_ACTIVE, mIsInteractive ? 1 : 0, mInFullscreenMode ? 1 : 0, + mCurClient)); + } + } + } + private boolean chooseNewDefaultIMELocked() { final InputMethodInfo imi = InputMethodUtils.getMostApplicableDefaultIME( mSettings.getEnabledInputMethodListLocked()); @@ -4885,6 +4902,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } @Override + public void setInteractive(boolean interactive) { + // Do everything in handler so as not to block the caller. + mService.mHandler.obtainMessage(MSG_SET_INTERACTIVE, interactive ? 1 : 0, 0) + .sendToTarget(); + } + + @Override public void hideCurrentInputMethod(@SoftInputShowHideReason int reason) { mService.mHandler.removeMessages(MSG_HIDE_CURRENT_INPUT_METHOD); mService.mHandler.obtainMessage(MSG_HIDE_CURRENT_INPUT_METHOD, reason).sendToTarget(); diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java index 0b73e4f0e9b4..2129e9bd34f3 100644 --- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java @@ -171,6 +171,11 @@ public final class MultiClientInputMethodManagerService { LocalServices.addService(InputMethodManagerInternal.class, new InputMethodManagerInternal() { @Override + public void setInteractive(boolean interactive) { + reportNotSupported(); + } + + @Override public void hideCurrentInputMethod(@SoftInputShowHideReason int reason) { reportNotSupported(); } diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java index 199cb4981fe6..0b95be15f157 100644 --- a/services/core/java/com/android/server/power/Notifier.java +++ b/services/core/java/com/android/server/power/Notifier.java @@ -413,6 +413,7 @@ public class Notifier { // Start input as soon as we start waking up or going to sleep. mInputManagerInternal.setInteractive(interactive); + mInputMethodManagerInternal.setInteractive(interactive); // Notify battery stats. try { diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 63952b086c1c..31fbaff17e78 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -2743,7 +2743,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub mContext, 0, Intent.createChooser(new Intent(Intent.ACTION_SET_WALLPAPER), mContext.getText(com.android.internal.R.string.chooser_wallpaper)), - 0, null, new UserHandle(serviceUserId))); + PendingIntent.FLAG_IMMUTABLE, null, new UserHandle(serviceUserId))); if (!mContext.bindServiceAsUser(intent, newConn, Context.BIND_AUTO_CREATE | Context.BIND_SHOWING_UI | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 131e44963033..c079bd5d0b80 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -1705,7 +1705,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags, IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning, - boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents) { + boolean allowTaskSnapshot, boolean activityCreated) { // If the display is frozen, we won't do anything until the actual window is // displayed so there is no reason to put in the starting window. if (!okToDisplay()) { @@ -1726,7 +1726,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mWmService.mTaskSnapshotController.getSnapshot(task.mTaskId, task.mUserId, false /* restoreFromDisk */, false /* isLowResolution */); final int type = getStartingWindowType(newTask, taskSwitch, processRunning, - allowTaskSnapshot, activityCreated, fromRecents, snapshot); + allowTaskSnapshot, activityCreated, snapshot); if (type == STARTING_WINDOW_TYPE_SNAPSHOT) { if (isActivityTypeHome()) { @@ -1888,12 +1888,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private final AddStartingWindow mAddStartingWindow = new AddStartingWindow(); private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning, - boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents, + boolean allowTaskSnapshot, boolean activityCreated, ActivityManager.TaskSnapshot snapshot) { if (newTask || !processRunning || (taskSwitch && !activityCreated)) { return STARTING_WINDOW_TYPE_SPLASH_SCREEN; } else if (taskSwitch && allowTaskSnapshot) { - if (snapshotOrientationSameAsTask(snapshot) || (snapshot != null && fromRecents)) { + if (isSnapshotCompatible(snapshot)) { return STARTING_WINDOW_TYPE_SNAPSHOT; } if (!isActivityTypeHome()) { @@ -1905,11 +1905,22 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } - private boolean snapshotOrientationSameAsTask(ActivityManager.TaskSnapshot snapshot) { + /** + * Returns {@code true} if the task snapshot is compatible with this activity (at least the + * rotation must be the same). + */ + @VisibleForTesting + boolean isSnapshotCompatible(ActivityManager.TaskSnapshot snapshot) { if (snapshot == null) { return false; } - return task.getConfiguration().orientation == snapshot.getOrientation(); + final int rotation = mDisplayContent.rotationForActivityInDifferentOrientation(this); + final int targetRotation = rotation != ROTATION_UNDEFINED + // The display may rotate according to the orientation of this activity. + ? rotation + // The activity won't change display orientation. + : task.getWindowConfiguration().getRotation(); + return snapshot.getRotation() == targetRotation; } void removeStartingWindow() { @@ -1929,7 +1940,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mStartingData = null; startingSurface = null; startingWindow = null; - startingDisplayed = false; if (surface == null) { ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "startingWindow was set but startingSurface==null, couldn't " @@ -4785,16 +4795,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (!task.hasChild(this)) { throw new IllegalStateException("Activity not found in its task"); } - final ActivityRecord activityAbove = task.getActivityAbove(this); - if (activityAbove == null) { - // It's the topmost activity in the task - should become resumed now - return true; - } - // Check if activity above is finishing now and this one becomes the topmost in task. - if (activityAbove.finishing) { - return true; - } - return false; + return task.topRunningActivity() == this; } void handleAlreadyVisible() { @@ -5453,7 +5454,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (mLastTransactionSequence != mWmService.mTransactionSequence) { mLastTransactionSequence = mWmService.mTransactionSequence; mNumDrawnWindows = 0; - startingDisplayed = false; // There is the main base application window, even if it is exiting, wait for it mNumInterestingWindows = findMainWindow(false /* includeStartingApp */) != null ? 1 : 0; @@ -5673,11 +5673,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch) { - showStartingWindow(prev, newTask, taskSwitch, false /* fromRecents */); - } - - void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch, - boolean fromRecents) { if (mTaskOverlay) { // We don't show starting window for overlay activities. return; @@ -5694,8 +5689,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags, prev != null ? prev.appToken : null, newTask, taskSwitch, isProcessRunning(), allowTaskSnapshot(), - mState.ordinal() >= STARTED.ordinal() && mState.ordinal() <= STOPPED.ordinal(), - fromRecents); + mState.ordinal() >= STARTED.ordinal() && mState.ordinal() <= STOPPED.ordinal()); if (shown) { mStartingWindowState = STARTING_WINDOW_SHOWN; } diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index dca086034dd0..cb2d98cc2afb 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -1307,17 +1307,8 @@ class ActivityStack extends Task { /** * Make sure that all activities that need to be visible in the stack (that is, they * currently can be seen by the user) actually are and update their configuration. - * @param starting The top most activity in the task. - * The activity is either starting or resuming. - * Caller should ensure starting activity is visible. - * @param preserveWindows Flag indicating whether windows should be preserved when updating - * configuration in {@link mEnsureActivitiesVisibleHelper}. - * @param configChanges Parts of the configuration that changed for this activity for evaluating - * if the screen should be frozen as part of - * {@link mEnsureActivitiesVisibleHelper}. - * */ - void ensureActivitiesVisible(@Nullable ActivityRecord starting, int configChanges, + void ensureActivitiesVisible(ActivityRecord starting, int configChanges, boolean preserveWindows) { ensureActivitiesVisible(starting, configChanges, preserveWindows, true /* notifyClients */); } @@ -1326,19 +1317,9 @@ class ActivityStack extends Task { * Ensure visibility with an option to also update the configuration of visible activities. * @see #ensureActivitiesVisible(ActivityRecord, int, boolean) * @see RootWindowContainer#ensureActivitiesVisible(ActivityRecord, int, boolean) - * @param starting The top most activity in the task. - * The activity is either starting or resuming. - * Caller should ensure starting activity is visible. - * @param notifyClients Flag indicating whether the visibility updates should be sent to the - * clients in {@link mEnsureActivitiesVisibleHelper}. - * @param preserveWindows Flag indicating whether windows should be preserved when updating - * configuration in {@link mEnsureActivitiesVisibleHelper}. - * @param configChanges Parts of the configuration that changed for this activity for evaluating - * if the screen should be frozen as part of - * {@link mEnsureActivitiesVisibleHelper}. */ // TODO: Should be re-worked based on the fact that each task as a stack in most cases. - void ensureActivitiesVisible(@Nullable ActivityRecord starting, int configChanges, + void ensureActivitiesVisible(ActivityRecord starting, int configChanges, boolean preserveWindows, boolean notifyClients) { mTopActivityOccludesKeyguard = false; mTopDismissingKeyguardActivity = null; diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index 3e7e0c8b936d..62979ffc4a8c 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -2498,7 +2498,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { mActivityMetricsLogger.notifyActivityLaunching(task.intent); try { mService.moveTaskToFrontLocked(null /* appThread */, null /* callingPackage */, - task.mTaskId, 0, options, true /* fromRecents */); + task.mTaskId, 0, options); // Apply options to prevent pendingOptions be taken by client to make sure // the override pending app transition will be applied immediately. targetActivity.applyOptionsLocked(); diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index bcdd6e39e85c..79e8ee3ec54b 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1539,10 +1539,7 @@ class ActivityStarter { * * Note: This method should only be called from {@link #startActivityUnchecked}. */ - - // TODO(b/152429287): Make it easier to exercise code paths through startActivityInner - @VisibleForTesting - int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord, + private int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags, boolean doResume, ActivityOptions options, Task inTask, boolean restrictedBgActivity) { @@ -1663,10 +1660,7 @@ class ActivityStarter { // Also, we don't want to resume activities in a task that currently has an overlay // as the starting activity just needs to be in the visible paused state until the // over is removed. - // Passing {@code null} as the start parameter ensures all activities are made - // visible. - mTargetStack.ensureActivitiesVisible(null /* starting */, - 0 /* configChanges */, !PRESERVE_WINDOWS); + mTargetStack.ensureActivitiesVisible(mStartActivity, 0, !PRESERVE_WINDOWS); // Go ahead and tell window manager to execute app transition for this activity // since the app transition will not be triggered through the resume channel. mTargetStack.getDisplay().mDisplayContent.executeAppTransition(); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index f21ec6b0e5cc..78e4237eb4a7 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -2476,13 +2476,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToFront: moving taskId=" + taskId); synchronized (mGlobalLock) { moveTaskToFrontLocked(appThread, callingPackage, taskId, flags, - SafeActivityOptions.fromBundle(bOptions), false /* fromRecents */); + SafeActivityOptions.fromBundle(bOptions)); } } void moveTaskToFrontLocked(@Nullable IApplicationThread appThread, - @Nullable String callingPackage, int taskId, int flags, SafeActivityOptions options, - boolean fromRecents) { + @Nullable String callingPackage, int taskId, int flags, SafeActivityOptions options) { final int callingPid = Binder.getCallingPid(); final int callingUid = Binder.getCallingUid(); assertPackageMatchesCallingUid(callingPackage); @@ -2527,7 +2526,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // We are reshowing a task, use a starting window to hide the initial draw delay // so the transition can start earlier. topActivity.showStartingWindow(null /* prev */, false /* newTask */, - true /* taskSwitch */, fromRecents); + true /* taskSwitch */); } } finally { Binder.restoreCallingIdentity(origId); @@ -3213,7 +3212,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (TextUtils.equals(pae.intent.getAction(), android.service.voice.VoiceInteractionService.SERVICE_INTERFACE)) { // Start voice interaction through VoiceInteractionManagerService. - mAssistUtils.showSessionForActiveService(sendBundle, SHOW_SOURCE_APPLICATION, + mAssistUtils.showSessionForActiveService(pae.extras, SHOW_SOURCE_APPLICATION, null, null); } else { pae.intent.replaceExtras(pae.extras); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 6655e92634e4..3acb127673e9 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -235,7 +235,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo implements WindowManagerPolicy.DisplayContentInfo { private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayContent" : TAG_WM; private static final String TAG_STACK = TAG + POSTFIX_STACK; - private static final int NO_ROTATION = -1; /** The default scaling mode that scales content automatically. */ static final int FORCE_SCALING_MODE_AUTO = 0; @@ -1394,36 +1393,40 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo final WindowContainer orientationSource = getLastOrientationSource(); final ActivityRecord r = orientationSource != null ? orientationSource.asActivityRecord() : null; - if (r != null && r.getTask() != null - && orientation != r.getTask().mLastReportedRequestedOrientation) { + if (r != null) { final Task task = r.getTask(); - task.mLastReportedRequestedOrientation = orientation; - mAtmService.getTaskChangeNotificationController() - .notifyTaskRequestedOrientationChanged(task.mTaskId, orientation); - } - // Currently there is no use case from non-activity. - if (r != null && handleTopActivityLaunchingInDifferentOrientation(r)) { - // Display orientation should be deferred until the top fixed rotation is finished. - return false; + if (task != null && orientation != task.mLastReportedRequestedOrientation) { + task.mLastReportedRequestedOrientation = orientation; + mAtmService.getTaskChangeNotificationController() + .notifyTaskRequestedOrientationChanged(task.mTaskId, orientation); + } + // Currently there is no use case from non-activity. + if (handleTopActivityLaunchingInDifferentOrientation(r, true /* checkOpening */)) { + // Display orientation should be deferred until the top fixed rotation is finished. + return false; + } } return mDisplayRotation.updateOrientation(orientation, forceUpdate); } - /** @return a valid rotation if the activity can use different orientation than the display. */ + /** + * Returns a valid rotation if the activity can use different orientation than the display. + * Otherwise {@link #ROTATION_UNDEFINED}. + */ @Surface.Rotation - private int rotationForActivityInDifferentOrientation(@NonNull ActivityRecord r) { + int rotationForActivityInDifferentOrientation(@NonNull ActivityRecord r) { if (!mWmService.mIsFixedRotationTransformEnabled) { - return NO_ROTATION; + return ROTATION_UNDEFINED; } if (r.inMultiWindowMode() || r.getRequestedConfigurationOrientation() == getConfiguration().orientation) { - return NO_ROTATION; + return ROTATION_UNDEFINED; } final int currentRotation = getRotation(); final int rotation = mDisplayRotation.rotationForOrientation(r.getRequestedOrientation(), currentRotation); if (rotation == currentRotation) { - return NO_ROTATION; + return ROTATION_UNDEFINED; } return rotation; } @@ -1433,9 +1436,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo * is launching until the launch animation is done to avoid showing the previous activity * inadvertently in a wrong orientation. * + * @param r The launching activity which may change display orientation. + * @param checkOpening Whether to check if the activity is animating by transition. Set to + * {@code true} if the caller is not sure whether the activity is launching. * @return {@code true} if the fixed rotation is started. */ - private boolean handleTopActivityLaunchingInDifferentOrientation(@NonNull ActivityRecord r) { + boolean handleTopActivityLaunchingInDifferentOrientation(@NonNull ActivityRecord r, + boolean checkOpening) { if (!mWmService.mIsFixedRotationTransformEnabled) { return false; } @@ -1446,19 +1453,18 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // It has been set and not yet finished. return true; } - if (!mAppTransition.isTransitionSet()) { - // Apply normal rotation animation in case of the activity set different requested - // orientation without activity switch. - return false; - } - if (!mOpeningApps.contains(r) - // Without screen rotation, the rotation behavior of non-top visible activities is - // undefined. So the fixed rotated activity needs to cover the screen. - && r.findMainWindow() != mDisplayPolicy.getTopFullscreenOpaqueWindow()) { + if (checkOpening) { + if (!mAppTransition.isTransitionSet() && !mOpeningApps.contains(r)) { + // Apply normal rotation animation in case of the activity set different requested + // orientation without activity switch. + return false; + } + } else if (r != topRunningActivity()) { + // If the transition has not started yet, the activity must be the top. return false; } final int rotation = rotationForActivityInDifferentOrientation(r); - if (rotation == NO_ROTATION) { + if (rotation == ROTATION_UNDEFINED) { return false; } if (!r.getParent().matchParentBounds()) { @@ -1511,6 +1517,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo sendNewConfiguration(); return; } + if (mDisplayRotation.isWaitingForRemoteRotation()) { + // There is pending rotation change to apply. + return; + } // The orientation of display is not changed. clearFixedRotationLaunchingApp(); } @@ -1551,7 +1561,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo */ void rotateInDifferentOrientationIfNeeded(ActivityRecord activityRecord) { int rotation = rotationForActivityInDifferentOrientation(activityRecord); - if (rotation != NO_ROTATION) { + if (rotation != ROTATION_UNDEFINED) { startFixedRotationTransform(activityRecord, rotation); } } diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java index 9bbd4cd0778a..5a2484735fb9 100644 --- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java +++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java @@ -135,6 +135,7 @@ class EmbeddedWindowController { final int mOwnerPid; final WindowManagerService mWmService; InputChannel mInputChannel; + final int mWindowType; /** * @param clientToken client token used to clean up the map if the embedding process dies @@ -146,7 +147,7 @@ class EmbeddedWindowController { * @param ownerPid calling pid used for anr blaming */ EmbeddedWindow(WindowManagerService service, IWindow clientToken, - WindowState hostWindowState, int ownerUid, int ownerPid) { + WindowState hostWindowState, int ownerUid, int ownerPid, int windowType) { mWmService = service; mClient = clientToken; mHostWindowState = hostWindowState; @@ -154,6 +155,7 @@ class EmbeddedWindowController { : null; mOwnerUid = ownerUid; mOwnerPid = ownerPid; + mWindowType = windowType; } String getName() { diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java index c4e03f5c65f5..c92de2b84df6 100644 --- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java +++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java @@ -21,7 +21,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static com.android.server.wm.ActivityStack.TAG_VISIBILITY; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY; -import android.annotation.Nullable; import android.util.Slog; import com.android.internal.util.function.pooled.PooledConsumer; @@ -43,16 +42,6 @@ class EnsureActivitiesVisibleHelper { mContiner = container; } - /** - * Update all attributes except {@link mContiner} to use in subsequent calculations. - * - * @param starting The activity that is being started - * @param configChanges Parts of the configuration that changed for this activity for evaluating - * if the screen should be frozen. - * @param preserveWindows Flag indicating whether windows should be preserved when updating. - * @param notifyClients Flag indicating whether the configuration and visibility changes shoulc - * be sent to the clients. - */ void reset(ActivityRecord starting, int configChanges, boolean preserveWindows, boolean notifyClients) { mStarting = starting; @@ -71,17 +60,8 @@ class EnsureActivitiesVisibleHelper { * Ensure visibility with an option to also update the configuration of visible activities. * @see ActivityStack#ensureActivitiesVisible(ActivityRecord, int, boolean) * @see RootWindowContainer#ensureActivitiesVisible(ActivityRecord, int, boolean) - * @param starting The top most activity in the task. - * The activity is either starting or resuming. - * Caller should ensure starting activity is visible. - * - * @param configChanges Parts of the configuration that changed for this activity for evaluating - * if the screen should be frozen. - * @param preserveWindows Flag indicating whether windows should be preserved when updating. - * @param notifyClients Flag indicating whether the configuration and visibility changes shoulc - * be sent to the clients. */ - void process(@Nullable ActivityRecord starting, int configChanges, boolean preserveWindows, + void process(ActivityRecord starting, int configChanges, boolean preserveWindows, boolean notifyClients) { reset(starting, configChanges, preserveWindows, notifyClients); diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index bf20cb907b71..6f5c440c9f83 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -661,17 +661,23 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { @Override public void grantInputChannel(int displayId, SurfaceControl surface, - IWindow window, IBinder hostInputToken, int flags, InputChannel outInputChannel) { + IWindow window, IBinder hostInputToken, int flags, int type, + InputChannel outInputChannel) { if (hostInputToken == null && !mCanAddInternalSystemWindow) { // Callers without INTERNAL_SYSTEM_WINDOW permission cannot grant input channel to // embedded windows without providing a host window input token throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission"); } + if (!mCanAddInternalSystemWindow && type != 0) { + Slog.w(TAG_WM, "Requires INTERNAL_SYSTEM_WINDOW permission if assign type to" + + " input"); + } + final long identity = Binder.clearCallingIdentity(); try { mService.grantInputChannel(mUid, mPid, displayId, surface, window, hostInputToken, - flags, outInputChannel); + flags, mCanAddInternalSystemWindow ? type : 0, outInputChannel); } finally { Binder.restoreCallingIdentity(identity); } diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index 0f5cafe9e4e6..1a2672bd0132 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -297,6 +297,13 @@ class TaskSnapshotController { Slog.w(TAG_WM, "Failed to take screenshot. No main window for " + task); return false; } + if (activity.hasFixedRotationTransform()) { + if (DEBUG_SCREENSHOT) { + Slog.i(TAG_WM, "Skip taking screenshot. App has fixed rotation " + activity); + } + // The activity is in a temporal state that it has different rotation than the task. + return false; + } builder.setIsRealSnapshot(true); builder.setId(System.currentTimeMillis()); diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java index 24cd7d1fa528..392557045b4a 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java @@ -195,6 +195,16 @@ class TaskSnapshotSurface implements StartingSurface { + activity); return null; } + if (topFullscreenActivity.getWindowConfiguration().getRotation() + != snapshot.getRotation()) { + // The snapshot should have been checked by ActivityRecord#isSnapshotCompatible + // that the activity will be updated to the same rotation as the snapshot. Since + // the transition is not started yet, fixed rotation transform needs to be applied + // earlier to make the snapshot show in a rotated container. + activity.mDisplayContent.handleTopActivityLaunchingInDifferentOrientation( + topFullscreenActivity, false /* checkOpening */); + } + sysUiVis = topFullscreenOpaqueWindow.getSystemUiVisibility(); WindowManager.LayoutParams attrs = topFullscreenOpaqueWindow.mAttrs; windowFlags = attrs.flags; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 4c1d6f3b9892..86d83c293f1a 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -8027,14 +8027,15 @@ public class WindowManagerService extends IWindowManager.Stub * views. */ void grantInputChannel(int callingUid, int callingPid, int displayId, SurfaceControl surface, - IWindow window, IBinder hostInputToken, int flags, InputChannel outInputChannel) { + IWindow window, IBinder hostInputToken, int flags, int type, + InputChannel outInputChannel) { final InputApplicationHandle applicationHandle; final String name; final InputChannel clientChannel; synchronized (mGlobalLock) { EmbeddedWindowController.EmbeddedWindow win = new EmbeddedWindowController.EmbeddedWindow(this, window, - mInputToWindowMap.get(hostInputToken), callingUid, callingPid); + mInputToWindowMap.get(hostInputToken), callingUid, callingPid, type); clientChannel = win.openInputChannel(); mEmbeddedWindowController.add(clientChannel.getToken(), win); applicationHandle = win.getApplicationHandle(); @@ -8042,7 +8043,7 @@ public class WindowManagerService extends IWindowManager.Stub } updateInputChannel(clientChannel.getToken(), callingUid, callingPid, displayId, surface, - name, applicationHandle, flags, null /* region */); + name, applicationHandle, flags, type, null /* region */); clientChannel.transferTo(outInputChannel); clientChannel.dispose(); @@ -8050,7 +8051,7 @@ public class WindowManagerService extends IWindowManager.Stub private void updateInputChannel(IBinder channelToken, int callingUid, int callingPid, int displayId, SurfaceControl surface, String name, - InputApplicationHandle applicationHandle, int flags, Region region) { + InputApplicationHandle applicationHandle, int flags, int type, Region region) { InputWindowHandle h = new InputWindowHandle(applicationHandle, displayId); h.token = channelToken; h.name = name; @@ -8058,7 +8059,7 @@ public class WindowManagerService extends IWindowManager.Stub final int sanitizedFlags = flags & (LayoutParams.FLAG_NOT_TOUCHABLE | LayoutParams.FLAG_SLIPPERY); h.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | sanitizedFlags; - h.layoutParamsType = 0; + h.layoutParamsType = type; h.dispatchingTimeoutNanos = DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS; h.canReceiveKeys = false; h.hasFocus = false; @@ -8082,6 +8083,7 @@ public class WindowManagerService extends IWindowManager.Stub t.setInputWindowInfo(surface, h); t.apply(); t.close(); + surface.release(); } /** @@ -8105,7 +8107,7 @@ public class WindowManagerService extends IWindowManager.Stub } updateInputChannel(channelToken, win.mOwnerUid, win.mOwnerPid, displayId, surface, name, - applicationHandle, flags, region); + applicationHandle, flags, win.mWindowType, region); } /** Return whether layer tracing is enabled */ diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index c4cb4b5091d8..707a7898f8b6 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -138,6 +138,13 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub Slog.e(TAG, "Attempt to operate on detached container: " + wc); continue; } + // Make sure we add to the syncSet before performing + // operations so we don't end up splitting effects between the WM + // pending transaction and the BLASTSync transaction. + if (syncId >= 0) { + mBLASTSyncEngine.addToSyncSet(syncId, wc); + } + int containerEffect = applyWindowContainerChange(wc, entry.getValue()); effects |= containerEffect; @@ -146,9 +153,6 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub && (containerEffect & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) { haveConfigChanges.add(wc); } - if (syncId >= 0) { - mBLASTSyncEngine.addToSyncSet(syncId, wc); - } } // Hierarchy changes final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps(); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index e925ce5c2dac..a948ecef48e7 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2191,9 +2191,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (wasVisible) { final int transit = (!startingWindow) ? TRANSIT_EXIT : TRANSIT_PREVIEW_DONE; - + final int flags = startingWindow ? 0 /* self */ : PARENTS; // Try starting an animation. - if (mWinAnimator.applyAnimationLocked(transit, false)) { + if (mWinAnimator.applyAnimationLocked(transit, false, flags)) { mAnimatingExit = true; // mAnimatingExit affects canAffectSystemUiFlags(). Run layout such that @@ -2205,7 +2205,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mWmService.mAccessibilityController.onWindowTransitionLocked(this, transit); } } - final boolean isAnimating = isAnimating(TRANSITION | PARENTS) + final boolean isAnimating = startingWindow + ? isAnimating(0) + : isAnimating(TRANSITION | PARENTS) && (mActivityRecord == null || !mActivityRecord.isWaitingForTransitionStart()); final boolean lastWindowIsStartingWindow = startingWindow && mActivityRecord != null && mActivityRecord.isLastWindow(this); @@ -2227,6 +2229,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } + if (startingWindow && mActivityRecord != null) { + mActivityRecord.startingDisplayed = false; + } removeImmediately(); // Removing a visible window will effect the computed orientation // So just update orientation if needed. diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index c570cf1d949f..e70f3e4ef9b9 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -1400,9 +1400,25 @@ class WindowStateAnimator { * the switch statement below. * @param isEntrance The animation type the last time this was called. Used to keep from * loading the same animation twice. - * @return true if an animation has been loaded. + * @return {@code true} if an animation has been loaded, includes the parents. + * */ boolean applyAnimationLocked(int transit, boolean isEntrance) { + return applyAnimationLocked(transit, isEntrance, PARENTS); + } + + /** + * Choose the correct animation and set it to the passed WindowState. + * @param transit If AppTransition.TRANSIT_PREVIEW_DONE and the app window has been drawn + * then the animation will be app_starting_exit. Any other value loads the animation from + * the switch statement below. + * @param isEntrance The animation type the last time this was called. Used to keep from + * loading the same animation twice. + * @param flags The combination of bitmask flags to specify targets and condition for + * checking animating status. See {@link WindowContainer.AnimationFlags}. + * @return {@code true} if an animation has been loaded. + */ + boolean applyAnimationLocked(int transit, boolean isEntrance, int flags) { if (mWin.isAnimating() && mAnimationIsEntrance == isEntrance) { // If we are trying to apply an animation, but already running // an animation of the same type, then just leave that one alone. @@ -1472,7 +1488,7 @@ class WindowStateAnimator { mWin.getDisplayContent().adjustForImeIfNeeded(); } - return mWin.isAnimating(PARENTS); + return mWin.isAnimating(flags); } void dumpDebug(ProtoOutputStream proto, long fieldId) { diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index 48e22f6c685c..e4102205ddbb 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -63,6 +63,7 @@ import android.app.usage.UsageEvents; import android.appwidget.AppWidgetManager; import android.content.Context; import android.content.ContextWrapper; +import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -107,6 +108,10 @@ public class AppStandbyControllerTests { private static final int UID_1 = 10000; private static final String PACKAGE_EXEMPTED_1 = "com.android.exempted"; private static final int UID_EXEMPTED_1 = 10001; + private static final String PACKAGE_SYSTEM_HEADFULL = "com.example.system.headfull"; + private static final int UID_SYSTEM_HEADFULL = 10002; + private static final String PACKAGE_SYSTEM_HEADLESS = "com.example.system.headless"; + private static final int UID_SYSTEM_HEADLESS = 10003; private static final int USER_ID = 0; private static final int USER_ID2 = 10; private static final UserHandle USER_HANDLE_USER2 = new UserHandle(USER_ID2); @@ -305,18 +310,33 @@ public class AppStandbyControllerTests { pie.packageName = PACKAGE_EXEMPTED_1; packages.add(pie); + PackageInfo pis = new PackageInfo(); + pis.activities = new ActivityInfo[]{mock(ActivityInfo.class)}; + pis.applicationInfo = new ApplicationInfo(); + pis.applicationInfo.uid = UID_SYSTEM_HEADFULL; + pis.applicationInfo.flags = ApplicationInfo.FLAG_SYSTEM; + pis.packageName = PACKAGE_SYSTEM_HEADFULL; + packages.add(pis); + + PackageInfo pish = new PackageInfo(); + pish.applicationInfo = new ApplicationInfo(); + pish.applicationInfo.uid = UID_SYSTEM_HEADLESS; + pish.applicationInfo.flags = ApplicationInfo.FLAG_SYSTEM; + pish.packageName = PACKAGE_SYSTEM_HEADLESS; + packages.add(pish); + doReturn(packages).when(mockPm).getInstalledPackagesAsUser(anyInt(), anyInt()); try { - doReturn(UID_1).when(mockPm).getPackageUidAsUser(eq(PACKAGE_1), anyInt()); - doReturn(UID_1).when(mockPm).getPackageUidAsUser(eq(PACKAGE_1), anyInt(), anyInt()); - doReturn(UID_EXEMPTED_1).when(mockPm).getPackageUidAsUser(eq(PACKAGE_EXEMPTED_1), - anyInt()); - doReturn(UID_EXEMPTED_1).when(mockPm).getPackageUidAsUser(eq(PACKAGE_EXEMPTED_1), - anyInt(), anyInt()); - doReturn(pi.applicationInfo).when(mockPm).getApplicationInfo(eq(pi.packageName), - anyInt()); - doReturn(pie.applicationInfo).when(mockPm).getApplicationInfo(eq(pie.packageName), - anyInt()); + for (int i = 0; i < packages.size(); ++i) { + PackageInfo pkg = packages.get(i); + + doReturn(pkg.applicationInfo.uid).when(mockPm) + .getPackageUidAsUser(eq(pkg.packageName), anyInt()); + doReturn(pkg.applicationInfo.uid).when(mockPm) + .getPackageUidAsUser(eq(pkg.packageName), anyInt(), anyInt()); + doReturn(pkg.applicationInfo).when(mockPm) + .getApplicationInfo(eq(pkg.packageName), anyInt()); + } } catch (PackageManager.NameNotFoundException nnfe) {} } @@ -514,7 +534,11 @@ public class AppStandbyControllerTests { } private void assertBucket(int bucket) { - assertEquals(bucket, getStandbyBucket(mController, PACKAGE_1)); + assertBucket(bucket, PACKAGE_1); + } + + private void assertBucket(int bucket, String pkg) { + assertEquals(bucket, getStandbyBucket(mController, pkg)); } private void assertNotBucket(int bucket) { @@ -1462,6 +1486,32 @@ public class AppStandbyControllerTests { assertBucket(STANDBY_BUCKET_RESTRICTED); } + @Test + public void testSystemHeadlessAppElevated() { + reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1); + reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, + PACKAGE_SYSTEM_HEADFULL); + reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, + PACKAGE_SYSTEM_HEADLESS); + mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD; + + + mController.setAppStandbyBucket(PACKAGE_SYSTEM_HEADFULL, USER_ID, STANDBY_BUCKET_RARE, + REASON_MAIN_TIMEOUT); + assertBucket(STANDBY_BUCKET_RARE, PACKAGE_SYSTEM_HEADFULL); + + // Make sure headless system apps don't get lowered. + mController.setAppStandbyBucket(PACKAGE_SYSTEM_HEADLESS, USER_ID, STANDBY_BUCKET_RARE, + REASON_MAIN_TIMEOUT); + assertBucket(STANDBY_BUCKET_ACTIVE, PACKAGE_SYSTEM_HEADLESS); + + // Package 1 doesn't have activities and is headless, but is not a system app, so it can + // be lowered. + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, + REASON_MAIN_TIMEOUT); + assertBucket(STANDBY_BUCKET_RARE, PACKAGE_1); + } + private String getAdminAppsStr(int userId) { return getAdminAppsStr(userId, mController.getActiveAdminAppsForTest(userId)); } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 8cfe96f0db5a..4da5adfeb872 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -35,6 +35,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; @@ -69,6 +70,7 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.never; +import android.app.ActivityManager.TaskSnapshot; import android.app.ActivityOptions; import android.app.WindowConfiguration; import android.app.servertransaction.ActivityConfigurationChangeItem; @@ -82,14 +84,18 @@ import android.content.res.Resources; import android.graphics.Rect; import android.os.Bundle; import android.os.PersistableBundle; +import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.util.MergedConfiguration; import android.util.MutableBoolean; import android.view.DisplayInfo; import android.view.IRemoteAnimationFinishedCallback; import android.view.IRemoteAnimationRunner.Stub; +import android.view.IWindowSession; import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationTarget; +import android.view.WindowManager; +import android.view.WindowManagerGlobal; import androidx.test.filters.MediumTest; @@ -488,6 +494,16 @@ public class ActivityRecordTests extends ActivityTestsBase { } @Test + public void testShouldMakeActive_nonTopVisible() { + ActivityRecord finishingActivity = new ActivityBuilder(mService).setTask(mTask).build(); + finishingActivity.finishing = true; + ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build(); + mActivity.setState(ActivityStack.ActivityState.STOPPED, "Testing"); + + assertEquals(false, mActivity.shouldMakeActive(null /* activeActivity */)); + } + + @Test public void testShouldResume_stackVisibility() { mActivity.setState(ActivityStack.ActivityState.STOPPED, "Testing"); spyOn(mStack); @@ -1394,6 +1410,10 @@ public class ActivityRecordTests extends ActivityTestsBase { assertTrue(displayRotation.isRotatingSeamlessly()); + // The launching rotated app should not be cleared when waiting for remote rotation. + display.continueUpdateOrientationForDiffOrienLaunchingApp(); + assertNotNull(display.mFixedRotationLaunchingApp); + // Simulate the rotation has been updated to previous one, e.g. sensor updates before the // remote rotation is completed. doReturn(originalRotation).when(displayRotation).rotationForOrientation( @@ -1426,6 +1446,73 @@ public class ActivityRecordTests extends ActivityTestsBase { } @Test + public void testIsSnapshotCompatible() { + mService.mWindowManager.mIsFixedRotationTransformEnabled = true; + final TaskSnapshot snapshot = new TaskSnapshotPersisterTestBase.TaskSnapshotBuilder() + .setRotation(mActivity.getWindowConfiguration().getRotation()) + .build(); + + assertTrue(mActivity.isSnapshotCompatible(snapshot)); + + setRotatedScreenOrientationSilently(mActivity); + + assertFalse(mActivity.isSnapshotCompatible(snapshot)); + } + + @Test + public void testFixedRotationSnapshotStartingWindow() { + mService.mWindowManager.mIsFixedRotationTransformEnabled = true; + // TaskSnapshotSurface requires a fullscreen opaque window. + final WindowManager.LayoutParams params = new WindowManager.LayoutParams( + WindowManager.LayoutParams.TYPE_APPLICATION_STARTING); + params.width = params.height = WindowManager.LayoutParams.MATCH_PARENT; + final WindowTestUtils.TestWindowState w = new WindowTestUtils.TestWindowState( + mService.mWindowManager, mock(Session.class), new TestIWindow(), params, mActivity); + mActivity.addWindow(w); + + // Assume the activity is launching in different rotation, and there was an available + // snapshot accepted by {@link Activity#isSnapshotCompatible}. + final TaskSnapshot snapshot = new TaskSnapshotPersisterTestBase.TaskSnapshotBuilder() + .setRotation((mActivity.getWindowConfiguration().getRotation() + 1) % 4) + .build(); + setRotatedScreenOrientationSilently(mActivity); + + final IWindowSession session = WindowManagerGlobal.getWindowSession(); + spyOn(session); + try { + // Return error to skip unnecessary operation. + doReturn(WindowManagerGlobal.ADD_STARTING_NOT_NEEDED).when(session).addToDisplay( + any() /* window */, anyInt() /* seq */, any() /* attrs */, + anyInt() /* viewVisibility */, anyInt() /* displayId */, any() /* outFrame */, + any() /* outContentInsets */, any() /* outStableInsets */, + any() /* outDisplayCutout */, any() /* outInputChannel */, + any() /* outInsetsState */, any() /* outActiveControls */); + TaskSnapshotSurface.create(mService.mWindowManager, mActivity, snapshot); + } catch (RemoteException ignored) { + } finally { + reset(session); + } + + // Because the rotation of snapshot and the corresponding top activity are different, fixed + // rotation should be applied when creating snapshot surface if the display rotation may be + // changed according to the activity orientation. + assertTrue(mActivity.hasFixedRotationTransform()); + assertEquals(mActivity, mActivity.mDisplayContent.mFixedRotationLaunchingApp); + } + + /** + * Sets orientation without notifying the parent to simulate that the display has not applied + * the requested orientation yet. + */ + private static void setRotatedScreenOrientationSilently(ActivityRecord r) { + final int rotatedOrentation = r.getConfiguration().orientation == ORIENTATION_PORTRAIT + ? SCREEN_ORIENTATION_LANDSCAPE + : SCREEN_ORIENTATION_PORTRAIT; + doReturn(false).when(r).onDescendantOrientationChanged(any(), any()); + r.setOrientation(rotatedOrentation); + } + + @Test public void testActivityOnDifferentDisplayUpdatesProcessOverride() { final ActivityRecord secondaryDisplayActivity = createActivityOnDisplay(false /* defaultDisplay */, null /* process */); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index 02d1f9b43e91..edc9756d6d0e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -49,7 +49,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; -import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS; import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; import static com.android.server.wm.WindowContainer.POSITION_TOP; @@ -57,10 +56,10 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyObject; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; @@ -100,6 +99,7 @@ import org.junit.runner.RunWith; @Presubmit @RunWith(WindowTestRunner.class) public class ActivityStarterTests extends ActivityTestsBase { + private ActivityStarter mStarter; private ActivityStartController mController; private ActivityMetricsLogger mActivityMetricsLogger; private PackageManagerInternal mMockPackageManager; @@ -127,6 +127,8 @@ public class ActivityStarterTests extends ActivityTestsBase { mController = mock(ActivityStartController.class); mActivityMetricsLogger = mock(ActivityMetricsLogger.class); clearInvocations(mActivityMetricsLogger); + mStarter = new ActivityStarter(mController, mService, mService.mStackSupervisor, + mock(ActivityStartInterceptor.class)); } @Test @@ -179,7 +181,6 @@ public class ActivityStarterTests extends ActivityTestsBase { * {@link ActivityStarter#execute} based on these preconditions and ensures the result matches * the expected. It is important to note that the method also checks side effects of the start, * such as ensuring {@link ActivityOptions#abort()} is called in the relevant scenarios. - * * @param preconditions A bitmask representing the preconditions for the launch * @param launchFlags The launch flags to be provided by the launch {@link Intent}. * @param expectedResult The expected result from the launch. @@ -201,7 +202,7 @@ public class ActivityStarterTests extends ActivityTestsBase { final WindowProcessController wpc = containsConditions(preconditions, PRECONDITION_NO_CALLER_APP) ? null : new WindowProcessController(service, ai, null, 0, -1, null, listener); - doReturn(wpc).when(service).getProcessController(any()); + doReturn(wpc).when(service).getProcessController(anyObject()); final Intent intent = new Intent(); intent.setFlags(launchFlags); @@ -1033,46 +1034,4 @@ public class ActivityStarterTests extends ActivityTestsBase { verify(recentTasks, times(1)).add(any()); } - - @Test - public void testStartActivityInner_allSplitScreenPrimaryActivitiesVisible() { - // Given - final ActivityStarter starter = prepareStarter(0, false); - - starter.setReason("testAllSplitScreenPrimaryActivitiesAreResumed"); - - final ActivityRecord targetRecord = new ActivityBuilder(mService).build(); - targetRecord.setFocusable(false); - targetRecord.setVisibility(false); - final ActivityRecord sourceRecord = new ActivityBuilder(mService).build(); - - final ActivityStack stack = spy( - mRootWindowContainer.getDefaultTaskDisplayArea() - .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, - /* onTop */true)); - - stack.addChild(targetRecord); - - doReturn(stack).when(mRootWindowContainer) - .getLaunchStack(any(), any(), any(), anyBoolean(), any(), anyInt(), anyInt()); - - starter.mStartActivity = new ActivityBuilder(mService).build(); - - // When - starter.startActivityInner( - /* r */targetRecord, - /* sourceRecord */ sourceRecord, - /* voiceSession */null, - /* voiceInteractor */ null, - /* startFlags */ 0, - /* doResume */true, - /* options */null, - /* inTask */null, - /* restrictedBgActivity */false); - - // Then - verify(stack).ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); - verify(targetRecord).makeVisibleIfNeeded(null, true); - assertTrue(targetRecord.mVisibleRequested); - } } diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java index 9263d8a2f9e3..8f18f648f8f0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java @@ -307,7 +307,7 @@ public class AppWindowTokenTests extends WindowTestsBase { public void testCreateRemoveStartingWindow() { mActivity.addStartingWindow(mPackageName, android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, - false, false); + false); waitUntilHandlersIdle(); assertHasStartingWindow(mActivity); mActivity.removeStartingWindow(); @@ -322,7 +322,7 @@ public class AppWindowTokenTests extends WindowTestsBase { for (int i = 0; i < 1000; i++) { appToken.addStartingWindow(mPackageName, android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, - false, false); + false); appToken.removeStartingWindow(); waitUntilHandlersIdle(); assertNoStartingWindow(appToken); @@ -335,11 +335,11 @@ public class AppWindowTokenTests extends WindowTestsBase { final ActivityRecord activity2 = createIsolatedTestActivityRecord(); activity1.addStartingWindow(mPackageName, android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, - false, false); + false); waitUntilHandlersIdle(); activity2.addStartingWindow(mPackageName, android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity1.appToken.asBinder(), - true, true, false, true, false, false); + true, true, false, true, false); waitUntilHandlersIdle(); assertNoStartingWindow(activity1); assertHasStartingWindow(activity2); @@ -355,11 +355,11 @@ public class AppWindowTokenTests extends WindowTestsBase { activity2.addStartingWindow(mPackageName, android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity1.appToken.asBinder(), true, true, false, - true, false, false); + true, false); }); activity1.addStartingWindow(mPackageName, android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, - false, false); + false); waitUntilHandlersIdle(); assertNoStartingWindow(activity1); assertHasStartingWindow(activity2); @@ -371,11 +371,11 @@ public class AppWindowTokenTests extends WindowTestsBase { final ActivityRecord activity2 = createIsolatedTestActivityRecord(); activity1.addStartingWindow(mPackageName, android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, - false, false); + false); waitUntilHandlersIdle(); activity2.addStartingWindow(mPackageName, android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity1.appToken.asBinder(), - true, true, false, true, false, false); + true, true, false, true, false); waitUntilHandlersIdle(); assertNoStartingWindow(activity1); assertHasStartingWindow(activity2); @@ -413,7 +413,7 @@ public class AppWindowTokenTests extends WindowTestsBase { // Add a starting window. activityTop.addStartingWindow(mPackageName, android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, - false, false); + false); waitUntilHandlersIdle(); // Make the top one invisible, and try transferring the starting window from the top to the diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java index 20d9aff5f3bf..f985a140bf0c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java @@ -192,10 +192,18 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { final ActivityManager.TaskSnapshot.Builder builder = new ActivityManager.TaskSnapshot.Builder(); - mWm.mTaskSnapshotController.prepareTaskSnapshot(mAppWindow.mActivityRecord.getTask(), - PixelFormat.UNKNOWN, builder); + boolean success = mWm.mTaskSnapshotController.prepareTaskSnapshot( + mAppWindow.mActivityRecord.getTask(), PixelFormat.UNKNOWN, builder); + assertTrue(success); // The pixel format should be selected automatically. assertNotEquals(PixelFormat.UNKNOWN, builder.getPixelFormat()); + + // Snapshot should not be taken while the rotation of activity and task are different. + doReturn(true).when(mAppWindow.mActivityRecord).hasFixedRotationTransform(); + success = mWm.mTaskSnapshotController.prepareTaskSnapshot( + mAppWindow.mActivityRecord.getTask(), PixelFormat.UNKNOWN, builder); + + assertFalse(success); } } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index e85dfbefc752..4246aef027bb 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -10415,7 +10415,7 @@ public class TelephonyManager { * <p>To recognize a carrier (including MVNO) as a first-class identity, Android assigns each * carrier with a canonical integer a.k.a. carrier id. The carrier ID is an Android * platform-wide identifier for a carrier. AOSP maintains carrier ID assignments in - * <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/carrier_list.textpb">here</a> + * <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/latest_carrier_id/carrier_list.textpb">here</a> * * <p>Apps which have carrier-specific configurations or business logic can use the carrier id * as an Android platform-wide identifier for carriers. @@ -10477,7 +10477,7 @@ public class TelephonyManager { * * <p>For carriers without fine-grained specific carrier ids, return {@link #getSimCarrierId()} * <p>Specific carrier ids are defined in the same way as carrier id - * <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/carrier_list.textpb">here</a> + * <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/latest_carrier_id/carrier_list.textpb">here</a> * except each with a "parent" id linking to its top-level carrier id. * * @return Returns fine-grained carrier id of the current subscription. @@ -10526,7 +10526,7 @@ public class TelephonyManager { * This is used for fallback when configurations/logic for exact carrier id * {@link #getSimCarrierId()} are not found. * - * Android carrier id table <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/carrier_list.textpb">here</a> + * Android carrier id table <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/latest_carrier_id/carrier_list.textpb">here</a> * can be updated out-of-band, its possible a MVNO (Mobile Virtual Network Operator) carrier * was not fully recognized and assigned to its MNO (Mobile Network Operator) carrier id * by default. After carrier id table update, a new carrier id was assigned. If apps don't @@ -10553,7 +10553,7 @@ public class TelephonyManager { * used for fallback when configurations/logic for exact carrier id {@link #getSimCarrierId()} * are not found. * - * Android carrier id table <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/carrier_list.textpb">here</a> + * Android carrier id table <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/latest_carrier_id/carrier_list.textpb">here</a> * can be updated out-of-band, its possible a MVNO (Mobile Virtual Network Operator) carrier * was not fully recognized and assigned to its MNO (Mobile Network Operator) carrier id * by default. After carrier id table update, a new carrier id was assigned. If apps don't |