Merge "Removed Settings.Global.LAST_ACTIVE_USER_ID." into rvc-dev
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 0ae4460..569a78c 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 @@
/**
* Utility class for IO.
+ *
+ * @hide
*/
public class IoUtils {
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 36a8b2c..81d059e 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -444,6 +444,11 @@
TvTunerStateChanged tv_tuner_state_changed = 276 [(module) = "framework"];
MediaOutputOpSwitchReported mediaoutput_op_switch_reported =
277 [(module) = "settings"];
+ CellBroadcastMessageFiltered cb_message_filtered =
+ 278 [(module) = "cellbroadcast"];
+ TvTunerDvrStatus tv_tuner_dvr_status = 279 [(module) = "framework"];
+ TvCasSessionOpenStatus tv_cas_session_open_status =
+ 280 [(module) = "framework"];
// StatsdStats tracks platform atoms with ids upto 500.
// Update StatsdStats::kMaxPushedAtomId when atom ids here approach that value.
@@ -9134,8 +9139,10 @@
/**
* 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 +9153,40 @@
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 +9213,7 @@
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
@@ -9205,6 +9245,58 @@
// new state
optional State state = 2;
}
+
+/**
+ * Logs the status of a dvr playback or record.
+ * This is atom ID 279.
+ *
+ * Logged from:
+ * frameworks/base/media/java/android/media/tv/tuner/dvr
+ */
+message TvTunerDvrStatus {
+ enum Type {
+ UNKNOWN_TYPE = 0;
+ PLAYBACK = 1; // is a playback
+ RECORD = 2; // is a record
+ }
+ enum State {
+ UNKNOWN_STATE = 0;
+ STARTED = 1; // DVR is started
+ STOPPED = 2; // DVR is stopped
+ }
+ // The uid of the application that sent this custom atom.
+ optional int32 uid = 1 [(is_uid) = true];
+ // DVR type
+ optional Type type = 2;
+ // DVR state
+ optional State state = 3;
+ // Identify the segment of a record or playback
+ optional int32 segment_id = 4;
+ // indicate how many overflow or underflow happened between started to stopped
+ optional int32 overflow_underflow_count = 5;
+}
+
+/**
+ * Logs when a cas session opened through MediaCas.
+ * This is atom ID 280.
+ *
+ * Logged from:
+ * frameworks/base/media/java/android/media/MediaCas.java
+ */
+message TvCasSessionOpenStatus {
+ enum State {
+ UNKNOWN = 0;
+ SUCCEEDED = 1; // indicate that the session is opened successfully.
+ FAILED = 2; // indicate that the session isn’t opened successfully.
+ }
+ // The uid of the application that sent this custom atom.
+ optional int32 uid = 1 [(is_uid) = true];
+ // Cas system Id
+ optional int32 cas_system_id = 2;
+ // State of the session
+ optional State state = 3;
+}
+
/**
* Logs when an app is frozen or unfrozen.
*
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index ed0ea55..ac00a04 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -520,6 +520,12 @@
*/
public static final int GLOBAL_ACTION_ACCESSIBILITY_SHORTCUT = 13;
+ /**
+ * Action to show Launcher's all apps.
+ * @hide
+ */
+ public static final int GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS = 14;
+
private static final String LOG_TAG = "AccessibilityService";
/**
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index cffa59c..108b9ee 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3252,18 +3252,56 @@
@Override
public void handleFixedRotationAdjustments(@NonNull IBinder token,
@Nullable FixedRotationAdjustments fixedRotationAdjustments) {
- final Consumer<DisplayAdjustments> override = fixedRotationAdjustments != null
- ? displayAdjustments -> displayAdjustments.setFixedRotationAdjustments(
- fixedRotationAdjustments)
- : null;
+ handleFixedRotationAdjustments(token, fixedRotationAdjustments, null /* overrideConfig */);
+ }
+
+ /**
+ * Applies the rotation adjustments to override display information in resources belong to the
+ * provided token. If the token is activity token, the adjustments also apply to application
+ * because the appearance of activity is usually more sensitive to the application resources.
+ *
+ * @param token The token to apply the adjustments.
+ * @param fixedRotationAdjustments The information to override the display adjustments of
+ * corresponding resources. If it is null, the exiting override
+ * will be cleared.
+ * @param overrideConfig The override configuration of activity. It is used to override
+ * application configuration. If it is non-null, it means the token is
+ * confirmed as activity token. Especially when launching new activity,
+ * {@link #mActivities} hasn't put the new token.
+ */
+ private void handleFixedRotationAdjustments(@NonNull IBinder token,
+ @Nullable FixedRotationAdjustments fixedRotationAdjustments,
+ @Nullable Configuration overrideConfig) {
+ // The element of application configuration override is set only if the application
+ // adjustments are needed, because activity already has its own override configuration.
+ final Configuration[] appConfigOverride;
+ final Consumer<DisplayAdjustments> override;
+ if (fixedRotationAdjustments != null) {
+ appConfigOverride = new Configuration[1];
+ override = displayAdjustments -> {
+ displayAdjustments.setFixedRotationAdjustments(fixedRotationAdjustments);
+ if (appConfigOverride[0] != null) {
+ displayAdjustments.getConfiguration().updateFrom(appConfigOverride[0]);
+ }
+ };
+ } else {
+ appConfigOverride = null;
+ override = null;
+ }
if (!mResourcesManager.overrideTokenDisplayAdjustments(token, override)) {
// No resources are associated with the token.
return;
}
- if (mActivities.get(token) == null) {
- // Only apply the override to application for activity token because the appearance of
- // activity is usually more sensitive to the application resources.
- return;
+ if (overrideConfig == null) {
+ final ActivityClientRecord r = mActivities.get(token);
+ if (r == null) {
+ // It is not an activity token. Nothing to do for application.
+ return;
+ }
+ overrideConfig = r.overrideConfig;
+ }
+ if (appConfigOverride != null) {
+ appConfigOverride[0] = overrideConfig;
}
// Apply the last override to application resources for compatibility. Because the Resources
@@ -3503,7 +3541,8 @@
// The rotation adjustments must be applied before creating the activity, so the activity
// can get the adjusted display info during creation.
if (r.mPendingFixedRotationAdjustments != null) {
- handleFixedRotationAdjustments(r.token, r.mPendingFixedRotationAdjustments);
+ handleFixedRotationAdjustments(r.token, r.mPendingFixedRotationAdjustments,
+ r.overrideConfig);
r.mPendingFixedRotationAdjustments = null;
}
@@ -7388,6 +7427,10 @@
}
}
+ public Bundle getCoreSettings() {
+ return mCoreSettings;
+ }
+
public int getIntCoreSetting(String key, int defaultValue) {
synchronized (mResourcesManager) {
if (mCoreSettings != null) {
@@ -7397,6 +7440,18 @@
}
}
+ /**
+ * 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/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index 2d06ee8..b68639e 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -216,4 +216,14 @@
* in {@link android.content.pm.ActivityInfo}.
*/
void onTaskRequestedOrientationChanged(int taskId, int requestedOrientation);
+
+ /**
+ * Called when a rotation is about to start on the foreground activity.
+ * This applies for:
+ * * free sensor rotation
+ * * forced rotation
+ * * rotation settings set through adb command line
+ * * rotation that occurs when rotation tile is toggled in quick settings
+ */
+ void onActivityRotation();
}
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 10f7835..f9b48e7 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -38,6 +38,7 @@
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.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 @@
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/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index 5d8daf8..843d1c7 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -199,4 +199,8 @@
@Override
public void onTaskRequestedOrientationChanged(int taskId, int requestedOrientation) {
}
+
+ @Override
+ public void onActivityRotation() {
+ }
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index e331471..be3cfef 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1884,6 +1884,9 @@
/**
* Activity action: Launch UI to manage auto-revoke state.
+ *
+ * This is equivalent to Intent#ACTION_APPLICATION_DETAILS_SETTINGS
+ *
* <p>
* Input: {@link Intent#setData data} should be a {@code package}-scheme {@link Uri} with
* a package name, whose auto-revoke state will be reviewed (mandatory).
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index ed75504..fc4ccd0 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1449,6 +1449,13 @@
/** {@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 @@
/**
* 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 @@
*
* <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 f354bdb..65ce1e7 100644
--- a/core/java/android/content/pm/PackageItemInfo.java
+++ b/core/java/android/content/pm/PackageItemInfo.java
@@ -49,8 +49,16 @@
* 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/content/pm/parsing/component/ParsedActivityUtils.java b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
index f64560a..fb8fd74 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
@@ -302,7 +302,14 @@
}
String permission = array.getNonConfigurationString(permissionAttr, 0);
- activity.setPermission(permission != null ? permission : pkg.getPermission());
+ if (isAlias) {
+ // An alias will override permissions to allow referencing an Activity through its alias
+ // without needing the original permission. If an alias needs the same permission,
+ // it must be re-declared.
+ activity.setPermission(permission);
+ } else {
+ activity.setPermission(permission != null ? permission : pkg.getPermission());
+ }
final boolean setExported = array.hasValue(exportedAttr);
if (setExported) {
diff --git a/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java b/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java
index b37b617..6811e06 100644
--- a/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java
@@ -20,7 +20,10 @@
import android.annotation.Nullable;
import android.content.pm.PackageManager;
import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.parsing.ParsingUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
@@ -29,9 +32,6 @@
import android.util.TypedValue;
import com.android.internal.annotations.VisibleForTesting;
-import android.content.pm.parsing.ParsingPackageUtils;
-import android.content.pm.parsing.result.ParseInput;
-import android.content.pm.parsing.result.ParseResult;
/** @hide */
class ParsedComponentUtils {
@@ -60,16 +60,27 @@
component.setName(className);
component.setPackageName(packageName);
- if (useRoundIcon) {
- component.icon = array.getResourceId(roundIconAttr, 0);
+ int roundIconVal = useRoundIcon ? array.getResourceId(roundIconAttr, 0) : 0;
+ if (roundIconVal != 0) {
+ component.icon = roundIconVal;
+ component.nonLocalizedLabel = null;
+ } else {
+ int iconVal = array.getResourceId(iconAttr, 0);
+ if (iconVal != 0) {
+ component.icon = iconVal;
+ component.nonLocalizedLabel = null;
+ }
}
- if (component.icon == 0) {
- component.icon = array.getResourceId(iconAttr, 0);
+ int logoVal = array.getResourceId(logoAttr, 0);
+ if (logoVal != 0) {
+ component.logo = logoVal;
}
- component.logo = array.getResourceId(logoAttr, 0);
- component.banner = array.getResourceId(bannerAttr, 0);
+ int bannerVal = array.getResourceId(bannerAttr, 0);
+ if (bannerVal != 0) {
+ component.banner = bannerVal;
+ }
if (descriptionAttr != null) {
component.descriptionRes = array.getResourceId(descriptionAttr, 0);
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index 6bc962b..2084065 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -18,6 +18,7 @@
import static com.android.internal.os.RoSystemProperties.PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH;
+import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -30,6 +31,7 @@
import android.annotation.SystemService;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.os.Binder;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.util.ArrayMap;
@@ -40,6 +42,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.Executor;
/**
* The {@link HdmiControlManager} class is used to send HDMI control messages
@@ -818,6 +821,24 @@
mHdmiControlStatusChangeListeners = new ArrayMap<>();
/**
+ * Listener used to get the status of the HDMI CEC volume control feature (enabled/disabled).
+ * @hide
+ */
+ public interface HdmiCecVolumeControlFeatureListener {
+ /**
+ * Called when the HDMI Control (CEC) volume control feature is enabled/disabled.
+ *
+ * @param enabled status of HDMI CEC volume control feature
+ * @see {@link HdmiControlManager#setHdmiCecVolumeControlEnabled(boolean)} ()}
+ **/
+ void onHdmiCecVolumeControlFeature(boolean enabled);
+ }
+
+ private final ArrayMap<HdmiCecVolumeControlFeatureListener,
+ IHdmiCecVolumeControlFeatureListener>
+ mHdmiCecVolumeControlFeatureListeners = new ArrayMap<>();
+
+ /**
* Listener used to get vendor-specific commands.
*/
public interface VendorCommandListener {
@@ -979,4 +1000,76 @@
};
}
+ /**
+ * Adds a listener to get informed of changes to the state of the HDMI CEC volume control
+ * feature.
+ *
+ * Upon adding a listener, the current state of the HDMI CEC volume control feature will be
+ * sent immediately.
+ *
+ * <p>To stop getting the notification,
+ * use {@link #removeHdmiCecVolumeControlFeatureListener(HdmiCecVolumeControlFeatureListener)}.
+ *
+ * @param listener {@link HdmiCecVolumeControlFeatureListener} instance
+ * @hide
+ * @see #removeHdmiCecVolumeControlFeatureListener(HdmiCecVolumeControlFeatureListener)
+ */
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public void addHdmiCecVolumeControlFeatureListener(@NonNull @CallbackExecutor Executor executor,
+ @NonNull HdmiCecVolumeControlFeatureListener listener) {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ return;
+ }
+ if (mHdmiCecVolumeControlFeatureListeners.containsKey(listener)) {
+ Log.e(TAG, "listener is already registered");
+ return;
+ }
+ IHdmiCecVolumeControlFeatureListener wrappedListener =
+ createHdmiCecVolumeControlFeatureListenerWrapper(executor, listener);
+ mHdmiCecVolumeControlFeatureListeners.put(listener, wrappedListener);
+ try {
+ mService.addHdmiCecVolumeControlFeatureListener(wrappedListener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Removes a listener to stop getting informed of changes to the state of the HDMI CEC volume
+ * control feature.
+ *
+ * @param listener {@link HdmiCecVolumeControlFeatureListener} instance to be removed
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public void removeHdmiCecVolumeControlFeatureListener(
+ HdmiCecVolumeControlFeatureListener listener) {
+ if (mService == null) {
+ Log.e(TAG, "HdmiControlService is not available");
+ return;
+ }
+ IHdmiCecVolumeControlFeatureListener wrappedListener =
+ mHdmiCecVolumeControlFeatureListeners.remove(listener);
+ if (wrappedListener == null) {
+ Log.e(TAG, "tried to remove not-registered listener");
+ return;
+ }
+ try {
+ mService.removeHdmiCecVolumeControlFeatureListener(wrappedListener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private IHdmiCecVolumeControlFeatureListener createHdmiCecVolumeControlFeatureListenerWrapper(
+ Executor executor, final HdmiCecVolumeControlFeatureListener listener) {
+ return new android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener.Stub() {
+ @Override
+ public void onHdmiCecVolumeControlFeature(boolean enabled) {
+ Binder.clearCallingIdentity();
+ executor.execute(() -> listener.onHdmiCecVolumeControlFeature(enabled));
+ }
+ };
+ }
}
diff --git a/core/java/android/hardware/hdmi/IHdmiCecVolumeControlFeatureListener.aidl b/core/java/android/hardware/hdmi/IHdmiCecVolumeControlFeatureListener.aidl
new file mode 100644
index 0000000..873438b
--- /dev/null
+++ b/core/java/android/hardware/hdmi/IHdmiCecVolumeControlFeatureListener.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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.hardware.hdmi;
+
+/**
+ * Listener used to get the status of the HDMI CEC volume control feature (enabled/disabled).
+ * @hide
+ */
+oneway interface IHdmiCecVolumeControlFeatureListener {
+
+ /**
+ * Called when the HDMI Control (CEC) volume control feature is enabled/disabled.
+ *
+ * @param enabled status of HDMI CEC volume control feature
+ * @see {@link HdmiControlManager#setHdmiCecVolumeControlEnabled(boolean)} ()}
+ **/
+ void onHdmiCecVolumeControlFeature(boolean enabled);
+}
diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
index 3582a92..4c724ef 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
@@ -18,6 +18,7 @@
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
+import android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.hdmi.IHdmiControlStatusChangeListener;
import android.hardware.hdmi.IHdmiDeviceEventListener;
@@ -44,6 +45,8 @@
void queryDisplayStatus(IHdmiControlCallback callback);
void addHdmiControlStatusChangeListener(IHdmiControlStatusChangeListener listener);
void removeHdmiControlStatusChangeListener(IHdmiControlStatusChangeListener listener);
+ void addHdmiCecVolumeControlFeatureListener(IHdmiCecVolumeControlFeatureListener listener);
+ void removeHdmiCecVolumeControlFeatureListener(IHdmiCecVolumeControlFeatureListener listener);
void addHotplugEventListener(IHdmiHotplugEventListener listener);
void removeHotplugEventListener(IHdmiHotplugEventListener listener);
void addDeviceEventListener(IHdmiDeviceEventListener listener);
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 034e6a7..df58a6c 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -22,6 +22,7 @@
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 @@
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 @@
}
/**
- * 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;
}
- /**
- * 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) {
- String layerPaths = "";
-
+ 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;
+ }
- if (isDebuggable() || canInjectLayers(ai)) {
+ /**
+ * Set up layer search paths for all apps
+ */
+ 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;
- final int enable = coreSettings.getInt(Settings.Global.ENABLE_GPU_DEBUG_LAYERS, 0);
+ 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 (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 7845200..a8391c2 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -4090,13 +4090,6 @@
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 a8af310..e10fcea 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1778,6 +1778,15 @@
= "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 b34268d..a2489b9 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -4325,6 +4325,15 @@
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 @@
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 c879653..16c3f1d 100644
--- a/core/java/android/service/autofill/InlineSuggestionRoot.java
+++ b/core/java/android/service/autofill/InlineSuggestionRoot.java
@@ -58,7 +58,9 @@
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 a9af595..f6c72c4 100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -289,11 +289,6 @@
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 @@
mTouchSlopSquare = touchSlop * touchSlop;
mDoubleTapTouchSlopSquare = doubleTapTouchSlop * doubleTapTouchSlop;
mDoubleTapSlopSquare = doubleTapSlop * doubleTapSlop;
- mExceptionForTouchStream = context != null
- && context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R;
}
/**
@@ -646,13 +639,6 @@
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 7042f29..4a65511 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -205,6 +205,8 @@
* <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 @@
* 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 @@
* 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 @@
* <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/android/window/VirtualDisplayTaskEmbedder.java b/core/java/android/window/VirtualDisplayTaskEmbedder.java
index d2614da..9ccb4c1 100644
--- a/core/java/android/window/VirtualDisplayTaskEmbedder.java
+++ b/core/java/android/window/VirtualDisplayTaskEmbedder.java
@@ -365,8 +365,8 @@
// Found the topmost stack on target display. Now check if the topmost task's
// description changed.
if (taskInfo.taskId == stackInfo.taskIds[stackInfo.taskIds.length - 1]) {
- mHost.onTaskBackgroundColorChanged(VirtualDisplayTaskEmbedder.this,
- taskInfo.taskDescription.getBackgroundColor());
+ mHost.post(()-> mHost.onTaskBackgroundColorChanged(VirtualDisplayTaskEmbedder.this,
+ taskInfo.taskDescription.getBackgroundColor()));
}
}
diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
index 493865a..b723db2 100644
--- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
@@ -151,6 +151,13 @@
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 @@
* {@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 2a43287..049a76c 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.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.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 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 @@
protected MetricsLogger mMetricsLogger;
private ContentPreviewCoordinator mPreviewCoord;
+ private int mScrollStatus = SCROLL_STATUS_IDLE;
@VisibleForTesting
protected ChooserMultiProfilePagerAdapter mChooserMultiProfilePagerAdapter;
@@ -2680,7 +2687,7 @@
offset = Math.min(offset, minHeight);
}
} else {
- ViewGroup currentEmptyStateView = getCurrentEmptyStateView();
+ ViewGroup currentEmptyStateView = getActiveEmptyStateView();
if (currentEmptyStateView.getVisibility() == View.VISIBLE) {
offset += currentEmptyStateView.getHeight();
}
@@ -2705,7 +2712,7 @@
return -1;
}
- private ViewGroup getCurrentEmptyStateView() {
+ private ViewGroup getActiveEmptyStateView() {
int currentPage = mChooserMultiProfilePagerAdapter.getCurrentPage();
return mChooserMultiProfilePagerAdapter.getItem(currentPage).getEmptyStateView();
}
@@ -2822,10 +2829,20 @@
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 @@
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 317a987..c50ebd9 100644
--- a/core/java/com/android/internal/app/ChooserGridLayoutManager.java
+++ b/core/java/com/android/internal/app/ChooserGridLayoutManager.java
@@ -28,6 +28,8 @@
*/
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 @@
// 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 774be3c..ffa6041 100644
--- a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
@@ -38,6 +38,7 @@
private final ChooserProfileDescriptor[] mItems;
private final boolean mIsSendAction;
+ private int mBottomOffset;
ChooserMultiProfilePagerAdapter(Context context,
ChooserActivity.ChooserGridAdapter adapter,
@@ -245,6 +246,16 @@
}
}
+ 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 e65d1fe..61a52bc 100644
--- a/core/java/com/android/internal/app/IntentForwarderActivity.java
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -18,6 +18,7 @@
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 @@
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 f96f560..f8eec57 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -184,6 +184,18 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 4eb6e3b..9cdfc2f 100644
--- a/core/java/com/android/internal/app/ResolverViewPager.java
+++ b/core/java/com/android/internal/app/ResolverViewPager.java
@@ -18,6 +18,7 @@
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 @@
*/
public class ResolverViewPager extends ViewPager {
+ private boolean mSwipingEnabled = true;
+
public ResolverViewPager(Context context) {
super(context);
}
@@ -70,4 +73,13 @@
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 67ffd4d..5212265 100644
--- a/core/java/com/android/internal/logging/UiEventLogger.java
+++ b/core/java/com/android/internal/logging/UiEventLogger.java
@@ -60,4 +60,28 @@
*/
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 4d171ec..c9156c1 100644
--- a/core/java/com/android/internal/logging/UiEventLoggerImpl.java
+++ b/core/java/com/android/internal/logging/UiEventLoggerImpl.java
@@ -48,4 +48,31 @@
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 180ab08..2d09434 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 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 @@
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 @@
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/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
index ad6c7e8..adc7ba3 100644
--- a/core/java/com/android/internal/util/ScreenshotHelper.java
+++ b/core/java/com/android/internal/util/ScreenshotHelper.java
@@ -8,10 +8,10 @@
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
-import android.graphics.Bitmap;
import android.graphics.Insets;
import android.graphics.Rect;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
@@ -37,10 +37,12 @@
private int mSource;
private boolean mHasStatusBar;
private boolean mHasNavBar;
- private Bitmap mBitmap;
+ private Bundle mBitmapBundle;
private Rect mBoundsInScreen;
private Insets mInsets;
private int mTaskId;
+ private int mUserId;
+ private ComponentName mTopComponent;
ScreenshotRequest(int source, boolean hasStatus, boolean hasNav) {
mSource = source;
@@ -48,24 +50,29 @@
mHasNavBar = hasNav;
}
- ScreenshotRequest(
- int source, Bitmap bitmap, Rect boundsInScreen, Insets insets, int taskId) {
+ ScreenshotRequest(int source, Bundle bitmapBundle, Rect boundsInScreen, Insets insets,
+ int taskId, int userId, ComponentName topComponent) {
mSource = source;
- mBitmap = bitmap;
+ mBitmapBundle = bitmapBundle;
mBoundsInScreen = boundsInScreen;
mInsets = insets;
mTaskId = taskId;
+ mUserId = userId;
+ mTopComponent = topComponent;
}
ScreenshotRequest(Parcel in) {
mSource = in.readInt();
mHasStatusBar = in.readBoolean();
mHasNavBar = in.readBoolean();
+
if (in.readInt() == 1) {
- mBitmap = in.readParcelable(Bitmap.class.getClassLoader());
+ mBitmapBundle = in.readBundle(getClass().getClassLoader());
mBoundsInScreen = in.readParcelable(Rect.class.getClassLoader());
mInsets = in.readParcelable(Insets.class.getClassLoader());
mTaskId = in.readInt();
+ mUserId = in.readInt();
+ mTopComponent = in.readParcelable(ComponentName.class.getClassLoader());
}
}
@@ -81,8 +88,8 @@
return mHasNavBar;
}
- public Bitmap getBitmap() {
- return mBitmap;
+ public Bundle getBitmapBundle() {
+ return mBitmapBundle;
}
public Rect getBoundsInScreen() {
@@ -97,6 +104,15 @@
return mTaskId;
}
+
+ public int getUserId() {
+ return mUserId;
+ }
+
+ public ComponentName getTopComponent() {
+ return mTopComponent;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -107,14 +123,16 @@
dest.writeInt(mSource);
dest.writeBoolean(mHasStatusBar);
dest.writeBoolean(mHasNavBar);
- if (mBitmap == null) {
+ if (mBitmapBundle == null) {
dest.writeInt(0);
} else {
dest.writeInt(1);
- dest.writeParcelable(mBitmap, 0);
+ dest.writeBundle(mBitmapBundle);
dest.writeParcelable(mBoundsInScreen, 0);
dest.writeParcelable(mInsets, 0);
dest.writeInt(mTaskId);
+ dest.writeInt(mUserId);
+ dest.writeParcelable(mTopComponent, 0);
}
}
@@ -234,19 +252,22 @@
/**
* Request that provided image be handled as if it was a screenshot.
*
- * @param screenshot The bitmap to treat as the screen shot.
+ * @param screenshotBundle Bundle containing the buffer and color space of the screenshot.
* @param boundsInScreen The bounds in screen coordinates that the bitmap orginated from.
* @param insets The insets that the image was shown with, inside the screenbounds.
* @param taskId The taskId of the task that the screen shot was taken of.
+ * @param userId The userId of user running the task provided in taskId.
+ * @param topComponent The component name of the top component running in the task.
* @param handler A handler used in case the screenshot times out
* @param completionConsumer Consumes `false` if a screenshot was not taken, and `true` if the
* screenshot was taken.
*/
- public void provideScreenshot(@NonNull Bitmap screenshot, @NonNull Rect boundsInScreen,
- @NonNull Insets insets, int taskId, int source,
+ public void provideScreenshot(@NonNull Bundle screenshotBundle, @NonNull Rect boundsInScreen,
+ @NonNull Insets insets, int taskId, int userId, ComponentName topComponent, int source,
@NonNull Handler handler, @Nullable Consumer<Uri> completionConsumer) {
ScreenshotRequest screenshotRequest =
- new ScreenshotRequest(source, screenshot, boundsInScreen, insets, taskId);
+ new ScreenshotRequest(source, screenshotBundle, boundsInScreen, insets, taskId,
+ userId, topComponent);
takeScreenshot(WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_TIMEOUT_MS,
handler, screenshotRequest, completionConsumer);
}
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index c75f72b..0d2dbef 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -1223,7 +1223,6 @@
mExpandButtonContainer.setVisibility(VISIBLE);
mExpandButtonInnerContainer.setOnClickListener(onClickListener);
} else {
- // TODO: handle content paddings to end of layout
mExpandButtonContainer.setVisibility(GONE);
}
updateContentEndPaddings();
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index fc2005a..3d8cae8 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -526,8 +526,16 @@
// 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 @@
}
}
- 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 23fcf6e..787074b 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 @@
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/proto/android/stats/mediametrics/mediametrics.proto b/core/proto/android/stats/mediametrics/mediametrics.proto
index e1af962..9f0ff59 100644
--- a/core/proto/android/stats/mediametrics/mediametrics.proto
+++ b/core/proto/android/stats/mediametrics/mediametrics.proto
@@ -131,7 +131,7 @@
* Logged from:
* frameworks/av/media/libstagefright/MediaCodec.cpp
* frameworks/av/services/mediaanalytics/statsd_codec.cpp
- * Next Tag: 21
+ * Next Tag: 26
*/
message CodecData {
optional string codec = 1;
@@ -156,6 +156,9 @@
optional int64 latency_unknown = 20;
optional int32 queue_input_buffer_error = 21;
optional int32 queue_secure_input_buffer_error = 22;
+ optional string bitrate_mode = 23;
+ optional int32 bitrate = 24;
+ optional int64 lifetime_millis = 25;
}
/**
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index fd8460f..464a470 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/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index c962256..f42b248 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1830,30 +1830,13 @@
<!-- @hide no longer used, kept to preserve padding -->
<attr name="allowAutoRevokePermissionsExemption" format="boolean" />
- <!-- Declare the app's tolerance to having its permissions automatically revoked when unused for an extended
- period of time -->
+ <!-- No longer used. Declaring this does nothing -->
<attr name="autoRevokePermissions">
- <!-- App supports re-requesting its permissions if revoked.
- Revoking app's permissions doesn't cause user experience issues, aside from a repeated permission request.
-
- Permissions may be automatically revoked from an app if unused. The app must check and possibly request the
- necessary permission on each permission-gated call-->
+ <!-- No longer used -->
<enum name="allowed" value="0" />
- <!-- App may experience degraded functionality when its previously-granted permissions are revoked.
- Revoking app's permissions may cause user experience issues, that are not critical to the user.
-
- Apps with this declaration can choose to request an exemption from auto revoke from user by starting
- an activity with {@code Intent.ACTION_AUTO_REVOKE_PERMISSIONS}. -->
+ <!-- No longer used -->
<enum name="discouraged" value="1" />
- <!-- User may experience severe consequences if this app's permissions are revoked unexpectedly.
-
- E.g. app may fail to do a user-critical background job that may likely impact user's
- safety/security/device accessibility.
-
- This declaration may cause an additional review when publishing your app.
-
- Apps with this declaration are exempt from auto revoke by default, though the user has the final say
- in both revoking the permissions as well as the app's auto revoke exemption status. -->
+ <!-- No longer used -->
<enum name="disallowed" value="2" />
</attr>
</declare-styleable>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index dc21e87..a1c2450 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1613,15 +1613,15 @@
<string name="face_error_no_space">Can\u2019t store new face data. Delete an old one first.</string>
<!-- Generic error message shown when the face operation (e.g. enrollment or authentication) is canceled. Generally not shown to the user. [CHAR LIMIT=50] -->
<string name="face_error_canceled">Face operation canceled.</string>
- <!-- Generic error message shown when the face unlock operation is canceled due to user input. Generally not shown to the user [CHAR LIMIT=54] -->
+ <!-- Generic error message shown when the face unlock operation is canceled due to user input. Generally not shown to the user [CHAR LIMIT=68] -->
<string name="face_error_user_canceled">Face unlock canceled by user.</string>
<!-- Generic error message shown when the face operation fails because too many attempts have been made. [CHAR LIMIT=50] -->
<string name="face_error_lockout">Too many attempts. Try again later.</string>
- <!-- Generic error message shown when the face operation fails because strong authentication is required. [CHAR LIMIT=71] -->
+ <!-- Generic error message shown when the face operation fails because strong authentication is required. [CHAR LIMIT=77] -->
<string name="face_error_lockout_permanent">Too many attempts. Face unlock disabled.</string>
<!-- Generic error message shown when the face hardware can't recognize the face. [CHAR LIMIT=50] -->
<string name="face_error_unable_to_process">Can\u2019t verify face. Try again.</string>
- <!-- Generic error message shown when the user has no enrolled face. [CHAR LIMIT=52] -->
+ <!-- Generic error message shown when the user has no enrolled face. [CHAR LIMIT=59] -->
<string name="face_error_not_enrolled">You haven\u2019t set up face unlock.</string>
<!-- Generic error message shown when the app requests face unlock on a device without a sensor. [CHAR LIMIT=61] -->
<string name="face_error_hw_not_present">Face unlock is not supported on this device.</string>
diff --git a/core/tests/PackageInstallerSessions/Android.bp b/core/tests/PackageInstallerSessions/Android.bp
new file mode 100644
index 0000000..e74f30e
--- /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 0000000..5b22d2b
--- /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 0000000..494c92a
--- /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/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
index 7cd2f3b..a4f2065 100644
--- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
+++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
@@ -363,6 +363,16 @@
public boolean isHdmiCecVolumeControlEnabled() {
return true;
}
+
+ @Override
+ public void addHdmiCecVolumeControlFeatureListener(
+ IHdmiCecVolumeControlFeatureListener listener) {
+ }
+
+ @Override
+ public void removeHdmiCecVolumeControlFeatureListener(
+ IHdmiCecVolumeControlFeatureListener listener) {
+ }
}
}
diff --git a/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java
index fe33cd8..4b81737 100644
--- a/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java
+++ b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java
@@ -29,11 +29,12 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
-import android.graphics.Bitmap;
import android.graphics.Insets;
import android.graphics.Rect;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.WindowManager;
@@ -91,8 +92,7 @@
@Test
public void testProvidedImageScreenshot() {
mScreenshotHelper.provideScreenshot(
- Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888), new Rect(),
- Insets.of(0, 0, 0, 0), 1,
+ new Bundle(), new Rect(), Insets.of(0, 0, 0, 0), 1, 1, new ComponentName("", ""),
WindowManager.ScreenshotSource.SCREENSHOT_OTHER, mHandler, null);
}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index e00813c..aa4e9f2 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -418,6 +418,8 @@
<permission name="android.permission.TV_INPUT_HARDWARE" />
<!-- Permission required for CTS test - PrivilegedLocationPermissionTest -->
<permission name="android.permission.LOCATION_HARDWARE" />
+ <!-- Permissions required for GTS test - GtsDialerAudioTestCases -->
+ <permission name="android.permission.CAPTURE_AUDIO_OUTPUT" />
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
diff --git a/data/keyboards/Vendor_045e_Product_0b12.kl b/data/keyboards/Vendor_045e_Product_0b12.kl
new file mode 100644
index 0000000..0b44c743
--- /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/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java
index c652628..590def4 100644
--- a/media/java/android/media/MediaCas.java
+++ b/media/java/android/media/MediaCas.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
+import android.app.ActivityManager;
import android.content.Context;
import android.hardware.cas.V1_0.HidlCasPluginDescriptor;
import android.hardware.cas.V1_0.ICas;
@@ -43,6 +44,8 @@
import android.util.Log;
import android.util.Singleton;
+import com.android.internal.util.FrameworkStatsLog;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -122,6 +125,7 @@
private String mTvInputServiceSessionId;
private int mClientId;
private int mCasSystemId;
+ private int mUserId;
private TunerResourceManager mTunerResourceManager = null;
private final Map<Session, Integer> mSessionMap = new HashMap<>();
@@ -673,6 +677,8 @@
*/
public MediaCas(int CA_system_id) throws UnsupportedCasException {
try {
+ mCasSystemId = CA_system_id;
+ mUserId = ActivityManager.getCurrentUser();
IMediaCasService service = getService();
android.hardware.cas.V1_2.IMediaCasService serviceV12 =
android.hardware.cas.V1_2.IMediaCasService.castFrom(service);
@@ -721,7 +727,6 @@
this(casSystemId);
Objects.requireNonNull(context, "context must not be null");
- mCasSystemId = casSystemId;
mTunerResourceManager = (TunerResourceManager)
context.getSystemService(Context.TV_TUNER_RESOURCE_MGR_SERVICE);
if (mTunerResourceManager != null) {
@@ -925,10 +930,18 @@
mICas.openSession(cb);
MediaCasException.throwExceptionIfNeeded(cb.mStatus);
addSessionToResourceMap(cb.mSession, sessionResourceHandle);
+ Log.d(TAG, "Write Stats Log for succeed to Open Session.");
+ FrameworkStatsLog
+ .write(FrameworkStatsLog.TV_CAS_SESSION_OPEN_STATUS, mUserId, mCasSystemId,
+ FrameworkStatsLog.TV_CAS_SESSION_OPEN_STATUS__STATE__SUCCEEDED);
return cb.mSession;
} catch (RemoteException e) {
cleanupAndRethrowIllegalState();
}
+ Log.d(TAG, "Write Stats Log for fail to Open Session.");
+ FrameworkStatsLog
+ .write(FrameworkStatsLog.TV_CAS_SESSION_OPEN_STATUS, mUserId, mCasSystemId,
+ FrameworkStatsLog.TV_CAS_SESSION_OPEN_STATUS__STATE__FAILED);
return null;
}
@@ -964,10 +977,18 @@
mICasV12.openSession_1_2(sessionUsage, scramblingMode, cb);
MediaCasException.throwExceptionIfNeeded(cb.mStatus);
addSessionToResourceMap(cb.mSession, sessionResourceHandle);
+ Log.d(TAG, "Write Stats Log for succeed to Open Session.");
+ FrameworkStatsLog
+ .write(FrameworkStatsLog.TV_CAS_SESSION_OPEN_STATUS, mUserId, mCasSystemId,
+ FrameworkStatsLog.TV_CAS_SESSION_OPEN_STATUS__STATE__SUCCEEDED);
return cb.mSession;
} catch (RemoteException e) {
cleanupAndRethrowIllegalState();
}
+ Log.d(TAG, "Write Stats Log for fail to Open Session.");
+ FrameworkStatsLog
+ .write(FrameworkStatsLog.TV_CAS_SESSION_OPEN_STATUS, mUserId, mCasSystemId,
+ FrameworkStatsLog.TV_CAS_SESSION_OPEN_STATUS__STATE__FAILED);
return null;
}
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index 981bf7a..05c6e3a 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -137,7 +137,7 @@
private final AtomicBoolean mStatePublishScheduled = new AtomicBoolean(false);
private MediaRoute2ProviderServiceStub mStub;
private IMediaRoute2ProviderServiceCallback mRemoteCallback;
- private MediaRoute2ProviderInfo mProviderInfo;
+ private volatile MediaRoute2ProviderInfo mProviderInfo;
@GuardedBy("mSessionLock")
private ArrayMap<String, RoutingSessionInfo> mSessionInfo = new ArrayMap<>();
@@ -167,8 +167,8 @@
/**
* Called when a volume setting is requested on a route of the provider
*
- * @param requestId the id of this request
- * @param routeId the id of the route
+ * @param requestId the ID of this request
+ * @param routeId the ID of the route
* @param volume the target volume
* @see MediaRoute2Info.Builder#setVolume(int)
*/
@@ -178,8 +178,8 @@
* Called when {@link MediaRouter2.RoutingController#setVolume(int)} is called on
* a routing session of the provider
*
- * @param requestId the id of this request
- * @param sessionId the id of the routing session
+ * @param requestId the ID of this request
+ * @param sessionId the ID of the routing session
* @param volume the target volume
* @see RoutingSessionInfo.Builder#setVolume(int)
*/
@@ -188,7 +188,7 @@
/**
* Gets information of the session with the given id.
*
- * @param sessionId id of the session
+ * @param sessionId the ID of the session
* @return information of the session with the given id.
* null if the session is released or ID is not valid.
*/
@@ -218,7 +218,7 @@
* If this session is created without any creation request, use {@link #REQUEST_ID_NONE}
* as the request ID.
*
- * @param requestId id of the previous request to create this session provided in
+ * @param requestId the ID of the previous request to create this session provided in
* {@link #onCreateSession(long, String, String, Bundle)}. Can be
* {@link #REQUEST_ID_NONE} if this session is created without any request.
* @param sessionInfo information of the new session.
@@ -237,18 +237,15 @@
return;
}
mSessionInfo.put(sessionInfo.getId(), sessionInfo);
- }
- if (mRemoteCallback == null) {
- return;
- }
- try {
- // TODO(b/157873487): Calling binder calls in multiple thread may cause timing issue.
- // Consider to change implementations to avoid the problems.
- // For example, post binder calls, always send all sessions at once, etc.
- mRemoteCallback.notifySessionCreated(requestId, sessionInfo);
- } catch (RemoteException ex) {
- Log.w(TAG, "Failed to notify session created.");
+ if (mRemoteCallback == null) {
+ return;
+ }
+ try {
+ mRemoteCallback.notifySessionCreated(requestId, sessionInfo);
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Failed to notify session created.");
+ }
}
}
@@ -267,22 +264,22 @@
Log.w(TAG, "Ignoring unknown session info.");
return;
}
- }
- if (mRemoteCallback == null) {
- return;
- }
- try {
- mRemoteCallback.notifySessionUpdated(sessionInfo);
- } catch (RemoteException ex) {
- Log.w(TAG, "Failed to notify session info changed.");
+ if (mRemoteCallback == null) {
+ return;
+ }
+ try {
+ mRemoteCallback.notifySessionUpdated(sessionInfo);
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Failed to notify session info changed.");
+ }
}
}
/**
* Notifies that the session is released.
*
- * @param sessionId id of the released session.
+ * @param sessionId the ID of the released session.
* @see #onReleaseSession(long, String)
*/
public final void notifySessionReleased(@NonNull String sessionId) {
@@ -292,20 +289,20 @@
RoutingSessionInfo sessionInfo;
synchronized (mSessionLock) {
sessionInfo = mSessionInfo.remove(sessionId);
- }
- if (sessionInfo == null) {
- Log.w(TAG, "Ignoring unknown session info.");
- return;
- }
+ if (sessionInfo == null) {
+ Log.w(TAG, "Ignoring unknown session info.");
+ return;
+ }
- if (mRemoteCallback == null) {
- return;
- }
- try {
- mRemoteCallback.notifySessionReleased(sessionInfo);
- } catch (RemoteException ex) {
- Log.w(TAG, "Failed to notify session info changed.");
+ if (mRemoteCallback == null) {
+ return;
+ }
+ try {
+ mRemoteCallback.notifySessionReleased(sessionInfo);
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Failed to notify session info changed.");
+ }
}
}
@@ -348,9 +345,9 @@
* If you can't create the session or want to reject the request, call
* {@link #notifyRequestFailed(long, int)} with the given {@code requestId}.
*
- * @param requestId the id of this request
+ * @param requestId the ID of this request
* @param packageName the package name of the application that selected the route
- * @param routeId the id of the route initially being connected
+ * @param routeId the ID of the route initially being connected
* @param sessionHints an optional bundle of app-specific arguments sent by
* {@link MediaRouter2}, or null if none. The contents of this bundle
* may affect the result of session creation.
@@ -372,8 +369,8 @@
* Note: Calling {@link #notifySessionReleased(String)} will <em>NOT</em> trigger
* this method to be called.
*
- * @param requestId the id of this request
- * @param sessionId id of the session being released.
+ * @param requestId the ID of this request
+ * @param sessionId the ID of the session being released.
* @see #notifySessionReleased(String)
* @see #getSessionInfo(String)
*/
@@ -384,9 +381,9 @@
* After the route is selected, call {@link #notifySessionUpdated(RoutingSessionInfo)}
* to update session info.
*
- * @param requestId the id of this request
- * @param sessionId id of the session
- * @param routeId id of the route
+ * @param requestId the ID of this request
+ * @param sessionId the ID of the session
+ * @param routeId the ID of the route
*/
public abstract void onSelectRoute(long requestId, @NonNull String sessionId,
@NonNull String routeId);
@@ -396,9 +393,9 @@
* After the route is deselected, call {@link #notifySessionUpdated(RoutingSessionInfo)}
* to update session info.
*
- * @param requestId the id of this request
- * @param sessionId id of the session
- * @param routeId id of the route
+ * @param requestId the ID of this request
+ * @param sessionId the ID of the session
+ * @param routeId the ID of the route
*/
public abstract void onDeselectRoute(long requestId, @NonNull String sessionId,
@NonNull String routeId);
@@ -408,9 +405,9 @@
* After the transfer is finished, call {@link #notifySessionUpdated(RoutingSessionInfo)}
* to update session info.
*
- * @param requestId the id of this request
- * @param sessionId id of the session
- * @param routeId id of the route
+ * @param requestId the ID of this request
+ * @param sessionId the ID of the session
+ * @param routeId the ID of the route
*/
public abstract void onTransferToRoute(long requestId, @NonNull String sessionId,
@NonNull String routeId);
@@ -475,13 +472,39 @@
final class MediaRoute2ProviderServiceStub extends IMediaRoute2ProviderService.Stub {
MediaRoute2ProviderServiceStub() { }
- boolean checkCallerisSystem() {
+ private boolean checkCallerIsSystem() {
return Binder.getCallingUid() == Process.SYSTEM_UID;
}
+ private boolean checkSessionIdIsValid(String sessionId, String description) {
+ if (TextUtils.isEmpty(sessionId)) {
+ Log.w(TAG, description + ": Ignoring empty sessionId from system service.");
+ return false;
+ }
+ if (getSessionInfo(sessionId) == null) {
+ Log.w(TAG, description + ": Ignoring unknown session from system service. "
+ + "sessionId=" + sessionId);
+ return false;
+ }
+ return true;
+ }
+
+ private boolean checkRouteIdIsValid(String routeId, String description) {
+ if (TextUtils.isEmpty(routeId)) {
+ Log.w(TAG, description + ": Ignoring empty routeId from system service.");
+ return false;
+ }
+ if (mProviderInfo == null || mProviderInfo.getRoute(routeId) == null) {
+ Log.w(TAG, description + ": Ignoring unknown route from system service. "
+ + "routeId=" + routeId);
+ return false;
+ }
+ return true;
+ }
+
@Override
public void setCallback(IMediaRoute2ProviderServiceCallback callback) {
- if (!checkCallerisSystem()) {
+ if (!checkCallerIsSystem()) {
return;
}
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::setCallback,
@@ -490,7 +513,7 @@
@Override
public void updateDiscoveryPreference(RouteDiscoveryPreference discoveryPreference) {
- if (!checkCallerisSystem()) {
+ if (!checkCallerIsSystem()) {
return;
}
mHandler.sendMessage(obtainMessage(
@@ -500,7 +523,10 @@
@Override
public void setRouteVolume(long requestId, String routeId, int volume) {
- if (!checkCallerisSystem()) {
+ if (!checkCallerIsSystem()) {
+ return;
+ }
+ if (!checkRouteIdIsValid(routeId, "setRouteVolume")) {
return;
}
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSetRouteVolume,
@@ -510,7 +536,10 @@
@Override
public void requestCreateSession(long requestId, String packageName, String routeId,
@Nullable Bundle requestCreateSession) {
- if (!checkCallerisSystem()) {
+ if (!checkCallerIsSystem()) {
+ return;
+ }
+ if (!checkRouteIdIsValid(routeId, "requestCreateSession")) {
return;
}
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onCreateSession,
@@ -518,14 +547,13 @@
requestCreateSession));
}
- //TODO(b/157873546): Ignore requests with unknown session ID. -> For all similar commands.
@Override
public void selectRoute(long requestId, String sessionId, String routeId) {
- if (!checkCallerisSystem()) {
+ if (!checkCallerIsSystem()) {
return;
}
- if (TextUtils.isEmpty(sessionId)) {
- Log.w(TAG, "selectRoute: Ignoring empty sessionId from system service.");
+ if (!checkSessionIdIsValid(sessionId, "selectRoute")
+ || !checkRouteIdIsValid(routeId, "selectRoute")) {
return;
}
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSelectRoute,
@@ -534,11 +562,11 @@
@Override
public void deselectRoute(long requestId, String sessionId, String routeId) {
- if (!checkCallerisSystem()) {
+ if (!checkCallerIsSystem()) {
return;
}
- if (TextUtils.isEmpty(sessionId)) {
- Log.w(TAG, "deselectRoute: Ignoring empty sessionId from system service.");
+ if (!checkSessionIdIsValid(sessionId, "deselectRoute")
+ || !checkRouteIdIsValid(routeId, "deselectRoute")) {
return;
}
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onDeselectRoute,
@@ -547,11 +575,11 @@
@Override
public void transferToRoute(long requestId, String sessionId, String routeId) {
- if (!checkCallerisSystem()) {
+ if (!checkCallerIsSystem()) {
return;
}
- if (TextUtils.isEmpty(sessionId)) {
- Log.w(TAG, "transferToRoute: Ignoring empty sessionId from system service.");
+ if (!checkSessionIdIsValid(sessionId, "transferToRoute")
+ || !checkRouteIdIsValid(routeId, "transferToRoute")) {
return;
}
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onTransferToRoute,
@@ -560,7 +588,10 @@
@Override
public void setSessionVolume(long requestId, String sessionId, int volume) {
- if (!checkCallerisSystem()) {
+ if (!checkCallerIsSystem()) {
+ return;
+ }
+ if (!checkSessionIdIsValid(sessionId, "setSessionVolume")) {
return;
}
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSetSessionVolume,
@@ -569,11 +600,10 @@
@Override
public void releaseSession(long requestId, String sessionId) {
- if (!checkCallerisSystem()) {
+ if (!checkCallerIsSystem()) {
return;
}
- if (TextUtils.isEmpty(sessionId)) {
- Log.w(TAG, "releaseSession: Ignoring empty sessionId from system service.");
+ if (!checkSessionIdIsValid(sessionId, "releaseSession")) {
return;
}
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onReleaseSession,
diff --git a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
index 68071b0..bb00bb3 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
@@ -20,12 +20,16 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.app.ActivityManager;
import android.hardware.tv.tuner.V1_0.Constants;
import android.media.tv.tuner.Tuner;
import android.media.tv.tuner.Tuner.Result;
import android.media.tv.tuner.TunerUtils;
import android.media.tv.tuner.filter.Filter;
import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import com.android.internal.util.FrameworkStatsLog;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -72,9 +76,15 @@
*/
public static final int PLAYBACK_STATUS_FULL = Constants.PlaybackStatus.SPACE_FULL;
+ private static final String TAG = "TvTunerPlayback";
+
private long mNativeContext;
private OnPlaybackStatusChangedListener mListener;
private Executor mExecutor;
+ private int mUserId;
+ private static int sInstantId = 0;
+ private int mSegmentId = 0;
+ private int mUnderflow;
private native int nativeAttachFilter(Filter filter);
private native int nativeDetachFilter(Filter filter);
@@ -88,6 +98,9 @@
private native long nativeRead(byte[] bytes, long offset, long size);
private DvrPlayback() {
+ mUserId = ActivityManager.getCurrentUser();
+ mSegmentId = (sInstantId & 0x0000ffff) << 16;
+ sInstantId++;
}
/** @hide */
@@ -98,6 +111,9 @@
}
private void onPlaybackStatusChanged(int status) {
+ if (status == PLAYBACK_STATUS_EMPTY) {
+ mUnderflow++;
+ }
if (mExecutor != null && mListener != null) {
mExecutor.execute(() -> mListener.onPlaybackStatusChanged(status));
}
@@ -154,6 +170,13 @@
*/
@Result
public int start() {
+ mSegmentId = (mSegmentId & 0xffff0000) | (((mSegmentId & 0x0000ffff) + 1) & 0x0000ffff);
+ mUnderflow = 0;
+ Log.d(TAG, "Write Stats Log for Playback.");
+ FrameworkStatsLog
+ .write(FrameworkStatsLog.TV_TUNER_DVR_STATUS, mUserId,
+ FrameworkStatsLog.TV_TUNER_DVR_STATUS__TYPE__PLAYBACK,
+ FrameworkStatsLog.TV_TUNER_DVR_STATUS__STATE__STARTED, mSegmentId, 0);
return nativeStartDvr();
}
@@ -167,6 +190,11 @@
*/
@Result
public int stop() {
+ Log.d(TAG, "Write Stats Log for Playback.");
+ FrameworkStatsLog
+ .write(FrameworkStatsLog.TV_TUNER_DVR_STATUS, mUserId,
+ FrameworkStatsLog.TV_TUNER_DVR_STATUS__TYPE__PLAYBACK,
+ FrameworkStatsLog.TV_TUNER_DVR_STATUS__STATE__STOPPED, mSegmentId, mUnderflow);
return nativeStopDvr();
}
diff --git a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
index 198bd0f..8871167 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
@@ -19,14 +19,19 @@
import android.annotation.BytesLong;
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.app.ActivityManager;
import android.media.tv.tuner.Tuner;
import android.media.tv.tuner.Tuner.Result;
import android.media.tv.tuner.TunerUtils;
import android.media.tv.tuner.filter.Filter;
import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import com.android.internal.util.FrameworkStatsLog;
import java.util.concurrent.Executor;
+
/**
* Digital Video Record (DVR) recorder class which provides record control on Demux's output buffer.
*
@@ -34,9 +39,14 @@
*/
@SystemApi
public class DvrRecorder implements AutoCloseable {
+ private static final String TAG = "TvTunerRecord";
private long mNativeContext;
private OnRecordStatusChangedListener mListener;
private Executor mExecutor;
+ private int mUserId;
+ private static int sInstantId = 0;
+ private int mSegmentId = 0;
+ private int mOverflow;
private native int nativeAttachFilter(Filter filter);
private native int nativeDetachFilter(Filter filter);
@@ -50,6 +60,9 @@
private native long nativeWrite(byte[] bytes, long offset, long size);
private DvrRecorder() {
+ mUserId = ActivityManager.getCurrentUser();
+ mSegmentId = (sInstantId & 0x0000ffff) << 16;
+ sInstantId++;
}
/** @hide */
@@ -60,6 +73,9 @@
}
private void onRecordStatusChanged(int status) {
+ if (status == Filter.STATUS_OVERFLOW) {
+ mOverflow++;
+ }
if (mExecutor != null && mListener != null) {
mExecutor.execute(() -> mListener.onRecordStatusChanged(status));
}
@@ -112,6 +128,13 @@
*/
@Result
public int start() {
+ mSegmentId = (mSegmentId & 0xffff0000) | (((mSegmentId & 0x0000ffff) + 1) & 0x0000ffff);
+ mOverflow = 0;
+ Log.d(TAG, "Write Stats Log for Record.");
+ FrameworkStatsLog
+ .write(FrameworkStatsLog.TV_TUNER_DVR_STATUS, mUserId,
+ FrameworkStatsLog.TV_TUNER_DVR_STATUS__TYPE__RECORD,
+ FrameworkStatsLog.TV_TUNER_DVR_STATUS__STATE__STARTED, mSegmentId, 0);
return nativeStartDvr();
}
@@ -124,6 +147,11 @@
*/
@Result
public int stop() {
+ Log.d(TAG, "Write Stats Log for Playback.");
+ FrameworkStatsLog
+ .write(FrameworkStatsLog.TV_TUNER_DVR_STATUS, mUserId,
+ FrameworkStatsLog.TV_TUNER_DVR_STATUS__TYPE__RECORD,
+ FrameworkStatsLog.TV_TUNER_DVR_STATUS__STATE__STOPPED, mSegmentId, mOverflow);
return nativeStopDvr();
}
diff --git a/packages/CarSystemUI/Android.bp b/packages/CarSystemUI/Android.bp
index 2a8a39a..32b33a7 100644
--- a/packages/CarSystemUI/Android.bp
+++ b/packages/CarSystemUI/Android.bp
@@ -32,6 +32,7 @@
"SystemUIPluginLib",
"SystemUISharedLib",
"SettingsLib",
+ "car-ui-lib",
"android.car.userlib",
"androidx.legacy_legacy-support-v4",
"androidx.recyclerview_recyclerview",
@@ -95,6 +96,7 @@
"androidx.slice_slice-builders",
"androidx.arch.core_core-runtime",
"androidx.lifecycle_lifecycle-extensions",
+ "car-ui-lib",
"SystemUI-tags",
"SystemUI-proto",
"metrics-helper-lib",
diff --git a/packages/CarSystemUI/res/drawable/nav_button_background.xml b/packages/CarSystemUI/res/drawable/nav_button_background.xml
deleted file mode 100644
index 376347c..0000000
--- a/packages/CarSystemUI/res/drawable/nav_button_background.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2018 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="@color/nav_bar_ripple_background_color">
- <item android:id="@android:id/mask">
- <shape android:shape="rectangle">
- <solid android:color="?android:colorAccent"/>
- <corners android:radius="6dp"/>
- </shape>
- </item>
-</ripple>
diff --git a/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
index a8c7098..94816f8 100644
--- a/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
@@ -79,7 +79,7 @@
android:gravity="bottom"
android:orientation="vertical">
- <com.android.keyguard.AlphaOptimizedImageButton
+ <com.android.systemui.statusbar.AlphaOptimizedImageView
android:id="@+id/note"
android:layout_height="wrap_content"
android:layout_width="match_parent"
diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_navigation_bar.xml
index 2a715d0..9317498 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_navigation_button.xml b/packages/CarSystemUI/res/layout/car_navigation_button.xml
index ca4e76e..a8f1157 100644
--- a/packages/CarSystemUI/res/layout/car_navigation_button.xml
+++ b/packages/CarSystemUI/res/layout/car_navigation_button.xml
@@ -27,7 +27,7 @@
android:animateLayoutChanges="true"
android:orientation="vertical">
- <com.android.keyguard.AlphaOptimizedImageButton
+ <com.android.systemui.statusbar.AlphaOptimizedImageView
android:id="@+id/car_nav_button_icon_image"
android:layout_height="@dimen/car_navigation_button_icon_height"
android:layout_width="match_parent"
@@ -40,7 +40,7 @@
android:clickable="false"
/>
- <com.android.keyguard.AlphaOptimizedImageButton
+ <com.android.systemui.statusbar.AlphaOptimizedImageView
android:id="@+id/car_nav_button_more_icon"
android:layout_height="wrap_content"
android:layout_width="match_parent"
diff --git a/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml
index fd75570..dc95833 100644
--- a/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml
@@ -82,7 +82,7 @@
android:gravity="bottom"
android:orientation="vertical">
- <com.android.keyguard.AlphaOptimizedImageButton
+ <com.android.systemui.statusbar.AlphaOptimizedImageView
android:id="@+id/note"
android:layout_height="wrap_content"
android:layout_width="match_parent"
diff --git a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
index 60e0d7e..cdc29eec 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/CarSystemUI/res/layout/headsup_container_bottom.xml b/packages/CarSystemUI/res/layout/headsup_container_bottom.xml
index caf1677..1782d25 100644
--- a/packages/CarSystemUI/res/layout/headsup_container_bottom.xml
+++ b/packages/CarSystemUI/res/layout/headsup_container_bottom.xml
@@ -29,6 +29,15 @@
android:orientation="horizontal"
app:layout_constraintGuide_begin="@dimen/headsup_scrim_height"/>
+ <!-- Include a FocusParkingView at the beginning or end. The rotary controller "parks" the
+ focus here when the user navigates to another window. This is also used to prevent
+ wrap-around which is why it must be first or last in Tab order. -->
+ <com.android.car.ui.FocusParkingView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:layout_constraintLeft_toLeftOf="parent"
+ app:layout_constraintTop_toTopOf="parent"/>
+
<View
android:id="@+id/scrim"
android:layout_width="match_parent"
diff --git a/packages/CarSystemUI/res/values/styles.xml b/packages/CarSystemUI/res/values/styles.xml
index 371bebd..7fc69e6 100644
--- a/packages/CarSystemUI/res/values/styles.xml
+++ b/packages/CarSystemUI/res/values/styles.xml
@@ -44,6 +44,6 @@
<style name="NavigationBarButton">
<item name="android:layout_height">96dp</item>
<item name="android:layout_width">96dp</item>
- <item name="android:background">@drawable/nav_button_background</item>
+ <item name="android:background">@*android:drawable/item_background_material</item>
</style>
</resources>
\ No newline at end of file
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
index ab61b44..2dad5f8 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
@@ -219,6 +219,14 @@
}
@Override
+ public void setOccluded(boolean occluded, boolean animate) {
+ getOverlayViewGlobalStateController().setOccluded(occluded);
+ if (!occluded) {
+ reset(/* hideBouncerWhenShowing= */ false);
+ }
+ }
+
+ @Override
public void onCancelClicked() {
if (mBouncer == null) return;
@@ -315,11 +323,6 @@
}
@Override
- public void setOccluded(boolean occluded, boolean animate) {
- // no-op
- }
-
- @Override
public boolean shouldDisableWindowAnimationsForUnlock() {
return false;
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java
index 20fc1bc..0ced402 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java
@@ -74,8 +74,10 @@
mDarkIconManager.setShouldLog(true);
Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager);
}
- // needs to be clickable so that it will receive ACTION_MOVE events
+ // Needs to be clickable so that it will receive ACTION_MOVE events.
setClickable(true);
+ // Needs to not be focusable so rotary won't highlight the entire nav bar.
+ setFocusable(false);
}
@Override
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationButton.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationButton.java
index 5e113d6..e7e33a5 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationButton.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationButton.java
@@ -32,8 +32,8 @@
import android.widget.LinearLayout;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.keyguard.AlphaOptimizedImageButton;
import com.android.systemui.R;
+import com.android.systemui.statusbar.AlphaOptimizedImageView;
import java.net.URISyntaxException;
@@ -53,8 +53,8 @@
private static final String EXTRA_BUTTON_PACKAGES = "packages";
private Context mContext;
- private AlphaOptimizedImageButton mIcon;
- private AlphaOptimizedImageButton mMoreIcon;
+ private AlphaOptimizedImageView mIcon;
+ private AlphaOptimizedImageView mMoreIcon;
private ImageView mUnseenIcon;
private String mIntent;
private String mLongIntent;
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java
index 3b7b48a..d60bc41 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java
@@ -24,6 +24,7 @@
import androidx.annotation.LayoutRes;
+import com.android.car.ui.FocusParkingView;
import com.android.systemui.R;
import javax.inject.Inject;
@@ -146,6 +147,12 @@
CarNavigationBarView view = (CarNavigationBarView) View.inflate(mContext, barLayout,
/* root= */ null);
+
+ // Include a FocusParkingView at the end. The rotary controller "parks" the focus here when
+ // the user navigates to another window. This is also used to prevent wrap-around which is
+ // why it must be first or last in Tab order.
+ view.addView(new FocusParkingView(mContext));
+
mCachedViewMap.put(type, view);
return mCachedViewMap.get(type);
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java
index aeb1d39..d4f72071 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java
@@ -24,7 +24,6 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
-import android.widget.FrameLayout;
import com.android.car.notification.R;
import com.android.car.notification.headsup.CarHeadsUpNotificationContainer;
@@ -44,7 +43,7 @@
private final OverlayViewGlobalStateController mOverlayViewGlobalStateController;
private final ViewGroup mWindow;
- private final FrameLayout mHeadsUpContentFrame;
+ private final ViewGroup mHeadsUpContentFrame;
@Inject
CarHeadsUpNotificationSystemContainer(Context context,
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
index 30e2657..3969f92 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
@@ -138,4 +138,11 @@
protected boolean shouldShowNavigationBar() {
return false;
}
+
+ /**
+ * Returns {@code true} if this view should be hidden during the occluded state.
+ */
+ protected boolean shouldShowWhenOccluded() {
+ return false;
+ }
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
index 70260b0..8e94109 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
@@ -24,7 +24,9 @@
import com.android.systemui.car.navigationbar.CarNavigationBarController;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
+import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
@@ -47,11 +49,16 @@
private static final int UNKNOWN_Z_ORDER = -1;
private final SystemUIOverlayWindowController mSystemUIOverlayWindowController;
private final CarNavigationBarController mCarNavigationBarController;
+
+ private boolean mIsOccluded;
+
@VisibleForTesting
Map<OverlayViewController, Integer> mZOrderMap;
@VisibleForTesting
SortedMap<Integer, OverlayViewController> mZOrderVisibleSortedMap;
@VisibleForTesting
+ Set<OverlayViewController> mViewsHiddenForOcclusion;
+ @VisibleForTesting
OverlayViewController mHighestZOrder;
@Inject
@@ -63,6 +70,7 @@
mCarNavigationBarController = carNavigationBarController;
mZOrderMap = new HashMap<>();
mZOrderVisibleSortedMap = new TreeMap<>();
+ mViewsHiddenForOcclusion = new HashSet<>();
}
/**
@@ -91,6 +99,10 @@
*/
public void showView(OverlayViewController viewController, @Nullable Runnable show) {
debugLog();
+ if (mIsOccluded && !viewController.shouldShowWhenOccluded()) {
+ mViewsHiddenForOcclusion.add(viewController);
+ return;
+ }
if (mZOrderVisibleSortedMap.isEmpty()) {
setWindowVisible(true);
}
@@ -147,6 +159,10 @@
*/
public void hideView(OverlayViewController viewController, @Nullable Runnable hide) {
debugLog();
+ if (mIsOccluded && mViewsHiddenForOcclusion.contains(viewController)) {
+ mViewsHiddenForOcclusion.remove(viewController);
+ return;
+ }
if (!viewController.isInflated()) {
Log.d(TAG, "Content cannot be hidden since it isn't inflated: "
+ viewController.getClass().getName());
@@ -240,6 +256,43 @@
return mZOrderVisibleSortedMap.isEmpty() || mHighestZOrder.shouldShowHUN();
}
+ /**
+ * Set the OverlayViewWindow to be in occluded or unoccluded state. When OverlayViewWindow is
+ * occluded, all views mounted to it that are not configured to be shown during occlusion will
+ * be hidden.
+ */
+ public void setOccluded(boolean occluded) {
+ if (occluded) {
+ // Hide views before setting mIsOccluded to true so the regular hideView logic is used,
+ // not the one used during occlusion.
+ hideViewsForOcclusion();
+ mIsOccluded = true;
+ } else {
+ mIsOccluded = false;
+ // show views after setting mIsOccluded to false so the regular showView logic is used,
+ // not the one used during occlusion.
+ showViewsHiddenForOcclusion();
+ }
+ }
+
+ private void hideViewsForOcclusion() {
+ HashSet<OverlayViewController> viewsCurrentlyShowing = new HashSet<>(
+ mZOrderVisibleSortedMap.values());
+ viewsCurrentlyShowing.forEach(overlayController -> {
+ if (!overlayController.shouldShowWhenOccluded()) {
+ hideView(overlayController, overlayController::hideInternal);
+ mViewsHiddenForOcclusion.add(overlayController);
+ }
+ });
+ }
+
+ private void showViewsHiddenForOcclusion() {
+ mViewsHiddenForOcclusion.forEach(overlayViewController -> {
+ showView(overlayViewController, overlayViewController::showInternal);
+ });
+ mViewsHiddenForOcclusion.clear();
+ }
+
private void debugLog() {
if (!DEBUG) {
return;
@@ -250,5 +303,8 @@
Log.d(TAG, "mZOrderVisibleSortedMap: " + mZOrderVisibleSortedMap);
Log.d(TAG, "mZOrderMap.size(): " + mZOrderMap.size());
Log.d(TAG, "mZOrderMap: " + mZOrderMap);
+ Log.d(TAG, "mIsOccluded: " + mIsOccluded);
+ Log.d(TAG, "mViewsHiddenForOcclusion: " + mViewsHiddenForOcclusion);
+ Log.d(TAG, "mViewsHiddenForOcclusion.size(): " + mViewsHiddenForOcclusion.size());
}
}
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java
index 38836d8..189e240 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java
@@ -22,6 +22,7 @@
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -169,6 +170,18 @@
}
@Test
+ public void setOccludedFalse_currentlyOccluded_bouncerReset() {
+ when(mBouncer.isSecure()).thenReturn(true);
+ mCarKeyguardViewController.show(/* options= */ null);
+ mCarKeyguardViewController.setOccluded(/* occluded= */ true, /* animate= */ false);
+ reset(mBouncer);
+
+ mCarKeyguardViewController.setOccluded(/* occluded= */ false, /* animate= */ false);
+
+ verify(mBouncer).show(/* resetSecuritySelection= */ true);
+ }
+
+ @Test
public void onCancelClicked_callsCancelClickedListener() {
when(mBouncer.isSecure()).thenReturn(true);
mCarKeyguardViewController.show(/* options= */ null);
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationButtonTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationButtonTest.java
index 54282d3..bcaa5e9 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationButtonTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationButtonTest.java
@@ -36,8 +36,8 @@
import androidx.test.filters.SmallTest;
-import com.android.keyguard.AlphaOptimizedImageButton;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.AlphaOptimizedImageView;
import com.android.systemui.tests.R;
import org.junit.Before;
@@ -74,7 +74,7 @@
@Test
public void onCreate_iconIsVisible() {
- AlphaOptimizedImageButton icon = mDefaultButton.findViewById(
+ AlphaOptimizedImageView icon = mDefaultButton.findViewById(
R.id.car_nav_button_icon_image);
assertThat(icon.getDrawable()).isNotNull();
@@ -83,12 +83,12 @@
@Test
public void onSelected_selectedIconDefined_togglesIcon() {
mDefaultButton.setSelected(true);
- Drawable selectedIconDrawable = ((AlphaOptimizedImageButton) mDefaultButton.findViewById(
+ Drawable selectedIconDrawable = ((AlphaOptimizedImageView) mDefaultButton.findViewById(
R.id.car_nav_button_icon_image)).getDrawable();
mDefaultButton.setSelected(false);
- Drawable unselectedIconDrawable = ((AlphaOptimizedImageButton) mDefaultButton.findViewById(
+ Drawable unselectedIconDrawable = ((AlphaOptimizedImageView) mDefaultButton.findViewById(
R.id.car_nav_button_icon_image)).getDrawable();
assertThat(selectedIconDrawable).isNotEqualTo(unselectedIconDrawable);
@@ -100,12 +100,12 @@
R.id.selected_icon_undefined);
selectedIconUndefinedButton.setSelected(true);
- Drawable selectedIconDrawable = ((AlphaOptimizedImageButton) mDefaultButton.findViewById(
+ Drawable selectedIconDrawable = ((AlphaOptimizedImageView) mDefaultButton.findViewById(
R.id.car_nav_button_icon_image)).getDrawable();
selectedIconUndefinedButton.setSelected(false);
- Drawable unselectedIconDrawable = ((AlphaOptimizedImageButton) mDefaultButton.findViewById(
+ Drawable unselectedIconDrawable = ((AlphaOptimizedImageView) mDefaultButton.findViewById(
R.id.car_nav_button_icon_image)).getDrawable();
assertThat(selectedIconDrawable).isEqualTo(unselectedIconDrawable);
@@ -150,7 +150,7 @@
@Test
public void onSelected_doesNotShowMoreWhenSelected_doesNotShowMoreIcon() {
mDefaultButton.setSelected(true);
- AlphaOptimizedImageButton moreIcon = mDefaultButton.findViewById(
+ AlphaOptimizedImageView moreIcon = mDefaultButton.findViewById(
R.id.car_nav_button_more_icon);
assertThat(moreIcon.getVisibility()).isEqualTo(View.GONE);
@@ -161,7 +161,7 @@
CarNavigationButton showMoreWhenSelected = mTestView.findViewById(
R.id.not_highlightable_more_button);
showMoreWhenSelected.setSelected(true);
- AlphaOptimizedImageButton moreIcon = showMoreWhenSelected.findViewById(
+ AlphaOptimizedImageView moreIcon = showMoreWhenSelected.findViewById(
R.id.car_nav_button_more_icon);
assertThat(moreIcon.getVisibility()).isEqualTo(View.VISIBLE);
@@ -173,7 +173,7 @@
R.id.highlightable_no_more_button);
showMoreWhenSelected.setSelected(true);
showMoreWhenSelected.setSelected(false);
- AlphaOptimizedImageButton moreIcon = showMoreWhenSelected.findViewById(
+ AlphaOptimizedImageView moreIcon = showMoreWhenSelected.findViewById(
R.id.car_nav_button_more_icon);
assertThat(moreIcon.getVisibility()).isEqualTo(View.GONE);
@@ -187,7 +187,7 @@
roleBasedButton.setSelected(false);
roleBasedButton.setAppIcon(appIcon);
- Drawable currentDrawable = ((AlphaOptimizedImageButton) roleBasedButton.findViewById(
+ Drawable currentDrawable = ((AlphaOptimizedImageView) roleBasedButton.findViewById(
R.id.car_nav_button_icon_image)).getDrawable();
assertThat(currentDrawable).isEqualTo(appIcon);
@@ -212,7 +212,7 @@
roleBasedButton.setSelected(true);
roleBasedButton.setAppIcon(appIcon);
- Drawable currentDrawable = ((AlphaOptimizedImageButton) roleBasedButton.findViewById(
+ Drawable currentDrawable = ((AlphaOptimizedImageView) roleBasedButton.findViewById(
R.id.car_nav_button_icon_image)).getDrawable();
assertThat(currentDrawable).isEqualTo(appIcon);
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
index 9e6e616..cba42e5 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
@@ -491,6 +491,81 @@
}
@Test
+ public void setOccludedTrue_viewToHideWhenOccludedVisible_viewHidden() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ when(mOverlayViewController1.shouldShowWhenOccluded()).thenReturn(false);
+
+ mOverlayViewGlobalStateController.setOccluded(true);
+
+ assertThat(mOverlayViewGlobalStateController.mZOrderVisibleSortedMap.containsValue(
+ mOverlayViewController1)).isFalse();
+ }
+
+ @Test
+ public void setOccludedTrue_viewToNotHideWhenOccludedVisible_viewShown() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ when(mOverlayViewController1.shouldShowWhenOccluded()).thenReturn(true);
+
+ mOverlayViewGlobalStateController.setOccluded(true);
+
+ assertThat(mOverlayViewGlobalStateController.mZOrderVisibleSortedMap.containsValue(
+ mOverlayViewController1)).isTrue();
+ }
+
+ @Test
+ public void hideViewAndThenSetOccludedTrue_viewHiddenForOcclusion_viewHiddenAfterOcclusion() {
+ setupOverlayViewController1();
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+ when(mOverlayViewController1.shouldShowWhenOccluded()).thenReturn(false);
+ mOverlayViewGlobalStateController.setOccluded(true);
+
+ mOverlayViewGlobalStateController.hideView(mOverlayViewController1, /* runnable= */ null);
+ mOverlayViewGlobalStateController.setOccluded(false);
+
+ assertThat(mOverlayViewGlobalStateController.mZOrderVisibleSortedMap.containsValue(
+ mOverlayViewController1)).isFalse();
+ }
+
+ @Test
+ public void setOccludedTrueAndThenShowView_viewToNotHideForOcclusion_viewShown() {
+ setupOverlayViewController1();
+ when(mOverlayViewController1.shouldShowWhenOccluded()).thenReturn(true);
+
+ mOverlayViewGlobalStateController.setOccluded(true);
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+
+ assertThat(mOverlayViewGlobalStateController.mZOrderVisibleSortedMap.containsValue(
+ mOverlayViewController1)).isTrue();
+ }
+
+ @Test
+ public void setOccludedTrueAndThenShowView_viewToHideForOcclusion_viewHidden() {
+ setupOverlayViewController1();
+ when(mOverlayViewController1.shouldShowWhenOccluded()).thenReturn(false);
+
+ mOverlayViewGlobalStateController.setOccluded(true);
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+
+ assertThat(mOverlayViewGlobalStateController.mZOrderVisibleSortedMap.containsValue(
+ mOverlayViewController1)).isFalse();
+ }
+
+ @Test
+ public void setOccludedFalse_viewShownAfterSetOccludedTrue_viewToHideForOcclusion_viewShown() {
+ setupOverlayViewController1();
+ when(mOverlayViewController1.shouldShowWhenOccluded()).thenReturn(false);
+ mOverlayViewGlobalStateController.setOccluded(true);
+ setOverlayViewControllerAsShowing(mOverlayViewController1);
+
+ mOverlayViewGlobalStateController.setOccluded(false);
+
+ assertThat(mOverlayViewGlobalStateController.mZOrderVisibleSortedMap.containsValue(
+ mOverlayViewController1)).isTrue();
+ }
+
+ @Test
public void inflateView_notInflated_inflates() {
when(mOverlayViewController2.isInflated()).thenReturn(false);
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index d039c9f..1b5062e 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 65f456e..d003ef0 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/SettingsLib/src/com/android/settingslib/applications/AppUtils.java b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
index a6202956..38eeda2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
@@ -19,10 +19,13 @@
import android.app.Application;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.hardware.usb.IUsbManager;
+import android.net.Uri;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -44,6 +47,15 @@
*/
private static InstantAppDataProvider sInstantAppDataProvider = null;
+ private static final Intent sBrowserIntent;
+
+ static {
+ sBrowserIntent = new Intent()
+ .setAction(Intent.ACTION_VIEW)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .setData(Uri.parse("http:"));
+ }
+
public static CharSequence getLaunchByDefaultSummary(ApplicationsState.AppEntry appEntry,
IUsbManager usbManager, PackageManager pm, Context context) {
String packageName = appEntry.info.packageName;
@@ -153,4 +165,22 @@
return com.android.settingslib.utils.applications.AppUtils.getAppContentDescription(context,
packageName, userId);
}
+
+ /**
+ * Returns a boolean indicating whether a given package is a browser app.
+ *
+ * An app is a "browser" if it has an activity resolution that wound up
+ * marked with the 'handleAllWebDataURI' flag.
+ */
+ public static boolean isBrowserApp(Context context, String packageName, int userId) {
+ sBrowserIntent.setPackage(packageName);
+ final List<ResolveInfo> list = context.getPackageManager().queryIntentActivitiesAsUser(
+ sBrowserIntent, PackageManager.MATCH_ALL, userId);
+ for (ResolveInfo info : list) {
+ if (info.activityInfo != null && info.handleAllWebDataURI) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index ae8e8e8..44349c5 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -306,6 +306,9 @@
<!-- Permission needed for CTS test - PrivilegedLocationPermissionTest -->
<uses-permission android:name="android.permission.LOCATION_HARDWARE" />
+ <!-- Permissions required for GTS test - GtsDialerAudioTestCases -->
+ <uses-permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT" />
+
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/res/drawable/dismiss_circle_background.xml b/packages/SystemUI/res/drawable/dismiss_circle_background.xml
index e311c52..7809c83 100644
--- a/packages/SystemUI/res/drawable/dismiss_circle_background.xml
+++ b/packages/SystemUI/res/drawable/dismiss_circle_background.xml
@@ -21,8 +21,8 @@
<stroke
android:width="1dp"
- android:color="#66FFFFFF" />
+ android:color="#AAFFFFFF" />
- <solid android:color="#B3000000" />
+ <solid android:color="#77000000" />
</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/dismiss_target_x.xml b/packages/SystemUI/res/drawable/dismiss_target_x.xml
deleted file mode 100644
index 3672eff..0000000
--- a/packages/SystemUI/res/drawable/dismiss_target_x.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2020 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<!-- 'X' icon. -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24.0dp"
- android:height="24.0dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
- <path
- android:pathData="M19.000000,6.400000l-1.400000,-1.400000 -5.600000,5.600000 -5.600000,-5.600000 -1.400000,1.400000 5.600000,5.600000 -5.600000,5.600000 1.400000,1.400000 5.600000,-5.600000 5.600000,5.600000 1.400000,-1.400000 -5.600000,-5.600000z"
- android:fillColor="#FFFFFFFF"
- android:strokeColor="#FF000000"/>
-</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/floating_dismiss_gradient.xml b/packages/SystemUI/res/drawable/floating_dismiss_gradient.xml
new file mode 100644
index 0000000..8f7fb10
--- /dev/null
+++ b/packages/SystemUI/res/drawable/floating_dismiss_gradient.xml
@@ -0,0 +1,24 @@
+<?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.
+-->
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <gradient
+ android:angle="270"
+ android:startColor="#00000000"
+ android:endColor="#77000000"
+ android:type="linear" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/floating_dismiss_gradient_transition.xml b/packages/SystemUI/res/drawable/floating_dismiss_gradient_transition.xml
new file mode 100644
index 0000000..6a0695e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/floating_dismiss_gradient_transition.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+<transition xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@color/transparent" />
+ <item android:drawable="@drawable/floating_dismiss_gradient" />
+</transition>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/partial_conversation_info.xml b/packages/SystemUI/res/layout/partial_conversation_info.xml
index b348222..803b0c6 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
- 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>
<TextView
- android:id="@+id/pkg_name"
+ android:id="@+id/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/parent_channel_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:textDirection="locale"
+ 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 c27b3a9..bf2eac3 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" />
+ android:gravity="center_horizontal"
+ android:layout_marginTop="16dp"
+ android:text="@string/priority_onboarding_title"
+ style="@style/TextAppearance.NotificationImportanceChannel"
+ />
- <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"
- />
+ <View
+ android:id="@+id/divider"
+ android:layout_width="match_parent"
+ android:layout_height="0.5dp"
+ android:layout_marginTop="20dp"
+ android:layout_marginBottom="20dp"
+ android:background="@color/material_grey_300" />
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/show_avatar_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/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" />
+ android:gravity="start"
+ android:text="@string/priority_onboarding_behavior"
+ style="@style/TextAppearance.NotificationImportanceChannelGroup"
+ />
- <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"
- />
-
- </LinearLayout>
-
- <!-- These rows show optionally -->
-
- <LinearLayout
- android:id="@+id/floating_bubble_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/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" />
-
- <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"
- 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/quick_settings_footer.xml b/packages/SystemUI/res/layout/quick_settings_footer.xml
index 846c538..15f398a 100644
--- a/packages/SystemUI/res/layout/quick_settings_footer.xml
+++ b/packages/SystemUI/res/layout/quick_settings_footer.xml
@@ -23,7 +23,7 @@
android:paddingStart="@dimen/qs_footer_padding_start"
android:paddingEnd="@dimen/qs_footer_padding_end"
android:gravity="center_vertical"
- android:background="?android:attr/colorPrimary" >
+ android:background="@android:color/transparent">
<TextView
android:id="@+id/footer_text"
@@ -32,7 +32,7 @@
android:gravity="start"
android:layout_weight="1"
android:textAppearance="@style/TextAppearance.QS.TileLabel"
- android:textColor="?android:attr/textColorSecondary"/>
+ style="@style/qs_security_footer"/>
<ImageView
android:id="@+id/footer_icon"
@@ -40,6 +40,6 @@
android:layout_height="@dimen/qs_footer_icon_size"
android:contentDescription="@null"
android:src="@drawable/ic_info_outline"
- android:tint="?android:attr/textColorSecondary"/>
+ style="@style/qs_security_footer"/>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml b/packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml
index af6f9bb..0c4d5a2 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-night/styles.xml b/packages/SystemUI/res/values-night/styles.xml
index 4fdeb6f..50261e1 100644
--- a/packages/SystemUI/res/values-night/styles.xml
+++ b/packages/SystemUI/res/values-night/styles.xml
@@ -29,4 +29,9 @@
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
+ <style name="qs_security_footer" parent="@style/qs_theme">
+ <item name="android:textColor">#B3FFFFFF</item> <!-- 70% white -->
+ <item name="android:tint">#FFFFFFFF</item>
+ </style>
+
</resources>
diff --git a/packages/SystemUI/res/values-sw320dp/dimens.xml b/packages/SystemUI/res/values-sw320dp/dimens.xml
deleted file mode 100644
index c110113..0000000
--- 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 e26f31c..73d8e9a 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>
@@ -973,7 +974,9 @@
<dimen name="recents_quick_scrub_onboarding_margin_start">8dp</dimen>
<!-- The height of the gradient indicating the dismiss edge when moving a PIP. -->
- <dimen name="floating_dismiss_gradient_height">176dp</dimen>
+ <dimen name="floating_dismiss_gradient_height">250dp</dimen>
+
+ <dimen name="floating_dismiss_bottom_margin">50dp</dimen>
<!-- The bottom margin of the PIP drag to dismiss info text shown when moving a PIP. -->
<dimen name="pip_dismiss_text_bottom_margin">24dp</dimen>
@@ -1031,8 +1034,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 97a0d03..39237ac 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/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index ed36bdb..39f78bf 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -387,6 +387,11 @@
<item name="android:homeAsUpIndicator">@drawable/ic_arrow_back</item>
</style>
+ <style name="qs_security_footer" parent="@style/qs_theme">
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:tint">?android:attr/textColorSecondary</item>
+ </style>
+
<style name="systemui_theme_remote_input" parent="@android:style/Theme.DeviceDefault.Light">
<item name="android:colorAccent">@color/remote_input_accent</item>
</style>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index 35ad422..655008b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -23,10 +23,11 @@
import android.view.MotionEvent;
import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
+import com.android.systemui.shared.recents.model.Task;
/**
* Temporary callbacks into SystemUI.
- * Next id = 26
+ * Next id = 27
*/
interface ISystemUiProxy {
@@ -122,6 +123,9 @@
/**
* Handle the provided image as if it was a screenshot.
+ *
+ * Deprecated, use handleImageBundleAsScreenshot with image bundle and UserTask
+ * @deprecated
*/
void handleImageAsScreenshot(in Bitmap screenImage, in Rect locationInScreen,
in Insets visibleInsets, int taskId) = 21;
@@ -146,4 +150,10 @@
* @param rotation indicates which Surface.Rotation the gesture was started in
*/
void onQuickSwitchToNewTask(int rotation) = 25;
+
+ /**
+ * Handle the provided image as if it was a screenshot.
+ */
+ void handleImageBundleAsScreenshot(in Bundle screenImageBundle, in Rect locationInScreen,
+ in Insets visibleInsets, in Task.TaskKey task) = 26;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.aidl
new file mode 100644
index 0000000..e7cad2a
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.model;
+
+parcelable Task.TaskKey;
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
index dcb134e..186379a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
@@ -26,6 +26,8 @@
import android.content.pm.ActivityInfo;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.view.ViewDebug;
import com.android.systemui.shared.recents.utilities.Utilities;
@@ -52,8 +54,10 @@
void onTaskWindowingModeChanged();
}
- /* The Task Key represents the unique primary key for the task */
- public static class TaskKey {
+ /**
+ * The Task Key represents the unique primary key for the task
+ */
+ public static class TaskKey implements Parcelable {
@ViewDebug.ExportedProperty(category="recents")
public final int id;
@ViewDebug.ExportedProperty(category="recents")
@@ -157,6 +161,48 @@
private void updateHashCode() {
mHashCode = Objects.hash(id, windowingMode, userId);
}
+
+ public static final Parcelable.Creator<TaskKey> CREATOR =
+ new Parcelable.Creator<TaskKey>() {
+ @Override
+ public TaskKey createFromParcel(Parcel source) {
+ return TaskKey.readFromParcel(source);
+ }
+
+ @Override
+ public TaskKey[] newArray(int size) {
+ return new TaskKey[size];
+ }
+ };
+
+ @Override
+ public final void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(id);
+ parcel.writeInt(windowingMode);
+ parcel.writeTypedObject(baseIntent, flags);
+ parcel.writeInt(userId);
+ parcel.writeLong(lastActiveTime);
+ parcel.writeInt(displayId);
+ parcel.writeTypedObject(sourceComponent, flags);
+ }
+
+ private static TaskKey readFromParcel(Parcel parcel) {
+ int id = parcel.readInt();
+ int windowingMode = parcel.readInt();
+ Intent baseIntent = parcel.readTypedObject(Intent.CREATOR);
+ int userId = parcel.readInt();
+ long lastActiveTime = parcel.readLong();
+ int displayId = parcel.readInt();
+ ComponentName sourceComponent = parcel.readTypedObject(ComponentName.CREATOR);
+
+ return new TaskKey(id, windowingMode, baseIntent, sourceComponent, userId,
+ lastActiveTime, displayId);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
}
@ViewDebug.ExportedProperty(deepExport=true, prefix="key_")
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 0000000..b79fcbd
--- /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/TaskStackChangeListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
index dd5cc7c..796aaee 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
@@ -114,4 +114,7 @@
/** @see ITaskStackListener#onRecentTaskListFrozenChanged(boolean) */
public void onRecentTaskListFrozenChanged(boolean frozen) { }
+
+ /** @see ITaskStackListener#onActivityRotation()*/
+ public void onActivityRotation() { }
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
index a76a901..13f7993 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
@@ -237,6 +237,11 @@
mHandler.obtainMessage(H.ON_TASK_DESCRIPTION_CHANGED, taskInfo).sendToTarget();
}
+ @Override
+ public void onActivityRotation() {
+ mHandler.obtainMessage(H.ON_ACTIVITY_ROTATION).sendToTarget();
+ }
+
private final class H extends Handler {
private static final int ON_TASK_STACK_CHANGED = 1;
private static final int ON_TASK_SNAPSHOT_CHANGED = 2;
@@ -260,6 +265,7 @@
private static final int ON_SINGLE_TASK_DISPLAY_EMPTY = 22;
private static final int ON_TASK_LIST_FROZEN_UNFROZEN = 23;
private static final int ON_TASK_DESCRIPTION_CHANGED = 24;
+ private static final int ON_ACTIVITY_ROTATION = 25;
public H(Looper looper) {
@@ -427,6 +433,12 @@
}
break;
}
+ case ON_ACTIVITY_ROTATION: {
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onActivityRotation();
+ }
+ break;
+ }
}
}
if (msg.obj instanceof SomeArgs) {
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 dd61326..73783ae 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 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 87990cd..ccb506d 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -124,7 +124,7 @@
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/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index dfa71ba..8a80c4d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -47,6 +47,7 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
+import android.graphics.drawable.TransitionDrawable;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
@@ -133,6 +134,9 @@
/** Percent to darken the bubbles when they're in the dismiss target. */
private static final float DARKEN_PERCENT = 0.3f;
+ /** Duration of the dismiss scrim fading in/out. */
+ private static final int DISMISS_TRANSITION_DURATION_MS = 200;
+
/** How long to wait, in milliseconds, before hiding the flyout. */
@VisibleForTesting
static final int FLYOUT_HIDE_AFTER = 5000;
@@ -752,7 +756,7 @@
final View targetView = new DismissCircleView(context);
final FrameLayout.LayoutParams newParams =
new FrameLayout.LayoutParams(targetSize, targetSize);
- newParams.gravity = Gravity.CENTER;
+ newParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
targetView.setLayoutParams(newParams);
mDismissTargetAnimator = PhysicsAnimator.getInstance(targetView);
@@ -761,9 +765,16 @@
MATCH_PARENT,
getResources().getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height),
Gravity.BOTTOM));
+
+ final int bottomMargin =
+ getResources().getDimensionPixelSize(R.dimen.floating_dismiss_bottom_margin);
+ mDismissTargetContainer.setPadding(0, 0, 0, bottomMargin);
+ mDismissTargetContainer.setClipToPadding(false);
mDismissTargetContainer.setClipChildren(false);
mDismissTargetContainer.addView(targetView);
mDismissTargetContainer.setVisibility(View.INVISIBLE);
+ mDismissTargetContainer.setBackgroundResource(
+ R.drawable.floating_dismiss_gradient_transition);
addView(mDismissTargetContainer);
// Start translated down so the target springs up.
@@ -1884,6 +1895,9 @@
mDismissTargetContainer.setZ(Short.MAX_VALUE - 1);
mDismissTargetContainer.setVisibility(VISIBLE);
+ ((TransitionDrawable) mDismissTargetContainer.getBackground()).startTransition(
+ DISMISS_TRANSITION_DURATION_MS);
+
mDismissTargetAnimator.cancel();
mDismissTargetAnimator
.spring(DynamicAnimation.TRANSLATION_Y, 0f, mDismissTargetSpring)
@@ -1901,6 +1915,9 @@
mShowingDismiss = false;
+ ((TransitionDrawable) mDismissTargetContainer.getBackground()).reverseTransition(
+ DISMISS_TRANSITION_DURATION_MS);
+
mDismissTargetAnimator
.spring(DynamicAnimation.TRANSLATION_Y, mDismissTargetContainer.getHeight(),
mDismissTargetSpring)
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 fd9fda3..93f0c7f 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.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 @@
}
private var userChanging: Boolean = true
+ private var userStructure: UserStructure
private var seedingInProgress = false
private val seedingCallbacks = mutableListOf<Consumer<Boolean>>()
@@ -97,7 +99,7 @@
internal var auxiliaryPersistenceWrapper: AuxiliaryPersistenceWrapper
init {
- val userStructure = UserStructure(context, currentUser)
+ userStructure = UserStructure(context, currentUser)
persistenceWrapper = optionalWrapper.orElseGet {
ControlsFavoritePersistenceWrapper(
@@ -116,7 +118,7 @@
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 @@
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 35ebac5..d31b6eb 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 @@
SelectionItem(it.loadLabel(), "", it.loadIcon(), it.componentName)
}
uiExecutor.execute {
- onResult(lastItems)
+ parent.removeAllViews()
+ if (lastItems.size > 0) {
+ onResult(lastItems)
+ }
}
}
}
@@ -189,8 +192,6 @@
}
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 @@
}
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 @@
}
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 f970152..9ec1452 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 @@
}
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 0ec4cc5..4003f41 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 @@
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 95a9006..951dc99 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 @@
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 1b13d4a..7e009b4 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 @@
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 @@
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 b272b60..baa2dfd 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -64,13 +64,16 @@
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;
+import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.stackdivider.Divider;
@@ -83,8 +86,6 @@
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 +102,9 @@
* 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 +125,6 @@
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;
@@ -383,8 +384,7 @@
@Override
public void handleImageAsScreenshot(Bitmap screenImage, Rect locationInScreen,
Insets visibleInsets, int taskId) {
- mScreenshotHelper.provideScreenshot(screenImage, locationInScreen, visibleInsets,
- taskId, SCREENSHOT_OVERVIEW, mHandler, null);
+ // Deprecated
}
@Override
@@ -434,6 +434,21 @@
}
}
+ @Override
+ public void handleImageBundleAsScreenshot(Bundle screenImageBundle, Rect locationInScreen,
+ Insets visibleInsets, Task.TaskKey task) {
+ mScreenshotHelper.provideScreenshot(
+ screenImageBundle,
+ locationInScreen,
+ visibleInsets,
+ task.id,
+ task.userId,
+ task.sourceComponent,
+ SCREENSHOT_OVERVIEW,
+ mHandler,
+ null);
+ }
+
private boolean verifyCaller(String reason) {
final int callerId = Binder.getCallingUserHandle().getIdentifier();
if (callerId != mCurrentBoundedUserId) {
@@ -480,7 +495,7 @@
return;
}
- mCurrentBoundedUserId = mDeviceProvisionedController.getCurrentUser();
+ mCurrentBoundedUserId = getCurrentUserId();
mOverviewProxy = IOverviewProxy.Stub.asInterface(service);
Bundle params = new Bundle();
@@ -523,22 +538,6 @@
}
};
- 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 +547,18 @@
@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 +579,7 @@
// 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 +603,12 @@
});
}
+ @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 +714,8 @@
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 +725,7 @@
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 +884,6 @@
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 960c501..3e268f6 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 @@
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 752f4fd..edbc3cf 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 @@
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 @@
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 @@
});
}
+ 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 c967648..8551c88 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 @@
*/
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 @@
// 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 @@
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 abd7e71..d057a8a 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 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 @@
});
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 2e0e746..3e78489 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 @@
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/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index a9d3772..9b1734d 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -35,6 +35,7 @@
import android.app.Notification;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
@@ -494,8 +495,10 @@
}
void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds,
- Insets visibleInsets, int taskId, Consumer<Uri> finisher, Runnable onComplete) {
- // TODO use taskId and visibleInsets
+ Insets visibleInsets, int taskId, int userId, ComponentName topComponent,
+ Consumer<Uri> finisher, Runnable onComplete) {
+ // TODO: use task Id, userId, topComponent for smart handler
+ // TODO: use visibleInsets for animation
mOnCompleteRunnable = onComplete;
takeScreenshot(screenshot, finisher, screenshotScreenBounds);
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java
index 095c32f..0017b1f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java
@@ -24,6 +24,7 @@
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.Nullable;
+import android.content.ComponentName;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -234,8 +235,10 @@
}
void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds,
- Insets visibleInsets, int taskId, Consumer<Uri> finisher) {
- // TODO use taskId and visibleInsets
+ Insets visibleInsets, int taskId, int userId, ComponentName topComponent,
+ Consumer<Uri> finisher) {
+ // TODO: use task Id, userId, topComponent for smart handler
+ // TODO: use visibleInsets for animation
takeScreenshot(screenshot, finisher, false, false, screenshotScreenBounds);
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 98030d4..8322fe0 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -20,6 +20,7 @@
import static com.android.internal.util.ScreenshotHelper.SCREENSHOT_MSG_URI;
import android.app.Service;
+import android.content.ComponentName;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Insets;
@@ -37,6 +38,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.util.ScreenshotHelper;
+import com.android.systemui.shared.recents.utilities.BitmapUtil;
import java.util.function.Consumer;
@@ -107,16 +109,19 @@
}
break;
case WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE:
- Bitmap screenshot = screenshotRequest.getBitmap();
+ Bitmap screenshot = BitmapUtil.bundleToHardwareBitmap(
+ screenshotRequest.getBitmapBundle());
Rect screenBounds = screenshotRequest.getBoundsInScreen();
Insets insets = screenshotRequest.getInsets();
int taskId = screenshotRequest.getTaskId();
+ int userId = screenshotRequest.getUserId();
+ ComponentName topComponent = screenshotRequest.getTopComponent();
if (useCornerFlow) {
- mScreenshot.handleImageAsScreenshot(
- screenshot, screenBounds, insets, taskId, uriConsumer, onComplete);
+ mScreenshot.handleImageAsScreenshot(screenshot, screenBounds, insets,
+ taskId, userId, topComponent, uriConsumer, onComplete);
} else {
- mScreenshotLegacy.handleImageAsScreenshot(
- screenshot, screenBounds, insets, taskId, uriConsumer);
+ mScreenshotLegacy.handleImageAsScreenshot(screenshot, screenBounds, insets,
+ taskId, userId, topComponent, uriConsumer);
}
break;
default:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 9925909..8a4fdc2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -557,9 +557,9 @@
private void focusExpandButtonIfNecessary() {
if (mFocusOnVisibilityChange) {
- NotificationHeaderView header = getVisibleNotificationHeader();
- if (header != null) {
- ImageView expandButton = header.getExpandButton();
+ NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
+ if (wrapper != null) {
+ View expandButton = wrapper.getExpandButton();
if (expandButton != null) {
expandButton.requestAccessibilityFocus();
}
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 daa4ffe..e9d8958 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.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 @@
private OnSnoozeClickListener mOnSnoozeClickListener;
private OnSettingsClickListener mOnSettingsClickListener;
private NotificationGuts mGutsContainer;
+ private OnConversationSettingsClickListener mOnConversationSettingsClickListener;
@VisibleForTesting
boolean mSkipPost = false;
@@ -164,6 +166,10 @@
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 @@
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 @@
}
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 @@
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 @@
mDelegatePkg = mSbn.getOpPkg();
mIsDeviceProvisioned = isDeviceProvisioned;
mOnSnoozeClickListener = onSnoozeClickListener;
+ mOnConversationSettingsClickListener = onConversationSettingsClickListener;
mIconFactory = conversationIconFactory;
mUserContext = userContext;
mBubbleMetadata = bubbleMetadata;
@@ -323,7 +327,6 @@
ImageView image = findViewById(R.id.conversation_icon);
image.setImageDrawable(mIconFactory.getConversationDrawable(
mShortcutInfo, mPackageName, mAppUid, important));
-
}
private void bindPackage() {
@@ -521,9 +524,9 @@
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 @@
.setView(onboardingView)
.setIgnoresDnd(ignoreDnd)
.setShowsAsBubble(showAsBubble)
+ .setIcon(((ImageView) findViewById(R.id.conversation_icon)).getDrawable())
+ .setOnSettingsClick(mOnConversationSettingsClickListener)
.build();
controller.init();
@@ -613,8 +618,7 @@
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 a64dcdf..1074adc3 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 @@
}
}
+ 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 @@
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 @@
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 84bc181..f1fe54a 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 @@
} 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 d1b4052..88c3258 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.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 @@
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 @@
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 @@
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 @@
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 @@
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/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
index 15499b8..fe70c81 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
@@ -50,6 +50,7 @@
private lateinit var conversationBadgeBg: View
private lateinit var expandButton: View
private lateinit var expandButtonContainer: View
+ private lateinit var expandButtonInnerContainer: View
private lateinit var imageMessageContainer: ViewGroup
private lateinit var messagingLinearLayout: MessagingLinearLayout
private lateinit var conversationTitleView: View
@@ -69,6 +70,8 @@
expandButton = requireViewById(com.android.internal.R.id.expand_button)
expandButtonContainer =
requireViewById(com.android.internal.R.id.expand_button_container)
+ expandButtonInnerContainer =
+ requireViewById(com.android.internal.R.id.expand_button_inner_container)
importanceRing = requireViewById(com.android.internal.R.id.conversation_icon_badge_ring)
appName = requireViewById(com.android.internal.R.id.app_name_text)
conversationTitleView = requireViewById(com.android.internal.R.id.conversation_text)
@@ -134,6 +137,8 @@
)
}
+ override fun getExpandButton() = expandButtonInnerContainer
+
override fun setShelfIconVisible(visible: Boolean) {
if (conversationLayout.isImportantConversation) {
if (conversationIconView.visibility != GONE) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index f8b7831..4c9cb20 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -317,6 +317,11 @@
}
@Override
+ public View getExpandButton() {
+ return mExpandButton;
+ }
+
+ @Override
public int getOriginalIconColor() {
return mIcon.getOriginalIconColor();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 02e537d..30080e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -240,6 +240,13 @@
return null;
}
+ /**
+ * @return the expand button if it exists
+ */
+ public @Nullable View getExpandButton() {
+ return null;
+ }
+
public int getOriginalIconColor() {
return Notification.COLOR_INVALID;
}
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 46c873d..4337e20 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.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 @@
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 2978772..6b37ac3 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 @@
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 f58cce5..76c51d6 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 @@
.setInterpolator(Interpolators.LINEAR)
.setDuration(AOD_ICONS_APPEAR_DURATION)
.start();
+ } else {
+ mAodIcons.setAlpha(1.0f);
+ mAodIcons.setTranslationY(0);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index b4de3cd..18a7add 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -640,8 +640,7 @@
+ " dataState=" + state.getDataRegistrationState());
}
mServiceState = state;
- // onDisplayInfoChanged is invoked directly after onServiceStateChanged, so not calling
- // updateTelephony() to prevent icon flickering in case of overrides.
+ updateTelephony();
}
@Override
@@ -651,12 +650,6 @@
+ " type=" + networkType);
}
mDataState = state;
- if (networkType != mTelephonyDisplayInfo.getNetworkType()) {
- Log.d(mTag, "onDataConnectionStateChanged:"
- + " network type change and reset displayInfo. type=" + networkType);
- mTelephonyDisplayInfo = new TelephonyDisplayInfo(networkType,
- TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE);
- }
updateTelephony();
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/DismissCircleView.java b/packages/SystemUI/src/com/android/systemui/util/DismissCircleView.java
index 6c3538c..a31ea7c 100644
--- a/packages/SystemUI/src/com/android/systemui/util/DismissCircleView.java
+++ b/packages/SystemUI/src/com/android/systemui/util/DismissCircleView.java
@@ -40,7 +40,7 @@
setBackground(res.getDrawable(R.drawable.dismiss_circle_background));
- mIconView.setImageDrawable(res.getDrawable(R.drawable.dismiss_target_x));
+ mIconView.setImageDrawable(res.getDrawable(R.drawable.ic_close_white));
addView(mIconView);
setViewSizes();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
index b018b59..ed4f8b3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.notification.row;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
@@ -30,11 +29,13 @@
import android.util.ArraySet;
import android.view.NotificationHeaderView;
import android.view.View;
+import android.view.ViewPropertyAnimator;
import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.widget.NotificationExpandButton;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
@@ -98,4 +99,42 @@
verify(mockExpanded, times(1)).setVisibility(View.VISIBLE);
verify(mockHeadsUp, times(1)).setVisibility(View.VISIBLE);
}
+
+ @Test
+ @UiThreadTest
+ public void testExpandButtonFocusIsCalled() {
+ View mockContractedEB = mock(NotificationExpandButton.class);
+ View mockContracted = mock(NotificationHeaderView.class);
+ when(mockContracted.animate()).thenReturn(mock(ViewPropertyAnimator.class));
+ when(mockContracted.findViewById(com.android.internal.R.id.expand_button)).thenReturn(
+ mockContractedEB);
+
+ View mockExpandedEB = mock(NotificationExpandButton.class);
+ View mockExpanded = mock(NotificationHeaderView.class);
+ when(mockExpanded.animate()).thenReturn(mock(ViewPropertyAnimator.class));
+ when(mockExpanded.findViewById(com.android.internal.R.id.expand_button)).thenReturn(
+ mockExpandedEB);
+
+ View mockHeadsUpEB = mock(NotificationExpandButton.class);
+ View mockHeadsUp = mock(NotificationHeaderView.class);
+ when(mockHeadsUp.animate()).thenReturn(mock(ViewPropertyAnimator.class));
+ when(mockHeadsUp.findViewById(com.android.internal.R.id.expand_button)).thenReturn(
+ mockHeadsUpEB);
+
+ // Set up all 3 child forms
+ mView.setContractedChild(mockContracted);
+ mView.setExpandedChild(mockExpanded);
+ mView.setHeadsUpChild(mockHeadsUp);
+
+ // This is required to call requestAccessibilityFocus()
+ mView.setFocusOnVisibilityChange();
+
+ // The following will initialize the view and switch from not visible to expanded.
+ // (heads-up is actually an alternate form of contracted, hence this enters expanded state)
+ mView.setHeadsUp(true);
+
+ verify(mockContractedEB, times(0)).requestAccessibilityFocus();
+ verify(mockExpandedEB, times(1)).requestAccessibilityFocus();
+ verify(mockHeadsUpEB, times(0)).requestAccessibilityFocus();
+ }
}
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 b1f67ce..4122cf5 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 @@
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
final ImageView view = mNotificationInfo.findViewById(R.id.conversation_icon);
assertEquals(mIconDrawable, view.getDrawable());
}
@@ -280,7 +280,7 @@
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 @@
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 @@
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 @@
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
assertEquals(GONE, nameView.getVisibility());
}
@@ -415,7 +415,7 @@
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 @@
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
settingsButton.performClick();
@@ -469,7 +469,7 @@
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
}
@@ -496,7 +496,7 @@
mBuilderProvider,
false,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
}
@@ -521,7 +521,7 @@
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
View view = mNotificationInfo.findViewById(R.id.silence);
assertThat(view.isSelected()).isTrue();
}
@@ -549,7 +549,7 @@
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 @@
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 @@
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
View fave = mNotificationInfo.findViewById(R.id.priority);
fave.performClick();
@@ -654,7 +654,7 @@
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
mNotificationInfo.findViewById(R.id.default_behavior).performClick();
mTestableLooper.processAllMessages();
@@ -697,7 +697,7 @@
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
View silence = mNotificationInfo.findViewById(R.id.silence);
@@ -741,7 +741,7 @@
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
View fave = mNotificationInfo.findViewById(R.id.priority);
fave.performClick();
@@ -778,7 +778,7 @@
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
View fave = mNotificationInfo.findViewById(R.id.priority);
fave.performClick();
@@ -793,6 +793,45 @@
}
@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 @@
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
mNotificationInfo.findViewById(R.id.default_behavior).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -849,7 +888,7 @@
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
mNotificationInfo.findViewById(R.id.default_behavior).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -885,7 +924,7 @@
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
mNotificationInfo.findViewById(R.id.default_behavior).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -920,7 +959,7 @@
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
View silence = mNotificationInfo.findViewById(R.id.silence);
silence.performClick();
@@ -954,7 +993,7 @@
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
verify(mMockINotificationManager, times(1)).createConversationNotificationChannelForPackage(
anyString(), anyInt(), anyString(), any(), eq(CONVERSATION_ID));
@@ -979,7 +1018,7 @@
mBuilderProvider,
true,
mTestHandler,
- mTestHandler);
+ mTestHandler, null);
verify(mMockINotificationManager, never()).createConversationNotificationChannelForPackage(
anyString(), anyInt(), anyString(), any(), eq(CONVERSATION_ID));
@@ -1014,10 +1053,14 @@
() -> 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 @@
() -> 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 e56ef5b..f327967 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 @@
}
@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 be43e19..177e845 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.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 @@
@Mock
NotificationMediaManager mNotificationMediaManager;
@Mock
+ NotificationIconContainer mNotificationIconContainer;
+ @Mock
DozeParameters mDozeParameters;
@Mock
NotificationShadeWindowView mNotificationShadeWindowView;
@@ -67,7 +70,7 @@
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 @@
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/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java
index f08429b..3fd9ee9 100644
--- a/packages/Tethering/src/android/net/ip/IpServer.java
+++ b/packages/Tethering/src/android/net/ip/IpServer.java
@@ -730,12 +730,7 @@
final String upstreamIface = v6only.getInterfaceName();
params = new RaParams();
- // When BPF offload is enabled, we advertise an mtu lower by 16, which is the closest
- // multiple of 8 >= 14, the ethernet header size. This makes kernel ebpf tethering
- // offload happy. This hack should be reverted once we have the kernel fixed up.
- // Note: this will automatically clamp to at least 1280 (ipv6 minimum mtu)
- // see RouterAdvertisementDaemon.java putMtu()
- params.mtu = mUsingBpfOffload ? v6only.getMtu() - 16 : v6only.getMtu();
+ params.mtu = v6only.getMtu();
params.hasDefaultRoute = v6only.hasIpv6DefaultRoute();
if (params.hasDefaultRoute) params.hopLimit = getHopLimit(upstreamIface, ttlAdjustment);
diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java
index e2330ca6..0ec8654 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java
@@ -75,9 +75,7 @@
@NonNull Consumer<InlineSuggestionsRequest> requestConsumer, @NonNull Bundle uiExtras) {
// TODO(b/151123764): rename the method to better reflect what it does.
if (mSession != null) {
- // Send an empty response to IME and destroy the existing session.
- mSession.onInlineSuggestionsResponseLocked(
- InlineFillUi.emptyUi(mSession.getAutofillIdLocked()));
+ // Destroy the existing session.
mSession.destroySessionLocked();
mInlineFillUi = null;
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 1634f6e..0ab5718 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1374,10 +1374,9 @@
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 efd3c3e..27d9ba0 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 @@
/** @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 @@
+ "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 @@
+ " 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 @@
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 @@
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 @@
@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 @@
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/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 9de95ab..b9669c74 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -40,6 +40,7 @@
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiHotplugEvent;
import android.hardware.hdmi.HdmiPortInfo;
+import android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.hdmi.IHdmiControlService;
import android.hardware.hdmi.IHdmiControlStatusChangeListener;
@@ -63,6 +64,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.PowerManager;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
@@ -268,6 +270,11 @@
private final ArrayList<HdmiControlStatusChangeListenerRecord>
mHdmiControlStatusChangeListenerRecords = new ArrayList<>();
+ // List of records for HDMI control volume control status change listener for death monitoring.
+ @GuardedBy("mLock")
+ private final RemoteCallbackList<IHdmiCecVolumeControlFeatureListener>
+ mHdmiCecVolumeControlFeatureListenerRecords = new RemoteCallbackList<>();
+
// List of records for hotplug event listener to handle the the caller killed in action.
@GuardedBy("mLock")
private final ArrayList<HotplugEventListenerRecord> mHotplugEventListenerRecords =
@@ -1814,6 +1821,21 @@
}
@Override
+ public void addHdmiCecVolumeControlFeatureListener(
+ final IHdmiCecVolumeControlFeatureListener listener) {
+ enforceAccessPermission();
+ HdmiControlService.this.addHdmiCecVolumeControlFeatureListener(listener);
+ }
+
+ @Override
+ public void removeHdmiCecVolumeControlFeatureListener(
+ final IHdmiCecVolumeControlFeatureListener listener) {
+ enforceAccessPermission();
+ HdmiControlService.this.removeHdmiControlVolumeControlStatusChangeListener(listener);
+ }
+
+
+ @Override
public void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
enforceAccessPermission();
HdmiControlService.this.addHotplugEventListener(listener);
@@ -2409,6 +2431,33 @@
}
}
+ @VisibleForTesting
+ void addHdmiCecVolumeControlFeatureListener(
+ final IHdmiCecVolumeControlFeatureListener listener) {
+ mHdmiCecVolumeControlFeatureListenerRecords.register(listener);
+
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ // Return the current status of mHdmiCecVolumeControlEnabled;
+ synchronized (mLock) {
+ try {
+ listener.onHdmiCecVolumeControlFeature(mHdmiCecVolumeControlEnabled);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to report HdmiControlVolumeControlStatusChange: "
+ + mHdmiCecVolumeControlEnabled, e);
+ }
+ }
+ }
+ });
+ }
+
+ @VisibleForTesting
+ void removeHdmiControlVolumeControlStatusChangeListener(
+ final IHdmiCecVolumeControlFeatureListener listener) {
+ mHdmiCecVolumeControlFeatureListenerRecords.unregister(listener);
+ }
+
private void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
final HotplugEventListenerRecord record = new HotplugEventListenerRecord(listener);
try {
@@ -2682,6 +2731,19 @@
}
}
+ private void announceHdmiCecVolumeControlFeatureChange(boolean isEnabled) {
+ assertRunOnServiceThread();
+ mHdmiCecVolumeControlFeatureListenerRecords.broadcast(listener -> {
+ try {
+ listener.onHdmiCecVolumeControlFeature(isEnabled);
+ } catch (RemoteException e) {
+ Slog.e(TAG,
+ "Failed to report HdmiControlVolumeControlStatusChange: "
+ + isEnabled);
+ }
+ });
+ }
+
public HdmiCecLocalDeviceTv tv() {
return (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiDeviceInfo.DEVICE_TV);
}
@@ -3026,6 +3088,7 @@
isHdmiCecVolumeControlEnabled);
}
}
+ announceHdmiCecVolumeControlFeatureChange(isHdmiCecVolumeControlEnabled);
}
boolean isHdmiCecVolumeControlEnabled() {
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index ccbe96f..067bdcb 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 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.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.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 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 @@
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 @@
@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 @@
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 @@
// 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 @@
}
}
- 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 @@
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 @@
* 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 @@
* 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 a3dcc40..53bff8e 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 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 @@
private final CopyOnWriteArrayList<UserListener> mListeners;
@GuardedBy("this")
+ @Nullable private ActivityManagerInternal mActivityManagerInternal;
+ @GuardedBy("this")
@Nullable private UserManager mUserManager;
- @UserIdInt private volatile int mCurrentUserId;
-
- @GuardedBy("this")
- @UserIdInt private int mCachedParentUserId;
- @GuardedBy("this")
- private int[] mCachedProfileUserIds;
-
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;
- }
-
- if (D) {
- Log.d(TAG, "current user switched from u" + mCurrentUserId + " to u" + newUserId);
- }
-
- int oldUserId = mCurrentUserId;
- mCurrentUserId = newUserId;
-
- onUserChanged(oldUserId, UserListener.USER_SWITCHED);
- onUserChanged(newUserId, UserListener.USER_SWITCHED);
- }
-
- private void onUserStarted(@UserIdInt int userId) {
+ protected void dispatchOnUserStarted(@UserIdInt int userId) {
if (D) {
Log.d(TAG, "u" + userId + " started");
}
- onUserChanged(userId, UserListener.USER_STARTED);
+ for (UserListener listener : mListeners) {
+ listener.onUserChanged(userId, USER_STARTED);
+ }
}
- private void onUserStopped(@UserIdInt int userId) {
+ protected void dispatchOnUserStopped(@UserIdInt int userId) {
if (D) {
Log.d(TAG, "u" + userId + " stopped");
}
- onUserChanged(userId, UserListener.USER_STOPPED);
- }
-
- private void onUserChanged(@UserIdInt int userId, @UserListener.UserChange int change) {
for (UserListener listener : mListeners) {
- listener.onUserChanged(userId, change);
+ listener.onUserChanged(userId, USER_STOPPED);
}
}
- 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};
+ protected void dispatchOnCurrentUserChanged(@UserIdInt int fromUserId,
+ @UserIdInt int toUserId) {
+ int[] fromUserIds = getProfileIds(fromUserId);
+ int[] toUserIds = getProfileIds(toUserId);
+ if (D) {
+ Log.d(TAG, "current user changed from u" + Arrays.toString(fromUserIds) + " to u"
+ + Arrays.toString(toUserIds));
+ }
+
+ for (UserListener listener : mListeners) {
+ for (int userId : fromUserIds) {
+ listener.onUserChanged(userId, CURRENT_USER_CHANGED);
+ }
+ }
+
+ 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 @@
* user.
*/
public boolean isCurrentUserId(@UserIdInt int userId) {
- int currentUserId = mCurrentUserId;
- return userId == currentUserId || ArrayUtils.contains(
- getProfileUserIdsForParentUser(currentUserId), userId);
- }
-
- @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);
- }
-
- mCachedParentUserId = parentUserId;
- mCachedProfileUserIds = mUserManager.getProfileIdsWithDisabled(parentUserId);
- } finally {
- Binder.restoreCallingIdentity(identity);
+ synchronized (this) {
+ if (mActivityManagerInternal == null) {
+ return false;
}
}
- return mCachedProfileUserIds;
+ long identity = Binder.clearCallingIdentity();
+ try {
+ return mActivityManagerInternal.isCurrentProfile(userId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ private int[] getProfileIds(@UserIdInt int userId) {
+ synchronized (this) {
+ Preconditions.checkState(mUserManager != null);
+ }
+
+ 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 9d3385f..a95dc30 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.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 @@
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 81ee7d9..52fdc79 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.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 @@
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 236a681..f827721 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.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 @@
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 @@
+ "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 @@
}
}
+ 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 6bb10c7..766fae6 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.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 @@
*/
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 @@
* </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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
"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 @@
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 @@
"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 @@
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 @@
// 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 @@
}
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 0d4cdf9..5a6fd09 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 @@
"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 @@
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 @@
@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 79805e3..8ccf837 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.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 @@
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 @@
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 @@
}
// 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 @@
// 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 @@
+ "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 @@
}
session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
errorMsg);
- abortCheckpoint(errorMsg);
+ abortCheckpoint(session.sessionId, errorMsg);
return;
}
if (!apexSessionInfo.isActivated && !apexSessionInfo.isSuccess) {
@@ -647,7 +667,7 @@
+ "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 @@
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 @@
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 d5c9424..40fa798 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 @@
// 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 b0d4d95..d3f3ba1 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.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 @@
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 @@
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 57a25ed..4412162 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 @@
* 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 @@
/** 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 6c1ff72..ab459fd 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 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 @@
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 5f63233..c38d649 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 @@
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/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index df0fa9c..6e9428e 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -61,6 +61,7 @@
private static final int NOTIFY_TASK_LIST_FROZEN_UNFROZEN_MSG = 26;
private static final int NOTIFY_TASK_FOCUS_CHANGED_MSG = 27;
private static final int NOTIFY_TASK_REQUESTED_ORIENTATION_CHANGED_MSG = 28;
+ private static final int NOTIFY_ACTIVITY_ROTATED_MSG = 29;
// Delay in notifying task stack change listeners (in millis)
private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
@@ -183,6 +184,10 @@
l.onTaskRequestedOrientationChanged(m.arg1, m.arg2);
};
+ private final TaskStackConsumer mNotifyOnActivityRotation = (l, m) -> {
+ l.onActivityRotation();
+ };
+
@FunctionalInterface
public interface TaskStackConsumer {
void accept(ITaskStackListener t, Message m) throws RemoteException;
@@ -277,6 +282,9 @@
case NOTIFY_TASK_REQUESTED_ORIENTATION_CHANGED_MSG:
forAllRemoteListeners(mNotifyTaskRequestedOrientationChanged, msg);
break;
+ case NOTIFY_ACTIVITY_ROTATED_MSG:
+ forAllRemoteListeners(mNotifyOnActivityRotation, msg);
+ break;
}
if (msg.obj instanceof SomeArgs) {
((SomeArgs) msg.obj).recycle();
@@ -574,4 +582,11 @@
forAllLocalListeners(mNotifyTaskRequestedOrientationChanged, msg);
msg.sendToTarget();
}
+
+ /** @see android.app.ITaskStackListener#onActivityRotation() */
+ void notifyOnActivityRotation() {
+ final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_ROTATED_MSG);
+ forAllLocalListeners(mNotifyOnActivityRotation, msg);
+ msg.sendToTarget();
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index f34510e..8934e8f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3848,6 +3848,11 @@
final boolean rotationChanged = displayContent.updateRotationUnchecked();
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ if (rotationChanged) {
+ mAtmService.getTaskChangeNotificationController()
+ .notifyOnActivityRotation();
+ }
+
if (!rotationChanged || forceRelayout) {
displayContent.setLayoutNeeded();
layoutNeeded = true;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 0450890..36232e1 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 @@
// 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 71e79b3..56727e8 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.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 @@
@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);
+ 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 UserInfoHelper(mContext);
+ 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 @@
UserListener listener = mock(UserListener.class);
mHelper.addListener(listener);
- switchUser(USER1_ID);
- verify(listener, never()).onUserChanged(anyInt(), anyInt());
+ 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);
- 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(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 @@
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 @@
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 @@
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/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 7af7a23..c34b8e1 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -33,6 +33,7 @@
import android.content.ContextWrapper;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPortInfo;
+import android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener;
import android.os.IPowerManager;
import android.os.IThermalService;
import android.os.Looper;
@@ -261,4 +262,89 @@
mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
assertThat(mHdmiControlService.isHdmiCecVolumeControlEnabled()).isTrue();
}
+
+ @Test
+ public void addHdmiCecVolumeControlFeatureListener_emitsCurrentState_enabled() {
+ mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
+ VolumeControlFeatureCallback callback = new VolumeControlFeatureCallback();
+
+ mHdmiControlService.addHdmiCecVolumeControlFeatureListener(callback);
+ mTestLooper.dispatchAll();
+
+ assertThat(callback.mCallbackReceived).isTrue();
+ assertThat(callback.mVolumeControlEnabled).isTrue();
+ }
+
+ @Test
+ public void addHdmiCecVolumeControlFeatureListener_emitsCurrentState_disabled() {
+ mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
+ VolumeControlFeatureCallback callback = new VolumeControlFeatureCallback();
+
+ mHdmiControlService.addHdmiCecVolumeControlFeatureListener(callback);
+ mTestLooper.dispatchAll();
+
+ assertThat(callback.mCallbackReceived).isTrue();
+ assertThat(callback.mVolumeControlEnabled).isFalse();
+ }
+
+ @Test
+ public void addHdmiCecVolumeControlFeatureListener_notifiesStateUpdate() {
+ mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
+ VolumeControlFeatureCallback callback = new VolumeControlFeatureCallback();
+
+ mHdmiControlService.addHdmiCecVolumeControlFeatureListener(callback);
+
+ mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
+ mTestLooper.dispatchAll();
+
+ assertThat(callback.mCallbackReceived).isTrue();
+ assertThat(callback.mVolumeControlEnabled).isTrue();
+ }
+
+ @Test
+ public void addHdmiCecVolumeControlFeatureListener_honorsUnregistration() {
+ mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
+ VolumeControlFeatureCallback callback = new VolumeControlFeatureCallback();
+
+ mHdmiControlService.addHdmiCecVolumeControlFeatureListener(callback);
+ mTestLooper.dispatchAll();
+
+ mHdmiControlService.removeHdmiControlVolumeControlStatusChangeListener(callback);
+ mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
+ mTestLooper.dispatchAll();
+
+ assertThat(callback.mCallbackReceived).isTrue();
+ assertThat(callback.mVolumeControlEnabled).isFalse();
+ }
+
+ @Test
+ public void addHdmiCecVolumeControlFeatureListener_notifiesStateUpdate_multiple() {
+ mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
+ VolumeControlFeatureCallback callback1 = new VolumeControlFeatureCallback();
+ VolumeControlFeatureCallback callback2 = new VolumeControlFeatureCallback();
+
+ mHdmiControlService.addHdmiCecVolumeControlFeatureListener(callback1);
+ mHdmiControlService.addHdmiCecVolumeControlFeatureListener(callback2);
+
+
+ mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
+ mTestLooper.dispatchAll();
+
+ assertThat(callback1.mCallbackReceived).isTrue();
+ assertThat(callback2.mCallbackReceived).isTrue();
+ assertThat(callback1.mVolumeControlEnabled).isTrue();
+ assertThat(callback2.mVolumeControlEnabled).isTrue();
+ }
+
+ private static class VolumeControlFeatureCallback extends
+ IHdmiCecVolumeControlFeatureListener.Stub {
+ boolean mCallbackReceived = false;
+ boolean mVolumeControlEnabled = false;
+
+ @Override
+ public void onHdmiCecVolumeControlFeature(boolean enabled) throws RemoteException {
+ this.mCallbackReceived = true;
+ this.mVolumeControlEnabled = enabled;
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt
index 5412bb5..74b4d12 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt
@@ -18,8 +18,8 @@
import android.content.pm.PackageManager
import android.platform.test.annotations.Presubmit
+import androidx.test.filters.LargeTest
import com.google.common.truth.Expect
-import com.google.common.truth.Truth.assertWithMessage
import org.junit.Rule
import org.junit.Test
@@ -52,6 +52,7 @@
}
}
+ @LargeTest
@Test
fun packageInfoEquality() {
val flags = PackageManager.GET_ACTIVITIES or
@@ -65,7 +66,9 @@
PackageManager.GET_SERVICES or
PackageManager.GET_SHARED_LIBRARY_FILES or
PackageManager.GET_SIGNATURES or
- PackageManager.GET_SIGNING_CERTIFICATES
+ PackageManager.GET_SIGNING_CERTIFICATES or
+ PackageManager.MATCH_DIRECT_BOOT_UNAWARE or
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
val oldPackageInfo = oldPackages.asSequence().map { oldPackageInfo(it, flags) }
val newPackageInfo = newPackages.asSequence().map { newPackageInfo(it, flags) }
@@ -77,11 +80,79 @@
} else {
"$firstName | $secondName"
}
- expect.withMessage("${it.first?.applicationInfo?.sourceDir} $packageName")
- .that(it.first?.dumpToString())
- .isEqualTo(it.second?.dumpToString())
+
+ // Main components are asserted independently to separate the failures. Otherwise the
+ // comparison would include every component in one massive string.
+
+ val prefix = "${it.first?.applicationInfo?.sourceDir} $packageName"
+
+ expect.withMessage("$prefix PackageInfo")
+ .that(it.second?.dumpToString())
+ .isEqualTo(it.first?.dumpToString())
+
+ expect.withMessage("$prefix ApplicationInfo")
+ .that(it.second?.applicationInfo?.dumpToString())
+ .isEqualTo(it.first?.applicationInfo?.dumpToString())
+
+ val firstActivityNames = it.first?.activities?.map { it.name } ?: emptyList()
+ val secondActivityNames = it.second?.activities?.map { it.name } ?: emptyList()
+ expect.withMessage("$prefix activities")
+ .that(secondActivityNames)
+ .containsExactlyElementsIn(firstActivityNames)
+ .inOrder()
+
+ if (!it.first?.activities.isNullOrEmpty() && !it.second?.activities.isNullOrEmpty()) {
+ it.first?.activities?.zip(it.second?.activities!!)?.forEach {
+ expect.withMessage("$prefix ${it.first.name}")
+ .that(it.second.dumpToString())
+ .isEqualTo(it.first.dumpToString())
+ }
+ }
+
+ val firstReceiverNames = it.first?.receivers?.map { it.name } ?: emptyList()
+ val secondReceiverNames = it.second?.receivers?.map { it.name } ?: emptyList()
+ expect.withMessage("$prefix receivers")
+ .that(secondReceiverNames)
+ .containsExactlyElementsIn(firstReceiverNames)
+ .inOrder()
+
+ if (!it.first?.receivers.isNullOrEmpty() && !it.second?.receivers.isNullOrEmpty()) {
+ it.first?.receivers?.zip(it.second?.receivers!!)?.forEach {
+ expect.withMessage("$prefix ${it.first.name}")
+ .that(it.second.dumpToString())
+ .isEqualTo(it.first.dumpToString())
+ }
+ }
+
+ val firstProviderNames = it.first?.providers?.map { it.name } ?: emptyList()
+ val secondProviderNames = it.second?.providers?.map { it.name } ?: emptyList()
+ expect.withMessage("$prefix providers")
+ .that(secondProviderNames)
+ .containsExactlyElementsIn(firstProviderNames)
+ .inOrder()
+
+ if (!it.first?.providers.isNullOrEmpty() && !it.second?.providers.isNullOrEmpty()) {
+ it.first?.providers?.zip(it.second?.providers!!)?.forEach {
+ expect.withMessage("$prefix ${it.first.name}")
+ .that(it.second.dumpToString())
+ .isEqualTo(it.first.dumpToString())
+ }
+ }
+
+ val firstServiceNames = it.first?.services?.map { it.name } ?: emptyList()
+ val secondServiceNames = it.second?.services?.map { it.name } ?: emptyList()
+ expect.withMessage("$prefix services")
+ .that(secondServiceNames)
+ .containsExactlyElementsIn(firstServiceNames)
+ .inOrder()
+
+ if (!it.first?.services.isNullOrEmpty() && !it.second?.services.isNullOrEmpty()) {
+ it.first?.services?.zip(it.second?.services!!)?.forEach {
+ expect.withMessage("$prefix ${it.first.name}")
+ .that(it.second.dumpToString())
+ .isEqualTo(it.first.dumpToString())
+ }
+ }
}
}
}
-
-
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
index 0f028f0..420ff19 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.content.pm.ActivityInfo
import android.content.pm.ApplicationInfo
+import android.content.pm.ComponentInfo
import android.content.pm.ConfigurationInfo
import android.content.pm.FeatureInfo
import android.content.pm.InstrumentationInfo
@@ -27,6 +28,8 @@
import android.content.pm.PackageUserState
import android.content.pm.PermissionInfo
import android.content.pm.ProviderInfo
+import android.content.pm.ServiceInfo
+import android.os.Bundle
import android.os.Debug
import android.os.Environment
import android.util.SparseArray
@@ -38,8 +41,10 @@
import com.android.server.testutils.mockThrowOnUnmocked
import com.android.server.testutils.whenever
import org.junit.BeforeClass
-import org.mockito.Mockito
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.anyString
import org.mockito.Mockito.mock
import java.io.File
@@ -47,7 +52,7 @@
companion object {
- private const val VERIFY_ALL_APKS = false
+ private const val VERIFY_ALL_APKS = true
/** For auditing memory usage differences */
private const val DUMP_HPROF_TO_EXTERNAL = false
@@ -81,10 +86,14 @@
.filter { file -> file.name.endsWith(".apk") }
.toList()
}
+ .distinct()
private val dummyUserState = mock(PackageUserState::class.java).apply {
installed = true
- Mockito.`when`(isAvailable(anyInt())).thenReturn(true)
+ whenever(isAvailable(anyInt())) { true }
+ whenever(isMatch(any<ComponentInfo>(), anyInt())) { true }
+ whenever(isMatch(anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(),
+ anyString(), anyInt())) { true }
}
lateinit var oldPackages: List<PackageParser.Package>
@@ -145,6 +154,7 @@
private fun mockPkgSetting(aPkg: AndroidPackage) = mockThrowOnUnmocked<PackageSetting> {
this.pkg = aPkg
whenever(pkgState) { PackageStateUnserialized() }
+ whenever(readUserState(anyInt())) { dummyUserState }
}
}
@@ -156,19 +166,10 @@
// The following methods prepend "this." because @hide APIs can cause an IDE to auto-import
// the R.attr constant instead of referencing the field in an attempt to fix the error.
- /**
- * Known exclusions:
- * - [ApplicationInfo.credentialProtectedDataDir]
- * - [ApplicationInfo.dataDir]
- * - [ApplicationInfo.deviceProtectedDataDir]
- * - [ApplicationInfo.processName]
- * - [ApplicationInfo.publicSourceDir]
- * - [ApplicationInfo.scanPublicSourceDir]
- * - [ApplicationInfo.scanSourceDir]
- * - [ApplicationInfo.sourceDir]
- * These attributes used to be assigned post-package-parsing as part of another component,
- * but are now adjusted directly inside [PackageImpl].
- */
+ // It's difficult to comment out a line in a triple quoted string, so this is used instead
+ // to ignore specific fields. A comment is required to explain why a field was ignored.
+ private fun Any?.ignored(comment: String): String = "IGNORED"
+
protected fun ApplicationInfo.dumpToString() = """
appComponentFactory=${this.appComponentFactory}
backupAgentName=${this.backupAgentName}
@@ -179,22 +180,31 @@
compatibleWidthLimitDp=${this.compatibleWidthLimitDp}
compileSdkVersion=${this.compileSdkVersion}
compileSdkVersionCodename=${this.compileSdkVersionCodename}
+ credentialProtectedDataDir=${this.credentialProtectedDataDir
+ .ignored("Deferred pre-R, but assigned immediately in R")}
+ crossProfile=${this.crossProfile.ignored("Added in R")}
+ dataDir=${this.dataDir.ignored("Deferred pre-R, but assigned immediately in R")}
descriptionRes=${this.descriptionRes}
+ deviceProtectedDataDir=${this.deviceProtectedDataDir
+ .ignored("Deferred pre-R, but assigned immediately in R")}
enabled=${this.enabled}
enabledSetting=${this.enabledSetting}
flags=${Integer.toBinaryString(this.flags)}
fullBackupContent=${this.fullBackupContent}
+ gwpAsanMode=${this.gwpAsanMode.ignored("Added in R")}
hiddenUntilInstalled=${this.hiddenUntilInstalled}
icon=${this.icon}
iconRes=${this.iconRes}
installLocation=${this.installLocation}
+ labelRes=${this.labelRes}
largestWidthLimitDp=${this.largestWidthLimitDp}
logo=${this.logo}
longVersionCode=${this.longVersionCode}
+ ${"".ignored("mHiddenApiPolicy is a private field")}
manageSpaceActivityName=${this.manageSpaceActivityName}
- maxAspectRatio.compareTo(that.maxAspectRatio)=${this.maxAspectRatio}
- metaData=${this.metaData}
- minAspectRatio.compareTo(that.minAspectRatio)=${this.minAspectRatio}
+ maxAspectRatio=${this.maxAspectRatio}
+ metaData=${this.metaData.dumpToString()}
+ minAspectRatio=${this.minAspectRatio}
minSdkVersion=${this.minSdkVersion}
name=${this.name}
nativeLibraryDir=${this.nativeLibraryDir}
@@ -206,18 +216,27 @@
permission=${this.permission}
primaryCpuAbi=${this.primaryCpuAbi}
privateFlags=${Integer.toBinaryString(this.privateFlags)}
+ processName=${this.processName.ignored("Deferred pre-R, but assigned immediately in R")}
+ publicSourceDir=${this.publicSourceDir
+ .ignored("Deferred pre-R, but assigned immediately in R")}
requiresSmallestWidthDp=${this.requiresSmallestWidthDp}
resourceDirs=${this.resourceDirs?.contentToString()}
roundIconRes=${this.roundIconRes}
- secondaryCpuAbi=${this.secondaryCpuAbi}
- secondaryNativeLibraryDir=${this.secondaryNativeLibraryDir}
+ scanPublicSourceDir=${this.scanPublicSourceDir
+ .ignored("Deferred pre-R, but assigned immediately in R")}
+ scanSourceDir=${this.scanSourceDir
+ .ignored("Deferred pre-R, but assigned immediately in R")}
seInfo=${this.seInfo}
seInfoUser=${this.seInfoUser}
+ secondaryCpuAbi=${this.secondaryCpuAbi}
+ secondaryNativeLibraryDir=${this.secondaryNativeLibraryDir}
sharedLibraryFiles=${this.sharedLibraryFiles?.contentToString()}
sharedLibraryInfos=${this.sharedLibraryInfos}
showUserIcon=${this.showUserIcon}
+ sourceDir=${this.sourceDir
+ .ignored("Deferred pre-R, but assigned immediately in R")}
splitClassLoaderNames=${this.splitClassLoaderNames?.contentToString()}
- splitDependencies=${this.splitDependencies}
+ splitDependencies=${this.splitDependencies.dumpToString()}
splitNames=${this.splitNames?.contentToString()}
splitPublicSourceDirs=${this.splitPublicSourceDirs?.contentToString()}
splitSourceDirs=${this.splitSourceDirs?.contentToString()}
@@ -226,8 +245,8 @@
targetSdkVersion=${this.targetSdkVersion}
taskAffinity=${this.taskAffinity}
theme=${this.theme}
- uid=${this.uid}
uiOptions=${this.uiOptions}
+ uid=${this.uid}
versionCode=${this.versionCode}
volumeUuid=${this.volumeUuid}
zygotePreloadName=${this.zygotePreloadName}
@@ -241,19 +260,27 @@
""".trimIndent()
protected fun InstrumentationInfo.dumpToString() = """
+ banner=${this.banner}
credentialProtectedDataDir=${this.credentialProtectedDataDir}
dataDir=${this.dataDir}
deviceProtectedDataDir=${this.deviceProtectedDataDir}
functionalTest=${this.functionalTest}
handleProfiling=${this.handleProfiling}
+ icon=${this.icon}
+ labelRes=${this.labelRes}
+ logo=${this.logo}
+ metaData=${this.metaData}
+ name=${this.name}
nativeLibraryDir=${this.nativeLibraryDir}
+ nonLocalizedLabel=${this.nonLocalizedLabel}
+ packageName=${this.packageName}
primaryCpuAbi=${this.primaryCpuAbi}
publicSourceDir=${this.publicSourceDir}
secondaryCpuAbi=${this.secondaryCpuAbi}
secondaryNativeLibraryDir=${this.secondaryNativeLibraryDir}
+ showUserIcon=${this.showUserIcon}
sourceDir=${this.sourceDir}
- splitDependencies=${this.splitDependencies.sequence()
- .map { it.first to it.second?.contentToString() }.joinToString()}
+ splitDependencies=${this.splitDependencies.dumpToString()}
splitNames=${this.splitNames?.contentToString()}
splitPublicSourceDirs=${this.splitPublicSourceDirs?.contentToString()}
splitSourceDirs=${this.splitSourceDirs?.contentToString()}
@@ -262,25 +289,40 @@
""".trimIndent()
protected fun ActivityInfo.dumpToString() = """
+ banner=${this.banner}
colorMode=${this.colorMode}
configChanges=${this.configChanges}
+ descriptionRes=${this.descriptionRes}
+ directBootAware=${this.directBootAware}
documentLaunchMode=${this.documentLaunchMode}
+ enabled=${this.enabled}
+ exported=${this.exported}
flags=${Integer.toBinaryString(this.flags)}
+ icon=${this.icon}
+ labelRes=${this.labelRes}
launchMode=${this.launchMode}
launchToken=${this.launchToken}
lockTaskLaunchMode=${this.lockTaskLaunchMode}
+ logo=${this.logo}
maxAspectRatio=${this.maxAspectRatio}
maxRecents=${this.maxRecents}
+ metaData=${this.metaData.dumpToString()}
minAspectRatio=${this.minAspectRatio}
+ name=${this.name}
+ nonLocalizedLabel=${this.nonLocalizedLabel}
+ packageName=${this.packageName}
parentActivityName=${this.parentActivityName}
permission=${this.permission}
- persistableMode=${this.persistableMode}
- privateFlags=${Integer.toBinaryString(this.privateFlags)}
+ persistableMode=${this.persistableMode.ignored("Could be dropped pre-R, fixed in R")}
+ privateFlags=${this.privateFlags}
+ processName=${this.processName.ignored("Deferred pre-R, but assigned immediately in R")}
requestedVrComponent=${this.requestedVrComponent}
resizeMode=${this.resizeMode}
rotationAnimation=${this.rotationAnimation}
screenOrientation=${this.screenOrientation}
+ showUserIcon=${this.showUserIcon}
softInputMode=${this.softInputMode}
+ splitName=${this.splitName}
targetActivity=${this.targetActivity}
taskAffinity=${this.taskAffinity}
theme=${this.theme}
@@ -300,30 +342,77 @@
protected fun PermissionInfo.dumpToString() = """
backgroundPermission=${this.backgroundPermission}
+ banner=${this.banner}
descriptionRes=${this.descriptionRes}
flags=${Integer.toBinaryString(this.flags)}
group=${this.group}
+ icon=${this.icon}
+ labelRes=${this.labelRes}
+ logo=${this.logo}
+ metaData=${this.metaData.dumpToString()}
+ name=${this.name}
nonLocalizedDescription=${this.nonLocalizedDescription}
+ nonLocalizedLabel=${this.nonLocalizedLabel}
+ packageName=${this.packageName}
protectionLevel=${this.protectionLevel}
requestRes=${this.requestRes}
+ showUserIcon=${this.showUserIcon}
""".trimIndent()
protected fun ProviderInfo.dumpToString() = """
+ applicationInfo=${this.applicationInfo.ignored("Already checked")}
authority=${this.authority}
+ banner=${this.banner}
+ descriptionRes=${this.descriptionRes}
+ directBootAware=${this.directBootAware}
+ enabled=${this.enabled}
+ exported=${this.exported}
flags=${Integer.toBinaryString(this.flags)}
forceUriPermissions=${this.forceUriPermissions}
grantUriPermissions=${this.grantUriPermissions}
+ icon=${this.icon}
initOrder=${this.initOrder}
isSyncable=${this.isSyncable}
+ labelRes=${this.labelRes}
+ logo=${this.logo}
+ metaData=${this.metaData.dumpToString()}
multiprocess=${this.multiprocess}
+ name=${this.name}
+ nonLocalizedLabel=${this.nonLocalizedLabel}
+ packageName=${this.packageName}
pathPermissions=${this.pathPermissions?.joinToString {
"readPermission=${it.readPermission}\nwritePermission=${it.writePermission}"
}}
+ processName=${this.processName.ignored("Deferred pre-R, but assigned immediately in R")}
readPermission=${this.readPermission}
+ showUserIcon=${this.showUserIcon}
+ splitName=${this.splitName}
uriPermissionPatterns=${this.uriPermissionPatterns?.contentToString()}
writePermission=${this.writePermission}
""".trimIndent()
+ protected fun ServiceInfo.dumpToString() = """
+ applicationInfo=${this.applicationInfo.ignored("Already checked")}
+ banner=${this.banner}
+ descriptionRes=${this.descriptionRes}
+ directBootAware=${this.directBootAware}
+ enabled=${this.enabled}
+ exported=${this.exported}
+ flags=${Integer.toBinaryString(this.flags)}
+ icon=${this.icon}
+ labelRes=${this.labelRes}
+ logo=${this.logo}
+ mForegroundServiceType"${this.mForegroundServiceType}
+ metaData=${this.metaData.dumpToString()}
+ name=${this.name}
+ nonLocalizedLabel=${this.nonLocalizedLabel}
+ packageName=${this.packageName}
+ permission=${this.permission}
+ processName=${this.processName.ignored("Deferred pre-R, but assigned immediately in R")}
+ showUserIcon=${this.showUserIcon}
+ splitName=${this.splitName}
+ """.trimIndent()
+
protected fun ConfigurationInfo.dumpToString() = """
reqGlEsVersion=${this.reqGlEsVersion}
reqInputFeatures=${this.reqInputFeatures}
@@ -333,8 +422,10 @@
""".trimIndent()
protected fun PackageInfo.dumpToString() = """
- activities=${this.activities?.joinToString { it.dumpToString() }}
- applicationInfo=${this.applicationInfo.dumpToString()}
+ activities=${this.activities?.joinToString { it.dumpToString() }
+ .ignored("Checked separately in test")}
+ applicationInfo=${this.applicationInfo.dumpToString()
+ .ignored("Checked separately in test")}
baseRevisionCode=${this.baseRevisionCode}
compileSdkVersion=${this.compileSdkVersion}
compileSdkVersionCodename=${this.compileSdkVersionCodename}
@@ -356,15 +447,18 @@
overlayTarget=${this.overlayTarget}
packageName=${this.packageName}
permissions=${this.permissions?.joinToString { it.dumpToString() }}
- providers=${this.providers?.joinToString { it.dumpToString() }}
- receivers=${this.receivers?.joinToString { it.dumpToString() }}
+ providers=${this.providers?.joinToString { it.dumpToString() }
+ .ignored("Checked separately in test")}
+ receivers=${this.receivers?.joinToString { it.dumpToString() }
+ .ignored("Checked separately in test")}
reqFeatures=${this.reqFeatures?.joinToString { it.dumpToString() }}
requestedPermissions=${this.requestedPermissions?.contentToString()}
requestedPermissionsFlags=${this.requestedPermissionsFlags?.contentToString()}
requiredAccountType=${this.requiredAccountType}
requiredForAllUsers=${this.requiredForAllUsers}
restrictedAccountType=${this.restrictedAccountType}
- services=${this.services?.contentToString()}
+ services=${this.services?.joinToString { it.dumpToString() }
+ .ignored("Checked separately in test")}
sharedUserId=${this.sharedUserId}
sharedUserLabel=${this.sharedUserLabel}
signatures=${this.signatures?.joinToString { it.toCharsString() }}
@@ -378,11 +472,17 @@
versionName=${this.versionName}
""".trimIndent()
- @Suppress("unused")
- private fun <T> SparseArray<T>.sequence(): Sequence<Pair<Int, T>> {
- var index = 0
- return generateSequence {
- index++.takeIf { it < size() }?.let { keyAt(it) to valueAt(index) }
+ private fun Bundle?.dumpToString() = this?.keySet()?.associateWith { get(it) }?.toString()
+
+ private fun <T> SparseArray<T>?.dumpToString(): String {
+ if (this == null) {
+ return "EMPTY"
}
+
+ val list = mutableListOf<Pair<Int, T>>()
+ for (index in (0 until size())) {
+ list += keyAt(index) to valueAt(index)
+ }
+ return list.toString()
}
}
diff --git a/telephony/java/android/telephony/SmsCbMessage.java b/telephony/java/android/telephony/SmsCbMessage.java
index 752707e..d366efe 100644
--- a/telephony/java/android/telephony/SmsCbMessage.java
+++ b/telephony/java/android/telephony/SmsCbMessage.java
@@ -594,6 +594,7 @@
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 @@
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 70c5e72..b841921 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -632,7 +632,6 @@
/**
* @hide
- * TODO: makes real freq boundaries
*/
public boolean is24GHz() {
return ScanResult.is24GHz(mFrequency);
@@ -640,7 +639,6 @@
/**
* @hide
- * TODO: makes real freq boundaries
*/
@UnsupportedAppUsage
public boolean is5GHz() {
@@ -648,6 +646,13 @@
}
/**
+ * @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