diff options
84 files changed, 1683 insertions, 864 deletions
diff --git a/apex/permission/service/java/com/android/permission/persistence/IoUtils.java b/apex/permission/service/java/com/android/permission/persistence/IoUtils.java index 0ae44603516e..569a78c0ab41 100644 --- a/apex/permission/service/java/com/android/permission/persistence/IoUtils.java +++ b/apex/permission/service/java/com/android/permission/persistence/IoUtils.java @@ -20,6 +20,8 @@ import android.annotation.NonNull; /** * Utility class for IO. + * + * @hide */ public class IoUtils { diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 36a8b2cfc084..9abae528b474 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -444,6 +444,8 @@ message Atom { TvTunerStateChanged tv_tuner_state_changed = 276 [(module) = "framework"]; MediaOutputOpSwitchReported mediaoutput_op_switch_reported = 277 [(module) = "settings"]; + CellBroadcastMessageFiltered cb_message_filtered = + 278 [(module) = "cellbroadcast"]; // StatsdStats tracks platform atoms with ids upto 500. // Update StatsdStats::kMaxPushedAtomId when atom ids here approach that value. @@ -9134,8 +9136,10 @@ message IntegrityRulesPushed { /** * Logs when a cell broadcast message is received on the device. * - * Logged from CellBroadcastService module: + * Logged from Cell Broadcast module and platform: * packages/modules/CellBroadcastService/src/com/android/cellbroadcastservice/ + * packages/apps/CellBroadcastReceiver/ + * frameworks/opt/telephony/src/java/com/android/internal/telephony/CellBroadcastServiceManager.java */ message CellBroadcastMessageReported { // The type of Cell Broadcast message @@ -9146,8 +9150,40 @@ message CellBroadcastMessageReported { CDMA_SPC = 3; } + // The parts of the cell broadcast message pipeline + enum ReportSource { + UNKNOWN_SOURCE = 0; + FRAMEWORK = 1; + CB_SERVICE = 2; + CB_RECEIVER_APP = 3; + } + // GSM, CDMA, CDMA-SCP optional CbType type = 1; + + // The source of the report + optional ReportSource source = 2; +} + +/** + * Logs when a cell broadcast message is filtered out, or otherwise intentionally not sent to CBR. + * + * Logged from CellBroadcastService module: + * packages/modules/CellBroadcastService/src/com/android/cellbroadcastservice/ + */ +message CellBroadcastMessageFiltered { + enum FilterReason { + NOT_FILTERED = 0; + DUPLICATE_MESSAGE = 1; + GEOFENCED_MESSAGE = 2; + AREA_INFO_MESSAGE = 3; + } + + // GSM, CDMA, CDMA-SCP + optional CellBroadcastMessageReported.CbType type = 1; + + // The source of the report + optional FilterReason filter = 2; } /** @@ -9174,6 +9210,7 @@ message CellBroadcastMessageError { UNEXPECTED_GSM_MESSAGE_TYPE_FROM_FWK = 12; UNEXPECTED_CDMA_MESSAGE_TYPE_FROM_FWK = 13; UNEXPECTED_CDMA_SCP_MESSAGE_TYPE_FROM_FWK = 14; + NO_CONNECTION_TO_CB_SERVICE = 15; } // What kind of error occurred diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index cffa59c06a53..d275159e9f87 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -7388,6 +7388,10 @@ public final class ActivityThread extends ClientTransactionHandler { } } + public Bundle getCoreSettings() { + return mCoreSettings; + } + public int getIntCoreSetting(String key, int defaultValue) { synchronized (mResourcesManager) { if (mCoreSettings != null) { @@ -7397,6 +7401,18 @@ public final class ActivityThread extends ClientTransactionHandler { } } + /** + * Get the string value of the given key from core settings. + */ + public String getStringCoreSetting(String key, String defaultValue) { + synchronized (mResourcesManager) { + if (mCoreSettings != null) { + return mCoreSettings.getString(key, defaultValue); + } + return defaultValue; + } + } + float getFloatCoreSetting(String key, float defaultValue) { synchronized (mResourcesManager) { if (mCoreSettings != null) { diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index 10f7835b3d69..f9b48e710148 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -38,6 +38,7 @@ import android.content.res.Resources; import android.os.Build; import android.os.Bundle; import android.os.FileUtils; +import android.os.GraphicsEnvironment; import android.os.Handler; import android.os.IBinder; import android.os.Process; @@ -46,6 +47,7 @@ import android.os.StrictMode; import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; +import android.provider.Settings; import android.security.net.config.NetworkSecurityConfigProvider; import android.sysprop.VndkProperties; import android.text.TextUtils; @@ -824,6 +826,32 @@ public final class LoadedApk { final String librarySearchPath = TextUtils.join(File.pathSeparator, libPaths); + if (mActivityThread != null) { + final String gpuDebugApp = mActivityThread.getStringCoreSetting( + Settings.Global.GPU_DEBUG_APP, ""); + if (!gpuDebugApp.isEmpty() && mPackageName.equals(gpuDebugApp)) { + + // The current application is used to debug, attempt to get the debug layers. + try { + // Get the ApplicationInfo from PackageManager so that metadata fields present. + final ApplicationInfo ai = ActivityThread.getPackageManager() + .getApplicationInfo(mPackageName, PackageManager.GET_META_DATA, + UserHandle.myUserId()); + final String debugLayerPath = GraphicsEnvironment.getInstance() + .getDebugLayerPathsFromSettings(mActivityThread.getCoreSettings(), + ActivityThread.getPackageManager(), mPackageName, ai); + if (debugLayerPath != null) { + libraryPermittedPath += File.pathSeparator + debugLayerPath; + } + } catch (RemoteException e) { + // Unlikely to fail for applications, but in case of failure, something is wrong + // inside the system server, hence just skip. + Slog.e(ActivityThread.TAG, + "RemoteException when fetching debug layer paths for: " + mPackageName); + } + } + } + // If we're not asked to include code, we construct a classloader that has // no code path included. We still need to set up the library search paths // and permitted path because NativeActivity relies on it (it attempts to diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index ed75504529b9..fc4ccd072e75 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -1449,6 +1449,13 @@ public class PackageInstaller { /** {@hide} */ public static final int UID_UNKNOWN = -1; + /** + * This value is derived from the maximum file name length. No package above this limit + * can ever be successfully installed on the device. + * @hide + */ + public static final int MAX_PACKAGE_NAME_LENGTH = 255; + /** {@hide} */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) public int mode = MODE_INVALID; @@ -1642,6 +1649,8 @@ public class PackageInstaller { /** * Optionally set a label representing the app being installed. + * + * This value will be trimmed to the first 1000 characters. */ public void setAppLabel(@Nullable CharSequence appLabel) { this.appLabel = (appLabel != null) ? appLabel.toString() : null; @@ -1711,7 +1720,8 @@ public class PackageInstaller { * * <p>Initially, all restricted permissions are whitelisted but you can change * which ones are whitelisted by calling this method or the corresponding ones - * on the {@link PackageManager}. + * on the {@link PackageManager}. Only soft or hard restricted permissions on the current + * Android version are supported and any invalid entries will be removed. * * @see PackageManager#addWhitelistedRestrictedPermission(String, String, int) * @see PackageManager#removeWhitelistedRestrictedPermission(String, String, int) diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java index f354bdb5a08b..65ce1e7ef079 100644 --- a/core/java/android/content/pm/PackageItemInfo.java +++ b/core/java/android/content/pm/PackageItemInfo.java @@ -49,8 +49,16 @@ import java.util.Objects; * in the implementation of Parcelable in subclasses. */ public class PackageItemInfo { - /** The maximum length of a safe label, in characters */ - private static final int MAX_SAFE_LABEL_LENGTH = 50000; + + /** + * The maximum length of a safe label, in characters + * + * TODO(b/157997155): It may make sense to expose this publicly so that apps can check for the + * value and truncate the strings/use a different label, without having to hardcode and make + * assumptions about the value. + * @hide + */ + public static final int MAX_SAFE_LABEL_LENGTH = 1000; /** @hide */ public static final float DEFAULT_MAX_LABEL_SIZE_PX = 500f; diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index 034e6a7a06c4..df58a6c636f5 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -22,6 +22,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; @@ -93,8 +94,8 @@ public class GraphicsEnvironment { private static final int GAME_DRIVER_GLOBAL_OPT_IN_OFF = 3; private ClassLoader mClassLoader; - private String mLayerPath; - private String mDebugLayerPath; + private String mLibrarySearchPaths; + private String mLibraryPermittedPaths; /** * Set up GraphicsEnvironment @@ -185,118 +186,131 @@ public class GraphicsEnvironment { } /** - * Store the layer paths available to the loader. + * Store the class loader for namespace lookup later. */ public void setLayerPaths(ClassLoader classLoader, - String layerPath, - String debugLayerPath) { + String searchPaths, + String permittedPaths) { // We have to store these in the class because they are set up before we // have access to the Context to properly set up GraphicsEnvironment mClassLoader = classLoader; - mLayerPath = layerPath; - mDebugLayerPath = debugLayerPath; + mLibrarySearchPaths = searchPaths; + mLibraryPermittedPaths = permittedPaths; + } + + /** + * Returns the debug layer paths from settings. + * Returns null if: + * 1) The application process is not debuggable or layer injection metadata flag is not + * true; Or + * 2) ENABLE_GPU_DEBUG_LAYERS is not true; Or + * 3) Package name is not equal to GPU_DEBUG_APP. + */ + public String getDebugLayerPathsFromSettings( + Bundle coreSettings, IPackageManager pm, String packageName, + ApplicationInfo ai) { + if (!debugLayerEnabled(coreSettings, packageName, ai)) { + return null; + } + Log.i(TAG, "GPU debug layers enabled for " + packageName); + String debugLayerPaths = ""; + + // Grab all debug layer apps and add to paths. + final String gpuDebugLayerApps = + coreSettings.getString(Settings.Global.GPU_DEBUG_LAYER_APP, ""); + if (!gpuDebugLayerApps.isEmpty()) { + Log.i(TAG, "GPU debug layer apps: " + gpuDebugLayerApps); + // If a colon is present, treat this as multiple apps, so Vulkan and GLES + // layer apps can be provided at the same time. + final String[] layerApps = gpuDebugLayerApps.split(":"); + for (int i = 0; i < layerApps.length; i++) { + String paths = getDebugLayerAppPaths(pm, layerApps[i]); + if (!paths.isEmpty()) { + // Append the path so files placed in the app's base directory will + // override the external path + debugLayerPaths += paths + File.pathSeparator; + } + } + } + return debugLayerPaths; } /** * Return the debug layer app's on-disk and in-APK lib directories */ - private static String getDebugLayerAppPaths(PackageManager pm, String app) { + private static String getDebugLayerAppPaths(IPackageManager pm, String packageName) { final ApplicationInfo appInfo; try { - appInfo = pm.getApplicationInfo(app, PackageManager.MATCH_ALL); - } catch (PackageManager.NameNotFoundException e) { - Log.w(TAG, "Debug layer app '" + app + "' not installed"); - - return null; + appInfo = pm.getApplicationInfo(packageName, PackageManager.MATCH_ALL, + UserHandle.myUserId()); + } catch (RemoteException e) { + return ""; + } + if (appInfo == null) { + Log.w(TAG, "Debug layer app '" + packageName + "' not installed"); } final String abi = chooseAbi(appInfo); - final StringBuilder sb = new StringBuilder(); sb.append(appInfo.nativeLibraryDir) - .append(File.pathSeparator); - sb.append(appInfo.sourceDir) + .append(File.pathSeparator) + .append(appInfo.sourceDir) .append("!/lib/") .append(abi); final String paths = sb.toString(); - if (DEBUG) Log.v(TAG, "Debug layer app libs: " + paths); return paths; } + private boolean debugLayerEnabled(Bundle coreSettings, String packageName, ApplicationInfo ai) { + // Only enable additional debug functionality if the following conditions are met: + // 1. App is debuggable or device is rooted or layer injection metadata flag is true + // 2. ENABLE_GPU_DEBUG_LAYERS is true + // 3. Package name is equal to GPU_DEBUG_APP + if (!isDebuggable() && !canInjectLayers(ai)) { + return false; + } + final int enable = coreSettings.getInt(Settings.Global.ENABLE_GPU_DEBUG_LAYERS, 0); + if (enable == 0) { + return false; + } + final String gpuDebugApp = coreSettings.getString(Settings.Global.GPU_DEBUG_APP, ""); + if (packageName == null + || (gpuDebugApp.isEmpty() || packageName.isEmpty()) + || !gpuDebugApp.equals(packageName)) { + return false; + } + return true; + } + /** * Set up layer search paths for all apps - * If debuggable, check for additional debug settings */ private void setupGpuLayers( Context context, Bundle coreSettings, PackageManager pm, String packageName, ApplicationInfo ai) { + final boolean enabled = debugLayerEnabled(coreSettings, packageName, ai); String layerPaths = ""; + if (enabled) { + layerPaths = mLibraryPermittedPaths; - // Only enable additional debug functionality if the following conditions are met: - // 1. App is debuggable or device is rooted or layer injection metadata flag is true - // 2. ENABLE_GPU_DEBUG_LAYERS is true - // 3. Package name is equal to GPU_DEBUG_APP + final String layers = coreSettings.getString(Settings.Global.GPU_DEBUG_LAYERS); + Log.i(TAG, "Vulkan debug layer list: " + layers); + if (layers != null && !layers.isEmpty()) { + setDebugLayers(layers); + } - if (isDebuggable() || canInjectLayers(ai)) { - - final int enable = coreSettings.getInt(Settings.Global.ENABLE_GPU_DEBUG_LAYERS, 0); - - if (enable != 0) { - - final String gpuDebugApp = coreSettings.getString(Settings.Global.GPU_DEBUG_APP); - - if ((gpuDebugApp != null && packageName != null) - && (!gpuDebugApp.isEmpty() && !packageName.isEmpty()) - && gpuDebugApp.equals(packageName)) { - Log.i(TAG, "GPU debug layers enabled for " + packageName); - - // Prepend the debug layer path as a searchable path. - // This will ensure debug layers added will take precedence over - // the layers specified by the app. - layerPaths = mDebugLayerPath + ":"; - - // If there is a debug layer app specified, add its path. - final String gpuDebugLayerApp = - coreSettings.getString(Settings.Global.GPU_DEBUG_LAYER_APP); - - if (gpuDebugLayerApp != null && !gpuDebugLayerApp.isEmpty()) { - Log.i(TAG, "GPU debug layer app: " + gpuDebugLayerApp); - // If a colon is present, treat this as multiple apps, so Vulkan and GLES - // layer apps can be provided at the same time. - String[] layerApps = gpuDebugLayerApp.split(":"); - for (int i = 0; i < layerApps.length; i++) { - String paths = getDebugLayerAppPaths(pm, layerApps[i]); - if (paths != null) { - // Append the path so files placed in the app's base directory will - // override the external path - layerPaths += paths + ":"; - } - } - } - - final String layers = coreSettings.getString(Settings.Global.GPU_DEBUG_LAYERS); - - Log.i(TAG, "Vulkan debug layer list: " + layers); - if (layers != null && !layers.isEmpty()) { - setDebugLayers(layers); - } - - final String layersGLES = - coreSettings.getString(Settings.Global.GPU_DEBUG_LAYERS_GLES); - - Log.i(TAG, "GLES debug layer list: " + layersGLES); - if (layersGLES != null && !layersGLES.isEmpty()) { - setDebugLayersGLES(layersGLES); - } - } + final String layersGLES = + coreSettings.getString(Settings.Global.GPU_DEBUG_LAYERS_GLES); + Log.i(TAG, "GLES debug layer list: " + layersGLES); + if (layersGLES != null && !layersGLES.isEmpty()) { + setDebugLayersGLES(layersGLES); } } // Include the app's lib directory in all cases - layerPaths += mLayerPath; - + layerPaths += mLibrarySearchPaths; setLayerPaths(mClassLoader, layerPaths); } diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 7845200f4bf7..a8391c2b5461 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -4090,13 +4090,6 @@ public class UserManager { public static int getMaxSupportedUsers() { // Don't allow multiple users on certain builds if (android.os.Build.ID.startsWith("JVP")) return 1; - if (ActivityManager.isLowRamDeviceStatic()) { - // Low-ram devices are Svelte. Most of the time they don't get multi-user. - if ((Resources.getSystem().getConfiguration().uiMode & Configuration.UI_MODE_TYPE_MASK) - != Configuration.UI_MODE_TYPE_TELEVISION) { - return 1; - } - } return SystemProperties.getInt("fw.max_users", Resources.getSystem().getInteger(R.integer.config_multiuserMaximumUsers)); } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 1b19e1290121..52764d568f29 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1778,6 +1778,15 @@ public final class Settings { = "android.settings.NOTIFICATION_SETTINGS"; /** + * Activity Action: Show conversation settings. + * + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_CONVERSATION_SETTINGS + = "android.settings.CONVERSATION_SETTINGS"; + + /** * Activity Action: Show notification history screen. * * @hide diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index b34268d04238..a2489b9b68d9 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -4325,6 +4325,15 @@ public final class Telephony { public static final String ETWS_WARNING_TYPE = "etws_warning_type"; /** + * ETWS (Earthquake and Tsunami Warning System) primary message or not (ETWS alerts only). + * <p>See {@link android.telephony.SmsCbEtwsInfo}</p> + * <P>Type: BOOLEAN</P> + * + * @hide // TODO: Unhide this for S. + */ + public static final String ETWS_IS_PRIMARY = "etws_is_primary"; + + /** * CMAS (Commercial Mobile Alert System) message class (CMAS alerts only). * <p>See {@link android.telephony.SmsCbCmasInfo}</p> * <P>Type: INTEGER</P> @@ -4464,37 +4473,6 @@ public final class Telephony { CMAS_URGENCY, CMAS_CERTAINTY }; - - /** - * Query columns for instantiating {@link android.telephony.SmsCbMessage} objects. - * @hide - */ - public static final String[] QUERY_COLUMNS_FWK = { - _ID, - SLOT_INDEX, - SUBSCRIPTION_ID, - GEOGRAPHICAL_SCOPE, - PLMN, - LAC, - CID, - SERIAL_NUMBER, - SERVICE_CATEGORY, - LANGUAGE_CODE, - MESSAGE_BODY, - MESSAGE_FORMAT, - MESSAGE_PRIORITY, - ETWS_WARNING_TYPE, - CMAS_MESSAGE_CLASS, - CMAS_CATEGORY, - CMAS_RESPONSE_TYPE, - CMAS_SEVERITY, - CMAS_URGENCY, - CMAS_CERTAINTY, - RECEIVED_TIME, - MESSAGE_BROADCASTED, - GEOMETRIES, - MAXIMUM_WAIT_TIME - }; } /** diff --git a/core/java/android/service/autofill/InlineSuggestionRoot.java b/core/java/android/service/autofill/InlineSuggestionRoot.java index c879653859d8..16c3f1d4e476 100644 --- a/core/java/android/service/autofill/InlineSuggestionRoot.java +++ b/core/java/android/service/autofill/InlineSuggestionRoot.java @@ -58,7 +58,9 @@ public class InlineSuggestionRoot extends FrameLayout { case MotionEvent.ACTION_DOWN: { mDownX = event.getX(); mDownY = event.getY(); - } break; + } + // Intentionally fall through to the next case so that when the window is obscured + // we transfer the touch to the remote IME window and don't handle it locally. case MotionEvent.ACTION_MOVE: { final float distance = MathUtils.dist(mDownX, mDownY, diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java index a9af59543c78..f6c72c4eefbc 100644 --- a/core/java/android/view/GestureDetector.java +++ b/core/java/android/view/GestureDetector.java @@ -289,11 +289,6 @@ public class GestureDetector { private VelocityTracker mVelocityTracker; /** - * True if the detector can throw exception when touch steam is unexpected . - */ - private boolean mExceptionForTouchStream; - - /** * Consistency verifier for debugging purposes. */ private final InputEventConsistencyVerifier mInputEventConsistencyVerifier = @@ -472,8 +467,6 @@ public class GestureDetector { mTouchSlopSquare = touchSlop * touchSlop; mDoubleTapTouchSlopSquare = doubleTapTouchSlop * doubleTapTouchSlop; mDoubleTapSlopSquare = doubleTapSlop * doubleTapSlop; - mExceptionForTouchStream = context != null - && context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R; } /** @@ -646,13 +639,6 @@ public class GestureDetector { break; case MotionEvent.ACTION_MOVE: - if (mExceptionForTouchStream && !mStillDown) { - throw new IllegalStateException("Incomplete event stream received: " - + "Received ACTION_MOVE before ACTION_DOWN. ACTION_DOWN must precede " - + "ACTION_MOVE following ACTION_UP or ACTION_CANCEL, or when this " - + "GestureDetector has not yet received any events."); - } - if (mInLongPress || mInContextClick) { break; } diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java index 7042f29fc4e4..4a6551176198 100644 --- a/core/java/android/webkit/WebChromeClient.java +++ b/core/java/android/webkit/WebChromeClient.java @@ -205,6 +205,8 @@ public class WebChromeClient { * <p>Note that if the {@link WebChromeClient} is set to be {@code null}, * or if {@link WebChromeClient} is not set at all, the default dialog will * be suppressed and Javascript execution will continue immediately. + * <p>Note that the default dialog does not inherit the {@link + * android.view.Display#FLAG_SECURE} flag from the parent window. * * @param view The WebView that initiated the callback. * @param url The url of the page requesting the dialog. @@ -240,6 +242,8 @@ public class WebChromeClient { * or if {@link WebChromeClient} is not set at all, the default dialog will * be suppressed and the default value of {@code false} will be returned to * the JavaScript code immediately. + * <p>Note that the default dialog does not inherit the {@link + * android.view.Display#FLAG_SECURE} flag from the parent window. * * @param view The WebView that initiated the callback. * @param url The url of the page requesting the dialog. @@ -274,6 +278,8 @@ public class WebChromeClient { * or if {@link WebChromeClient} is not set at all, the default dialog will * be suppressed and {@code null} will be returned to the JavaScript code * immediately. + * <p>Note that the default dialog does not inherit the {@link + * android.view.Display#FLAG_SECURE} flag from the parent window. * * @param view The WebView that initiated the callback. * @param url The url of the page requesting the dialog. @@ -308,6 +314,8 @@ public class WebChromeClient { * <p>Note that if the {@link WebChromeClient} is set to be {@code null}, * or if {@link WebChromeClient} is not set at all, the default dialog will * be suppressed and the navigation will be resumed immediately. + * <p>Note that the default dialog does not inherit the {@link + * android.view.Display#FLAG_SECURE} flag from the parent window. * * @param view The WebView that initiated the callback. * @param url The url of the page requesting the dialog. diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java index 493865ac563f..b723db287823 100644 --- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java +++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java @@ -151,6 +151,13 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter { mOnProfileSelectedListener.onProfileSelected(position); } } + + @Override + public void onPageScrollStateChanged(int state) { + if (mOnProfileSelectedListener != null) { + mOnProfileSelectedListener.onProfilePageStateChanged(state); + } + } }); viewPager.setAdapter(this); viewPager.setCurrentItem(mCurrentPage); @@ -606,6 +613,17 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter { * {@link #PROFILE_WORK} if the work profile was selected. */ void onProfileSelected(int profileIndex); + + + /** + * Callback for when the scroll state changes. Useful for discovering when the user begins + * dragging, when the pager is automatically settling to the current page, or when it is + * fully stopped/idle. + * @param state {@link ViewPager#SCROLL_STATE_IDLE}, {@link ViewPager#SCROLL_STATE_DRAGGING} + * or {@link ViewPager#SCROLL_STATE_SETTLING} + * @see ViewPager.OnPageChangeListener#onPageScrollStateChanged + */ + void onProfilePageStateChanged(int state); } /** diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 2a43287a3ae3..049a76c89815 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -102,6 +102,7 @@ import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.view.ViewTreeObserver; +import android.view.WindowInsets; import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; import android.widget.Button; @@ -129,6 +130,7 @@ import com.android.internal.util.FrameworkStatsLog; import com.android.internal.widget.GridLayoutManager; import com.android.internal.widget.RecyclerView; import com.android.internal.widget.ResolverDrawerLayout; +import com.android.internal.widget.ViewPager; import com.google.android.collect.Lists; @@ -204,6 +206,10 @@ public class ChooserActivity extends ResolverActivity implements public static final int SELECTION_TYPE_STANDARD = 3; public static final int SELECTION_TYPE_COPY = 4; + private static final int SCROLL_STATUS_IDLE = 0; + private static final int SCROLL_STATUS_SCROLLING_VERTICAL = 1; + private static final int SCROLL_STATUS_SCROLLING_HORIZONTAL = 2; + // statsd logger wrapper protected ChooserActivityLogger mChooserActivityLogger; @@ -293,6 +299,7 @@ public class ChooserActivity extends ResolverActivity implements protected MetricsLogger mMetricsLogger; private ContentPreviewCoordinator mPreviewCoord; + private int mScrollStatus = SCROLL_STATUS_IDLE; @VisibleForTesting protected ChooserMultiProfilePagerAdapter mChooserMultiProfilePagerAdapter; @@ -2680,7 +2687,7 @@ public class ChooserActivity extends ResolverActivity implements offset = Math.min(offset, minHeight); } } else { - ViewGroup currentEmptyStateView = getCurrentEmptyStateView(); + ViewGroup currentEmptyStateView = getActiveEmptyStateView(); if (currentEmptyStateView.getVisibility() == View.VISIBLE) { offset += currentEmptyStateView.getHeight(); } @@ -2705,7 +2712,7 @@ public class ChooserActivity extends ResolverActivity implements return -1; } - private ViewGroup getCurrentEmptyStateView() { + private ViewGroup getActiveEmptyStateView() { int currentPage = mChooserMultiProfilePagerAdapter.getCurrentPage(); return mChooserMultiProfilePagerAdapter.getItem(currentPage).getEmptyStateView(); } @@ -2822,10 +2829,20 @@ public class ChooserActivity extends ResolverActivity implements final float defaultElevation = elevatedView.getElevation(); final float chooserHeaderScrollElevation = getResources().getDimensionPixelSize(R.dimen.chooser_header_scroll_elevation); - mChooserMultiProfilePagerAdapter.getActiveAdapterView().addOnScrollListener( new RecyclerView.OnScrollListener() { public void onScrollStateChanged(RecyclerView view, int scrollState) { + if (scrollState == RecyclerView.SCROLL_STATE_IDLE) { + if (mScrollStatus == SCROLL_STATUS_SCROLLING_VERTICAL) { + mScrollStatus = SCROLL_STATUS_IDLE; + setHorizontalScrollingEnabled(true); + } + } else if (scrollState == RecyclerView.SCROLL_STATE_DRAGGING) { + if (mScrollStatus == SCROLL_STATUS_IDLE) { + mScrollStatus = SCROLL_STATUS_SCROLLING_VERTICAL; + setHorizontalScrollingEnabled(false); + } + } } public void onScrolled(RecyclerView view, int dx, int dy) { @@ -3026,8 +3043,42 @@ public class ChooserActivity extends ResolverActivity implements currentRootAdapter.updateDirectShareExpansion(); } - void prepareIntentForCrossProfileLaunch(Intent intent) { - intent.fixUris(UserHandle.myUserId()); + @Override + protected WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { + if (shouldShowTabs()) { + mChooserMultiProfilePagerAdapter + .setEmptyStateBottomOffset(insets.getSystemWindowInsetBottom()); + mChooserMultiProfilePagerAdapter.setupContainerPadding( + getActiveEmptyStateView().findViewById(R.id.resolver_empty_state_container)); + } + return super.onApplyWindowInsets(v, insets); + } + + private void setHorizontalScrollingEnabled(boolean enabled) { + ResolverViewPager viewPager = findViewById(R.id.profile_pager); + viewPager.setSwipingEnabled(enabled); + } + + private void setVerticalScrollEnabled(boolean enabled) { + ChooserGridLayoutManager layoutManager = + (ChooserGridLayoutManager) mChooserMultiProfilePagerAdapter.getActiveAdapterView() + .getLayoutManager(); + layoutManager.setVerticalScrollEnabled(enabled); + } + + @Override + void onHorizontalSwipeStateChanged(int state) { + if (state == ViewPager.SCROLL_STATE_DRAGGING) { + if (mScrollStatus == SCROLL_STATUS_IDLE) { + mScrollStatus = SCROLL_STATUS_SCROLLING_HORIZONTAL; + setVerticalScrollEnabled(false); + } + } else if (state == ViewPager.SCROLL_STATE_IDLE) { + if (mScrollStatus == SCROLL_STATUS_SCROLLING_VERTICAL) { + mScrollStatus = SCROLL_STATUS_IDLE; + setVerticalScrollEnabled(true); + } + } } /** diff --git a/core/java/com/android/internal/app/ChooserGridLayoutManager.java b/core/java/com/android/internal/app/ChooserGridLayoutManager.java index 317a987cf359..c50ebd9562c9 100644 --- a/core/java/com/android/internal/app/ChooserGridLayoutManager.java +++ b/core/java/com/android/internal/app/ChooserGridLayoutManager.java @@ -28,6 +28,8 @@ import com.android.internal.widget.RecyclerView; */ public class ChooserGridLayoutManager extends GridLayoutManager { + private boolean mVerticalScrollEnabled = true; + /** * Constructor used when layout manager is set in XML by RecyclerView attribute * "layoutManager". If spanCount is not specified in the XML, it defaults to a @@ -67,4 +69,13 @@ public class ChooserGridLayoutManager extends GridLayoutManager { // Do not count the footer view in the official count return super.getRowCountForAccessibility(recycler, state) - 1; } + + void setVerticalScrollEnabled(boolean verticalScrollEnabled) { + mVerticalScrollEnabled = verticalScrollEnabled; + } + + @Override + public boolean canScrollVertically() { + return mVerticalScrollEnabled && super.canScrollVertically(); + } } diff --git a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java index 774be3c9c4b8..ffa6041721c6 100644 --- a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java +++ b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java @@ -38,6 +38,7 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd private final ChooserProfileDescriptor[] mItems; private final boolean mIsSendAction; + private int mBottomOffset; ChooserMultiProfilePagerAdapter(Context context, ChooserActivity.ChooserGridAdapter adapter, @@ -245,6 +246,16 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd } } + void setEmptyStateBottomOffset(int bottomOffset) { + mBottomOffset = bottomOffset; + } + + @Override + protected void setupContainerPadding(View container) { + container.setPadding(container.getPaddingLeft(), container.getPaddingTop(), + container.getPaddingRight(), container.getPaddingBottom() + mBottomOffset); + } + class ChooserProfileDescriptor extends ProfileDescriptor { private ChooserActivity.ChooserGridAdapter chooserGridAdapter; private RecyclerView recyclerView; diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java index e65d1fe9ce53..61a52bcc03f9 100644 --- a/core/java/com/android/internal/app/IntentForwarderActivity.java +++ b/core/java/com/android/internal/app/IntentForwarderActivity.java @@ -18,6 +18,7 @@ package com.android.internal.app; import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY; +import static com.android.internal.app.ResolverActivity.EXTRA_CALLING_USER; import static com.android.internal.app.ResolverActivity.EXTRA_SELECTED_PROFILE; import android.annotation.Nullable; @@ -246,6 +247,7 @@ public class IntentForwarderActivity extends Activity { int selectedProfile = findSelectedProfile(className); sanitizeIntent(intentReceived); intentReceived.putExtra(EXTRA_SELECTED_PROFILE, selectedProfile); + intentReceived.putExtra(EXTRA_CALLING_USER, UserHandle.of(callingUserId)); startActivityAsCaller(intentReceived, null, null, false, userId); finish(); } diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index f96f560fc60f..f8eec57bbd6b 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -184,6 +184,18 @@ public class ResolverActivity extends Activity implements static final String EXTRA_SELECTED_PROFILE = "com.android.internal.app.ResolverActivity.EXTRA_SELECTED_PROFILE"; + /** + * {@link UserHandle} extra to indicate the user of the user that the starting intent + * originated from. + * <p>This is not necessarily the same as {@link #getUserId()} or {@link UserHandle#myUserId()}, + * as there are edge cases when the intent resolver is launched in the other profile. + * For example, when we have 0 resolved apps in current profile and multiple resolved + * apps in the other profile, opening a link from the current profile launches the intent + * resolver in the other one. b/148536209 for more info. + */ + static final String EXTRA_CALLING_USER = + "com.android.internal.app.ResolverActivity.EXTRA_CALLING_USER"; + static final int PROFILE_PERSONAL = AbstractMultiProfilePagerAdapter.PROFILE_PERSONAL; static final int PROFILE_WORK = AbstractMultiProfilePagerAdapter.PROFILE_WORK; @@ -470,17 +482,20 @@ public class ResolverActivity extends Activity implements // the intent resolver is started in the other profile. Since this is the only case when // this happens, we check for it here and set the current profile's tab. int selectedProfile = getCurrentProfile(); - UserHandle intentUser = UserHandle.of(getLaunchingUserId()); + UserHandle intentUser = getIntent().hasExtra(EXTRA_CALLING_USER) + ? getIntent().getParcelableExtra(EXTRA_CALLING_USER) + : getUser(); if (!getUser().equals(intentUser)) { if (getPersonalProfileUserHandle().equals(intentUser)) { selectedProfile = PROFILE_PERSONAL; } else if (getWorkProfileUserHandle().equals(intentUser)) { selectedProfile = PROFILE_WORK; } - } - int selectedProfileExtra = getSelectedProfileExtra(); - if (selectedProfileExtra != -1) { - selectedProfile = selectedProfileExtra; + } else { + int selectedProfileExtra = getSelectedProfileExtra(); + if (selectedProfileExtra != -1) { + selectedProfile = selectedProfileExtra; + } } // We only show the default app for the profile of the current user. The filterLastUsed // flag determines whether to show a default app and that app is not shown in the @@ -535,22 +550,6 @@ public class ResolverActivity extends Activity implements return selectedProfile; } - /** - * Returns the user id of the user that the starting intent originated from. - * <p>This is not necessarily equal to {@link #getUserId()} or {@link UserHandle#myUserId()}, - * as there are edge cases when the intent resolver is launched in the other profile. - * For example, when we have 0 resolved apps in current profile and multiple resolved apps - * in the other profile, opening a link from the current profile launches the intent resolver - * in the other one. b/148536209 for more info. - */ - private int getLaunchingUserId() { - int contentUserHint = getIntent().getContentUserHint(); - if (contentUserHint == UserHandle.USER_CURRENT) { - return UserHandle.myUserId(); - } - return contentUserHint; - } - protected @Profile int getCurrentProfile() { return (UserHandle.myUserId() == UserHandle.USER_SYSTEM ? PROFILE_PERSONAL : PROFILE_WORK); } @@ -1250,7 +1249,9 @@ public class ResolverActivity extends Activity implements return true; } - void prepareIntentForCrossProfileLaunch(Intent intent) {} + private void prepareIntentForCrossProfileLaunch(Intent intent) { + intent.fixUris(UserHandle.myUserId()); + } private boolean isLaunchingTargetInOtherProfile() { return mMultiProfilePagerAdapter.getCurrentUserHandle().getIdentifier() @@ -1653,10 +1654,18 @@ public class ResolverActivity extends Activity implements viewPager.setVisibility(View.VISIBLE); tabHost.setCurrentTab(mMultiProfilePagerAdapter.getCurrentPage()); mMultiProfilePagerAdapter.setOnProfileSelectedListener( - index -> { - tabHost.setCurrentTab(index); - resetButtonBar(); - resetCheckedItem(); + new AbstractMultiProfilePagerAdapter.OnProfileSelectedListener() { + @Override + public void onProfileSelected(int index) { + tabHost.setCurrentTab(index); + resetButtonBar(); + resetCheckedItem(); + } + + @Override + public void onProfilePageStateChanged(int state) { + onHorizontalSwipeStateChanged(state); + } }); mMultiProfilePagerAdapter.setOnSwitchOnWorkSelectedListener( () -> { @@ -1668,6 +1677,8 @@ public class ResolverActivity extends Activity implements findViewById(R.id.resolver_tab_divider).setVisibility(View.VISIBLE); } + void onHorizontalSwipeStateChanged(int state) {} + private void maybeHideDivider() { if (!isIntentPicker()) { return; diff --git a/core/java/com/android/internal/app/ResolverViewPager.java b/core/java/com/android/internal/app/ResolverViewPager.java index 4eb6e3bd2071..9cdfc2f5c763 100644 --- a/core/java/com/android/internal/app/ResolverViewPager.java +++ b/core/java/com/android/internal/app/ResolverViewPager.java @@ -18,6 +18,7 @@ package com.android.internal.app; import android.content.Context; import android.util.AttributeSet; +import android.view.MotionEvent; import android.view.View; import com.android.internal.widget.ViewPager; @@ -30,6 +31,8 @@ import com.android.internal.widget.ViewPager; */ public class ResolverViewPager extends ViewPager { + private boolean mSwipingEnabled = true; + public ResolverViewPager(Context context) { super(context); } @@ -70,4 +73,13 @@ public class ResolverViewPager extends ViewPager { heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); super.onMeasure(widthMeasureSpec, heightMeasureSpec); } + + void setSwipingEnabled(boolean swipingEnabled) { + mSwipingEnabled = swipingEnabled; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + return mSwipingEnabled && super.onInterceptTouchEvent(ev); + } } diff --git a/core/java/com/android/internal/logging/UiEventLogger.java b/core/java/com/android/internal/logging/UiEventLogger.java index 67ffd4d93404..5212265f6c8a 100644 --- a/core/java/com/android/internal/logging/UiEventLogger.java +++ b/core/java/com/android/internal/logging/UiEventLogger.java @@ -60,4 +60,28 @@ public interface UiEventLogger { */ void logWithInstanceId(@NonNull UiEventEnum event, int uid, @Nullable String packageName, @Nullable InstanceId instance); + + /** + * Log an event with ranked-choice information along with package. + * Does nothing if event.getId() <= 0. + * @param event an enum implementing UiEventEnum interface. + * @param uid the uid of the relevant app, if known (0 otherwise). + * @param packageName the package name of the relevant app, if known (null otherwise). + * @param position the position picked. + */ + void logWithPosition(@NonNull UiEventEnum event, int uid, @Nullable String packageName, + int position); + + /** + * Log an event with ranked-choice information along with package and instance ID. + * Does nothing if event.getId() <= 0. + * @param event an enum implementing UiEventEnum interface. + * @param uid the uid of the relevant app, if known (0 otherwise). + * @param packageName the package name of the relevant app, if known (null otherwise). + * @param instance An identifier obtained from an InstanceIdSequence. If null, reduces to + * logWithPosition(). + * @param position the position picked. + */ + void logWithInstanceIdAndPosition(@NonNull UiEventEnum event, int uid, + @Nullable String packageName, @Nullable InstanceId instance, int position); } diff --git a/core/java/com/android/internal/logging/UiEventLoggerImpl.java b/core/java/com/android/internal/logging/UiEventLoggerImpl.java index 4d171ec8a3a8..c9156c13aae3 100644 --- a/core/java/com/android/internal/logging/UiEventLoggerImpl.java +++ b/core/java/com/android/internal/logging/UiEventLoggerImpl.java @@ -48,4 +48,31 @@ public class UiEventLoggerImpl implements UiEventLogger { log(event, uid, packageName); } } + + @Override + public void logWithPosition(UiEventEnum event, int uid, String packageName, int position) { + final int eventID = event.getId(); + if (eventID > 0) { + FrameworkStatsLog.write(FrameworkStatsLog.RANKING_SELECTED, + /* event_id = 1 */ eventID, + /* package_name = 2 */ packageName, + /* instance_id = 3 */ 0, + /* position_picked = 4 */ position); + } + } + + @Override + public void logWithInstanceIdAndPosition(UiEventEnum event, int uid, String packageName, + InstanceId instance, int position) { + final int eventID = event.getId(); + if ((eventID > 0) && (instance != null)) { + FrameworkStatsLog.write(FrameworkStatsLog.RANKING_SELECTED, + /* event_id = 1 */ eventID, + /* package_name = 2 */ packageName, + /* instance_id = 3 */ instance.getId(), + /* position_picked = 4 */ position); + } else { + logWithPosition(event, uid, packageName, position); + } + } } diff --git a/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java b/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java index 180ab0810f5b..2d09434807a6 100644 --- a/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java +++ b/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java @@ -35,13 +35,15 @@ public class UiEventLoggerFake implements UiEventLogger { public final int eventId; public final int uid; public final String packageName; - public final InstanceId instanceId; // Used only for WithInstanceId variant + public final InstanceId instanceId; // Used only for WithInstanceId variants + public final int position; // Used only for Position variants FakeUiEvent(int eventId, int uid, String packageName) { this.eventId = eventId; this.uid = uid; this.packageName = packageName; this.instanceId = null; + this.position = 0; } FakeUiEvent(int eventId, int uid, String packageName, InstanceId instanceId) { @@ -49,6 +51,15 @@ public class UiEventLoggerFake implements UiEventLogger { this.uid = uid; this.packageName = packageName; this.instanceId = instanceId; + this.position = 0; + } + + FakeUiEvent(int eventId, int uid, String packageName, InstanceId instanceId, int position) { + this.eventId = eventId; + this.uid = uid; + this.packageName = packageName; + this.instanceId = instanceId; + this.position = position; } } @@ -92,4 +103,21 @@ public class UiEventLoggerFake implements UiEventLogger { mLogs.add(new FakeUiEvent(eventId, uid, packageName, instance)); } } + + @Override + public void logWithPosition(UiEventEnum event, int uid, String packageName, int position) { + final int eventId = event.getId(); + if (eventId > 0) { + mLogs.add(new FakeUiEvent(eventId, uid, packageName, null, position)); + } + } + + @Override + public void logWithInstanceIdAndPosition(UiEventEnum event, int uid, String packageName, + InstanceId instance, int position) { + final int eventId = event.getId(); + if (eventId > 0) { + mLogs.add(new FakeUiEvent(eventId, uid, packageName, instance, position)); + } + } } diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index fc2005a31696..3d8cae8e74d0 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -526,8 +526,16 @@ static void UnsetChldSignalHandler() { // Calls POSIX setgroups() using the int[] object as an argument. // A nullptr argument is tolerated. -static void SetGids(JNIEnv* env, jintArray managed_gids, fail_fn_t fail_fn) { +static void SetGids(JNIEnv* env, jintArray managed_gids, jboolean is_child_zygote, + fail_fn_t fail_fn) { if (managed_gids == nullptr) { + if (is_child_zygote) { + // For child zygotes like webview and app zygote, we want to clear out + // any supplemental groups the parent zygote had. + if (setgroups(0, NULL) == -1) { + fail_fn(CREATE_ERROR("Failed to remove supplementary groups for child zygote")); + } + } return; } @@ -1665,7 +1673,7 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, } } - SetGids(env, gids, fail_fn); + SetGids(env, gids, is_child_zygote, fail_fn); SetRLimits(env, rlimits, fail_fn); if (need_pre_initialize_native_bridge) { diff --git a/core/proto/android/server/connectivity/data_stall_event.proto b/core/proto/android/server/connectivity/data_stall_event.proto index 23fcf6ebc2cc..787074ba494e 100644 --- a/core/proto/android/server/connectivity/data_stall_event.proto +++ b/core/proto/android/server/connectivity/data_stall_event.proto @@ -32,6 +32,7 @@ enum ApBand { AP_BAND_UNKNOWN = 0; AP_BAND_2GHZ = 1; AP_BAND_5GHZ = 2; + AP_BAND_6GHZ = 3; } // Refer to definition in TelephonyManager.java. diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index fd8460f9c478..464a47002b17 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3656,7 +3656,8 @@ <p>The package installer v2 APIs are still a work in progress and we're currently validating they work in all scenarios. <p>Not for use by third-party applications. - TODO(b/152310230): remove this permission once the APIs are confirmed to be sufficient. + TODO(b/152310230): use this permission to protect only Incremental installations + once the APIs are confirmed to be sufficient. @hide --> <permission android:name="com.android.permission.USE_INSTALLER_V2" diff --git a/core/tests/PackageInstallerSessions/Android.bp b/core/tests/PackageInstallerSessions/Android.bp new file mode 100644 index 000000000000..e74f30ee10a4 --- /dev/null +++ b/core/tests/PackageInstallerSessions/Android.bp @@ -0,0 +1,42 @@ +// +// Copyright 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +android_test { + name: "FrameworksCorePackageInstallerSessionsTests", + + srcs: [ + "src/**/*.kt", + ], + static_libs: [ + "androidx.test.rules", + "compatibility-device-util-axt", + "frameworks-base-testutils", + "platform-test-annotations", + "testng", + "truth-prebuilt", + ], + + libs: [ + "android.test.runner", + "android.test.base", + "framework", + "framework-res", + ], + + platform_apis: true, + sdk_version: "core_platform", + test_suites: ["device-tests"], +} diff --git a/core/tests/PackageInstallerSessions/AndroidManifest.xml b/core/tests/PackageInstallerSessions/AndroidManifest.xml new file mode 100644 index 000000000000..5b22d2b4f3e3 --- /dev/null +++ b/core/tests/PackageInstallerSessions/AndroidManifest.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + --> + +<manifest + xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.frameworks.coretests.package_installer_sessions" + > + + <application> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.frameworks.coretests.package_installer_sessions"/> +</manifest> diff --git a/core/tests/PackageInstallerSessions/src/android/content/pm/PackageSessionTests.kt b/core/tests/PackageInstallerSessions/src/android/content/pm/PackageSessionTests.kt new file mode 100644 index 000000000000..494c92a8aa3f --- /dev/null +++ b/core/tests/PackageInstallerSessions/src/android/content/pm/PackageSessionTests.kt @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm + +import android.content.Context +import android.content.pm.PackageInstaller.SessionParams +import android.platform.test.annotations.Presubmit +import androidx.test.InstrumentationRegistry +import androidx.test.filters.LargeTest +import com.android.compatibility.common.util.ShellIdentityUtils +import com.google.common.truth.Truth.assertThat +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.testng.Assert.assertThrows +import kotlin.random.Random + +/** + * For verifying public [PackageInstaller] session APIs. This differs from + * [com.android.server.pm.PackageInstallerSessionTest] in services because that mocks the session, + * whereas this test uses the installer on device. + */ +@Presubmit +class PackageSessionTests { + + companion object { + /** + * Permissions marked "hardRestricted" or "softRestricted" in core/res/AndroidManifest.xml. + */ + private val RESTRICTED_PERMISSIONS = listOf( + "android.permission.SEND_SMS", + "android.permission.RECEIVE_SMS", + "android.permission.READ_SMS", + "android.permission.RECEIVE_WAP_PUSH", + "android.permission.RECEIVE_MMS", + "android.permission.READ_CELL_BROADCASTS", + "android.permission.ACCESS_BACKGROUND_LOCATION", + "android.permission.READ_CALL_LOG", + "android.permission.WRITE_CALL_LOG", + "android.permission.PROCESS_OUTGOING_CALLS" + ) + } + + private val context: Context = InstrumentationRegistry.getContext() + + private val installer = context.packageManager.packageInstaller + + @Before + @After + fun abandonAllSessions() { + installer.mySessions.asSequence() + .map { it.sessionId } + .forEach { + try { + installer.abandonSession(it) + } catch (ignored: Exception) { + // Querying for sessions checks by calling package name, but abandoning + // checks by UID, which won't match if this test failed to clean up + // on a previous install + run + uninstall, so ignore these failures. + } + } + } + + @Test + fun truncateAppLabel() { + val longLabel = invalidAppLabel() + val params = SessionParams(SessionParams.MODE_FULL_INSTALL).apply { + setAppLabel(longLabel) + } + + createSession(params) { + assertThat(installer.getSessionInfo(it)?.appLabel) + .isEqualTo(longLabel.take(PackageItemInfo.MAX_SAFE_LABEL_LENGTH)) + } + } + + @Test + fun removeInvalidAppPackageName() { + val longName = invalidPackageName() + val params = SessionParams(SessionParams.MODE_FULL_INSTALL).apply { + setAppPackageName(longName) + } + + createSession(params) { + assertThat(installer.getSessionInfo(it)?.appPackageName) + .isEqualTo(null) + } + } + + @Test + fun removeInvalidInstallerPackageName() { + val longName = invalidPackageName() + val params = SessionParams(SessionParams.MODE_FULL_INSTALL).apply { + setInstallerPackageName(longName) + } + + createSession(params) { + // If a custom installer name is dropped, it defaults to the caller + assertThat(installer.getSessionInfo(it)?.installerPackageName) + .isEqualTo(context.packageName) + } + } + + @Test + fun truncateWhitelistPermissions() { + val params = SessionParams(SessionParams.MODE_FULL_INSTALL).apply { + setWhitelistedRestrictedPermissions(invalidPermissions()) + } + + createSession(params) { + assertThat(installer.getSessionInfo(it)?.whitelistedRestrictedPermissions!!) + .containsExactlyElementsIn(RESTRICTED_PERMISSIONS) + } + } + + @LargeTest + @Test + fun allocateMaxSessionsWithPermission() { + ShellIdentityUtils.invokeWithShellPermissions { + repeat(1024) { createDummySession() } + assertThrows(IllegalStateException::class.java) { createDummySession() } + } + } + + @LargeTest + @Test + fun allocateMaxSessionsNoPermission() { + repeat(50) { createDummySession() } + assertThrows(IllegalStateException::class.java) { createDummySession() } + } + + private fun createDummySession() { + installer.createSession(SessionParams(SessionParams.MODE_FULL_INSTALL) + .apply { + setAppPackageName(invalidPackageName()) + setAppLabel(invalidAppLabel()) + setWhitelistedRestrictedPermissions(invalidPermissions()) + }) + } + + private fun invalidPackageName(maxLength: Int = SessionParams.MAX_PACKAGE_NAME_LENGTH): String { + return (0 until (maxLength + 10)) + .asSequence() + .mapIndexed { index, _ -> + // A package name needs at least one separator + if (index == 2) { + '.' + } else { + Random.nextInt('z' - 'a').toChar() + 'a'.toInt() + } + } + .joinToString(separator = "") + } + + private fun invalidAppLabel() = (0 until PackageItemInfo.MAX_SAFE_LABEL_LENGTH + 10) + .asSequence() + .map { Random.nextInt(Char.MAX_VALUE.toInt()).toChar() } + .joinToString(separator = "") + + private fun invalidPermissions() = RESTRICTED_PERMISSIONS.toMutableSet() + .apply { + // Add some invalid permission names + repeat(10) { add(invalidPackageName(300)) } + } + + private fun createSession(params: SessionParams, block: (Int) -> Unit = {}) { + val sessionId = installer.createSession(params) + try { + block(sessionId) + } finally { + installer.abandonSession(sessionId) + } + } +} diff --git a/data/keyboards/Vendor_045e_Product_0b12.kl b/data/keyboards/Vendor_045e_Product_0b12.kl new file mode 100644 index 000000000000..0b44c7434af2 --- /dev/null +++ b/data/keyboards/Vendor_045e_Product_0b12.kl @@ -0,0 +1,59 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# XBox USB Controller +# + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt + +# Two overlapping rectangles +key 314 BUTTON_SELECT + +# The branded "X" button in the center of the controller +key 316 BUTTON_MODE + +# Three parallel horizontal lines (hamburger menu) +key 315 BUTTON_START + +#Button below the "X" button +key 167 MEDIA_RECORD + diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_navigation_bar.xml index 2a715d0c3494..93174983b116 100644 --- a/packages/CarSystemUI/res/layout/car_navigation_bar.xml +++ b/packages/CarSystemUI/res/layout/car_navigation_bar.xml @@ -29,9 +29,10 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" - android:paddingStart="20dp" + android:gravity="center" + android:layoutDirection="ltr" android:paddingEnd="20dp" - android:gravity="center"> + android:paddingStart="20dp"> <com.android.systemui.car.navigationbar.CarNavigationButton android:id="@+id/home" @@ -135,9 +136,10 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" - android:paddingStart="@dimen/car_keyline_1" - android:paddingEnd="@dimen/car_keyline_1" android:gravity="center" + android:layoutDirection="ltr" + android:paddingEnd="@dimen/car_keyline_1" + android:paddingStart="@dimen/car_keyline_1" android:visibility="gone" /> diff --git a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml index 60e0d7e430df..cdc29eec21cd 100644 --- a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml +++ b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml @@ -27,7 +27,8 @@ <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_weight="1"> + android:layout_weight="1" + android:layoutDirection="ltr"> <FrameLayout android:id="@+id/left_hvac_container" diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index d039c9f646df..1b5062efa23e 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -140,8 +140,8 @@ <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"Адкрытая сетка"</string> <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"Бяспечная сетка"</string> <string name="process_kernel_label" msgid="950292573930336765">"АС Android"</string> - <string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"Выдаленыя прыкладанні"</string> - <string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"Выдаленыя прыкладанні і карыстальнiкi"</string> + <string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"Выдаленыя праграмы"</string> + <string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"Выдаленыя праграмы і карыстальнiкi"</string> <string name="data_usage_ota" msgid="7984667793701597001">"Абнаўленні сістэмы"</string> <string name="tether_settings_title_usb" msgid="3728686573430917722">"USB-мадэм"</string> <string name="tether_settings_title_wifi" msgid="4803402057533895526">"Партатыўны хот-спот"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index 65f456e20e4e..d003ef0b9c71 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -449,7 +449,7 @@ <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> täislaadimiseni"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Tundmatu"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Laadimine"</string> - <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Kiiresti laadimine"</string> + <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Kiirlaadimine"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Aeglaselt laadimine"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Ei lae"</string> <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Vooluvõrgus, praegu ei saa laadida"</string> diff --git a/packages/SystemUI/res/layout/partial_conversation_info.xml b/packages/SystemUI/res/layout/partial_conversation_info.xml index b34822291815..803b0c61e6da 100644 --- a/packages/SystemUI/res/layout/partial_conversation_info.xml +++ b/packages/SystemUI/res/layout/partial_conversation_info.xml @@ -52,39 +52,20 @@ android:gravity="center_vertical" android:layout_alignEnd="@id/conversation_icon" android:layout_toEndOf="@id/conversation_icon"> - <LinearLayout + <TextView + android:id="@+id/name" android:layout_width="match_parent" android:layout_height="wrap_content" - android:gravity="start" - android:orientation="horizontal"> - <TextView - android:id="@+id/name" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - style="@style/TextAppearance.NotificationImportanceChannel"/> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_centerVertical="true" - style="@style/TextAppearance.NotificationImportanceHeader" - android:layout_marginStart="2dp" - android:layout_marginEnd="2dp" - android:text="@*android:string/notification_header_divider_symbol" /> - <TextView - android:id="@+id/parent_channel_name" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - style="@style/TextAppearance.NotificationImportanceChannel"/> - - </LinearLayout> + android:ellipsize="end" + android:textDirection="locale" + style="@style/TextAppearance.NotificationImportanceChannel"/> <TextView - android:id="@+id/pkg_name" + android:id="@+id/parent_channel_name" android:layout_width="match_parent" android:layout_height="wrap_content" - style="@style/TextAppearance.NotificationImportanceChannelGroup" android:ellipsize="end" android:textDirection="locale" - android:maxLines="1"/> + style="@style/TextAppearance.NotificationImportanceChannel"/> <TextView android:id="@+id/group_name" android:layout_width="match_parent" diff --git a/packages/SystemUI/res/layout/priority_onboarding_half_shell.xml b/packages/SystemUI/res/layout/priority_onboarding_half_shell.xml index c27b3a9b3bf4..bf2eac3c8ff3 100644 --- a/packages/SystemUI/res/layout/priority_onboarding_half_shell.xml +++ b/packages/SystemUI/res/layout/priority_onboarding_half_shell.xml @@ -38,157 +38,67 @@ android:background="@drawable/rounded_bg_full" > - <!-- We have a known number of rows that can be shown; just design them all here --> - <LinearLayout - android:id="@+id/show_at_top_tip" + <ImageView + android:id="@+id/conversation_icon" + android:layout_width="@dimen/notification_guts_conversation_icon_size" + android:layout_height="@dimen/notification_guts_conversation_icon_size" + android:layout_gravity="center_horizontal" /> + + <TextView + android:id="@+id/title" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingTop="8dp" - android:paddingBottom="8dp" - android:paddingStart="4dp" - android:paddingEnd="4dp" - android:orientation="horizontal" - > - <ImageView - android:id="@+id/bell_icon" - android:layout_width="24dp" - android:layout_height="24dp" - android:layout_gravity="center_vertical" - android:src="@drawable/ic_notifications_alert" - android:tint="?android:attr/colorControlNormal" /> - - <TextView - android:id="@+id/show_at_top_text" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingStart="16dp" - android:paddingEnd="16dp" - android:gravity="center_vertical|start" - android:textSize="15sp" - android:ellipsize="end" - android:maxLines="2" - android:text="@string/priority_onboarding_show_at_top_text" - style="@style/TextAppearance.NotificationInfo" - /> - - </LinearLayout> - - <LinearLayout - android:id="@+id/show_avatar_tip" + android:gravity="center_horizontal" + android:layout_marginTop="16dp" + android:text="@string/priority_onboarding_title" + style="@style/TextAppearance.NotificationImportanceChannel" + /> + + <View + android:id="@+id/divider" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingTop="8dp" - android:paddingBottom="8dp" - android:paddingStart="4dp" - android:paddingEnd="4dp" - android:orientation="horizontal" - > - <ImageView - android:id="@+id/avatar_icon" - android:layout_width="24dp" - android:layout_height="24dp" - android:layout_gravity="center_vertical" - android:src="@drawable/ic_person" - android:tint="?android:attr/colorControlNormal" /> - - <TextView - android:id="@+id/avatar_text" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingStart="16dp" - android:paddingEnd="16dp" - android:gravity="center_vertical|start" - android:textSize="15sp" - android:ellipsize="end" - android:maxLines="2" - android:text="@string/priority_onboarding_show_avatar_text" - style="@style/TextAppearance.NotificationInfo" - /> + android:layout_height="0.5dp" + android:layout_marginTop="20dp" + android:layout_marginBottom="20dp" + android:background="@color/material_grey_300" /> - </LinearLayout> - - <!-- These rows show optionally --> - - <LinearLayout - android:id="@+id/floating_bubble_tip" + <TextView android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingTop="8dp" - android:paddingBottom="8dp" - android:paddingStart="4dp" - android:paddingEnd="4dp" - android:orientation="horizontal" - > - - <ImageView - android:id="@+id/bubble_icon" - android:layout_width="24dp" - android:layout_height="24dp" - android:layout_gravity="center_vertical" - android:src="@drawable/ic_create_bubble" - android:tint="?android:attr/colorControlNormal" /> + android:gravity="start" + android:text="@string/priority_onboarding_behavior" + style="@style/TextAppearance.NotificationImportanceChannelGroup" + /> - <TextView - android:id="@+id/bubble_text" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingStart="16dp" - android:paddingEnd="16dp" - android:gravity="center_vertical|start" - android:textSize="15sp" - android:ellipsize="end" - android:maxLines="2" - android:text="@string/priority_onboarding_appear_as_bubble_text" - style="@style/TextAppearance.NotificationInfo" - /> - - </LinearLayout> - - <LinearLayout - android:id="@+id/ignore_dnd_tip" + <TextView + android:id="@+id/behaviors" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingTop="8dp" - android:paddingBottom="8dp" - android:paddingStart="4dp" - android:paddingEnd="4dp" - android:orientation="horizontal" - > - - <ImageView - android:id="@+id/dnd_icon" - android:layout_width="24dp" - android:layout_height="24dp" - android:layout_gravity="center_vertical" - android:src="@drawable/moon" - android:tint="?android:attr/colorControlNormal" /> - - <TextView - android:id="@+id/dnd_text" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingStart="16dp" - android:paddingEnd="16dp" - android:gravity="center_vertical|start" - android:textSize="15sp" - android:ellipsize="end" - android:maxLines="2" - android:text="@string/priority_onboarding_ignores_dnd_text" - style="@style/TextAppearance.NotificationInfo" - /> - - </LinearLayout> + android:gravity="start" + android:layout_marginTop="8dp" + style="@style/TextAppearance.NotificationImportanceChannelGroup" + /> <!-- Bottom button container --> <RelativeLayout android:id="@+id/button_container" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingStart="4dp" - android:paddingEnd="4dp" + android:layout_marginTop="32dp" android:orientation="horizontal" > <TextView + android:id="@+id/settings_button" + android:text="@string/priority_onboarding_settings_button_title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentStart="true" + android:gravity="start|center_vertical" + android:minWidth="@dimen/notification_importance_toggle_size" + android:minHeight="@dimen/notification_importance_toggle_size" + android:maxWidth="125dp" + style="@style/TextAppearance.NotificationInfo.Button"/> + <TextView android:id="@+id/done_button" android:text="@string/priority_onboarding_done_button_title" android:layout_width="wrap_content" diff --git a/packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml b/packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml index af6f9bb827f6..0c4d5a26f95b 100644 --- a/packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml +++ b/packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml @@ -16,21 +16,20 @@ --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" + android:layout_width="250dp" android:layout_height="48dp" android:orientation="vertical" - android:padding="10dp" - android:layout_weight="1"> + android:padding="13dp"> <TextView android:id="@+id/screen_recording_dialog_source_text" - android:layout_width="match_parent" + android:layout_width="250dp" android:layout_height="match_parent" android:layout_gravity="center_vertical" android:textAppearance="?android:attr/textAppearanceSmall" android:textColor="?android:attr/textColorPrimary"/> <TextView android:id="@+id/screen_recording_dialog_source_description" - android:layout_width="wrap_content" + android:layout_width="250dp" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceSmall" android:textColor="?android:attr/textColorSecondary"/> diff --git a/packages/SystemUI/res/values-sw320dp/dimens.xml b/packages/SystemUI/res/values-sw320dp/dimens.xml deleted file mode 100644 index c110113e91f4..000000000000 --- a/packages/SystemUI/res/values-sw320dp/dimens.xml +++ /dev/null @@ -1,36 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2019 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License - --> -<resources> - <!-- Global actions grid --> - <dimen name="global_actions_grid_vertical_padding">3dp</dimen> - <dimen name="global_actions_grid_horizontal_padding">3dp</dimen> - - <dimen name="global_actions_grid_item_side_margin">5dp</dimen> - <dimen name="global_actions_grid_item_vertical_margin">4dp</dimen> - <dimen name="global_actions_grid_item_width">64dp</dimen> - <dimen name="global_actions_grid_item_height">64dp</dimen> - - <dimen name="global_actions_grid_item_icon_width">20dp</dimen> - <dimen name="global_actions_grid_item_icon_height">20dp</dimen> - <dimen name="global_actions_grid_item_icon_top_margin">12dp</dimen> - <dimen name="global_actions_grid_item_icon_side_margin">22dp</dimen> - <dimen name="global_actions_grid_item_icon_bottom_margin">4dp</dimen> - - <!-- Home Controls --> - <dimen name="global_actions_side_margin">10dp</dimen> -</resources> - diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index e26f31cdf5ad..c46460853683 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -224,6 +224,7 @@ <dimen name="notification_guts_conversation_icon_size">56dp</dimen> <dimen name="notification_guts_conversation_action_height">56dp</dimen> <dimen name="notification_guts_conversation_action_text_padding_start">32dp</dimen> + <dimen name="conversation_onboarding_bullet_gap_width">6dp</dimen> <!-- The height of the header in inline settings --> <dimen name="notification_guts_header_height">24dp</dimen> @@ -1031,8 +1032,23 @@ <dimen name="global_actions_grid_container_shadow_offset">20dp</dimen> <dimen name="global_actions_grid_container_negative_shadow_offset">-20dp</dimen> + <!-- Global actions grid --> + <dimen name="global_actions_grid_vertical_padding">3dp</dimen> + <dimen name="global_actions_grid_horizontal_padding">3dp</dimen> + + <dimen name="global_actions_grid_item_side_margin">5dp</dimen> + <dimen name="global_actions_grid_item_vertical_margin">4dp</dimen> + <dimen name="global_actions_grid_item_width">64dp</dimen> + <dimen name="global_actions_grid_item_height">64dp</dimen> + + <dimen name="global_actions_grid_item_icon_width">20dp</dimen> + <dimen name="global_actions_grid_item_icon_height">20dp</dimen> + <dimen name="global_actions_grid_item_icon_top_margin">12dp</dimen> + <dimen name="global_actions_grid_item_icon_side_margin">22dp</dimen> + <dimen name="global_actions_grid_item_icon_bottom_margin">4dp</dimen> + <!-- Margins at the left and right of the power menu and home controls widgets. --> - <dimen name="global_actions_side_margin">16dp</dimen> + <dimen name="global_actions_side_margin">10dp</dimen> <!-- Amount to shift the layout when exiting/entering for controls activities --> <dimen name="global_actions_controls_y_translation">20dp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 97a0d032c4a7..39237ac246eb 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2667,6 +2667,11 @@ <string name="inattentive_sleep_warning_title">Standby</string> <!-- Priority conversation onboarding screen --> + <!-- title of priority onboarding [CHAR LIMIT=75] --> + <string name="priority_onboarding_title">Conversation set to priority</string> + <!-- Text explaining that the following actions are the behaviors of priority conversations. + E.g. priority conversations will show at the top of the conversation section [CHAR LIMIT=75] --> + <string name="priority_onboarding_behavior">Priority conversations will:</string> <!-- Text explaining that priority conversations show at the top of the conversation section [CHAR LIMIT=75] --> <string name="priority_onboarding_show_at_top_text">Show at top of conversation section</string> <!-- Text explaining that priority conversations show an avatar on the lock screen [CHAR LIMIT=75] --> @@ -2677,6 +2682,8 @@ <string name="priority_onboarding_ignores_dnd_text">Interrupt Do Not Disturb</string> <!-- Title for the affirmative button [CHAR LIMIT=50] --> <string name="priority_onboarding_done_button_title">Got it</string> + <!-- Title for the settings button button [CHAR LIMIT=50] --> + <string name="priority_onboarding_settings_button_title">Settings</string> <!-- Window Magnification strings --> <!-- Title for Magnification Overlay Window [CHAR LIMIT=NONE] --> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/BitmapUtil.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/BitmapUtil.java new file mode 100644 index 000000000000..b79fcbd43972 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/BitmapUtil.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shared.recents.utilities; + +import android.graphics.Bitmap; +import android.graphics.ColorSpace; +import android.graphics.ParcelableColorSpace; +import android.hardware.HardwareBuffer; +import android.os.Bundle; + +import java.util.Objects; + +/** + * Utils for working with Bitmaps. + */ +public final class BitmapUtil { + private static final String KEY_BUFFER = "bitmap_util_buffer"; + private static final String KEY_COLOR_SPACE = "bitmap_util_color_space"; + + private BitmapUtil(){ } + + /** + * Creates a Bundle that represents the given Bitmap. + * <p>The Bundle will contain a wrapped version of the Bitmaps HardwareBuffer, so will avoid + * copies when passing across processes, only pass to processes you trust. + * + * <p>Returns a new Bundle rather than modifying an exiting one to avoid key collisions, the + * returned Bundle should be treated as a standalone object. + * + * @param bitmap to convert to bundle + * @return a Bundle representing the bitmap, should only be parsed by + * {@link #bundleToHardwareBitmap(Bundle)} + */ + public static Bundle hardwareBitmapToBundle(Bitmap bitmap) { + if (bitmap.getConfig() != Bitmap.Config.HARDWARE) { + throw new IllegalArgumentException( + "Passed bitmap must have hardware config, found: " + bitmap.getConfig()); + } + + // Bitmap assumes SRGB for null color space + ParcelableColorSpace colorSpace = + bitmap.getColorSpace() == null + ? new ParcelableColorSpace(ColorSpace.get(ColorSpace.Named.SRGB)) + : new ParcelableColorSpace(bitmap.getColorSpace()); + + Bundle bundle = new Bundle(); + bundle.putParcelable(KEY_BUFFER, bitmap.getHardwareBuffer()); + bundle.putParcelable(KEY_COLOR_SPACE, colorSpace); + + return bundle; + } + + /** + * Extracts the Bitmap added to a Bundle with {@link #hardwareBitmapToBundle(Bitmap)} .} + * + * <p>This Bitmap contains the HardwareBuffer from the original caller, be careful passing this + * Bitmap on to any other source. + * + * @param bundle containing the bitmap + * @return a hardware Bitmap + */ + public static Bitmap bundleToHardwareBitmap(Bundle bundle) { + if (!bundle.containsKey(KEY_BUFFER) || !bundle.containsKey(KEY_COLOR_SPACE)) { + throw new IllegalArgumentException("Bundle does not contain a hardware bitmap"); + } + + HardwareBuffer buffer = bundle.getParcelable(KEY_BUFFER); + ParcelableColorSpace colorSpace = bundle.getParcelable(KEY_COLOR_SPACE); + + return Bitmap.wrapHardwareBuffer(Objects.requireNonNull(buffer), colorSpace); + } +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java index dd613263e5c2..73783ae7ece2 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java @@ -15,6 +15,7 @@ */ package com.android.systemui.shared.system; +import android.graphics.HardwareRenderer; import android.view.SurfaceControl; import android.view.View; import android.view.ViewRootImpl; @@ -50,7 +51,13 @@ public class ViewRootImplCompat { public void registerRtFrameCallback(LongConsumer callback) { if (mViewRoot != null) { - mViewRoot.registerRtFrameCallback(callback::accept); + mViewRoot.registerRtFrameCallback( + new HardwareRenderer.FrameDrawingCallback() { + @Override + public void onFrameDraw(long l) { + callback.accept(l); + } + }); } } } diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java index 87990cd3bffa..ccb506de6d8b 100644 --- a/packages/SystemUI/src/com/android/systemui/Prefs.java +++ b/packages/SystemUI/src/com/android/systemui/Prefs.java @@ -124,7 +124,7 @@ public final class Prefs { String HAS_SEEN_BUBBLES_MANAGE_EDUCATION = "HasSeenBubblesManageOnboarding"; String CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT = "ControlsStructureSwipeTooltipCount"; /** Tracks whether the user has seen the onboarding screen for priority conversations */ - String HAS_SEEN_PRIORITY_ONBOARDING = "HasSeenPriorityOnboarding"; + String HAS_SEEN_PRIORITY_ONBOARDING = "HaveShownPriorityOnboarding"; } public static boolean getBoolean(Context context, @Key String key, boolean defaultValue) { diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt index fd9fda3662a3..93f0c7f41ce3 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt @@ -44,6 +44,7 @@ import com.android.systemui.controls.management.ControlsListingController import com.android.systemui.controls.ui.ControlsUiController import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dump.DumpManager +import com.android.systemui.globalactions.GlobalActionsDialog import com.android.systemui.util.concurrency.DelayableExecutor import java.io.FileDescriptor import java.io.PrintWriter @@ -79,6 +80,7 @@ class ControlsControllerImpl @Inject constructor ( } private var userChanging: Boolean = true + private var userStructure: UserStructure private var seedingInProgress = false private val seedingCallbacks = mutableListOf<Consumer<Boolean>>() @@ -97,7 +99,7 @@ class ControlsControllerImpl @Inject constructor ( internal var auxiliaryPersistenceWrapper: AuxiliaryPersistenceWrapper init { - val userStructure = UserStructure(context, currentUser) + userStructure = UserStructure(context, currentUser) persistenceWrapper = optionalWrapper.orElseGet { ControlsFavoritePersistenceWrapper( @@ -116,7 +118,7 @@ class ControlsControllerImpl @Inject constructor ( private fun setValuesForUser(newUser: UserHandle) { Log.d(TAG, "Changing to user: $newUser") currentUser = newUser - val userStructure = UserStructure(context, currentUser) + userStructure = UserStructure(context, currentUser) persistenceWrapper.changeFileAndBackupManager( userStructure.file, BackupManager(userStructure.userContext) @@ -192,6 +194,16 @@ class ControlsControllerImpl @Inject constructor ( it.componentName }.toSet() + // When a component is uninstalled, allow seeding to happen again if the user + // reinstalls the app + val prefs = userStructure.userContext.getSharedPreferences( + GlobalActionsDialog.PREFS_CONTROLS_FILE, Context.MODE_PRIVATE) + val completedSeedingPackageSet = prefs.getStringSet( + GlobalActionsDialog.PREFS_CONTROLS_SEEDING_COMPLETED, mutableSetOf<String>()) + val favoritePackageSet = favoriteComponentSet.map { it.packageName } + prefs.edit().putStringSet(GlobalActionsDialog.PREFS_CONTROLS_SEEDING_COMPLETED, + completedSeedingPackageSet.intersect(favoritePackageSet)).apply() + var changed = false favoriteComponentSet.subtract(serviceInfoSet).forEach { changed = true diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt index 35ebac5b1ed5..d31b6eb9d857 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt @@ -129,7 +129,10 @@ class ControlsUiControllerImpl @Inject constructor ( SelectionItem(it.loadLabel(), "", it.loadIcon(), it.componentName) } uiExecutor.execute { - onResult(lastItems) + parent.removeAllViews() + if (lastItems.size > 0) { + onResult(lastItems) + } } } } @@ -189,8 +192,6 @@ class ControlsUiControllerImpl @Inject constructor ( } private fun showSeedingView(items: List<SelectionItem>) { - parent.removeAllViews() - val inflater = LayoutInflater.from(context) inflater.inflate(R.layout.controls_no_favorites, parent, true) val subtitle = parent.requireViewById<TextView>(R.id.controls_subtitle) @@ -198,8 +199,6 @@ class ControlsUiControllerImpl @Inject constructor ( } private fun showInitialSetupView(items: List<SelectionItem>) { - parent.removeAllViews() - val inflater = LayoutInflater.from(context) inflater.inflate(R.layout.controls_no_favorites, parent, true) @@ -263,7 +262,6 @@ class ControlsUiControllerImpl @Inject constructor ( } private fun showControlsView(items: List<SelectionItem>) { - parent.removeAllViews() controlViewsById.clear() createListView() diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt index f97015282222..9ec14523a809 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt @@ -64,6 +64,10 @@ class DetailDialog( } override fun onActivityViewDestroyed(view: ActivityView) {} + + override fun onTaskRemovalStarted(taskId: Int) { + dismiss() + } } init { diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt index 0ec4cc555b9d..4003f41b33dc 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt @@ -268,7 +268,7 @@ class ToggleRangeBehavior : Behavior { private fun format(primaryFormat: String, backupFormat: String, value: Float): String { return try { - String.format(primaryFormat, value) + String.format(primaryFormat, findNearestStep(value)) } catch (e: IllegalFormatException) { Log.w(ControlsUiController.TAG, "Illegal format in range template", e) if (backupFormat == "") { diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java index 95a9006c854a..951dc9936e1c 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java @@ -71,7 +71,7 @@ public class DozeFactory { DockManager dockManager, @Nullable IWallpaperManager wallpaperManager, ProximitySensor proximitySensor, DelayedWakeLock.Builder delayedWakeLockBuilder, @Main Handler handler, - DelayableExecutor delayableExecutor, + @Main DelayableExecutor delayableExecutor, BiometricUnlockController biometricUnlockController, BroadcastDispatcher broadcastDispatcher, DozeHost dozeHost) { mFalsingManager = falsingManager; diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index 1b13d4a49fec..7e009b459ede 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -183,8 +183,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, static final String GLOBAL_ACTION_KEY_EMERGENCY = "emergency"; static final String GLOBAL_ACTION_KEY_SCREENSHOT = "screenshot"; - private static final String PREFS_CONTROLS_SEEDING_COMPLETED = "SeedingCompleted"; - private static final String PREFS_CONTROLS_FILE = "controls_prefs"; + public static final String PREFS_CONTROLS_SEEDING_COMPLETED = "SeedingCompleted"; + public static final String PREFS_CONTROLS_FILE = "controls_prefs"; private static final int SEEDING_MAX = 2; private final Context mContext; @@ -391,7 +391,15 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, if (controlsComponent.getControlsListingController().isPresent()) { controlsComponent.getControlsListingController().get() - .addCallback(list -> mControlsServiceInfos = list); + .addCallback(list -> { + mControlsServiceInfos = list; + // This callback may occur after the dialog has been shown. + // If so, add controls into the already visible space + if (mDialog != null && !mDialog.isShowingControls() + && shouldShowControls()) { + mDialog.showControls(mControlsUiControllerOptional.get()); + } + }); } // Need to be user-specific with the context to make sure we read the correct prefs diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index b272b60f3593..bfdd5c8bcbd8 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -64,10 +64,12 @@ import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActiv import com.android.internal.policy.ScreenDecorationsUtils; import com.android.internal.util.ScreenshotHelper; import com.android.systemui.Dumpable; +import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.model.SysUiState; import com.android.systemui.pip.PipAnimationController; import com.android.systemui.pip.PipUI; import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener; +import com.android.systemui.settings.CurrentUserTracker; import com.android.systemui.shared.recents.IOverviewProxy; import com.android.systemui.shared.recents.IPinnedStackAnimationListener; import com.android.systemui.shared.recents.ISystemUiProxy; @@ -83,8 +85,6 @@ import com.android.systemui.statusbar.phone.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarWindowCallback; import com.android.systemui.statusbar.policy.CallbackController; -import com.android.systemui.statusbar.policy.DeviceProvisionedController; -import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -101,8 +101,9 @@ import dagger.Lazy; * Class to send information from overview to launcher with a binder. */ @Singleton -public class OverviewProxyService implements CallbackController<OverviewProxyListener>, - NavigationModeController.ModeChangedListener, Dumpable { +public class OverviewProxyService extends CurrentUserTracker implements + CallbackController<OverviewProxyListener>, NavigationModeController.ModeChangedListener, + Dumpable { private static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE"; @@ -123,7 +124,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis private final NotificationShadeWindowController mStatusBarWinController; private final Runnable mConnectionRunnable = this::internalConnectToCurrentUser; private final ComponentName mRecentsComponentName; - private final DeviceProvisionedController mDeviceProvisionedController; private final List<OverviewProxyListener> mConnectionCallbacks = new ArrayList<>(); private final Intent mQuickStepIntent; private final ScreenshotHelper mScreenshotHelper; @@ -480,7 +480,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis return; } - mCurrentBoundedUserId = mDeviceProvisionedController.getCurrentUser(); + mCurrentBoundedUserId = getCurrentUserId(); mOverviewProxy = IOverviewProxy.Stub.asInterface(service); Bundle params = new Bundle(); @@ -523,22 +523,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis } }; - private final DeviceProvisionedListener mDeviceProvisionedCallback = - new DeviceProvisionedListener() { - @Override - public void onUserSetupChanged() { - if (mDeviceProvisionedController.isCurrentUserSetup()) { - internalConnectToCurrentUser(); - } - } - - @Override - public void onUserSwitched() { - mConnectionBackoffAttempts = 0; - internalConnectToCurrentUser(); - } - }; - private final StatusBarWindowCallback mStatusBarWindowCallback = this::onStatusBarStateChanged; // This is the death handler for the binder from the launcher service @@ -548,18 +532,18 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis @SuppressWarnings("OptionalUsedAsFieldOrParameterType") @Inject public OverviewProxyService(Context context, CommandQueue commandQueue, - DeviceProvisionedController provisionController, NavigationBarController navBarController, NavigationModeController navModeController, NotificationShadeWindowController statusBarWinController, SysUiState sysUiState, PipUI pipUI, Optional<Divider> dividerOptional, - Optional<Lazy<StatusBar>> statusBarOptionalLazy) { + Optional<Lazy<StatusBar>> statusBarOptionalLazy, + BroadcastDispatcher broadcastDispatcher) { + super(broadcastDispatcher); mContext = context; mPipUI = pipUI; mStatusBarOptionalLazy = statusBarOptionalLazy; mHandler = new Handler(); mNavBarController = navBarController; mStatusBarWinController = statusBarWinController; - mDeviceProvisionedController = provisionController; mConnectionBackoffAttempts = 0; mDividerOptional = dividerOptional; mRecentsComponentName = ComponentName.unflattenFromString(context.getString( @@ -580,7 +564,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis // Listen for device provisioned/user setup updateEnabledState(); - mDeviceProvisionedController.addCallback(mDeviceProvisionedCallback); + startTracking(); // Listen for launcher package changes IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); @@ -604,6 +588,12 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis }); } + @Override + public void onUserSwitched(int newUserId) { + mConnectionBackoffAttempts = 0; + internalConnectToCurrentUser(); + } + public void notifyBackAction(boolean completed, int downX, int downY, boolean isButton, boolean gestureSwipeLeft) { try { @@ -709,10 +699,8 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis disconnectFromLauncherService(); // If user has not setup yet or already connected, do not try to connect - if (!mDeviceProvisionedController.isCurrentUserSetup() || !isEnabled()) { - Log.v(TAG_OPS, "Cannot attempt connection, is setup " - + mDeviceProvisionedController.isCurrentUserSetup() + ", is enabled " - + isEnabled()); + if (!isEnabled()) { + Log.v(TAG_OPS, "Cannot attempt connection, is enabled " + isEnabled()); return; } mHandler.removeCallbacks(mConnectionRunnable); @@ -722,7 +710,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis mBound = mContext.bindServiceAsUser(launcherServiceIntent, mOverviewServiceConnection, Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE, - UserHandle.of(mDeviceProvisionedController.getCurrentUser())); + UserHandle.of(getCurrentUserId())); } catch (SecurityException e) { Log.e(TAG_OPS, "Unable to bind because of security error", e); } @@ -881,8 +869,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis pw.println(TAG_OPS + " state:"); pw.print(" recentsComponentName="); pw.println(mRecentsComponentName); pw.print(" isConnected="); pw.println(mOverviewProxy != null); - pw.print(" isCurrentUserSetup="); pw.println(mDeviceProvisionedController - .isCurrentUserSetup()); pw.print(" connectionBackoffAttempts="); pw.println(mConnectionBackoffAttempts); pw.print(" quickStepIntent="); pw.println(mQuickStepIntent); diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java index 960c50129a56..3e268f63d65e 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java @@ -225,8 +225,8 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis res.getString(R.string.screenrecord_name)); String notificationTitle = mAudioSource == ScreenRecordingAudioSource.NONE - ? res.getString(R.string.screenrecord_ongoing_screen_and_audio) - : res.getString(R.string.screenrecord_ongoing_screen_only); + ? res.getString(R.string.screenrecord_ongoing_screen_only) + : res.getString(R.string.screenrecord_ongoing_screen_and_audio); mRecordingNotificationBuilder = new Notification.Builder(this, CHANNEL_ID) .setSmallIcon(R.drawable.ic_screenrecord) diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java index 752f4fddf24b..edbc3cfdece5 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java @@ -38,6 +38,7 @@ import java.nio.ByteBuffer; public class ScreenInternalAudioRecorder { private static String TAG = "ScreenAudioRecorder"; private static final int TIMEOUT = 500; + private static final float MIC_VOLUME_SCALE = 1.4f; private final Context mContext; private AudioRecord mAudioRecord; private AudioRecord mAudioRecordMic; @@ -148,6 +149,10 @@ public class ScreenInternalAudioRecorder { readShortsInternal = mAudioRecord.read(bufferInternal, 0, bufferInternal.length); readShortsMic = mAudioRecordMic.read(bufferMic, 0, bufferMic.length); + + // modify the volume + bufferMic = scaleValues(bufferMic, + readShortsMic, MIC_VOLUME_SCALE); readBytes = Math.min(readShortsInternal, readShortsMic) * 2; buffer = addAndConvertBuffers(bufferInternal, readShortsInternal, bufferMic, readShortsMic); @@ -168,6 +173,19 @@ public class ScreenInternalAudioRecorder { }); } + private short[] scaleValues(short[] buff, int len, float scale) { + for (int i = 0; i < len; i++) { + int oldValue = buff[i]; + int newValue = (int) (buff[i] * scale); + if (newValue > Short.MAX_VALUE) { + newValue = Short.MAX_VALUE; + } else if (newValue < Short.MIN_VALUE) { + newValue = Short.MIN_VALUE; + } + buff[i] = (short) (newValue); + } + return buff; + } private byte[] addAndConvertBuffers(short[] a1, int a1Limit, short[] a2, int a2Limit) { int size = Math.max(a1Limit, a2Limit); if (size < 0) return new byte[0]; diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java index c967648c544e..8551c88d133a 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java @@ -55,9 +55,9 @@ import java.util.Date; */ public class ScreenMediaRecorder { private static final int TOTAL_NUM_TRACKS = 1; - private static final int VIDEO_BIT_RATE = 10000000; private static final int VIDEO_FRAME_RATE = 30; - private static final int AUDIO_BIT_RATE = 16; + private static final int VIDEO_FRAME_RATE_TO_RESOLUTION_RATIO = 6; + private static final int AUDIO_BIT_RATE = 196000; private static final int AUDIO_SAMPLE_RATE = 44100; private static final int MAX_DURATION_MS = 60 * 60 * 1000; private static final long MAX_FILESIZE_BYTES = 5000000000L; @@ -108,7 +108,7 @@ public class ScreenMediaRecorder { // Set up audio source if (mAudioSource == MIC) { - mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); + mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT); } mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE); @@ -121,10 +121,13 @@ public class ScreenMediaRecorder { wm.getDefaultDisplay().getRealMetrics(metrics); int screenWidth = metrics.widthPixels; int screenHeight = metrics.heightPixels; + int refereshRate = (int) wm.getDefaultDisplay().getRefreshRate(); + int vidBitRate = screenHeight * screenWidth * refereshRate / VIDEO_FRAME_RATE + * VIDEO_FRAME_RATE_TO_RESOLUTION_RATIO; mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); mMediaRecorder.setVideoSize(screenWidth, screenHeight); - mMediaRecorder.setVideoFrameRate(VIDEO_FRAME_RATE); - mMediaRecorder.setVideoEncodingBitRate(VIDEO_BIT_RATE); + mMediaRecorder.setVideoFrameRate(refereshRate); + mMediaRecorder.setVideoEncodingBitRate(vidBitRate); mMediaRecorder.setMaxDuration(MAX_DURATION_MS); mMediaRecorder.setMaxFileSize(MAX_FILESIZE_BYTES); diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java index abd7e7159260..d057a8a43c43 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java @@ -24,12 +24,9 @@ import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.NONE; import android.app.Activity; import android.app.PendingIntent; import android.os.Bundle; -import android.util.Log; import android.view.Gravity; -import android.view.View; import android.view.ViewGroup; import android.view.Window; -import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.Spinner; @@ -88,8 +85,8 @@ public class ScreenRecordDialog extends Activity { }); mModes = new ArrayList<>(); - mModes.add(INTERNAL); mModes.add(MIC); + mModes.add(INTERNAL); mModes.add(MIC_AND_INTERNAL); mAudioSwitch = findViewById(R.id.screenrecord_audio_switch); diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingAdapter.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingAdapter.java index 2e0e746594b4..3e78489e5707 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingAdapter.java @@ -88,12 +88,6 @@ public class ScreenRecordingAdapter extends ArrayAdapter<ScreenRecordingAudioSou return layout; } - private void setDescription(LinearLayout layout, int description) { - if (description != Resources.ID_NULL) { - ((TextView) layout.getChildAt(1)).setText(description); - } - } - @Override public View getDropDownView(int position, View convertView, ViewGroup parent) { switch (getItem(position)) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java index daa4ffe88b4f..e9d89589172e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java @@ -53,6 +53,7 @@ import android.transition.TransitionManager; import android.transition.TransitionSet; import android.util.AttributeSet; import android.util.Log; +import android.util.Slog; import android.view.LayoutInflater; import android.view.View; import android.view.accessibility.AccessibilityEvent; @@ -115,6 +116,7 @@ public class NotificationConversationInfo extends LinearLayout implements private OnSnoozeClickListener mOnSnoozeClickListener; private OnSettingsClickListener mOnSettingsClickListener; private NotificationGuts mGutsContainer; + private OnConversationSettingsClickListener mOnConversationSettingsClickListener; @VisibleForTesting boolean mSkipPost = false; @@ -164,6 +166,10 @@ public class NotificationConversationInfo extends LinearLayout implements private OnClickListener mOnDone = v -> { mPressedApply = true; + // If the user selected Priority, maybe show the priority onboarding + if (mSelectedAction == ACTION_FAVORITE && shouldShowPriorityOnboarding()) { + showPriorityOnboarding(); + } mGutsContainer.closeControls(v, true); }; @@ -175,6 +181,10 @@ public class NotificationConversationInfo extends LinearLayout implements void onClick(View v, NotificationChannel channel, int appUid); } + public interface OnConversationSettingsClickListener { + void onClick(); + } + public interface OnAppSettingsClickListener { void onClick(View v, Intent intent); } @@ -190,14 +200,6 @@ public class NotificationConversationInfo extends LinearLayout implements } mSelectedAction = selectedAction; - onSelectedActionChanged(); - } - - private void onSelectedActionChanged() { - // If the user selected Priority, maybe show the priority onboarding - if (mSelectedAction == ACTION_FAVORITE && shouldShowPriorityOnboarding()) { - showPriorityOnboarding(); - } } public void bindNotification( @@ -216,7 +218,8 @@ public class NotificationConversationInfo extends LinearLayout implements Provider<PriorityOnboardingDialogController.Builder> builderProvider, boolean isDeviceProvisioned, @Main Handler mainHandler, - @Background Handler bgHandler) { + @Background Handler bgHandler, + OnConversationSettingsClickListener onConversationSettingsClickListener) { mSelectedAction = -1; mINotificationManager = iNotificationManager; mVisualStabilityManager = visualStabilityManager; @@ -231,6 +234,7 @@ public class NotificationConversationInfo extends LinearLayout implements mDelegatePkg = mSbn.getOpPkg(); mIsDeviceProvisioned = isDeviceProvisioned; mOnSnoozeClickListener = onSnoozeClickListener; + mOnConversationSettingsClickListener = onConversationSettingsClickListener; mIconFactory = conversationIconFactory; mUserContext = userContext; mBubbleMetadata = bubbleMetadata; @@ -323,7 +327,6 @@ public class NotificationConversationInfo extends LinearLayout implements ImageView image = findViewById(R.id.conversation_icon); image.setImageDrawable(mIconFactory.getConversationDrawable( mShortcutInfo, mPackageName, mAppUid, important)); - } private void bindPackage() { @@ -521,9 +524,9 @@ public class NotificationConversationInfo extends LinearLayout implements boolean ignoreDnd = false; try { - ignoreDnd = (mINotificationManager - .getConsolidatedNotificationPolicy().priorityConversationSenders - & NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT) != 0; + ignoreDnd = mINotificationManager + .getConsolidatedNotificationPolicy().priorityConversationSenders == + NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT; } catch (RemoteException e) { Log.e(TAG, "Could not check conversation senders", e); } @@ -538,6 +541,8 @@ public class NotificationConversationInfo extends LinearLayout implements .setView(onboardingView) .setIgnoresDnd(ignoreDnd) .setShowsAsBubble(showAsBubble) + .setIcon(((ImageView) findViewById(R.id.conversation_icon)).getDrawable()) + .setOnSettingsClick(mOnConversationSettingsClickListener) .build(); controller.init(); @@ -613,8 +618,7 @@ public class NotificationConversationInfo extends LinearLayout implements try { switch (mAction) { case ACTION_FAVORITE: - mChannelToUpdate.setImportantConversation( - !mChannelToUpdate.isImportantConversation()); + mChannelToUpdate.setImportantConversation(true); if (mChannelToUpdate.isImportantConversation()) { mChannelToUpdate.setAllowBubbles(true); if (mAppBubble == BUBBLE_PREFERENCE_NONE) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java index a64dcdffff1e..1074adc3383d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java @@ -216,6 +216,11 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx } } + private void startConversationSettingsActivity(int uid, ExpandableNotificationRow row) { + final Intent intent = new Intent(Settings.ACTION_CONVERSATION_SETTINGS); + mNotificationActivityStarter.startNotificationGutsIntent(intent, uid, row); + } + private boolean bindGuts(final ExpandableNotificationRow row) { row.ensureGutsInflated(); return bindGuts(row, mGutsMenuItem); @@ -438,6 +443,12 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx mListContainer.getSwipeActionHelper().snooze(sbn, hours); }; + final NotificationConversationInfo.OnConversationSettingsClickListener + onConversationSettingsListener = + () -> { + startConversationSettingsActivity(sbn.getUid(), row); + }; + if (!userHandle.equals(UserHandle.ALL) || mLockscreenUserManager.getCurrentUserId() == UserHandle.USER_SYSTEM) { onSettingsClick = (View v, NotificationChannel channel, int appUid) -> { @@ -468,7 +479,8 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx mBuilderProvider, mDeviceProvisionedController.isDeviceProvisioned(), mMainHandler, - mBgHandler); + mBgHandler, + onConversationSettingsListener); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java index 84bc181a77c4..f1fe54ad4024 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java @@ -241,7 +241,6 @@ public class PartialConversationInfo extends LinearLayout implements } catch (PackageManager.NameNotFoundException e) { mPkgIcon = mPm.getDefaultActivityIcon(); } - ((TextView) findViewById(R.id.pkg_name)).setText(mAppName); } private void bindDelegate() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PriorityOnboardingDialogController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PriorityOnboardingDialogController.kt index d1b405256f39..88c325880241 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PriorityOnboardingDialogController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PriorityOnboardingDialogController.kt @@ -21,19 +21,21 @@ import android.content.Context import android.graphics.Color import android.graphics.PixelFormat import android.graphics.drawable.ColorDrawable +import android.graphics.drawable.Drawable +import android.text.SpannableStringBuilder +import android.text.style.BulletSpan import android.view.Gravity import android.view.View -import android.view.View.GONE import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import android.view.Window import android.view.WindowInsets.Type.statusBars import android.view.WindowManager -import android.widget.LinearLayout +import android.widget.ImageView import android.widget.TextView import com.android.systemui.Prefs import com.android.systemui.R -import java.lang.IllegalStateException +import com.android.systemui.statusbar.notification.row.NotificationConversationInfo.OnConversationSettingsClickListener import javax.inject.Inject /** @@ -43,7 +45,9 @@ class PriorityOnboardingDialogController @Inject constructor( val view: View, val context: Context, val ignoresDnd: Boolean, - val showsAsBubble: Boolean + val showsAsBubble: Boolean, + val icon : Drawable, + val onConversationSettingsClickListener : OnConversationSettingsClickListener ) { private lateinit var dialog: Dialog @@ -62,11 +66,21 @@ class PriorityOnboardingDialogController @Inject constructor( dialog.dismiss() } + private fun settings() { + // Log that the user has seen the onboarding + Prefs.putBoolean(context, Prefs.Key.HAS_SEEN_PRIORITY_ONBOARDING, true) + dialog.dismiss() + onConversationSettingsClickListener?.onClick() + } + class Builder @Inject constructor() { private lateinit var view: View private lateinit var context: Context private var ignoresDnd = false private var showAsBubble = false + private lateinit var icon: Drawable + private lateinit var onConversationSettingsClickListener + : OnConversationSettingsClickListener fun setView(v: View): Builder { view = v @@ -88,9 +102,20 @@ class PriorityOnboardingDialogController @Inject constructor( return this } + fun setIcon(draw : Drawable) : Builder { + icon = draw + return this + } + + fun setOnSettingsClick(onClick : OnConversationSettingsClickListener) : Builder { + onConversationSettingsClickListener = onClick + return this + } + fun build(): PriorityOnboardingDialogController { val controller = PriorityOnboardingDialogController( - view, context, ignoresDnd, showAsBubble) + view, context, ignoresDnd, showAsBubble, icon, + onConversationSettingsClickListener) return controller } } @@ -113,13 +138,32 @@ class PriorityOnboardingDialogController @Inject constructor( done() } - if (!ignoresDnd) { - findViewById<LinearLayout>(R.id.ignore_dnd_tip).visibility = GONE + findViewById<TextView>(R.id.settings_button)?.setOnClickListener { + settings() } - if (!showsAsBubble) { - findViewById<LinearLayout>(R.id.floating_bubble_tip).visibility = GONE + findViewById<ImageView>(R.id.conversation_icon)?.setImageDrawable(icon) + + val gapWidth = dialog.context.getResources().getDimensionPixelSize( + R.dimen.conversation_onboarding_bullet_gap_width) + val description = SpannableStringBuilder() + description.append(context.getText(R.string.priority_onboarding_show_at_top_text), + BulletSpan(gapWidth), /* flags */0) + description.append(System.lineSeparator()) + description.append(context.getText(R.string.priority_onboarding_show_avatar_text), + BulletSpan(gapWidth), /* flags */0) + if (showsAsBubble) { + description.append(System.lineSeparator()) + description.append(context.getText( + R.string.priority_onboarding_appear_as_bubble_text), + BulletSpan(gapWidth), /* flags */0) + } + if (ignoresDnd) { + description.append(System.lineSeparator()) + description.append(context.getText(R.string.priority_onboarding_ignores_dnd_text), + BulletSpan(gapWidth), /* flags */0) } + findViewById<TextView>(R.id.behaviors).setText(description) window?.apply { setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) @@ -129,7 +173,7 @@ class PriorityOnboardingDialogController @Inject constructor( attributes = attributes.apply { format = PixelFormat.TRANSLUCENT - title = ChannelEditorDialogController::class.java.simpleName + title = PriorityOnboardingDialogController::class.java.simpleName gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL fitInsetsTypes = attributes.fitInsetsTypes and statusBars().inv() width = MATCH_PARENT diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java index 46c873db8a08..4337e20c0a39 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java @@ -40,6 +40,7 @@ import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.phone.ReverseLinearLayout.ReverseRelativeLayout; import com.android.systemui.statusbar.policy.KeyButtonView; +import java.io.PrintWriter; import java.util.Objects; public class NavigationBarInflaterView extends FrameLayout @@ -469,4 +470,10 @@ public class NavigationBarInflaterView extends FrameLayout private static float convertDpToPx(Context context, float dp) { return dp * context.getResources().getDisplayMetrics().density; } + + public void dump(PrintWriter pw) { + pw.println("NavigationBarInflaterView {"); + pw.println(" mCurrentLayout: " + mCurrentLayout); + pw.println(" }"); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index 2978772cac5e..6b37ac317cdd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -1198,6 +1198,9 @@ public class NavigationBarView extends FrameLayout implements pw.println(" }"); + if (mNavigationInflaterView != null) { + mNavigationInflaterView.dump(pw); + } mContextualButtonGroup.dump(pw); mRecentsOnboarding.dump(pw); mRegionSamplingHelper.dump(pw); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java index f58cce58af74..76c51d61459a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java @@ -572,6 +572,9 @@ public class NotificationIconAreaController implements DarkReceiver, .setInterpolator(Interpolators.LINEAR) .setDuration(AOD_ICONS_APPEAR_DURATION) .start(); + } else { + mAodIcons.setAlpha(1.0f); + mAodIcons.setTranslationY(0); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java index b1f67ce8a581..4122cf5466e2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java @@ -256,7 +256,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler); + mTestHandler, null); final ImageView view = mNotificationInfo.findViewById(R.id.conversation_icon); assertEquals(mIconDrawable, view.getDrawable()); } @@ -280,7 +280,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler); + mTestHandler, null); final TextView textView = mNotificationInfo.findViewById(R.id.pkg_name); assertTrue(textView.getText().toString().contains("App Name")); assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility()); @@ -331,7 +331,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler); + mTestHandler, null); final TextView textView = mNotificationInfo.findViewById(R.id.group_name); assertTrue(textView.getText().toString().contains(group.getName())); assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility()); @@ -356,7 +356,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler); + mTestHandler, null); final TextView textView = mNotificationInfo.findViewById(R.id.group_name); assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility()); assertEquals(GONE, textView.getVisibility()); @@ -380,7 +380,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler); + mTestHandler, null); final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name); assertEquals(GONE, nameView.getVisibility()); } @@ -415,7 +415,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler); + mTestHandler, null); final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name); assertEquals(VISIBLE, nameView.getVisibility()); assertTrue(nameView.getText().toString().contains("Proxied")); @@ -443,7 +443,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler); + mTestHandler, null); final View settingsButton = mNotificationInfo.findViewById(R.id.info); settingsButton.performClick(); @@ -469,7 +469,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler); + mTestHandler, null); final View settingsButton = mNotificationInfo.findViewById(R.id.info); assertTrue(settingsButton.getVisibility() != View.VISIBLE); } @@ -496,7 +496,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, false, mTestHandler, - mTestHandler); + mTestHandler, null); final View settingsButton = mNotificationInfo.findViewById(R.id.info); assertTrue(settingsButton.getVisibility() != View.VISIBLE); } @@ -521,7 +521,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler); + mTestHandler, null); View view = mNotificationInfo.findViewById(R.id.silence); assertThat(view.isSelected()).isTrue(); } @@ -549,7 +549,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler); + mTestHandler, null); View view = mNotificationInfo.findViewById(R.id.default_behavior); assertThat(view.isSelected()).isTrue(); assertThat(((TextView) view.findViewById(R.id.default_summary)).getText()).isEqualTo( @@ -580,7 +580,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler); + mTestHandler, null); View view = mNotificationInfo.findViewById(R.id.default_behavior); assertThat(view.isSelected()).isTrue(); assertThat(((TextView) view.findViewById(R.id.default_summary)).getText()).isEqualTo( @@ -610,7 +610,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler); + mTestHandler, null); View fave = mNotificationInfo.findViewById(R.id.priority); fave.performClick(); @@ -654,7 +654,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler); + mTestHandler, null); mNotificationInfo.findViewById(R.id.default_behavior).performClick(); mTestableLooper.processAllMessages(); @@ -697,7 +697,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler); + mTestHandler, null); View silence = mNotificationInfo.findViewById(R.id.silence); @@ -741,7 +741,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler); + mTestHandler, null); View fave = mNotificationInfo.findViewById(R.id.priority); fave.performClick(); @@ -778,7 +778,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler); + mTestHandler, null); View fave = mNotificationInfo.findViewById(R.id.priority); fave.performClick(); @@ -793,6 +793,45 @@ public class NotificationConversationInfoTest extends SysuiTestCase { } @Test + public void testFavorite_thenDefaultThenFavorite_andSave_nothingChanged() throws Exception { + mConversationChannel.setOriginalImportance(IMPORTANCE_HIGH); + mConversationChannel.setImportance(IMPORTANCE_HIGH); + mConversationChannel.setImportantConversation(true); + + mNotificationInfo.bindNotification( + mShortcutManager, + mMockPackageManager, + mMockINotificationManager, + mVisualStabilityManager, + TEST_PACKAGE_NAME, + mNotificationChannel, + mEntry, + mBubbleMetadata, + null, + null, + mIconFactory, + mContext, + mBuilderProvider, + true, + mTestHandler, + mTestHandler, null); + + View fave = mNotificationInfo.findViewById(R.id.priority); + fave.performClick(); + mNotificationInfo.findViewById(R.id.default_behavior).performClick(); + fave.performClick(); + mNotificationInfo.findViewById(R.id.done).performClick(); + mTestableLooper.processAllMessages(); + + ArgumentCaptor<NotificationChannel> captor = + ArgumentCaptor.forClass(NotificationChannel.class); + verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( + anyString(), anyInt(), captor.capture()); + assertEquals(IMPORTANCE_HIGH, captor.getValue().getImportance()); + assertTrue(captor.getValue().isImportantConversation()); + } + + @Test public void testDefault_andSave() throws Exception { mConversationChannel.setAllowBubbles(true); mConversationChannel.setOriginalImportance(IMPORTANCE_HIGH); @@ -813,7 +852,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler); + mTestHandler, null); mNotificationInfo.findViewById(R.id.default_behavior).performClick(); mNotificationInfo.findViewById(R.id.done).performClick(); @@ -849,7 +888,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler); + mTestHandler, null); mNotificationInfo.findViewById(R.id.default_behavior).performClick(); mNotificationInfo.findViewById(R.id.done).performClick(); @@ -885,7 +924,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler); + mTestHandler, null); mNotificationInfo.findViewById(R.id.default_behavior).performClick(); mNotificationInfo.findViewById(R.id.done).performClick(); @@ -920,7 +959,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler); + mTestHandler, null); View silence = mNotificationInfo.findViewById(R.id.silence); silence.performClick(); @@ -954,7 +993,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler); + mTestHandler, null); verify(mMockINotificationManager, times(1)).createConversationNotificationChannelForPackage( anyString(), anyInt(), anyString(), any(), eq(CONVERSATION_ID)); @@ -979,7 +1018,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mBuilderProvider, true, mTestHandler, - mTestHandler); + mTestHandler, null); verify(mMockINotificationManager, never()).createConversationNotificationChannelForPackage( anyString(), anyInt(), anyString(), any(), eq(CONVERSATION_ID)); @@ -1014,10 +1053,14 @@ public class NotificationConversationInfoTest extends SysuiTestCase { () -> b, true, mTestHandler, - mTestHandler); + mTestHandler, null); // WHEN user clicks "priority" mNotificationInfo.setSelectedAction(NotificationConversationInfo.ACTION_FAVORITE); + verify(controller, never()).show(); + + // and then done + mNotificationInfo.findViewById(R.id.done).performClick(); // THEN the user is presented with the priority onboarding screen verify(controller, atLeastOnce()).show(); @@ -1050,7 +1093,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { () -> b, true, mTestHandler, - mTestHandler); + mTestHandler, null); // WHEN user clicks "priority" mNotificationInfo.setSelectedAction(NotificationConversationInfo.ACTION_FAVORITE); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java index e56ef5b92f87..f327967ebd73 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java @@ -161,25 +161,6 @@ public class PartialConversationInfoTest extends SysuiTestCase { } @Test - public void testBindNotification_SetsTextApplicationName() throws Exception { - when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name"); - mInfo.bindNotification( - mMockPackageManager, - mMockINotificationManager, - mChannelEditorDialogController, - TEST_PACKAGE_NAME, - mNotificationChannel, - mNotificationChannelSet, - mEntry, - null, - true, - false); - final TextView textView = mInfo.findViewById(R.id.pkg_name); - assertTrue(textView.getText().toString().contains("App Name")); - assertEquals(VISIBLE, mInfo.findViewById(R.id.header).getVisibility()); - } - - @Test public void testBindNotification_SetsName() { mInfo.bindNotification( mMockPackageManager, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java index be43e19cfc70..177e845bfead 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java @@ -19,6 +19,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.testing.AndroidTestingRunner; @@ -56,6 +57,8 @@ public class NotificationIconAreaControllerTest extends SysuiTestCase { @Mock NotificationMediaManager mNotificationMediaManager; @Mock + NotificationIconContainer mNotificationIconContainer; + @Mock DozeParameters mDozeParameters; @Mock NotificationShadeWindowView mNotificationShadeWindowView; @@ -67,7 +70,7 @@ public class NotificationIconAreaControllerTest extends SysuiTestCase { when(mStatusBar.getNotificationShadeWindowView()).thenReturn(mNotificationShadeWindowView); when(mNotificationShadeWindowView.findViewById(anyInt())).thenReturn( - mock(NotificationIconContainer.class)); + mNotificationIconContainer); mController = new NotificationIconAreaController(mContext, mStatusBar, mStatusBarStateController, mWakeUpCoordinator, mKeyguardBypassController, @@ -87,4 +90,12 @@ public class NotificationIconAreaControllerTest extends SysuiTestCase { assertTrue(mController.shouldShouldLowPriorityIcons()); } + + @Test + public void testAppearResetsTranslation() { + when(mDozeParameters.shouldControlScreenOff()).thenReturn(false); + mController.appearAodIcons(); + verify(mNotificationIconContainer).setTranslationY(0); + verify(mNotificationIconContainer).setAlpha(1.0f); + } } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 1634f6e62897..0ab571854c72 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -1374,10 +1374,9 @@ public class ConnectivityService extends IConnectivityManager.Stub if (nri == null || net == null || !LOGD_BLOCKED_NETWORKINFO) { return; } - String action = blocked ? "BLOCKED" : "UNBLOCKED"; - log(String.format("Blocked status changed to %s for %d(%d) on netId %d", blocked, - nri.mUid, nri.request.requestId, net.netId)); - mNetworkInfoBlockingLogs.log(action + " " + nri.mUid); + final String action = blocked ? "BLOCKED" : "UNBLOCKED"; + mNetworkInfoBlockingLogs.log(String.format( + "%s %d(%d) on netId %d", action, nri.mUid, nri.request.requestId, net.netId)); } /** diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index efd3c3e9bfc1..27d9ba08e4a2 100755 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -1908,8 +1908,11 @@ public class AudioService extends IAudioService.Stub /** @see AudioManager#adjustVolume(int, int) */ public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags, String callingPackage, String caller) { + boolean hasModifyAudioSettings = + mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS) + == PackageManager.PERMISSION_GRANTED; adjustSuggestedStreamVolume(direction, suggestedStreamType, flags, callingPackage, - caller, Binder.getCallingUid(), hasModifyAudioSettings(), VOL_ADJUST_NORMAL); + caller, Binder.getCallingUid(), hasModifyAudioSettings, VOL_ADJUST_NORMAL); } private void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags, @@ -2014,10 +2017,13 @@ public class AudioService extends IAudioService.Stub + "CHANGE_ACCESSIBILITY_VOLUME / callingPackage=" + callingPackage); return; } + final boolean hasModifyAudioSettings = + mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS) + == PackageManager.PERMISSION_GRANTED; sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_ADJUST_STREAM_VOL, streamType, direction/*val1*/, flags/*val2*/, callingPackage)); adjustStreamVolume(streamType, direction, flags, callingPackage, callingPackage, - Binder.getCallingUid(), hasModifyAudioSettings(), VOL_ADJUST_NORMAL); + Binder.getCallingUid(), hasModifyAudioSettings, VOL_ADJUST_NORMAL); } protected void adjustStreamVolume(int streamType, int direction, int flags, @@ -2528,10 +2534,13 @@ public class AudioService extends IAudioService.Stub + " MODIFY_AUDIO_ROUTING callingPackage=" + callingPackage); return; } + final boolean hasModifyAudioSettings = + mContext.checkCallingOrSelfPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS) + == PackageManager.PERMISSION_GRANTED; sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_SET_STREAM_VOL, streamType, index/*val1*/, flags/*val2*/, callingPackage)); setStreamVolume(streamType, index, flags, callingPackage, callingPackage, - Binder.getCallingUid(), hasModifyAudioSettings()); + Binder.getCallingUid(), hasModifyAudioSettings); } private boolean canChangeAccessibilityVolume() { @@ -3197,7 +3206,8 @@ public class AudioService extends IAudioService.Stub ensureValidStreamType(streamType); final boolean isPrivileged = Binder.getCallingUid() == Process.SYSTEM_UID - || (hasModifyAudioSettings()) + || (mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS) + == PackageManager.PERMISSION_GRANTED) || (mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_ROUTING) == PackageManager.PERMISSION_GRANTED); return (mStreamStates[streamType].getMinIndex(isPrivileged) + 5) / 10; @@ -4755,18 +4765,9 @@ public class AudioService extends IAudioService.Stub handler.sendMessageAtTime(handler.obtainMessage(msg, arg1, arg2, obj), time); } - private boolean hasModifyAudioSettings() { - return mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS) - == PackageManager.PERMISSION_GRANTED; - } - - private boolean hasModifyAudioSettings(int pid, int uid) { - return mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid) - == PackageManager.PERMISSION_GRANTED; - } - boolean checkAudioSettingsPermission(String method) { - if (hasModifyAudioSettings()) { + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS) + == PackageManager.PERMISSION_GRANTED) { return true; } String msg = "Audio Settings Permission Denial: " + method + " from pid=" @@ -7688,10 +7689,13 @@ public class AudioService extends IAudioService.Stub @Override public void adjustSuggestedStreamVolumeForUid(int streamType, int direction, int flags, String callingPackage, int uid, int pid) { + final boolean hasModifyAudioSettings = + mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid) + == PackageManager.PERMISSION_GRANTED; // direction and stream type swap here because the public // adjustSuggested has a different order than the other methods. adjustSuggestedStreamVolume(direction, streamType, flags, callingPackage, - callingPackage, uid, hasModifyAudioSettings(pid, uid), VOL_ADJUST_NORMAL); + callingPackage, uid, hasModifyAudioSettings, VOL_ADJUST_NORMAL); } @Override @@ -7702,15 +7706,21 @@ public class AudioService extends IAudioService.Stub direction/*val1*/, flags/*val2*/, new StringBuilder(callingPackage) .append(" uid:").append(uid).toString())); } + final boolean hasModifyAudioSettings = + mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid) + == PackageManager.PERMISSION_GRANTED; adjustStreamVolume(streamType, direction, flags, callingPackage, - callingPackage, uid, hasModifyAudioSettings(pid, uid), VOL_ADJUST_NORMAL); + callingPackage, uid, hasModifyAudioSettings, VOL_ADJUST_NORMAL); } @Override public void setStreamVolumeForUid(int streamType, int direction, int flags, String callingPackage, int uid, int pid) { + final boolean hasModifyAudioSettings = + mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid) + == PackageManager.PERMISSION_GRANTED; setStreamVolume(streamType, direction, flags, callingPackage, callingPackage, uid, - hasModifyAudioSettings(pid, uid)); + hasModifyAudioSettings); } @Override diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java index ccbe96f30e04..067bdcb111fb 100644 --- a/services/core/java/com/android/server/location/LocationManagerService.java +++ b/services/core/java/com/android/server/location/LocationManagerService.java @@ -28,6 +28,9 @@ import static android.os.PowerManager.locationPowerSaveModeToString; import static com.android.server.location.CallerIdentity.PERMISSION_COARSE; import static com.android.server.location.CallerIdentity.PERMISSION_FINE; +import static com.android.server.location.UserInfoHelper.UserListener.CURRENT_USER_CHANGED; +import static com.android.server.location.UserInfoHelper.UserListener.USER_STARTED; +import static com.android.server.location.UserInfoHelper.UserListener.USER_STOPPED; import static java.util.concurrent.TimeUnit.NANOSECONDS; @@ -64,6 +67,7 @@ import android.location.LocationProvider; import android.location.LocationRequest; import android.location.LocationTime; import android.os.Binder; +import android.os.Build; import android.os.Bundle; import android.os.CancellationSignal; import android.os.Handler; @@ -101,7 +105,7 @@ import com.android.server.location.AbstractLocationProvider.State; import com.android.server.location.CallerIdentity.PermissionLevel; import com.android.server.location.LocationRequestStatistics.PackageProviderKey; import com.android.server.location.LocationRequestStatistics.PackageStatistics; -import com.android.server.location.UserInfoHelper.UserListener; +import com.android.server.location.UserInfoHelper.UserListener.UserChange; import com.android.server.location.gnss.GnssManagerService; import com.android.server.pm.permission.PermissionManagerServiceInternal; @@ -132,11 +136,13 @@ public class LocationManagerService extends ILocationManager.Stub { */ public static class Lifecycle extends SystemService { + private final UserInfoHelper mUserInfoHelper; private final LocationManagerService mService; public Lifecycle(Context context) { super(context); - mService = new LocationManagerService(context); + mUserInfoHelper = new SystemUserInfoHelper(context); + mService = new LocationManagerService(context, mUserInfoHelper); } @Override @@ -161,6 +167,29 @@ public class LocationManagerService extends ILocationManager.Stub { mService.onSystemThirdPartyAppsCanStart(); } } + + @Override + public void onUserStarting(TargetUser user) { + mUserInfoHelper.dispatchOnUserStarted(user.getUserIdentifier()); + } + + @Override + public void onUserSwitching(TargetUser from, TargetUser to) { + mUserInfoHelper.dispatchOnCurrentUserChanged(from.getUserIdentifier(), + to.getUserIdentifier()); + } + + @Override + public void onUserStopped(TargetUser user) { + mUserInfoHelper.dispatchOnUserStopped(user.getUserIdentifier()); + } + + private static class SystemUserInfoHelper extends UserInfoHelper { + + SystemUserInfoHelper(Context context) { + super(context); + } + } } public static final String TAG = "LocationManagerService"; @@ -232,7 +261,7 @@ public class LocationManagerService extends ILocationManager.Stub { @PowerManager.LocationPowerSaveMode private int mBatterySaverMode; - private LocationManagerService(Context context) { + private LocationManagerService(Context context, UserInfoHelper userInfoHelper) { mContext = context.createAttributionContext(ATTRIBUTION_TAG); mHandler = FgThread.getHandler(); mLocalService = new LocalService(); @@ -240,7 +269,7 @@ public class LocationManagerService extends ILocationManager.Stub { LocalServices.addService(LocationManagerInternal.class, mLocalService); mAppOpsHelper = new AppOpsHelper(mContext); - mUserInfoHelper = new UserInfoHelper(mContext); + mUserInfoHelper = userInfoHelper; mSettingsHelper = new SettingsHelper(mContext, mHandler); mAppForegroundHelper = new AppForegroundHelper(mContext); mLocationUsageLogger = new LocationUsageLogger(); @@ -342,7 +371,7 @@ public class LocationManagerService extends ILocationManager.Stub { // initialize the current users. we would get the user started notifications for these // users eventually anyways, but this takes care of it as early as possible. for (int userId: mUserInfoHelper.getCurrentUserIds()) { - onUserChanged(userId, UserListener.USER_STARTED); + onUserChanged(userId, USER_STARTED); } } } @@ -596,32 +625,23 @@ public class LocationManagerService extends ILocationManager.Stub { } } - private void onUserChanged(@UserIdInt int userId, @UserListener.UserChange int change) { + private void onUserChanged(@UserIdInt int userId, @UserChange int change) { switch (change) { - case UserListener.USER_SWITCHED: - if (D) { - Log.d(TAG, "user " + userId + " current status changed"); - } + case CURRENT_USER_CHANGED: synchronized (mLock) { for (LocationProviderManager manager : mProviderManagers) { manager.onEnabledChangedLocked(userId); } } break; - case UserListener.USER_STARTED: - if (D) { - Log.d(TAG, "user " + userId + " started"); - } + case USER_STARTED: synchronized (mLock) { for (LocationProviderManager manager : mProviderManagers) { manager.onUserStarted(userId); } } break; - case UserListener.USER_STOPPED: - if (D) { - Log.d(TAG, "user " + userId + " stopped"); - } + case USER_STOPPED: synchronized (mLock) { for (LocationProviderManager manager : mProviderManagers) { manager.onUserStopped(userId); @@ -957,10 +977,22 @@ public class LocationManagerService extends ILocationManager.Stub { pw.increaseIndent(); // for now we only dump for the parent user - int userId = mUserInfoHelper.getCurrentUserIds()[0]; - pw.println("last location=" + mLastLocation.get(userId)); - pw.println("last coarse location=" + mLastCoarseLocation.get(userId)); - pw.println("enabled=" + isEnabled(userId)); + int[] userIds = mUserInfoHelper.getCurrentUserIds(); + if (userIds.length == 1) { + int userId = userIds[0]; + pw.println("last location=" + mLastLocation.get(userId)); + pw.println("last coarse location=" + mLastCoarseLocation.get(userId)); + pw.println("enabled=" + isEnabled(userId)); + } else { + for (int userId : userIds) { + pw.println("user " + userId + ":"); + pw.increaseIndent(); + pw.println("last location=" + mLastLocation.get(userId)); + pw.println("last coarse location=" + mLastCoarseLocation.get(userId)); + pw.println("enabled=" + isEnabled(userId)); + pw.decreaseIndent(); + } + } } mProvider.dump(fd, pw, args); @@ -1666,6 +1698,9 @@ public class LocationManagerService extends ILocationManager.Stub { * Note: must be constructed with lock held. */ private UpdateRecord(String provider, LocationRequest request, Receiver receiver) { + if (Build.IS_DEBUGGABLE) { + Preconditions.checkState(Thread.holdsLock(mLock)); + } mExpirationRealtimeMs = request.getExpirationRealtimeMs(SystemClock.elapsedRealtime()); mProvider = provider; mRealRequest = request; @@ -1703,6 +1738,10 @@ public class LocationManagerService extends ILocationManager.Stub { * Method to be called when a record will no longer be used. */ private void disposeLocked(boolean removeReceiver) { + if (Build.IS_DEBUGGABLE) { + Preconditions.checkState(Thread.holdsLock(mLock)); + } + CallerIdentity identity = mReceiver.mCallerIdentity; mRequestStatistics.stopRequesting(identity.packageName, identity.featureId, mProvider); diff --git a/services/core/java/com/android/server/location/UserInfoHelper.java b/services/core/java/com/android/server/location/UserInfoHelper.java index a3dcc40bdf2d..53bff8eacb4c 100644 --- a/services/core/java/com/android/server/location/UserInfoHelper.java +++ b/services/core/java/com/android/server/location/UserInfoHelper.java @@ -20,48 +20,48 @@ import static android.os.UserManager.DISALLOW_SHARE_LOCATION; import static com.android.server.location.LocationManagerService.D; import static com.android.server.location.LocationManagerService.TAG; +import static com.android.server.location.UserInfoHelper.UserListener.CURRENT_USER_CHANGED; +import static com.android.server.location.UserInfoHelper.UserListener.USER_STARTED; +import static com.android.server.location.UserInfoHelper.UserListener.USER_STOPPED; +import android.annotation.CallSuper; import android.annotation.IntDef; import android.annotation.Nullable; import android.annotation.UserIdInt; -import android.app.ActivityManager; -import android.content.BroadcastReceiver; +import android.app.ActivityManagerInternal; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.os.Binder; -import android.os.Build; import android.os.UserHandle; import android.os.UserManager; import android.util.Log; import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.ArrayUtils; import com.android.internal.util.Preconditions; -import com.android.server.FgThread; +import com.android.server.LocalServices; import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; +import java.util.Objects; import java.util.concurrent.CopyOnWriteArrayList; /** * Provides accessors and listeners for all user info. */ -public class UserInfoHelper { +public abstract class UserInfoHelper { /** * Listener for current user changes. */ public interface UserListener { - int USER_SWITCHED = 1; + int CURRENT_USER_CHANGED = 1; int USER_STARTED = 2; int USER_STOPPED = 3; - @IntDef({USER_SWITCHED, USER_STARTED, USER_STOPPED}) + @IntDef({CURRENT_USER_CHANGED, USER_STARTED, USER_STOPPED}) @Retention(RetentionPolicy.SOURCE) @interface UserChange {} @@ -75,143 +75,101 @@ public class UserInfoHelper { private final CopyOnWriteArrayList<UserListener> mListeners; @GuardedBy("this") - @Nullable private UserManager mUserManager; - - @UserIdInt private volatile int mCurrentUserId; - + @Nullable private ActivityManagerInternal mActivityManagerInternal; @GuardedBy("this") - @UserIdInt private int mCachedParentUserId; - @GuardedBy("this") - private int[] mCachedProfileUserIds; + @Nullable private UserManager mUserManager; public UserInfoHelper(Context context) { mContext = context; mListeners = new CopyOnWriteArrayList<>(); - - mCurrentUserId = UserHandle.USER_NULL; - mCachedParentUserId = UserHandle.USER_NULL; - mCachedProfileUserIds = new int[]{UserHandle.USER_NULL}; } /** Called when system is ready. */ + @CallSuper public synchronized void onSystemReady() { - if (mUserManager != null) { + if (mActivityManagerInternal != null) { return; } + mActivityManagerInternal = Objects.requireNonNull( + LocalServices.getService(ActivityManagerInternal.class)); mUserManager = mContext.getSystemService(UserManager.class); - - IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(Intent.ACTION_USER_SWITCHED); - intentFilter.addAction(Intent.ACTION_USER_STARTED); - intentFilter.addAction(Intent.ACTION_USER_STOPPED); - intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED); - intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED); - - mContext.registerReceiverAsUser(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (action == null) { - return; - } - int userId; - switch (action) { - case Intent.ACTION_USER_SWITCHED: - userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); - if (userId != UserHandle.USER_NULL) { - onCurrentUserChanged(userId); - } - break; - case Intent.ACTION_USER_STARTED: - userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); - if (userId != UserHandle.USER_NULL) { - onUserStarted(userId); - } - break; - case Intent.ACTION_USER_STOPPED: - userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); - if (userId != UserHandle.USER_NULL) { - onUserStopped(userId); - } - break; - case Intent.ACTION_MANAGED_PROFILE_ADDED: - case Intent.ACTION_MANAGED_PROFILE_REMOVED: - onUserProfilesChanged(); - break; - } - } - }, UserHandle.ALL, intentFilter, null, FgThread.getHandler()); - - mCurrentUserId = ActivityManager.getCurrentUser(); } /** * Adds a listener for user changed events. Callbacks occur on an unspecified thread. */ - public void addListener(UserListener listener) { + public final void addListener(UserListener listener) { mListeners.add(listener); } /** * Removes a listener for user changed events. */ - public void removeListener(UserListener listener) { + public final void removeListener(UserListener listener) { mListeners.remove(listener); } - private void onCurrentUserChanged(@UserIdInt int newUserId) { - if (newUserId == mCurrentUserId) { - return; - } - + protected void dispatchOnUserStarted(@UserIdInt int userId) { if (D) { - Log.d(TAG, "current user switched from u" + mCurrentUserId + " to u" + newUserId); + Log.d(TAG, "u" + userId + " started"); } - int oldUserId = mCurrentUserId; - mCurrentUserId = newUserId; - - onUserChanged(oldUserId, UserListener.USER_SWITCHED); - onUserChanged(newUserId, UserListener.USER_SWITCHED); + for (UserListener listener : mListeners) { + listener.onUserChanged(userId, USER_STARTED); + } } - private void onUserStarted(@UserIdInt int userId) { + protected void dispatchOnUserStopped(@UserIdInt int userId) { if (D) { - Log.d(TAG, "u" + userId + " started"); + Log.d(TAG, "u" + userId + " stopped"); } - onUserChanged(userId, UserListener.USER_STARTED); + for (UserListener listener : mListeners) { + listener.onUserChanged(userId, USER_STOPPED); + } } - private void onUserStopped(@UserIdInt int userId) { + protected void dispatchOnCurrentUserChanged(@UserIdInt int fromUserId, + @UserIdInt int toUserId) { + int[] fromUserIds = getProfileIds(fromUserId); + int[] toUserIds = getProfileIds(toUserId); if (D) { - Log.d(TAG, "u" + userId + " stopped"); + Log.d(TAG, "current user changed from u" + Arrays.toString(fromUserIds) + " to u" + + Arrays.toString(toUserIds)); } - onUserChanged(userId, UserListener.USER_STOPPED); - } - - private void onUserChanged(@UserIdInt int userId, @UserListener.UserChange int change) { for (UserListener listener : mListeners) { - listener.onUserChanged(userId, change); + for (int userId : fromUserIds) { + listener.onUserChanged(userId, CURRENT_USER_CHANGED); + } } - } - private synchronized void onUserProfilesChanged() { - // this intent is only sent to the current user - if (mCachedParentUserId == mCurrentUserId) { - mCachedParentUserId = UserHandle.USER_NULL; - mCachedProfileUserIds = new int[]{UserHandle.USER_NULL}; + for (UserListener listener : mListeners) { + for (int userId : toUserIds) { + listener.onUserChanged(userId, CURRENT_USER_CHANGED); + } } } /** * Returns an array of current user ids. This will always include the current user, and will - * also include any profiles of the current user. + * also include any profiles of the current user. The caller must never mutate the returned + * array. */ public int[] getCurrentUserIds() { - return getProfileUserIdsForParentUser(mCurrentUserId); + synchronized (this) { + if (mActivityManagerInternal == null) { + return new int[] {}; + } + } + + long identity = Binder.clearCallingIdentity(); + try { + return mActivityManagerInternal.getCurrentProfileIds(); + } finally { + Binder.restoreCallingIdentity(identity); + } } /** @@ -219,54 +177,47 @@ public class UserInfoHelper { * user. */ public boolean isCurrentUserId(@UserIdInt int userId) { - int currentUserId = mCurrentUserId; - return userId == currentUserId || ArrayUtils.contains( - getProfileUserIdsForParentUser(currentUserId), userId); - } + synchronized (this) { + if (mActivityManagerInternal == null) { + return false; + } + } - @GuardedBy("this") - private synchronized int[] getProfileUserIdsForParentUser(@UserIdInt int parentUserId) { - if (parentUserId != mCachedParentUserId) { - long identity = Binder.clearCallingIdentity(); - try { - Preconditions.checkState(mUserManager != null); - - // more expensive check - check that argument really is a parent user id - if (Build.IS_DEBUGGABLE) { - Preconditions.checkArgument( - mUserManager.getProfileParent(parentUserId) == null); - } + long identity = Binder.clearCallingIdentity(); + try { + return mActivityManagerInternal.isCurrentProfile(userId); + } finally { + Binder.restoreCallingIdentity(identity); + } + } - mCachedParentUserId = parentUserId; - mCachedProfileUserIds = mUserManager.getProfileIdsWithDisabled(parentUserId); - } finally { - Binder.restoreCallingIdentity(identity); - } + private int[] getProfileIds(@UserIdInt int userId) { + synchronized (this) { + Preconditions.checkState(mUserManager != null); } - return mCachedProfileUserIds; + long identity = Binder.clearCallingIdentity(); + try { + return mUserManager.getEnabledProfileIds(userId); + } finally { + Binder.restoreCallingIdentity(identity); + } } /** * Dump info for debugging. */ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - boolean systemRunning; - synchronized (this) { - systemRunning = mUserManager != null; - } - - if (systemRunning) { - int[] currentUserIds = getProfileUserIdsForParentUser(mCurrentUserId); - pw.println("current users: " + Arrays.toString(currentUserIds)); - for (int userId : currentUserIds) { - if (mUserManager.hasUserRestrictionForUser(DISALLOW_SHARE_LOCATION, + int[] currentUserProfiles = getCurrentUserIds(); + pw.println("current users: " + Arrays.toString(currentUserProfiles)); + UserManager userManager = mContext.getSystemService(UserManager.class); + if (userManager != null) { + for (int userId : currentUserProfiles) { + if (userManager.hasUserRestrictionForUser(DISALLOW_SHARE_LOCATION, UserHandle.of(userId))) { pw.println(" u" + userId + " restricted"); } } - } else { - pw.println("current user: " + mCurrentUserId); } } } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 9d3385f20695..a95dc3035200 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -246,6 +246,7 @@ import com.android.internal.os.SomeArgs; import com.android.internal.statusbar.NotificationVisibility; import com.android.internal.util.ArrayUtils; import com.android.internal.util.CollectionUtils; +import com.android.internal.util.ConcurrentUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.Preconditions; @@ -2173,19 +2174,19 @@ public class NotificationManagerService extends SystemService { mStatsManager.setPullAtomCallback( PACKAGE_NOTIFICATION_PREFERENCES, null, // use default PullAtomMetadata values - BackgroundThread.getExecutor(), + ConcurrentUtils.DIRECT_EXECUTOR, mPullAtomCallback ); mStatsManager.setPullAtomCallback( PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES, null, // use default PullAtomMetadata values - BackgroundThread.getExecutor(), + ConcurrentUtils.DIRECT_EXECUTOR, mPullAtomCallback ); mStatsManager.setPullAtomCallback( PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES, null, // use default PullAtomMetadata values - BackgroundThread.getExecutor(), + ConcurrentUtils.DIRECT_EXECUTOR, mPullAtomCallback ); } diff --git a/services/core/java/com/android/server/pm/DataLoaderManagerService.java b/services/core/java/com/android/server/pm/DataLoaderManagerService.java index 81ee7d9eeef7..52fdc7983636 100644 --- a/services/core/java/com/android/server/pm/DataLoaderManagerService.java +++ b/services/core/java/com/android/server/pm/DataLoaderManagerService.java @@ -21,7 +21,6 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; -import android.content.pm.ApplicationInfo; import android.content.pm.DataLoaderParamsParcel; import android.content.pm.IDataLoader; import android.content.pm.IDataLoaderManager; @@ -122,19 +121,7 @@ public class DataLoaderManagerService extends SystemService { ri.serviceInfo.packageName, ri.serviceInfo.name); // There should only be one matching provider inside the given package. // If there's more than one, return the first one found. - try { - ApplicationInfo ai = pm.getApplicationInfo(resolved.getPackageName(), 0); - if (!ai.isPrivilegedApp()) { - Slog.w(TAG, - "Data loader: " + resolved + " is not a privileged app, skipping."); - continue; - } - return resolved; - } catch (PackageManager.NameNotFoundException ex) { - Slog.w(TAG, - "Privileged data loader: " + resolved + " not found, skipping."); - } - + return resolved; } Slog.e(TAG, "Didn't find any matching data loader service provider."); return null; diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 236a6816b3e3..f827721be3b7 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -40,6 +40,7 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageInstaller; import android.content.pm.PackageInstaller.SessionInfo; import android.content.pm.PackageInstaller.SessionParams; +import android.content.pm.PackageItemInfo; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; import android.content.pm.VersionedPackage; @@ -126,8 +127,10 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements private static final long MAX_AGE_MILLIS = 3 * DateUtils.DAY_IN_MILLIS; /** Automatically destroy staged sessions that have not changed state in this time */ private static final long MAX_TIME_SINCE_UPDATE_MILLIS = 7 * DateUtils.DAY_IN_MILLIS; - /** Upper bound on number of active sessions for a UID */ - private static final long MAX_ACTIVE_SESSIONS = 1024; + /** Upper bound on number of active sessions for a UID that has INSTALL_PACKAGES */ + private static final long MAX_ACTIVE_SESSIONS_WITH_PERMISSION = 1024; + /** Upper bound on number of active sessions for a UID without INSTALL_PACKAGES */ + private static final long MAX_ACTIVE_SESSIONS_NO_PERMISSION = 50; /** Upper bound on number of historical sessions for a UID */ private static final long MAX_HISTORICAL_SESSIONS = 1048576; @@ -503,7 +506,18 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements + "to use a data loader"); } - String requestedInstallerPackageName = params.installerPackageName != null + // App package name and label length is restricted so that really long strings aren't + // written to disk. + if (params.appPackageName != null + && params.appPackageName.length() > SessionParams.MAX_PACKAGE_NAME_LENGTH) { + params.appPackageName = null; + } + + params.appLabel = TextUtils.trimToSize(params.appLabel, + PackageItemInfo.MAX_SAFE_LABEL_LENGTH); + + String requestedInstallerPackageName = (params.installerPackageName != null + && params.installerPackageName.length() < SessionParams.MAX_PACKAGE_NAME_LENGTH) ? params.installerPackageName : installerPackageName; if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) { @@ -635,12 +649,23 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } } + if (params.whitelistedRestrictedPermissions != null) { + mPermissionManager.retainHardAndSoftRestrictedPermissions( + params.whitelistedRestrictedPermissions); + } + final int sessionId; final PackageInstallerSession session; synchronized (mSessions) { // Sanity check that installer isn't going crazy final int activeCount = getSessionCount(mSessions, callingUid); - if (activeCount >= MAX_ACTIVE_SESSIONS) { + if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES) + == PackageManager.PERMISSION_GRANTED) { + if (activeCount >= MAX_ACTIVE_SESSIONS_WITH_PERMISSION) { + throw new IllegalStateException( + "Too many active sessions for UID " + callingUid); + } + } else if (activeCount >= MAX_ACTIVE_SESSIONS_NO_PERMISSION) { throw new IllegalStateException( "Too many active sessions for UID " + callingUid); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 6bb10c79d382..766fae64f647 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -148,6 +148,8 @@ import android.app.ResourcesManager; import android.app.admin.IDevicePolicyManager; import android.app.admin.SecurityLog; import android.app.backup.IBackupManager; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledAfter; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; @@ -639,6 +641,19 @@ public class PackageManagerService extends IPackageManager.Stub */ private static final int DEFAULT_VERIFICATION_RESPONSE = PackageManager.VERIFICATION_ALLOW; + /** + * Adding an installer package name to a package that does not have one set requires the + * INSTALL_PACKAGES permission. + * + * If the caller targets R, this will throw a SecurityException. Otherwise the request will + * fail silently. In both cases, and regardless of whether this change is enabled, the + * installer package will remain unchanged. + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q) + private static final long THROW_EXCEPTION_ON_REQUIRE_INSTALL_PACKAGES_TO_ADD_INSTALLER_PACKAGE = + 150857253; + public static final String PLATFORM_PACKAGE_NAME = "android"; private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive"; @@ -5264,15 +5279,17 @@ public class PackageManagerService extends IPackageManager.Stub * </ul> */ int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps, - boolean matchSystemOnly) { + boolean isImplicitImageCaptureIntentAndNotSetByDpc) { return updateFlagsForResolve(flags, userId, callingUid, - wantInstantApps, matchSystemOnly, false /*onlyExposedExplicitly*/); + wantInstantApps, false /*onlyExposedExplicitly*/, + isImplicitImageCaptureIntentAndNotSetByDpc); } int updateFlagsForResolve(int flags, int userId, int callingUid, - boolean wantInstantApps, boolean onlyExposedExplicitly, boolean matchSystemOnly) { + boolean wantInstantApps, boolean onlyExposedExplicitly, + boolean isImplicitImageCaptureIntentAndNotSetByDpc) { // Safe mode means we shouldn't match any third-party components - if (mSafeMode || matchSystemOnly) { + if (mSafeMode || isImplicitImageCaptureIntentAndNotSetByDpc) { flags |= PackageManager.MATCH_SYSTEM_ONLY; } if (getInstantAppPackageName(callingUid) != null) { @@ -6400,7 +6417,8 @@ public class PackageManagerService extends IPackageManager.Stub if (!mUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart, - intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/); + isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType, + flags)); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "resolve intent"); @@ -6438,7 +6456,7 @@ public class PackageManagerService extends IPackageManager.Stub final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver()); final int flags = updateFlagsForResolve( 0, userId, callingUid, false /*includeInstantApps*/, - intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/); + isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType, 0)); final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType, flags, userId); synchronized (mLock) { @@ -6684,6 +6702,40 @@ public class PackageManagerService extends IPackageManager.Stub return true; } + /** + * From Android R, camera intents have to match system apps. The only exception to this is if + * the DPC has set the camera persistent preferred activity. This case was introduced + * because it is important that the DPC has the ability to set both system and non-system + * camera persistent preferred activities. + * + * @return {@code true} if the intent is a camera intent and the persistent preferred + * activity was not set by the DPC. + */ + @GuardedBy("mLock") + private boolean isImplicitImageCaptureIntentAndNotSetByDpcLocked(Intent intent, int userId, + String resolvedType, int flags) { + return intent.isImplicitImageCaptureIntent() && !isPersistentPreferredActivitySetByDpm( + intent, userId, resolvedType, flags); + } + + private boolean isPersistentPreferredActivitySetByDpm(Intent intent, int userId, + String resolvedType, int flags) { + PersistentPreferredIntentResolver ppir = mSettings.mPersistentPreferredActivities + .get(userId); + //TODO(b/158003772): Remove double query + List<PersistentPreferredActivity> pprefs = ppir != null + ? ppir.queryIntent(intent, resolvedType, + (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, + userId) + : new ArrayList<>(); + for (PersistentPreferredActivity ppa : pprefs) { + if (ppa.mIsSetByDpm) { + return true; + } + } + return false; + } + @GuardedBy("mLock") private ResolveInfo findPersistentPreferredActivityLP(Intent intent, String resolvedType, int flags, List<ResolveInfo> query, boolean debug, int userId) { @@ -6767,7 +6819,8 @@ public class PackageManagerService extends IPackageManager.Stub android.provider.Settings.Global.DEVICE_PROVISIONED, 0) == 1; flags = updateFlagsForResolve( flags, userId, callingUid, false /*includeInstantApps*/, - intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/); + isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType, + flags)); intent = updateIntentForResolve(intent); // writer synchronized (mLock) { @@ -6980,7 +7033,8 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mLock) { int flags = updateFlagsForResolve(0, parent.id, callingUid, false /*includeInstantApps*/, - intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/); + isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, parent.id, + resolvedType, 0)); CrossProfileDomainInfo xpDomainInfo = getCrossProfileDomainPreferredLpr( intent, resolvedType, flags, sourceUserId, parent.id); return xpDomainInfo != null; @@ -7067,7 +7121,8 @@ public class PackageManagerService extends IPackageManager.Stub flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart, comp != null || pkgName != null /*onlyExposedExplicitly*/, - intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/); + isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType, + flags)); if (comp != null) { final List<ResolveInfo> list = new ArrayList<>(1); final ActivityInfo ai = getActivityInfo(comp, flags, userId); @@ -7856,7 +7911,8 @@ public class PackageManagerService extends IPackageManager.Stub if (!mUserManager.exists(userId)) return Collections.emptyList(); final int callingUid = Binder.getCallingUid(); flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/, - intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/); + isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType, + flags)); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "query intent activity options"); @@ -8043,7 +8099,8 @@ public class PackageManagerService extends IPackageManager.Stub "query intent receivers"); final String instantAppPkgName = getInstantAppPackageName(callingUid); flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/, - intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/); + isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType, + flags)); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { @@ -8134,7 +8191,7 @@ public class PackageManagerService extends IPackageManager.Stub int userId, int callingUid) { if (!mUserManager.exists(userId)) return null; flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/, - false /* matchSystemOnly */); + false /* isImplicitImageCaptureIntentAndNotSetByDpc */); List<ResolveInfo> query = queryIntentServicesInternal( intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/); if (query != null) { @@ -8166,7 +8223,7 @@ public class PackageManagerService extends IPackageManager.Stub "query intent receivers"); final String instantAppPkgName = getInstantAppPackageName(callingUid); flags = updateFlagsForResolve(flags, userId, callingUid, includeInstantApps, - false /* matchSystemOnly */); + false /* isImplicitImageCaptureIntentAndNotSetByDpc */); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { @@ -8304,7 +8361,7 @@ public class PackageManagerService extends IPackageManager.Stub final int callingUid = Binder.getCallingUid(); final String instantAppPkgName = getInstantAppPackageName(callingUid); flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/, - false /* matchSystemOnly */); + false /* isImplicitImageCaptureIntentAndNotSetByDpc */); ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { @@ -14130,19 +14187,38 @@ public class PackageManagerService extends IPackageManager.Stub // be signed with the same cert as the caller. String targetInstallerPackageName = targetPackageSetting.installSource.installerPackageName; - if (targetInstallerPackageName != null) { - PackageSetting setting = mSettings.mPackages.get( - targetInstallerPackageName); - // If the currently set package isn't valid, then it's always - // okay to change it. - if (setting != null) { - if (compareSignatures(callerSignature, - setting.signatures.mSigningDetails.signatures) - != PackageManager.SIGNATURE_MATCH) { - throw new SecurityException( - "Caller does not have same cert as old installer package " - + targetInstallerPackageName); + PackageSetting targetInstallerPkgSetting = targetInstallerPackageName == null ? null : + mSettings.mPackages.get(targetInstallerPackageName); + + if (targetInstallerPkgSetting != null) { + if (compareSignatures(callerSignature, + targetInstallerPkgSetting.signatures.mSigningDetails.signatures) + != PackageManager.SIGNATURE_MATCH) { + throw new SecurityException( + "Caller does not have same cert as old installer package " + + targetInstallerPackageName); + } + } else if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES) + != PackageManager.PERMISSION_GRANTED) { + // This is probably an attempt to exploit vulnerability b/150857253 of taking + // privileged installer permissions when the installer has been uninstalled or + // was never set. + EventLog.writeEvent(0x534e4554, "150857253", callingUid, ""); + + long binderToken = Binder.clearCallingIdentity(); + try { + if (mInjector.getCompatibility().isChangeEnabledByUid( + THROW_EXCEPTION_ON_REQUIRE_INSTALL_PACKAGES_TO_ADD_INSTALLER_PACKAGE, + callingUid)) { + throw new SecurityException("Neither user " + callingUid + + " nor current process has " + + Manifest.permission.INSTALL_PACKAGES); + } else { + // If change disabled, fail silently for backwards compatibility + return; } + } finally { + Binder.restoreCallingIdentity(binderToken); } } @@ -19840,7 +19916,7 @@ public class PackageManagerService extends IPackageManager.Stub } synchronized (mLock) { mSettings.editPersistentPreferredActivitiesLPw(userId).addFilter( - new PersistentPreferredActivity(filter, activity)); + new PersistentPreferredActivity(filter, activity, true)); scheduleWritePackageRestrictionsLocked(userId); } updateDefaultHomeNotLocked(userId); diff --git a/services/core/java/com/android/server/pm/PersistentPreferredActivity.java b/services/core/java/com/android/server/pm/PersistentPreferredActivity.java index 0d4cdf9dee53..5a6fd0923f53 100644 --- a/services/core/java/com/android/server/pm/PersistentPreferredActivity.java +++ b/services/core/java/com/android/server/pm/PersistentPreferredActivity.java @@ -16,31 +16,34 @@ package com.android.server.pm; +import android.content.ComponentName; +import android.content.IntentFilter; +import android.util.Log; + import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; -import android.content.ComponentName; -import android.content.IntentFilter; -import android.util.Log; - import java.io.IOException; class PersistentPreferredActivity extends IntentFilter { private static final String ATTR_NAME = "name"; // component name private static final String ATTR_FILTER = "filter"; // filter + private static final String ATTR_SET_BY_DPM = "set-by-dpm"; // set by DPM private static final String TAG = "PersistentPreferredActivity"; private static final boolean DEBUG_FILTERS = false; final ComponentName mComponent; + final boolean mIsSetByDpm; - PersistentPreferredActivity(IntentFilter filter, ComponentName activity) { + PersistentPreferredActivity(IntentFilter filter, ComponentName activity, boolean isSetByDpm) { super(filter); mComponent = activity; + mIsSetByDpm = isSetByDpm; } PersistentPreferredActivity(XmlPullParser parser) throws XmlPullParserException, IOException { @@ -52,6 +55,8 @@ class PersistentPreferredActivity extends IntentFilter { "Bad activity name " + shortComponent + " at " + parser.getPositionDescription()); } + mIsSetByDpm = Boolean.parseBoolean(parser.getAttributeValue(null, ATTR_SET_BY_DPM)); + int outerDepth = parser.getDepth(); String tagName = parser.getName(); int type; @@ -83,6 +88,7 @@ class PersistentPreferredActivity extends IntentFilter { public void writeToXml(XmlSerializer serializer) throws IOException { serializer.attribute(null, ATTR_NAME, mComponent.flattenToShortString()); + serializer.attribute(null, ATTR_SET_BY_DPM, Boolean.toString(mIsSetByDpm)); serializer.startTag(null, ATTR_FILTER); super.writeToXml(serializer); serializer.endTag(null, ATTR_FILTER); @@ -91,6 +97,7 @@ class PersistentPreferredActivity extends IntentFilter { @Override public String toString() { return "PersistentPreferredActivity{0x" + Integer.toHexString(System.identityHashCode(this)) - + " " + mComponent.flattenToShortString() + "}"; + + " " + mComponent.flattenToShortString() + + ", mIsSetByDpm=" + mIsSetByDpm + "}"; } } diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 79805e3b42ae..8ccf837f64dc 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -77,7 +77,11 @@ import com.android.server.pm.parsing.pkg.AndroidPackageUtils; import com.android.server.pm.parsing.pkg.ParsedPackage; import com.android.server.rollback.WatchdogRollbackLogger; +import java.io.BufferedReader; +import java.io.BufferedWriter; import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -102,6 +106,9 @@ public class StagingManager { private final PreRebootVerificationHandler mPreRebootVerificationHandler; private final Supplier<PackageParser2> mPackageParserSupplier; + private final File mFailureReasonFile = new File("/metadata/staged-install/failure_reason.txt"); + private String mFailureReason; + @GuardedBy("mStagedSessions") private final SparseArray<PackageInstallerSession> mStagedSessions = new SparseArray<>(); @@ -125,6 +132,12 @@ public class StagingManager { mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); mPreRebootVerificationHandler = new PreRebootVerificationHandler( BackgroundThread.get().getLooper()); + + if (mFailureReasonFile.exists()) { + try (BufferedReader reader = new BufferedReader(new FileReader(mFailureReasonFile))) { + mFailureReason = reader.readLine(); + } catch (Exception ignore) { } + } } /** @@ -383,10 +396,19 @@ public class StagingManager { } // Reverts apex sessions and user data (if checkpoint is supported). Also reboots the device. - private void abortCheckpoint(String errorMsg) { - Slog.e(TAG, "Aborting checkpoint: " + errorMsg); + private void abortCheckpoint(int sessionId, String errorMsg) { + String failureReason = "Failed to install sessionId: " + sessionId + " Error: " + errorMsg; + Slog.e(TAG, failureReason); try { if (supportsCheckpoint() && needsCheckpoint()) { + // Store failure reason for next reboot + try (BufferedWriter writer = + new BufferedWriter(new FileWriter(mFailureReasonFile))) { + writer.write(failureReason); + } catch (Exception e) { + Slog.w(TAG, "Failed to save failure reason: ", e); + } + // Only revert apex sessions if device supports updating apex if (mApexManager.isApexSupported()) { mApexManager.revertActiveSessions(); @@ -592,14 +614,12 @@ public class StagingManager { // If checkpoint is supported, then we only resume sessions if we are in checkpointing // mode. If not, we fail all sessions. if (supportsCheckpoint() && !needsCheckpoint()) { - // TODO(b/146343545): Persist failure reason across checkpoint reboot - Slog.d(TAG, "Reverting back to safe state. Marking " + session.sessionId - + " as failed."); - String errorMsg = "Reverting back to safe state"; - if (!TextUtils.isEmpty(mNativeFailureReason)) { - errorMsg = "Entered fs-rollback mode and reverted session due to crashing " - + "native process: " + mNativeFailureReason; + String errorMsg = "Reverting back to safe state. Marking " + session.sessionId + + " as failed"; + if (!TextUtils.isEmpty(mFailureReason)) { + errorMsg = errorMsg + ": " + mFailureReason; } + Slog.d(TAG, errorMsg); session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN, errorMsg); return; } @@ -624,7 +644,7 @@ public class StagingManager { + "supposed to be activated"; session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMsg); - abortCheckpoint(errorMsg); + abortCheckpoint(session.sessionId, errorMsg); return; } if (isApexSessionFailed(apexSessionInfo)) { @@ -636,7 +656,7 @@ public class StagingManager { } session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMsg); - abortCheckpoint(errorMsg); + abortCheckpoint(session.sessionId, errorMsg); return; } if (!apexSessionInfo.isActivated && !apexSessionInfo.isSuccess) { @@ -647,7 +667,7 @@ public class StagingManager { + "didn't activate nor fail. Marking it as failed anyway."; session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMsg); - abortCheckpoint(errorMsg); + abortCheckpoint(session.sessionId, errorMsg); return; } } @@ -664,7 +684,7 @@ public class StagingManager { installApksInSession(session); } catch (PackageManagerException e) { session.setStagedSessionFailed(e.error, e.getMessage()); - abortCheckpoint(e.getMessage()); + abortCheckpoint(session.sessionId, e.getMessage()); // If checkpoint is not supported, we have to handle failure for one staged session. if (!hasApex) { @@ -1189,6 +1209,8 @@ public class StagingManager { ctx.unregisterReceiver(this); } }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED)); + + mFailureReasonFile.delete(); } private static class LocalIntentReceiverAsync { diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index d5c9424528bd..40fa798309c1 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -2250,9 +2250,6 @@ public class UserManagerService extends IUserManager.Stub { // Managed profiles have their own specific rules. final boolean isManagedProfile = type.isManagedProfile(); if (isManagedProfile) { - if (ActivityManager.isLowRamDeviceStatic()) { - return false; - } if (!mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_MANAGED_USERS)) { return false; diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index b0d4d957fc21..d3f3ba1dc6bb 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -26,6 +26,7 @@ import static android.content.pm.ApplicationInfo.AUTO_REVOKE_DISCOURAGED; import static android.content.pm.PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT; import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION; import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT; +import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE; import static android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME; import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED; import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; @@ -1804,8 +1805,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { continue; } - // If this permission was granted by default, make sure it is. - if ((oldFlags & FLAG_PERMISSION_GRANTED_BY_DEFAULT) != 0) { + // If this permission was granted by default or role, make sure it is. + if ((oldFlags & FLAG_PERMISSION_GRANTED_BY_DEFAULT) != 0 + || (oldFlags & FLAG_PERMISSION_GRANTED_BY_ROLE) != 0) { // PermissionPolicyService will handle the app op for runtime permissions later. grantRuntimePermissionInternal(permName, packageName, false, Process.SYSTEM_UID, userId, delayingPermCallback); @@ -4948,6 +4950,20 @@ public class PermissionManagerService extends IPermissionManager.Stub { StorageManager.UUID_PRIVATE_INTERNAL, true, mDefaultPermissionCallback); } } + + @Override + public void retainHardAndSoftRestrictedPermissions(@NonNull List<String> permissions) { + synchronized (mLock) { + Iterator<String> iterator = permissions.iterator(); + while (iterator.hasNext()) { + String permission = iterator.next(); + BasePermission basePermission = mSettings.mPermissions.get(permission); + if (basePermission == null || !basePermission.isHardOrSoftRestricted()) { + iterator.remove(); + } + } + } + } } private static final class OnPermissionChangeListeners extends Handler { diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java index 57a25eddf7ce..4412162a5cc8 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java @@ -36,6 +36,7 @@ import java.util.function.Consumer; * TODO: Should be merged into PermissionManagerInternal, but currently uses internal classes. */ public abstract class PermissionManagerServiceInternal extends PermissionManagerInternal { + /** * Provider for package names. */ @@ -455,4 +456,10 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager /** Called when a new user has been created. */ public abstract void onNewUserCreated(@UserIdInt int userId); + + /** + * Removes invalid permissions which are not {@link PermissionInfo#FLAG_HARD_RESTRICTED} or + * {@link PermissionInfo#FLAG_SOFT_RESTRICTED} from the input. + */ + public abstract void retainHardAndSoftRestrictedPermissions(@NonNull List<String> permissions); } diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index 6c1ff728e6b9..ab459fdadd21 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -3319,8 +3319,8 @@ public class StatsPullAtomService extends SystemService { public void run() { try { estimateAppOpsSamplingRate(); - } catch (Exception e) { - Slog.e(TAG, "AppOps sampling ratio estimation failed"); + } catch (Throwable e) { + Slog.e(TAG, "AppOps sampling ratio estimation failed: ", e); synchronized (mAppOpsSamplingRateLock) { mAppOpsSamplingRate = min(mAppOpsSamplingRate, 10); } @@ -3361,7 +3361,7 @@ public class StatsPullAtomService extends SystemService { Instant.now().minus(1, ChronoUnit.DAYS).toEpochMilli(), Long.MAX_VALUE).setFlags( OP_FLAGS_PULLED).build(); - appOps.getHistoricalOps(histOpsRequest, mContext.getMainExecutor(), ops::complete); + appOps.getHistoricalOps(histOpsRequest, AsyncTask.THREAD_POOL_EXECUTOR, ops::complete); HistoricalOps histOps = ops.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); List<AppOpEntry> opsList = diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java index 5f6323369d0a..c38d649ada9b 100644 --- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java +++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java @@ -115,7 +115,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { private static final String TAG = "UriGrantsManagerService"; // Maximum number of persisted Uri grants a package is allowed private static final int MAX_PERSISTED_URI_GRANTS = 128; - private static final boolean ENABLE_DYNAMIC_PERMISSIONS = true; + private static final boolean ENABLE_DYNAMIC_PERMISSIONS = false; private final Object mLock = new Object(); private final H mH; diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 045089082fd8..36232e13fcf1 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -5790,10 +5790,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // be invoked and we need to invoke it ourself. if (mLocalSyncId >= 0) { mBLASTSyncEngine.setReady(mLocalSyncId); - } else { - mWaitingListener.onTransactionReady(mWaitingSyncId, mBLASTSyncTransaction); + return mWinAnimator.finishDrawingLocked(null); } + mWaitingListener.onTransactionReady(mWaitingSyncId, mBLASTSyncTransaction); mUsingBLASTSyncTransaction = false; mWaitingSyncId = 0; diff --git a/services/tests/mockingservicestests/src/com/android/server/location/UserInfoHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/UserInfoHelperTest.java index 71e79b331cae..56727e81bda5 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/UserInfoHelperTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/UserInfoHelperTest.java @@ -16,34 +16,23 @@ package com.android.server.location; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.MockitoAnnotations.initMocks; -import android.app.ActivityManager; -import android.content.BroadcastReceiver; +import android.app.ActivityManagerInternal; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.UserInfo; -import android.os.Handler; -import android.os.UserHandle; import android.os.UserManager; import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import com.android.dx.mockito.inline.extended.StaticMockitoSession; +import com.android.server.LocalServices; import com.android.server.location.UserInfoHelper.UserListener; import org.junit.After; @@ -51,16 +40,18 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.quality.Strictness; - -import java.util.ArrayList; -import java.util.List; @Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class UserInfoHelperTest { + private static class TestUserInfoHelper extends UserInfoHelper { + TestUserInfoHelper(Context context) { + super(context); + } + } + private static final int USER1_ID = 1; private static final int USER1_MANAGED_ID = 11; private static final int[] USER1_PROFILES = new int[]{USER1_ID, USER1_MANAGED_ID}; @@ -70,69 +61,30 @@ public class UserInfoHelperTest { @Mock private Context mContext; @Mock private UserManager mUserManager; + @Mock private ActivityManagerInternal mActivityManagerInternal; - private StaticMockitoSession mMockingSession; - private List<BroadcastReceiver> mBroadcastReceivers = new ArrayList<>(); - - private UserInfoHelper mHelper; + private TestUserInfoHelper mHelper; @Before public void setUp() { - mMockingSession = mockitoSession() - .initMocks(this) - .spyStatic(ActivityManager.class) - .strictness(Strictness.WARN) - .startMocking(); + initMocks(this); + LocalServices.addService(ActivityManagerInternal.class, mActivityManagerInternal); doReturn(mUserManager).when(mContext).getSystemService(UserManager.class); - doAnswer(invocation -> { - mBroadcastReceivers.add(invocation.getArgument(0)); - return null; - }).when(mContext).registerReceiverAsUser(any(BroadcastReceiver.class), any( - UserHandle.class), any(IntentFilter.class), isNull(), any(Handler.class)); - doReturn(USER1_PROFILES).when(mUserManager).getProfileIdsWithDisabled(USER1_ID); - doReturn(USER2_PROFILES).when(mUserManager).getProfileIdsWithDisabled(USER2_ID); - doReturn(new UserInfo(USER1_ID, "", 0)).when(mUserManager).getProfileParent( - USER1_MANAGED_ID); - doReturn(new UserInfo(USER2_ID, "", 0)).when(mUserManager).getProfileParent( - USER2_MANAGED_ID); - - doReturn(USER1_ID).when(ActivityManager::getCurrentUser); - - mHelper = new UserInfoHelper(mContext); + + doReturn(USER1_PROFILES).when(mUserManager).getEnabledProfileIds(USER1_ID); + doReturn(USER2_PROFILES).when(mUserManager).getEnabledProfileIds(USER2_ID); + doReturn(true).when(mActivityManagerInternal).isCurrentProfile(USER1_ID); + doReturn(true).when(mActivityManagerInternal).isCurrentProfile(USER1_MANAGED_ID); + doReturn(USER1_PROFILES).when(mActivityManagerInternal).getCurrentProfileIds(); + + mHelper = new TestUserInfoHelper(mContext); mHelper.onSystemReady(); } @After public void tearDown() { - if (mMockingSession != null) { - mMockingSession.finishMocking(); - } - } - - private void switchUser(int userId) { - doReturn(userId).when(ActivityManager::getCurrentUser); - Intent intent = new Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, - userId); - for (BroadcastReceiver broadcastReceiver : mBroadcastReceivers) { - broadcastReceiver.onReceive(mContext, intent); - } - } - - private void startUser(int userId) { - Intent intent = new Intent(Intent.ACTION_USER_STARTED).putExtra(Intent.EXTRA_USER_HANDLE, - userId); - for (BroadcastReceiver broadcastReceiver : mBroadcastReceivers) { - broadcastReceiver.onReceive(mContext, intent); - } - } - - private void stopUser(int userId) { - Intent intent = new Intent(Intent.ACTION_USER_STOPPED).putExtra(Intent.EXTRA_USER_HANDLE, - userId); - for (BroadcastReceiver broadcastReceiver : mBroadcastReceivers) { - broadcastReceiver.onReceive(mContext, intent); - } + LocalServices.removeServiceForTest(ActivityManagerInternal.class); } @Test @@ -140,16 +92,21 @@ public class UserInfoHelperTest { UserListener listener = mock(UserListener.class); mHelper.addListener(listener); - switchUser(USER1_ID); - verify(listener, never()).onUserChanged(anyInt(), anyInt()); - - switchUser(USER2_ID); - verify(listener, times(1)).onUserChanged(USER1_ID, UserListener.USER_SWITCHED); - verify(listener, times(1)).onUserChanged(USER2_ID, UserListener.USER_SWITCHED); - - switchUser(USER1_ID); - verify(listener, times(2)).onUserChanged(USER1_ID, UserListener.USER_SWITCHED); - verify(listener, times(2)).onUserChanged(USER2_ID, UserListener.USER_SWITCHED); + mHelper.dispatchOnCurrentUserChanged(USER1_ID, USER2_ID); + verify(listener, times(1)).onUserChanged(USER1_ID, UserListener.CURRENT_USER_CHANGED); + verify(listener, times(1)).onUserChanged(USER1_MANAGED_ID, + UserListener.CURRENT_USER_CHANGED); + verify(listener, times(1)).onUserChanged(USER2_ID, UserListener.CURRENT_USER_CHANGED); + verify(listener, times(1)).onUserChanged(USER2_MANAGED_ID, + UserListener.CURRENT_USER_CHANGED); + + mHelper.dispatchOnCurrentUserChanged(USER2_ID, USER1_ID); + verify(listener, times(2)).onUserChanged(USER2_ID, UserListener.CURRENT_USER_CHANGED); + verify(listener, times(2)).onUserChanged(USER2_MANAGED_ID, + UserListener.CURRENT_USER_CHANGED); + verify(listener, times(2)).onUserChanged(USER1_ID, UserListener.CURRENT_USER_CHANGED); + verify(listener, times(2)).onUserChanged(USER1_MANAGED_ID, + UserListener.CURRENT_USER_CHANGED); } @Test @@ -157,11 +114,11 @@ public class UserInfoHelperTest { UserListener listener = mock(UserListener.class); mHelper.addListener(listener); - startUser(USER1_ID); + mHelper.dispatchOnUserStarted(USER1_ID); verify(listener).onUserChanged(USER1_ID, UserListener.USER_STARTED); - startUser(USER2_ID); - verify(listener).onUserChanged(USER2_ID, UserListener.USER_STARTED); + mHelper.dispatchOnUserStarted(USER1_MANAGED_ID); + verify(listener).onUserChanged(USER1_MANAGED_ID, UserListener.USER_STARTED); } @Test @@ -169,24 +126,22 @@ public class UserInfoHelperTest { UserListener listener = mock(UserListener.class); mHelper.addListener(listener); - stopUser(USER1_ID); - verify(listener).onUserChanged(USER1_ID, UserListener.USER_STOPPED); - - stopUser(USER2_ID); + mHelper.dispatchOnUserStopped(USER2_ID); verify(listener).onUserChanged(USER2_ID, UserListener.USER_STOPPED); + + mHelper.dispatchOnUserStopped(USER2_MANAGED_ID); + verify(listener).onUserChanged(USER2_MANAGED_ID, UserListener.USER_STOPPED); } @Test public void testCurrentUserIds() { assertThat(mHelper.getCurrentUserIds()).isEqualTo(USER1_PROFILES); - switchUser(USER2_ID); + doReturn(true).when(mActivityManagerInternal).isCurrentProfile(USER2_ID); + doReturn(true).when(mActivityManagerInternal).isCurrentProfile(USER2_MANAGED_ID); + doReturn(USER2_PROFILES).when(mActivityManagerInternal).getCurrentProfileIds(); assertThat(mHelper.getCurrentUserIds()).isEqualTo(USER2_PROFILES); - - switchUser(USER1_ID); - - assertThat(mHelper.getCurrentUserIds()).isEqualTo(USER1_PROFILES); } @Test @@ -196,7 +151,11 @@ public class UserInfoHelperTest { assertThat(mHelper.isCurrentUserId(USER2_ID)).isFalse(); assertThat(mHelper.isCurrentUserId(USER2_MANAGED_ID)).isFalse(); - switchUser(USER2_ID); + doReturn(false).when(mActivityManagerInternal).isCurrentProfile(USER1_ID); + doReturn(false).when(mActivityManagerInternal).isCurrentProfile(USER1_MANAGED_ID); + doReturn(true).when(mActivityManagerInternal).isCurrentProfile(USER2_ID); + doReturn(true).when(mActivityManagerInternal).isCurrentProfile(USER2_MANAGED_ID); + doReturn(USER2_PROFILES).when(mActivityManagerInternal).getCurrentProfileIds(); assertThat(mHelper.isCurrentUserId(USER1_ID)).isFalse(); assertThat(mHelper.isCurrentUserId(USER2_ID)).isTrue(); diff --git a/telephony/java/android/telephony/SmsCbMessage.java b/telephony/java/android/telephony/SmsCbMessage.java index 752707e5a5dc..d366efe0d979 100644 --- a/telephony/java/android/telephony/SmsCbMessage.java +++ b/telephony/java/android/telephony/SmsCbMessage.java @@ -594,6 +594,7 @@ public final class SmsCbMessage implements Parcelable { SmsCbEtwsInfo etwsInfo = getEtwsWarningInfo(); if (etwsInfo != null) { cv.put(CellBroadcasts.ETWS_WARNING_TYPE, etwsInfo.getWarningType()); + cv.put(CellBroadcasts.ETWS_IS_PRIMARY, etwsInfo.isPrimary()); } SmsCbCmasInfo cmasInfo = getCmasWarningInfo(); @@ -667,9 +668,12 @@ public final class SmsCbMessage implements Parcelable { SmsCbEtwsInfo etwsInfo; int etwsWarningTypeColumn = cursor.getColumnIndex(CellBroadcasts.ETWS_WARNING_TYPE); - if (etwsWarningTypeColumn != -1 && !cursor.isNull(etwsWarningTypeColumn)) { + int etwsIsPrimaryColumn = cursor.getColumnIndex(CellBroadcasts.ETWS_IS_PRIMARY); + if (etwsWarningTypeColumn != -1 && !cursor.isNull(etwsWarningTypeColumn) + && etwsIsPrimaryColumn != -1 && !cursor.isNull(etwsIsPrimaryColumn)) { int warningType = cursor.getInt(etwsWarningTypeColumn); - etwsInfo = new SmsCbEtwsInfo(warningType, false, false, false, null); + boolean isPrimary = cursor.getInt(etwsIsPrimaryColumn) != 0; + etwsInfo = new SmsCbEtwsInfo(warningType, false, false, isPrimary, null); } else { etwsInfo = null; } diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java index 70c5e72e4e0c..b841921355e9 100644 --- a/wifi/java/android/net/wifi/WifiInfo.java +++ b/wifi/java/android/net/wifi/WifiInfo.java @@ -632,7 +632,6 @@ public class WifiInfo implements Parcelable { /** * @hide - * TODO: makes real freq boundaries */ public boolean is24GHz() { return ScanResult.is24GHz(mFrequency); @@ -640,7 +639,6 @@ public class WifiInfo implements Parcelable { /** * @hide - * TODO: makes real freq boundaries */ @UnsupportedAppUsage public boolean is5GHz() { @@ -648,6 +646,13 @@ public class WifiInfo implements Parcelable { } /** + * @hide + */ + public boolean is6GHz() { + return ScanResult.is6GHz(mFrequency); + } + + /** * Record the MAC address of the WLAN interface * @param macAddress the MAC address in {@code XX:XX:XX:XX:XX:XX} form * @hide |