Merge "Remove unused Media 2.0 APIs"
diff --git a/Android.bp b/Android.bp
index c672315..01dfe30 100644
--- a/Android.bp
+++ b/Android.bp
@@ -254,6 +254,7 @@
":statsd_aidl",
"core/java/android/os/ISystemUpdateManager.aidl",
"core/java/android/os/IThermalEventListener.aidl",
+ "core/java/android/os/IThermalStatusListener.aidl",
"core/java/android/os/IThermalService.aidl",
"core/java/android/os/IUpdateLock.aidl",
"core/java/android/os/IUserManager.aidl",
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index ec3366c..5936ee4 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -10,3 +10,5 @@
ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py -f ${PREUPLOAD_FILES}
owners_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "OWNERS$"
+
+shell_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "^packages/Shell/"
diff --git a/api/current.txt b/api/current.txt
old mode 100755
new mode 100644
index ff78d48..edd37ab
--- a/api/current.txt
+++ b/api/current.txt
@@ -7307,7 +7307,10 @@
method public android.content.Intent createRequestRoleIntent(java.lang.String);
method public boolean isRoleAvailable(java.lang.String);
method public boolean isRoleHeld(java.lang.String);
+ field public static final java.lang.String ROLE_BROWSER = "android.app.role.BROWSER";
field public static final java.lang.String ROLE_DIALER = "android.app.role.DIALER";
+ field public static final java.lang.String ROLE_GALLERY = "android.app.role.GALLERY";
+ field public static final java.lang.String ROLE_MUSIC = "android.app.role.MUSIC";
field public static final java.lang.String ROLE_SMS = "android.app.role.SMS";
}
@@ -9598,6 +9601,7 @@
method public abstract void unbindService(android.content.ServiceConnection);
method public void unregisterComponentCallbacks(android.content.ComponentCallbacks);
method public abstract void unregisterReceiver(android.content.BroadcastReceiver);
+ method public abstract void updateServiceGroup(android.content.ServiceConnection, int, int);
field public static final java.lang.String ACCESSIBILITY_SERVICE = "accessibility";
field public static final java.lang.String ACCOUNT_SERVICE = "account";
field public static final java.lang.String ACTIVITY_SERVICE = "activity";
@@ -9796,6 +9800,7 @@
method public boolean stopService(android.content.Intent);
method public void unbindService(android.content.ServiceConnection);
method public void unregisterReceiver(android.content.BroadcastReceiver);
+ method public void updateServiceGroup(android.content.ServiceConnection, int, int);
}
public deprecated class CursorLoader extends android.content.AsyncTaskLoader {
@@ -34630,14 +34635,14 @@
package android.preference {
- public class CheckBoxPreference extends android.preference.TwoStatePreference {
+ public deprecated class CheckBoxPreference extends android.preference.TwoStatePreference {
ctor public CheckBoxPreference(android.content.Context, android.util.AttributeSet, int);
ctor public CheckBoxPreference(android.content.Context, android.util.AttributeSet, int, int);
ctor public CheckBoxPreference(android.content.Context, android.util.AttributeSet);
ctor public CheckBoxPreference(android.content.Context);
}
- public abstract class DialogPreference extends android.preference.Preference implements android.content.DialogInterface.OnClickListener android.content.DialogInterface.OnDismissListener android.preference.PreferenceManager.OnActivityDestroyListener {
+ public abstract deprecated class DialogPreference extends android.preference.Preference implements android.content.DialogInterface.OnClickListener android.content.DialogInterface.OnDismissListener android.preference.PreferenceManager.OnActivityDestroyListener {
ctor public DialogPreference(android.content.Context, android.util.AttributeSet, int, int);
ctor public DialogPreference(android.content.Context, android.util.AttributeSet, int);
ctor public DialogPreference(android.content.Context, android.util.AttributeSet);
@@ -34670,7 +34675,7 @@
method protected void showDialog(android.os.Bundle);
}
- public class EditTextPreference extends android.preference.DialogPreference {
+ public deprecated class EditTextPreference extends android.preference.DialogPreference {
ctor public EditTextPreference(android.content.Context, android.util.AttributeSet, int, int);
ctor public EditTextPreference(android.content.Context, android.util.AttributeSet, int);
ctor public EditTextPreference(android.content.Context, android.util.AttributeSet);
@@ -34681,7 +34686,7 @@
method public void setText(java.lang.String);
}
- public class ListPreference extends android.preference.DialogPreference {
+ public deprecated class ListPreference extends android.preference.DialogPreference {
ctor public ListPreference(android.content.Context, android.util.AttributeSet, int, int);
ctor public ListPreference(android.content.Context, android.util.AttributeSet, int);
ctor public ListPreference(android.content.Context, android.util.AttributeSet);
@@ -34699,7 +34704,7 @@
method public void setValueIndex(int);
}
- public class MultiSelectListPreference extends android.preference.DialogPreference {
+ public deprecated class MultiSelectListPreference extends android.preference.DialogPreference {
ctor public MultiSelectListPreference(android.content.Context, android.util.AttributeSet, int, int);
ctor public MultiSelectListPreference(android.content.Context, android.util.AttributeSet, int);
ctor public MultiSelectListPreference(android.content.Context, android.util.AttributeSet);
@@ -34715,7 +34720,7 @@
method public void setValues(java.util.Set<java.lang.String>);
}
- public class Preference implements java.lang.Comparable {
+ public deprecated class Preference implements java.lang.Comparable {
ctor public Preference(android.content.Context, android.util.AttributeSet, int, int);
ctor public Preference(android.content.Context, android.util.AttributeSet, int);
ctor public Preference(android.content.Context, android.util.AttributeSet);
@@ -34812,21 +34817,21 @@
field public static final int DEFAULT_ORDER = 2147483647; // 0x7fffffff
}
- public static class Preference.BaseSavedState extends android.view.AbsSavedState {
+ public static deprecated class Preference.BaseSavedState extends android.view.AbsSavedState {
ctor public Preference.BaseSavedState(android.os.Parcel);
ctor public Preference.BaseSavedState(android.os.Parcelable);
field public static final android.os.Parcelable.Creator<android.preference.Preference.BaseSavedState> CREATOR;
}
- public static abstract interface Preference.OnPreferenceChangeListener {
+ public static abstract deprecated interface Preference.OnPreferenceChangeListener {
method public abstract boolean onPreferenceChange(android.preference.Preference, java.lang.Object);
}
- public static abstract interface Preference.OnPreferenceClickListener {
+ public static abstract deprecated interface Preference.OnPreferenceClickListener {
method public abstract boolean onPreferenceClick(android.preference.Preference);
}
- public abstract class PreferenceActivity extends android.app.ListActivity implements android.preference.PreferenceFragment.OnPreferenceStartFragmentCallback {
+ public abstract deprecated class PreferenceActivity extends android.app.ListActivity implements android.preference.PreferenceFragment.OnPreferenceStartFragmentCallback {
ctor public PreferenceActivity();
method public deprecated void addPreferencesFromIntent(android.content.Intent);
method public deprecated void addPreferencesFromResource(int);
@@ -34866,7 +34871,7 @@
field public static final long HEADER_ID_UNDEFINED = -1L; // 0xffffffffffffffffL
}
- public static final class PreferenceActivity.Header implements android.os.Parcelable {
+ public static final deprecated class PreferenceActivity.Header implements android.os.Parcelable {
ctor public PreferenceActivity.Header();
method public int describeContents();
method public java.lang.CharSequence getBreadCrumbShortTitle(android.content.res.Resources);
@@ -34892,14 +34897,14 @@
field public int titleRes;
}
- public class PreferenceCategory extends android.preference.PreferenceGroup {
+ public deprecated class PreferenceCategory extends android.preference.PreferenceGroup {
ctor public PreferenceCategory(android.content.Context, android.util.AttributeSet, int, int);
ctor public PreferenceCategory(android.content.Context, android.util.AttributeSet, int);
ctor public PreferenceCategory(android.content.Context, android.util.AttributeSet);
ctor public PreferenceCategory(android.content.Context);
}
- public abstract interface PreferenceDataStore {
+ public abstract deprecated interface PreferenceDataStore {
method public default boolean getBoolean(java.lang.String, boolean);
method public default float getFloat(java.lang.String, float);
method public default int getInt(java.lang.String, int);
@@ -34929,7 +34934,7 @@
method public abstract boolean onPreferenceStartFragment(android.preference.PreferenceFragment, android.preference.Preference);
}
- public abstract class PreferenceGroup extends android.preference.Preference {
+ public abstract deprecated class PreferenceGroup extends android.preference.Preference {
ctor public PreferenceGroup(android.content.Context, android.util.AttributeSet, int, int);
ctor public PreferenceGroup(android.content.Context, android.util.AttributeSet, int);
ctor public PreferenceGroup(android.content.Context, android.util.AttributeSet);
@@ -34948,7 +34953,7 @@
method public void setOrderingAsAdded(boolean);
}
- public class PreferenceManager {
+ public deprecated class PreferenceManager {
method public android.preference.PreferenceScreen createPreferenceScreen(android.content.Context);
method public android.preference.Preference findPreference(java.lang.CharSequence);
method public static android.content.SharedPreferences getDefaultSharedPreferences(android.content.Context);
@@ -34970,19 +34975,19 @@
field public static final java.lang.String METADATA_KEY_PREFERENCES = "android.preference";
}
- public static abstract interface PreferenceManager.OnActivityDestroyListener {
+ public static abstract deprecated interface PreferenceManager.OnActivityDestroyListener {
method public abstract void onActivityDestroy();
}
- public static abstract interface PreferenceManager.OnActivityResultListener {
+ public static abstract deprecated interface PreferenceManager.OnActivityResultListener {
method public abstract boolean onActivityResult(int, int, android.content.Intent);
}
- public static abstract interface PreferenceManager.OnActivityStopListener {
+ public static abstract deprecated interface PreferenceManager.OnActivityStopListener {
method public abstract void onActivityStop();
}
- public final class PreferenceScreen extends android.preference.PreferenceGroup implements android.widget.AdapterView.OnItemClickListener android.content.DialogInterface.OnDismissListener {
+ public final deprecated class PreferenceScreen extends android.preference.PreferenceGroup implements android.widget.AdapterView.OnItemClickListener android.content.DialogInterface.OnDismissListener {
method public void bind(android.widget.ListView);
method public android.app.Dialog getDialog();
method public android.widget.ListAdapter getRootAdapter();
@@ -34991,7 +34996,7 @@
method public void onItemClick(android.widget.AdapterView, android.view.View, int, long);
}
- public class RingtonePreference extends android.preference.Preference implements android.preference.PreferenceManager.OnActivityResultListener {
+ public deprecated class RingtonePreference extends android.preference.Preference implements android.preference.PreferenceManager.OnActivityResultListener {
ctor public RingtonePreference(android.content.Context, android.util.AttributeSet, int, int);
ctor public RingtonePreference(android.content.Context, android.util.AttributeSet, int);
ctor public RingtonePreference(android.content.Context, android.util.AttributeSet);
@@ -35008,7 +35013,7 @@
method public void setShowSilent(boolean);
}
- public class SwitchPreference extends android.preference.TwoStatePreference {
+ public deprecated class SwitchPreference extends android.preference.TwoStatePreference {
ctor public SwitchPreference(android.content.Context, android.util.AttributeSet, int, int);
ctor public SwitchPreference(android.content.Context, android.util.AttributeSet, int);
ctor public SwitchPreference(android.content.Context, android.util.AttributeSet);
@@ -35021,7 +35026,7 @@
method public void setSwitchTextOn(int);
}
- public abstract class TwoStatePreference extends android.preference.Preference {
+ public abstract deprecated class TwoStatePreference extends android.preference.Preference {
ctor public TwoStatePreference(android.content.Context, android.util.AttributeSet, int, int);
ctor public TwoStatePreference(android.content.Context, android.util.AttributeSet, int);
ctor public TwoStatePreference(android.content.Context, android.util.AttributeSet);
@@ -42856,6 +42861,7 @@
field public static final java.lang.String KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL = "editable_voicemail_number_bool";
field public static final java.lang.String KEY_EDITABLE_VOICEMAIL_NUMBER_SETTING_BOOL = "editable_voicemail_number_setting_bool";
field public static final java.lang.String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool";
+ field public static final java.lang.String KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL = "enhanced_4g_lte_on_by_default_bool";
field public static final java.lang.String KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool";
field public static final java.lang.String KEY_GSM_DTMF_TONE_DELAY_INT = "gsm_dtmf_tone_delay_int";
field public static final java.lang.String KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY = "gsm_nonroaming_networks_string_array";
diff --git a/api/system-current.txt b/api/system-current.txt
index f013b6d..4d590a3 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -75,6 +75,7 @@
field public static final java.lang.String GET_PROCESS_STATE_AND_OOM_SCORE = "android.permission.GET_PROCESS_STATE_AND_OOM_SCORE";
field public static final java.lang.String GET_TOP_ACTIVITY_INFO = "android.permission.GET_TOP_ACTIVITY_INFO";
field public static final java.lang.String GLOBAL_SEARCH = "android.permission.GLOBAL_SEARCH";
+ field public static final java.lang.String GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS = "android.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS";
field public static final java.lang.String GRANT_RUNTIME_PERMISSIONS = "android.permission.GRANT_RUNTIME_PERMISSIONS";
field public static final java.lang.String HARDWARE_TEST = "android.permission.HARDWARE_TEST";
field public static final java.lang.String HDMI_CEC = "android.permission.HDMI_CEC";
@@ -586,6 +587,7 @@
method public boolean packageHasActiveAdmins(java.lang.String);
method public deprecated boolean setActiveProfileOwner(android.content.ComponentName, java.lang.String) throws java.lang.IllegalArgumentException;
method public void setDeviceProvisioningConfigApplied();
+ method public void setProfileOwnerCanAccessDeviceIdsForUser(android.content.ComponentName, android.os.UserHandle);
field public static final java.lang.String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_ALLOWED";
field public static final java.lang.String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_DISALLOWED";
field public static final java.lang.String ACTION_PROVISION_FINALIZATION = "android.app.action.PROVISION_FINALIZATION";
@@ -867,6 +869,7 @@
method public java.util.List<java.lang.String> getRoleHoldersAsUser(java.lang.String, android.os.UserHandle);
method public void removeRoleHolderAsUser(java.lang.String, java.lang.String, android.os.UserHandle, java.util.concurrent.Executor, android.app.role.RoleManagerCallback);
method public boolean removeRoleHolderFromController(java.lang.String, java.lang.String);
+ method public void setRoleNamesFromController(java.util.List<java.lang.String>);
field public static final java.lang.String EXTRA_REQUEST_ROLE_NAME = "android.app.role.extra.REQUEST_ROLE_NAME";
}
@@ -4354,7 +4357,7 @@
package android.preference {
- public class PreferenceManager {
+ public deprecated class PreferenceManager {
method public boolean isStorageCredentialProtected();
method public void setStorageCredentialProtected();
}
@@ -6251,6 +6254,45 @@
field public static final android.os.Parcelable.Creator<android.telephony.ims.ImsExternalCallState> CREATOR;
}
+ public class ImsMmTelManager {
+ method public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(android.content.Context, int);
+ method public int getVoWiFiModeSetting();
+ method public boolean isAdvancedCallingSettingEnabled();
+ method public boolean isAvailable(int, int);
+ method public boolean isCapable(int, int);
+ method public boolean isVoWiFiRoamingSettingEnabled();
+ method public boolean isVoWiFiSettingEnabled();
+ method public boolean isVtSettingEnabled();
+ method public void registerImsRegistrationCallback(java.util.concurrent.Executor, android.telephony.ims.ImsMmTelManager.RegistrationCallback);
+ method public void registerMmTelCapabilityCallback(java.util.concurrent.Executor, android.telephony.ims.ImsMmTelManager.CapabilityCallback);
+ method public void setAdvancedCallingSetting(boolean);
+ method public void setRttCapabilitySetting(boolean);
+ method public void setVoWiFiModeSetting(int);
+ method public void setVoWiFiNonPersistent(boolean, int);
+ method public void setVoWiFiRoamingModeSetting(int);
+ method public void setVoWiFiRoamingSetting(boolean);
+ method public void setVoWiFiSetting(boolean);
+ method public void setVtSetting(boolean);
+ method public void unregisterImsRegistrationCallback(android.telephony.ims.ImsMmTelManager.RegistrationCallback);
+ method public void unregisterMmTelCapabilityCallback(android.telephony.ims.ImsMmTelManager.CapabilityCallback);
+ field public static final int WIFI_MODE_CELLULAR_PREFERRED = 1; // 0x1
+ field public static final int WIFI_MODE_WIFI_ONLY = 0; // 0x0
+ field public static final int WIFI_MODE_WIFI_PREFERRED = 2; // 0x2
+ }
+
+ public static class ImsMmTelManager.CapabilityCallback {
+ ctor public ImsMmTelManager.CapabilityCallback();
+ method public void onCapabilitiesStatusChanged(android.telephony.ims.feature.MmTelFeature.MmTelCapabilities);
+ }
+
+ public static class ImsMmTelManager.RegistrationCallback {
+ ctor public ImsMmTelManager.RegistrationCallback();
+ method public void onDeregistered(android.telephony.ims.ImsReasonInfo);
+ method public void onRegistered(int);
+ method public void onRegistering(int);
+ method public void onTechnologyChangeFailed(int, android.telephony.ims.ImsReasonInfo);
+ }
+
public final class ImsReasonInfo implements android.os.Parcelable {
ctor public ImsReasonInfo(int, int, java.lang.String);
method public int describeContents();
diff --git a/cmds/bootanimation/Android.bp b/cmds/bootanimation/Android.bp
new file mode 100644
index 0000000..31bd612
--- /dev/null
+++ b/cmds/bootanimation/Android.bp
@@ -0,0 +1,90 @@
+cc_defaults {
+ name: "bootanimation_defaults",
+
+ cflags: [
+ "-DGL_GLEXT_PROTOTYPES",
+ "-DEGL_EGLEXT_PROTOTYPES",
+
+ "-Wall",
+ "-Werror",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+
+ shared_libs: [
+ "libandroidfw",
+ "libbase",
+ "libbinder",
+ "libcutils",
+ "liblog",
+ "libutils",
+ ],
+}
+
+// bootanimation executable
+// =========================================================
+
+cc_binary {
+ name: "bootanimation",
+ defaults: ["bootanimation_defaults"],
+
+ shared_libs: [
+ "libOpenSLES",
+ "libbootanimation",
+ ],
+
+ srcs: [
+ "BootAnimationUtil.cpp",
+
+ "bootanimation_main.cpp",
+ "audioplay.cpp",
+ ],
+
+ product_variables: {
+ product_is_iot: {
+ shared_libs: [
+ "libandroidthings",
+ "libandroidthings_protos",
+ "libchrome",
+ "libprotobuf-cpp-lite",
+ ],
+ static_libs: ["libjsoncpp"],
+ srcs: [
+ "iot/iotbootanimation_main.cpp",
+ "iot/BootAction.cpp",
+ "iot/BootParameters.cpp",
+ ],
+ exclude_srcs: [
+ "bootanimation_main.cpp",
+ "audioplay.cpp",
+ ],
+ },
+ },
+
+ init_rc: ["bootanim.rc"],
+}
+
+// libbootanimation
+// ===========================================================
+
+cc_library_shared {
+ name: "libbootanimation",
+ defaults: ["bootanimation_defaults"],
+
+ srcs: ["BootAnimation.cpp"],
+
+ shared_libs: [
+ "libui",
+ "libhwui",
+ "libEGL",
+ "libGLESv1_CM",
+ "libgui",
+ "libtinyalsa",
+ ],
+
+ product_variables: {
+ product_is_iot: {
+ init_rc: ["iot/bootanim_iot.rc"],
+ },
+ },
+}
diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk
deleted file mode 100644
index 6943dab..0000000
--- a/cmds/bootanimation/Android.mk
+++ /dev/null
@@ -1,103 +0,0 @@
-bootanimation_CommonCFlags = -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
-bootanimation_CommonCFlags += -Wall -Werror -Wunused -Wunreachable-code
-
-
-# bootanimation executable
-# =========================================================
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_CFLAGS += ${bootanimation_CommonCFlags}
-
-LOCAL_SHARED_LIBRARIES := \
- libOpenSLES \
- libandroidfw \
- libbase \
- libbinder \
- libbootanimation \
- libcutils \
- liblog \
- libutils \
-
-LOCAL_SRC_FILES:= \
- BootAnimationUtil.cpp \
-
-ifeq ($(PRODUCT_IOT),true)
-
-LOCAL_SHARED_LIBRARIES += \
- libandroidthings \
- libandroidthings_protos \
- libchrome \
- libprotobuf-cpp-lite \
-
-LOCAL_STATIC_LIBRARIES += \
- libjsoncpp
-
-LOCAL_SRC_FILES += \
- iot/iotbootanimation_main.cpp \
- iot/BootAction.cpp \
- iot/BootParameters.cpp \
-
-else
-
-LOCAL_SRC_FILES += \
- bootanimation_main.cpp \
- audioplay.cpp \
-
-endif # PRODUCT_IOT
-
-LOCAL_MODULE:= bootanimation
-
-LOCAL_INIT_RC := bootanim.rc
-
-ifdef TARGET_32_BIT_SURFACEFLINGER
-LOCAL_32_BIT_ONLY := true
-endif
-
-include $(BUILD_EXECUTABLE)
-
-
-# libbootanimation
-# ===========================================================
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libbootanimation
-LOCAL_CFLAGS += ${bootanimation_CommonCFlags}
-
-LOCAL_SRC_FILES:= \
- BootAnimation.cpp
-
-LOCAL_CFLAGS += ${bootanimation_CommonCFlags}
-
-LOCAL_C_INCLUDES += \
- external/tinyalsa/include \
- frameworks/wilhelm/include
-
-LOCAL_SHARED_LIBRARIES := \
- libcutils \
- liblog \
- libandroidfw \
- libutils \
- libbinder \
- libui \
- libhwui \
- libEGL \
- libGLESv1_CM \
- libgui \
- libtinyalsa \
- libbase
-
-ifeq ($(PRODUCT_IOT),true)
-
-LOCAL_INIT_RC := iot/bootanim_iot.rc
-
-endif # PRODUCT_IOT
-
-ifdef TARGET_32_BIT_SURFACEFLINGER
-LOCAL_32_BIT_ONLY := true
-endif
-
-include ${BUILD_SHARED_LIBRARY}
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/cmds/bootanimation/iot/Android.bp b/cmds/bootanimation/iot/Android.bp
new file mode 100644
index 0000000..1f248ad
--- /dev/null
+++ b/cmds/bootanimation/iot/Android.bp
@@ -0,0 +1,49 @@
+// 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.
+//
+
+// libbootanimation_iot_test
+// ===========================================================
+cc_test {
+ name: "libbootanimation_iot_test",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+
+ shared_libs: [
+ "libandroidthings",
+ "libandroidthings_protos",
+ "libbase",
+ "libchrome",
+ "liblog",
+ "libprotobuf-cpp-lite",
+ ],
+
+ static_libs: ["libjsoncpp"],
+
+ srcs: [
+ "BootParameters.cpp",
+ "BootParameters_test.cpp",
+ ],
+
+ enabled: false,
+ product_variables: {
+ product_is_iot: {
+ enabled: true,
+ },
+ },
+}
diff --git a/cmds/bootanimation/iot/Android.mk b/cmds/bootanimation/iot/Android.mk
deleted file mode 100644
index 3d288e4..0000000
--- a/cmds/bootanimation/iot/Android.mk
+++ /dev/null
@@ -1,43 +0,0 @@
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-ifeq ($(PRODUCT_IOT),true)
-
-# libbootanimation_iot_test
-# ===========================================================
-include $(CLEAR_VARS)
-LOCAL_MODULE := libbootanimation_iot_test
-LOCAL_CFLAGS := -Wall -Werror -Wunused -Wunreachable-code
-
-LOCAL_SHARED_LIBRARIES := \
- libandroidthings \
- libandroidthings_protos \
- libbase \
- libchrome \
- liblog \
- libprotobuf-cpp-lite \
-
-LOCAL_STATIC_LIBRARIES += \
- libjsoncpp
-
-LOCAL_SRC_FILES := \
- BootParameters.cpp \
- BootParameters_test.cpp \
-
-include $(BUILD_NATIVE_TEST)
-
-endif # PRODUCT_IOT
diff --git a/cmds/dpm/src/com/android/commands/dpm/Dpm.java b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
index 376b13c..6c6797a 100644
--- a/cmds/dpm/src/com/android/commands/dpm/Dpm.java
+++ b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
@@ -48,6 +48,8 @@
private static final String COMMAND_CLEAR_FREEZE_PERIOD_RECORD = "clear-freeze-period-record";
private static final String COMMAND_FORCE_NETWORK_LOGS = "force-network-logs";
private static final String COMMAND_FORCE_SECURITY_LOGS = "force-security-logs";
+ private static final String COMMAND_GRANT_PO_DEVICE_ID_ACCESS =
+ "grant-profile-owner-device-ids-access";
private IDevicePolicyManager mDevicePolicyManager;
private int mUserId = UserHandle.USER_SYSTEM;
@@ -89,7 +91,10 @@
"the DPC and triggers DeviceAdminReceiver.onNetworkLogsAvailable() if needed.\n" +
"\n" +
"dpm " + COMMAND_FORCE_SECURITY_LOGS + ": makes all security logs available to " +
- "the DPC and triggers DeviceAdminReceiver.onSecurityLogsAvailable() if needed.");
+ "the DPC and triggers DeviceAdminReceiver.onSecurityLogsAvailable() if needed."
+ + "\n"
+ + "usage: dpm " + COMMAND_GRANT_PO_DEVICE_ID_ACCESS + ": "
+ + "[ --user <USER_ID> | current ] <COMPONENT>\n");
}
@Override
@@ -124,6 +129,9 @@
case COMMAND_FORCE_SECURITY_LOGS:
runForceSecurityLogs();
break;
+ case COMMAND_GRANT_PO_DEVICE_ID_ACCESS:
+ runGrantProfileOwnerDeviceIdsAccess();
+ break;
default:
throw new IllegalArgumentException ("unknown command '" + command + "'");
}
@@ -242,6 +250,13 @@
System.out.println("Success");
}
+
+ private void runGrantProfileOwnerDeviceIdsAccess() throws RemoteException {
+ parseArgs(/*canHaveName=*/ false);
+ mDevicePolicyManager.grantDeviceIdsAccessToProfileOwner(mComponent, mUserId);
+ System.out.println("Success");
+ }
+
private ComponentName parseComponentName(String component) {
ComponentName cn = ComponentName.unflattenFromString(component);
if (cn == null) {
diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp
index 1191e6a..020c443 100644
--- a/cmds/idmap2/idmap2/Lookup.cpp
+++ b/cmds/idmap2/idmap2/Lookup.cpp
@@ -48,8 +48,6 @@
using android::Res_value;
using android::ResStringPool;
using android::ResTable_config;
-using android::String16;
-using android::String8;
using android::StringPiece16;
using android::base::StringPrintf;
using android::idmap2::CommandLineOptions;
diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt
index e5764f0..2dcf50f 100644
--- a/config/boot-image-profile.txt
+++ b/config/boot-image-profile.txt
@@ -12274,7 +12274,7 @@
HPLorg/ccil/cowan/tagsoup/AttributesImpl;->setAttribute(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
HPLorg/ccil/cowan/tagsoup/Element;->anonymize()V
HPLorg/ccil/cowan/tagsoup/Element;->preclose()V
-HPLorg/ccil/cowan/tagsoup/HTMLSchema;-><init>()V
+# HPLorg/ccil/cowan/tagsoup/HTMLSchema;-><init>()V b/76145463
HPLorg/json/JSONArray;->optDouble(I)D
HPLorg/json/JSONArray;->optDouble(ID)D
HPLorg/json/JSONStringer;->value(J)Lorg/json/JSONStringer;
@@ -15623,7 +15623,7 @@
HSPLandroid/app/admin/IDevicePolicyManager$Stub$Proxy;->isLockTaskPermitted(Ljava/lang/String;)Z
HSPLandroid/app/admin/IDevicePolicyManager$Stub$Proxy;->isProvisioningAllowed(Ljava/lang/String;Ljava/lang/String;)Z
HSPLandroid/app/admin/IDevicePolicyManager$Stub;-><init>()V
-HSPLandroid/app/admin/IDevicePolicyManager$Stub;->onTransact(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z
+# HSPLandroid/app/admin/IDevicePolicyManager$Stub;->onTransact(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z b/76145463
HSPLandroid/app/admin/IDevicePolicyManager;->addCrossProfileIntentFilter(Landroid/content/ComponentName;Landroid/content/IntentFilter;I)V
HSPLandroid/app/admin/IDevicePolicyManager;->addCrossProfileWidgetProvider(Landroid/content/ComponentName;Ljava/lang/String;)Z
HSPLandroid/app/admin/IDevicePolicyManager;->addOverrideApn(Landroid/content/ComponentName;Landroid/telephony/data/ApnSetting;)I
@@ -25254,7 +25254,7 @@
HSPLandroid/media/MediaCodecInfo$VideoCapabilities;->parseFromInfo(Landroid/media/MediaFormat;)V
HSPLandroid/media/MediaCodecInfo$VideoCapabilities;->parseWidthHeightRanges(Ljava/lang/Object;)Landroid/util/Pair;
HSPLandroid/media/MediaCodecInfo$VideoCapabilities;->supports(Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Number;)Z
-HSPLandroid/media/MediaCodecInfo$VideoCapabilities;->updateLimits()V
+# HSPLandroid/media/MediaCodecInfo$VideoCapabilities;->updateLimits()V b/76145463
HSPLandroid/media/MediaCodecInfo;-><init>(Ljava/lang/String;Z[Landroid/media/MediaCodecInfo$CodecCapabilities;)V
HSPLandroid/media/MediaCodecInfo;->access$200()Landroid/util/Range;
HSPLandroid/media/MediaCodecInfo;->access$300()Landroid/util/Range;
@@ -37845,7 +37845,7 @@
HSPLandroid/widget/ViewSwitcher;->getNextView()Landroid/view/View;
HSPLandroid/widget/WrapperListAdapter;->getWrappedAdapter()Landroid/widget/ListAdapter;
HSPLcom/android/i18n/phonenumbers/AlternateFormatsCountryCodeSet;->getCountryCodeSet()Ljava/util/Set;
-HSPLcom/android/i18n/phonenumbers/CountryCodeToRegionCodeMap;->getCountryCodeToRegionCodeMap()Ljava/util/Map;
+# HSPLcom/android/i18n/phonenumbers/CountryCodeToRegionCodeMap;->getCountryCodeToRegionCodeMap()Ljava/util/Map; b/76145463
HSPLcom/android/i18n/phonenumbers/MetadataLoader;->loadMetadata(Ljava/lang/String;)Ljava/io/InputStream;
HSPLcom/android/i18n/phonenumbers/MetadataManager$1;-><init>()V
HSPLcom/android/i18n/phonenumbers/MetadataManager$1;->loadMetadata(Ljava/lang/String;)Ljava/io/InputStream;
@@ -37999,7 +37999,7 @@
HSPLcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->setCountryCodeSource(Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber$CountryCodeSource;)Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;
HSPLcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->setNationalNumber(J)Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;
HSPLcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;->setRawInput(Ljava/lang/String;)Lcom/android/i18n/phonenumbers/Phonenumber$PhoneNumber;
-HSPLcom/android/i18n/phonenumbers/ShortNumbersRegionCodeSet;->getRegionCodeSet()Ljava/util/Set;
+# HSPLcom/android/i18n/phonenumbers/ShortNumbersRegionCodeSet;->getRegionCodeSet()Ljava/util/Set; b/76145463
HSPLcom/android/i18n/phonenumbers/internal/MatcherApi;->matchNationalNumber(Ljava/lang/CharSequence;Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneNumberDesc;Z)Z
HSPLcom/android/i18n/phonenumbers/internal/RegexBasedMatcher;->matchNationalNumber(Ljava/lang/CharSequence;Lcom/android/i18n/phonenumbers/Phonemetadata$PhoneNumberDesc;Z)Z
HSPLcom/android/i18n/phonenumbers/internal/RegexCache$LRUCache$1;->removeEldestEntry(Ljava/util/Map$Entry;)Z
@@ -38978,7 +38978,7 @@
HSPLcom/android/internal/os/BatteryStatsImpl$Uid;->updateUidProcessStateLocked(I)V
HSPLcom/android/internal/os/BatteryStatsImpl$Uid;->writeJobCompletionsToParcelLocked(Landroid/os/Parcel;)V
HSPLcom/android/internal/os/BatteryStatsImpl$UserInfoProvider;->getUserIds()[I
-HSPLcom/android/internal/os/BatteryStatsImpl;-><init>(Lcom/android/internal/os/BatteryStatsImpl$Clocks;Landroid/os/Parcel;)V
+# HSPLcom/android/internal/os/BatteryStatsImpl;-><init>(Lcom/android/internal/os/BatteryStatsImpl$Clocks;Landroid/os/Parcel;)V b/76145463
HSPLcom/android/internal/os/BatteryStatsImpl;-><init>(Lcom/android/internal/os/BatteryStatsImpl$Clocks;Ljava/io/File;Landroid/os/Handler;Lcom/android/internal/os/BatteryStatsImpl$PlatformIdleStateCallback;Lcom/android/internal/os/BatteryStatsImpl$UserInfoProvider;)V
HSPLcom/android/internal/os/BatteryStatsImpl;-><init>(Ljava/io/File;Landroid/os/Handler;Lcom/android/internal/os/BatteryStatsImpl$PlatformIdleStateCallback;Lcom/android/internal/os/BatteryStatsImpl$UserInfoProvider;)V
HSPLcom/android/internal/os/BatteryStatsImpl;->addHistoryBufferLocked(JBLandroid/os/BatteryStats$HistoryItem;)V
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 553acc8..83fab7e 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1432,7 +1432,15 @@
if (mAutoFillResetNeeded) {
if (!mAutoFillIgnoreFirstResumePause) {
View focus = getCurrentFocus();
- if (focus != null && focus.canNotifyAutofillEnterExitEvent()) {
+ // On Activity rotation situation (mRestoredFromBundle is true),
+ // we should not call on AutofillManager in onResume()
+ // since the next Layout pass will do that.
+ // However, there are both cases where Activity#getCurrentFocus()
+ // will return null (window not preserved) and not null (window IS
+ // preserved), so we need to explicitly check for mRestoredFromBundle
+ // here.
+ if (!mRestoredFromBundle && focus != null
+ && focus.canNotifyAutofillEnterExitEvent()) {
// TODO: in Activity killed/recreated case, i.e. SessionLifecycleTest#
// testDatasetVisibleWhileAutofilledAppIsLifecycled: the View's initial
// window visibility after recreation is INVISIBLE in onResume() and next frame
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 9837deb..28ecb27 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1724,6 +1724,24 @@
}
@Override
+ public void updateServiceGroup(@NonNull ServiceConnection conn, int group, int importance) {
+ if (conn == null) {
+ throw new IllegalArgumentException("connection is null");
+ }
+ if (mPackageInfo != null) {
+ IServiceConnection sd = mPackageInfo.forgetServiceDispatcher(
+ getOuterContext(), conn);
+ try {
+ ActivityManager.getService().updateServiceGroup(sd, group, importance);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } else {
+ throw new RuntimeException("Not supported in system context");
+ }
+ }
+
+ @Override
public void unbindService(ServiceConnection conn) {
if (conn == null) {
throw new IllegalArgumentException("connection is null");
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index f27c667..e83bcd0 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -140,6 +140,7 @@
int bindIsolatedService(in IApplicationThread caller, in IBinder token, in Intent service,
in String resolvedType, in IServiceConnection connection, int flags,
in String instanceName, in String callingPackage, int userId);
+ void updateServiceGroup(in IServiceConnection connection, int group, int importance);
boolean unbindService(in IServiceConnection connection);
void publishService(in IBinder token, in Intent intent, in IBinder service);
void setDebugApp(in String packageName, boolean waitForDebugger, boolean persistent);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 450efdf..f2a3e44 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3193,6 +3193,25 @@
}
/**
+ * Returns the actions that are contextual (marked as SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION) out
+ * of the actions in this notification.
+ *
+ * @hide
+ */
+ public List<Notification.Action> getContextualActions() {
+ if (actions == null) return Collections.emptyList();
+
+ List<Notification.Action> contextualActions = new ArrayList<>();
+ for (Notification.Action action : actions) {
+ if (action.getSemanticAction()
+ == Notification.Action.SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION) {
+ contextualActions.add(action);
+ }
+ }
+ return contextualActions;
+ }
+
+ /**
* Builder class for {@link Notification} objects.
*
* Provides a convenient way to set the various fields of a {@link Notification} and generate
@@ -7461,7 +7480,11 @@
return mRemoteInputHistory;
}
- private Bundle toBundle() {
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public Bundle toBundle() {
Bundle bundle = new Bundle();
if (mText != null) {
bundle.putCharSequence(KEY_TEXT, mText);
diff --git a/core/java/android/app/Person.java b/core/java/android/app/Person.java
index 3884a8d..a2dae3b 100644
--- a/core/java/android/app/Person.java
+++ b/core/java/android/app/Person.java
@@ -22,6 +22,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Objects;
+
/**
* Provides an immutable reference to an entity that appears repeatedly on different surfaces of the
* platform. For example, this could represent the sender of a message.
@@ -121,6 +123,26 @@
}
@Override
+ public boolean equals(Object obj) {
+ if (obj instanceof Person) {
+ final Person other = (Person) obj;
+ return Objects.equals(mName, other.mName)
+ && mIcon == null ? other.mIcon == null :
+ (other.mIcon != null && mIcon.sameAs(other.mIcon))
+ && Objects.equals(mUri, other.mUri)
+ && Objects.equals(mKey, other.mKey)
+ && mIsBot == other.mIsBot
+ && mIsImportant == other.mIsImportant;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mName, mIcon, mUri, mKey, mIsBot, mIsImportant);
+ }
+
+ @Override
public int describeContents() {
return 0;
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index f129a71..24ee7f7 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -9792,7 +9792,6 @@
}
}
-
/**
* Sets the global Private DNS mode and host to be used.
* May only be called by the device owner.
@@ -9867,4 +9866,31 @@
throw re.rethrowFromSystemServer();
}
}
+
+ /**
+ * Grants the profile owner of the given user access to device identifiers (such as
+ * serial number, IMEI and MEID).
+ *
+ * <p>This lets the profile owner request inclusion of device identifiers when calling
+ * {@link generateKeyPair}.
+ *
+ * <p>This grant is necessary to guarantee that profile owners can access device identifiers.
+ *
+ * <p>Privileged system API - meant to be called by the system, particularly the managed
+ * provisioning app, when a work profile is set up.
+ *
+ * @hide
+ */
+ @SystemApi
+ public void setProfileOwnerCanAccessDeviceIdsForUser(
+ @NonNull ComponentName who, @NonNull UserHandle userHandle) {
+ if (mService == null) {
+ return;
+ }
+ try {
+ mService.grantDeviceIdsAccessToProfileOwner(who, userHandle.getIdentifier());
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index ce1f4ef..918c127 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -417,4 +417,6 @@
void setGlobalPrivateDns(in ComponentName admin, int mode, in String privateDnsHost);
int getGlobalPrivateDnsMode(in ComponentName admin);
String getGlobalPrivateDnsHost(in ComponentName admin);
+
+ void grantDeviceIdsAccessToProfileOwner(in ComponentName who, int userId);
}
diff --git a/core/java/android/app/role/IRoleManager.aidl b/core/java/android/app/role/IRoleManager.aidl
index 2cf13ec2..3ca8ec0 100644
--- a/core/java/android/app/role/IRoleManager.aidl
+++ b/core/java/android/app/role/IRoleManager.aidl
@@ -37,6 +37,8 @@
void clearRoleHoldersAsUser(in String roleName, int userId, in IRoleManagerCallback callback);
+ void setRoleNamesFromController(in List<String> roleNames);
+
boolean addRoleHolderFromController(in String roleName, in String packageName);
boolean removeRoleHolderFromController(in String roleName, in String packageName);
diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java
index ef86b01..7cb245a 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -67,10 +67,33 @@
/**
* The name of the SMS role.
+ *
+ * @see Intent#CATEGORY_APP_MESSAGING
*/
public static final String ROLE_SMS = "android.app.role.SMS";
/**
+ * The name of the browser role.
+ *
+ * @see Intent#CATEGORY_APP_BROWSER
+ */
+ public static final String ROLE_BROWSER = "android.app.role.BROWSER";
+
+ /**
+ * The name of the gallery role.
+ *
+ * @see Intent#CATEGORY_APP_GALLERY
+ */
+ public static final String ROLE_GALLERY = "android.app.role.GALLERY";
+
+ /**
+ * The name of the music player role.
+ *
+ * @see Intent#CATEGORY_APP_MUSIC
+ */
+ public static final String ROLE_MUSIC = "android.app.role.MUSIC";
+
+ /**
* The action used to request user approval of a role for an application.
*
* @hide
@@ -92,8 +115,8 @@
*
* @hide
*/
- public static final String PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER =
- "com.android.permissioncontroller.permission.MANAGE_ROLE_HOLDERS_FROM_CONTROLLER";
+ public static final String PERMISSION_MANAGE_ROLES_FROM_CONTROLLER =
+ "com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER";
@NonNull
private final Context mContext;
@@ -342,12 +365,36 @@
}
/**
+ * Set the names of all the available roles. Should only be called from
+ * {@link android.rolecontrollerservice.RoleControllerService}.
+ * <p>
+ * <strong>Note:</strong> Using this API requires holding
+ * {@link #PERMISSION_MANAGE_ROLES_FROM_CONTROLLER}.
+ *
+ * @param roleNames the names of all the available roles
+ *
+ * @throws IllegalArgumentException if the list of role names is {@code null}.
+ *
+ * @hide
+ */
+ @RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER)
+ @SystemApi
+ public void setRoleNamesFromController(@NonNull List<String> roleNames) {
+ Preconditions.checkNotNull(roleNames, "roleNames cannot be null");
+ try {
+ mService.setRoleNamesFromController(roleNames);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Add a specific application to the holders of a role, only modifying records inside
* {@link RoleManager}. Should only be called from
* {@link android.rolecontrollerservice.RoleControllerService}.
* <p>
* <strong>Note:</strong> Using this API requires holding
- * {@link #PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER}.
+ * {@link #PERMISSION_MANAGE_ROLES_FROM_CONTROLLER}.
*
* @param roleName the name of the role to add the role holder for
* @param packageName the package name of the application to add to the role holders
@@ -362,7 +409,7 @@
*
* @hide
*/
- @RequiresPermission(PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER)
+ @RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER)
@SystemApi
public boolean addRoleHolderFromController(@NonNull String roleName,
@NonNull String packageName) {
@@ -381,7 +428,7 @@
* {@link android.rolecontrollerservice.RoleControllerService}.
* <p>
* <strong>Note:</strong> Using this API requires holding
- * {@link #PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER}.
+ * {@link #PERMISSION_MANAGE_ROLES_FROM_CONTROLLER}.
*
* @param roleName the name of the role to remove the role holder for
* @param packageName the package name of the application to remove from the role holders
@@ -396,7 +443,7 @@
*
* @hide
*/
- @RequiresPermission(PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER)
+ @RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER)
@SystemApi
public boolean removeRoleHolderFromController(@NonNull String roleName,
@NonNull String packageName) {
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 0aa0535..235dc5c 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -347,7 +347,7 @@
* device are requested to be fetched using Service Discovery Protocol
* <p> Always contains the extra field {@link #EXTRA_DEVICE}
* <p> Always contains the extra field {@link #EXTRA_UUID}
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} to receive.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_UUID =
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 636b1b9..8d9d340 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -973,11 +973,11 @@
*/
@UnsupportedAppUsage
public void phoneStateChanged(int numActive, int numHeld, int callState, String number,
- int type) {
+ int type, String name) {
final IBluetoothHeadset service = mService;
if (service != null && isEnabled()) {
try {
- service.phoneStateChanged(numActive, numHeld, callState, number, type);
+ service.phoneStateChanged(numActive, numHeld, callState, number, type, name);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
diff --git a/core/java/android/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java
index 47c4ee6..6ed7942 100644
--- a/core/java/android/bluetooth/BluetoothHearingAid.java
+++ b/core/java/android/bluetooth/BluetoothHearingAid.java
@@ -51,7 +51,7 @@
*/
public final class BluetoothHearingAid implements BluetoothProfile {
private static final String TAG = "BluetoothHearingAid";
- private static final boolean DBG = false;
+ private static final boolean DBG = true;
private static final boolean VDBG = false;
/**
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 004417b..cec8ef5 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -425,6 +425,15 @@
*/
public static final int BIND_EXTERNAL_SERVICE = 0x80000000;
+ /**
+ * These bind flags reduce the strength of the binding such that we shouldn't
+ * consider it as pulling the process up to the level of the one that is bound to it.
+ * @hide
+ */
+ public static final int BIND_REDUCTION_FLAGS =
+ Context.BIND_ALLOW_OOM_MANAGEMENT | Context.BIND_WAIVE_PRIORITY
+ | Context.BIND_ADJUST_BELOW_PERCEPTIBLE | Context.BIND_NOT_VISIBLE;
+
/** @hide */
@IntDef(flag = true, prefix = { "RECEIVER_VISIBLE_" }, value = {
RECEIVER_VISIBLE_TO_INSTANT_APPS
@@ -2982,6 +2991,31 @@
}
/**
+ * For a service previously bound with {@link #bindService} or a related method, change
+ * how the system manages that service's process in relation to other processes. This
+ * doesn't modify the original bind flags that were passed in when binding, but adjusts
+ * how the process will be managed in some cases based on those flags. Currently only
+ * works on isolated processes (will be ignored for non-isolated processes).
+ *
+ * @param conn The connection interface previously supplied to bindService(). This
+ * parameter must not be null.
+ * @param group A group to put this connection's process in. Upon calling here, this
+ * will override any previous group that was set for that process. The group
+ * tells the system about processes that are logically grouped together, so
+ * should be managed as one unit of importance (such as when being considered
+ * a recently used app). All processes in the same app with the same group
+ * are considered to be related. Supplying 0 reverts to the default behavior
+ * of not grouping.
+ * @param importance Additional importance of the processes within a group. Upon calling
+ * here, this will override any previous group that was set for that
+ * process. This fine-tunes process killing of all processes within
+ * a related groups -- higher importance values will be killed before
+ * lower ones.
+ */
+ public abstract void updateServiceGroup(@NonNull ServiceConnection conn, int group,
+ int importance);
+
+ /**
* Disconnect from an application service. You will no longer receive
* calls as the service is restarted, and the service is now allowed to
* stop at any time.
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 88696b0e..2db44b4 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -726,6 +726,11 @@
}
@Override
+ public void updateServiceGroup(ServiceConnection conn, int group, int importance) {
+ mBase.updateServiceGroup(conn, group, importance);
+ }
+
+ @Override
public void unbindService(ServiceConnection conn) {
mBase.unbindService(conn);
}
diff --git a/core/java/android/os/IThermalService.aidl b/core/java/android/os/IThermalService.aidl
index 287a5ed..8160338 100644
--- a/core/java/android/os/IThermalService.aidl
+++ b/core/java/android/os/IThermalService.aidl
@@ -17,6 +17,7 @@
package android.os;
import android.os.IThermalEventListener;
+import android.os.IThermalStatusListener;
import android.os.Temperature;
import java.util.List;
@@ -30,31 +31,60 @@
* @param listener the IThermalEventListener to be notified.
* {@hide}
*/
- void registerThermalEventListener(in IThermalEventListener listener);
+ boolean registerThermalEventListener(in IThermalEventListener listener);
+
/**
* Register a listener for thermal events on given temperature type.
* @param listener the IThermalEventListener to be notified.
* @param type the temperature type IThermalEventListener to be notified.
+ * @return true if registered successfully.
* {@hide}
*/
- void registerThermalEventListenerWithType(in IThermalEventListener listener, in int type);
+ boolean registerThermalEventListenerWithType(in IThermalEventListener listener, in int type);
+
/**
* Unregister a previously-registered listener for thermal events.
* @param listener the IThermalEventListener to no longer be notified.
+ * @return true if unregistered successfully.
* {@hide}
*/
- void unregisterThermalEventListener(in IThermalEventListener listener);
+ boolean unregisterThermalEventListener(in IThermalEventListener listener);
+
/**
* Get current temperature with its throttling status.
* @return list of android.os.Temperature
* {@hide}
*/
List<Temperature> getCurrentTemperatures();
+
/**
* Get current temperature with its throttling status on given temperature type.
* @param type the temperature type to query.
- * @return list of android.os.Temperature
+ * @return list of {@link android.os.Temperature}.
* {@hide}
*/
List<Temperature> getCurrentTemperaturesWithType(in int type);
+
+ /**
+ * Register a listener for thermal status change.
+ * @param listener the IThermalStatusListener to be notified.
+ * @return true if registered successfully.
+ * {@hide}
+ */
+ boolean registerThermalStatusListener(in IThermalStatusListener listener);
+
+ /**
+ * Unregister a previously-registered listener for thermal status.
+ * @param listener the IThermalStatusListener to no longer be notified.
+ * @return true if unregistered successfully.
+ * {@hide}
+ */
+ boolean unregisterThermalStatusListener(in IThermalStatusListener listener);
+
+ /**
+ * Get current thermal status.
+ * @return status defined in {@link android.os.Temperature}.
+ * {@hide}
+ */
+ int getCurrentStatus();
}
diff --git a/core/java/android/os/IThermalStatusListener.aidl b/core/java/android/os/IThermalStatusListener.aidl
new file mode 100644
index 0000000..a6da7d0
--- /dev/null
+++ b/core/java/android/os/IThermalStatusListener.aidl
@@ -0,0 +1,29 @@
+/*
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.os;
+
+/**
+ * Listener for thermal status.
+ * {@hide}
+ */
+oneway interface IThermalStatusListener {
+ /**
+ * Called when overall thermal throttling status changed.
+ * @param status defined in {@link android.os.Temperature#ThrottlingStatus}.
+ */
+ void onStatusChange(int status);
+}
diff --git a/core/java/android/os/Temperature.java b/core/java/android/os/Temperature.java
index 37ed52c..bf85fbd 100644
--- a/core/java/android/os/Temperature.java
+++ b/core/java/android/os/Temperature.java
@@ -25,9 +25,7 @@
/**
* Temperature values used by IThermalService.
- */
-
-/**
+ *
* @hide
*/
public class Temperature implements Parcelable {
@@ -40,7 +38,6 @@
/** The level of the sensor is currently in throttling */
private int mStatus;
- /** @hide */
@IntDef(prefix = { "THROTTLING_" }, value = {
THROTTLING_NONE,
THROTTLING_LIGHT,
@@ -62,7 +59,6 @@
public static final int THROTTLING_WARNING = ThrottlingSeverity.WARNING;
public static final int THROTTLING_SHUTDOWN = ThrottlingSeverity.SHUTDOWN;
- /** @hide */
@IntDef(prefix = { "TYPE_" }, value = {
TYPE_UNKNOWN,
TYPE_CPU,
@@ -95,19 +91,28 @@
*
* @return true if a temperature type is valid otherwise false.
*/
- public static boolean isValidType(int type) {
+ public static boolean isValidType(@Type int type) {
return type >= TYPE_UNKNOWN && type <= TYPE_BCL_PERCENTAGE;
}
+ /**
+ * Verify a valid throttling status.
+ *
+ * @return true if a status is valid otherwise false.
+ */
+ public static boolean isValidStatus(@ThrottlingStatus int status) {
+ return status >= THROTTLING_NONE && status <= THROTTLING_SHUTDOWN;
+ }
+
public Temperature() {
this(Float.NaN, TYPE_UNKNOWN, "", THROTTLING_NONE);
}
- public Temperature(float value, @Type int type, String name, int status) {
+ public Temperature(float value, @Type int type, String name, @ThrottlingStatus int status) {
mValue = value;
mType = isValidType(type) ? type : TYPE_UNKNOWN;
mName = name;
- mStatus = status;
+ mStatus = isValidStatus(status) ? status : THROTTLING_NONE;
}
/**
diff --git a/core/java/android/preference/CheckBoxPreference.java b/core/java/android/preference/CheckBoxPreference.java
index fee3f0f1..948c6aa 100644
--- a/core/java/android/preference/CheckBoxPreference.java
+++ b/core/java/android/preference/CheckBoxPreference.java
@@ -27,11 +27,18 @@
* functionality.
* <p>
* This preference will store a boolean into the SharedPreferences.
- *
+ *
* @attr ref android.R.styleable#CheckBoxPreference_summaryOff
* @attr ref android.R.styleable#CheckBoxPreference_summaryOn
* @attr ref android.R.styleable#CheckBoxPreference_disableDependentsState
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public class CheckBoxPreference extends TwoStatePreference {
public CheckBoxPreference(Context context, AttributeSet attrs, int defStyleAttr) {
diff --git a/core/java/android/preference/DialogPreference.java b/core/java/android/preference/DialogPreference.java
index 4b5a7b4..96c8589 100644
--- a/core/java/android/preference/DialogPreference.java
+++ b/core/java/android/preference/DialogPreference.java
@@ -50,7 +50,14 @@
* @attr ref android.R.styleable#DialogPreference_dialogLayout
* @attr ref android.R.styleable#DialogPreference_positiveButtonText
* @attr ref android.R.styleable#DialogPreference_negativeButtonText
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public abstract class DialogPreference extends Preference implements
DialogInterface.OnClickListener, DialogInterface.OnDismissListener,
PreferenceManager.OnActivityDestroyListener {
diff --git a/core/java/android/preference/EditTextPreference.java b/core/java/android/preference/EditTextPreference.java
index 4d2ac67..c09cec8 100644
--- a/core/java/android/preference/EditTextPreference.java
+++ b/core/java/android/preference/EditTextPreference.java
@@ -42,7 +42,14 @@
* This preference will store a string into the SharedPreferences.
* <p>
* See {@link android.R.styleable#EditText EditText Attributes}.
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public class EditTextPreference extends DialogPreference {
/**
* The edit text shown in the dialog.
diff --git a/core/java/android/preference/GenericInflater.java b/core/java/android/preference/GenericInflater.java
index 3319e64..7edc987 100644
--- a/core/java/android/preference/GenericInflater.java
+++ b/core/java/android/preference/GenericInflater.java
@@ -16,13 +16,6 @@
package android.preference;
-import java.io.IOException;
-import java.lang.reflect.Constructor;
-import java.util.HashMap;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
import android.annotation.XmlRes;
import android.content.Context;
import android.content.res.XmlResourceParser;
@@ -32,6 +25,13 @@
import android.view.InflateException;
import android.view.LayoutInflater;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.util.HashMap;
+
// TODO: fix generics
/**
* Generic XML inflater. This has been adapted from {@link LayoutInflater} and
@@ -41,7 +41,14 @@
* @param T The type of the items to inflate
* @param P The type of parents (that is those items that contain other items).
* Must implement {@link GenericInflater.Parent}
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
abstract class GenericInflater<T, P extends GenericInflater.Parent> {
private final boolean DEBUG = false;
diff --git a/core/java/android/preference/ListPreference.java b/core/java/android/preference/ListPreference.java
index c0c71af..14c1dc81 100644
--- a/core/java/android/preference/ListPreference.java
+++ b/core/java/android/preference/ListPreference.java
@@ -36,7 +36,14 @@
*
* @attr ref android.R.styleable#ListPreference_entries
* @attr ref android.R.styleable#ListPreference_entryValues
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public class ListPreference extends DialogPreference {
private CharSequence[] mEntries;
private CharSequence[] mEntryValues;
diff --git a/core/java/android/preference/MultiCheckPreference.java b/core/java/android/preference/MultiCheckPreference.java
index c1260a4..e3d0e26 100644
--- a/core/java/android/preference/MultiCheckPreference.java
+++ b/core/java/android/preference/MultiCheckPreference.java
@@ -16,8 +16,6 @@
package android.preference;
-import java.util.Arrays;
-
import android.annotation.ArrayRes;
import android.app.AlertDialog.Builder;
import android.content.Context;
@@ -27,6 +25,8 @@
import android.os.Parcelable;
import android.util.AttributeSet;
+import java.util.Arrays;
+
/**
* @hide
* A {@link Preference} that displays a list of entries as
@@ -34,7 +34,14 @@
*
* @attr ref android.R.styleable#ListPreference_entries
* @attr ref android.R.styleable#ListPreference_entryValues
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public class MultiCheckPreference extends DialogPreference {
private CharSequence[] mEntries;
private String[] mEntryValues;
diff --git a/core/java/android/preference/MultiSelectListPreference.java b/core/java/android/preference/MultiSelectListPreference.java
index 138bd878..43182d9 100644
--- a/core/java/android/preference/MultiSelectListPreference.java
+++ b/core/java/android/preference/MultiSelectListPreference.java
@@ -35,10 +35,17 @@
* This preference will store a set of strings into the SharedPreferences.
* This set will contain one or more values from the
* {@link #setEntryValues(CharSequence[])} array.
- *
+ *
* @attr ref android.R.styleable#MultiSelectListPreference_entries
* @attr ref android.R.styleable#MultiSelectListPreference_entryValues
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public class MultiSelectListPreference extends DialogPreference {
private CharSequence[] mEntries;
private CharSequence[] mEntryValues;
@@ -65,25 +72,25 @@
public MultiSelectListPreference(Context context, AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.dialogPreferenceStyle);
}
-
+
public MultiSelectListPreference(Context context) {
this(context, null);
}
-
+
/**
* Sets the human-readable entries to be shown in the list. This will be
* shown in subsequent dialogs.
* <p>
* Each entry must have a corresponding index in
* {@link #setEntryValues(CharSequence[])}.
- *
+ *
* @param entries The entries.
* @see #setEntryValues(CharSequence[])
*/
public void setEntries(CharSequence[] entries) {
mEntries = entries;
}
-
+
/**
* @see #setEntries(CharSequence[])
* @param entriesResId The entries array as a resource.
@@ -91,21 +98,21 @@
public void setEntries(@ArrayRes int entriesResId) {
setEntries(getContext().getResources().getTextArray(entriesResId));
}
-
+
/**
* The list of entries to be shown in the list in subsequent dialogs.
- *
+ *
* @return The list as an array.
*/
public CharSequence[] getEntries() {
return mEntries;
}
-
+
/**
* The array to find the value to save for a preference when an entry from
* entries is selected. If a user clicks on the second item in entries, the
* second item in this array will be saved to the preference.
- *
+ *
* @param entryValues The array to be used as values to save for the preference.
*/
public void setEntryValues(CharSequence[] entryValues) {
@@ -119,20 +126,20 @@
public void setEntryValues(@ArrayRes int entryValuesResId) {
setEntryValues(getContext().getResources().getTextArray(entryValuesResId));
}
-
+
/**
* Returns the array of values to be saved for the preference.
- *
+ *
* @return The array of values.
*/
public CharSequence[] getEntryValues() {
return mEntryValues;
}
-
+
/**
* Sets the value of the key. This should contain entries in
* {@link #getEntryValues()}.
- *
+ *
* @param values The values to set for the key.
*/
public void setValues(Set<String> values) {
@@ -141,17 +148,17 @@
persistStringSet(values);
}
-
+
/**
* Retrieves the current value of the key.
*/
public Set<String> getValues() {
return mValues;
}
-
+
/**
* Returns the index of the given value (in the entry values array).
- *
+ *
* @param value The value whose index should be returned.
* @return The index of the value, or -1 if not found.
*/
@@ -165,17 +172,17 @@
}
return -1;
}
-
+
@Override
protected void onPrepareDialogBuilder(Builder builder) {
super.onPrepareDialogBuilder(builder);
-
+
if (mEntries == null || mEntryValues == null) {
throw new IllegalStateException(
"MultiSelectListPreference requires an entries array and " +
"an entryValues array.");
}
-
+
boolean[] checkedItems = getSelectedItems();
builder.setMultiChoiceItems(mEntries, checkedItems,
new DialogInterface.OnMultiChoiceClickListener() {
@@ -190,24 +197,24 @@
mNewValues.clear();
mNewValues.addAll(mValues);
}
-
+
private boolean[] getSelectedItems() {
final CharSequence[] entries = mEntryValues;
final int entryCount = entries.length;
final Set<String> values = mValues;
boolean[] result = new boolean[entryCount];
-
+
for (int i = 0; i < entryCount; i++) {
result[i] = values.contains(entries[i].toString());
}
-
+
return result;
}
-
+
@Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
-
+
if (positiveResult && mPreferenceChanged) {
final Set<String> values = mNewValues;
if (callChangeListener(values)) {
@@ -216,25 +223,25 @@
}
mPreferenceChanged = false;
}
-
+
@Override
protected Object onGetDefaultValue(TypedArray a, int index) {
final CharSequence[] defaultValues = a.getTextArray(index);
final int valueCount = defaultValues.length;
final Set<String> result = new HashSet<String>();
-
+
for (int i = 0; i < valueCount; i++) {
result.add(defaultValues[i].toString());
}
-
+
return result;
}
-
+
@Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
setValues(restoreValue ? getPersistedStringSet(mValues) : (Set<String>) defaultValue);
}
-
+
@Override
protected Parcelable onSaveInstanceState() {
final Parcelable superState = super.onSaveInstanceState();
@@ -242,42 +249,42 @@
// No need to save instance state
return superState;
}
-
+
final SavedState myState = new SavedState(superState);
myState.values = getValues();
return myState;
}
-
+
private static class SavedState extends BaseSavedState {
Set<String> values;
-
+
public SavedState(Parcel source) {
super(source);
values = new HashSet<String>();
String[] strings = source.readStringArray();
-
+
final int stringCount = strings.length;
for (int i = 0; i < stringCount; i++) {
values.add(strings[i]);
}
}
-
+
public SavedState(Parcelable superState) {
super(superState);
}
-
+
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeStringArray(values.toArray(new String[0]));
}
-
+
public static final Parcelable.Creator<SavedState> CREATOR =
new Parcelable.Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
-
+
public SavedState[] newArray(int size) {
return new SavedState[size];
}
diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java
index 2387657..3c1ba9d 100644
--- a/core/java/android/preference/Preference.java
+++ b/core/java/android/preference/Preference.java
@@ -86,7 +86,14 @@
* @attr ref android.R.styleable#Preference_recycleEnabled
* @attr ref android.R.styleable#Preference_singleLineTitle
* @attr ref android.R.styleable#Preference_iconSpaceReserved
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public class Preference implements Comparable<Preference> {
/**
* Specify for {@link #setOrder(int)} if a specific order is not required.
@@ -165,7 +172,14 @@
* {@link Preference} has been changed by the user and is
* about to be set and/or persisted. This gives the client a chance
* to prevent setting and/or persisting the value.
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices.
+ * For more information on using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+ @Deprecated
public interface OnPreferenceChangeListener {
/**
* Called when a Preference has been changed by the user. This is
@@ -182,7 +196,14 @@
/**
* Interface definition for a callback to be invoked when a {@link Preference} is
* clicked.
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices.
+ * For more information on using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+ @Deprecated
public interface OnPreferenceClickListener {
/**
* Called when a Preference has been clicked.
@@ -2070,7 +2091,14 @@
/**
* A base class for managing the instance state of a {@link Preference}.
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices.
+ * For more information on using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+ @Deprecated
public static class BaseSavedState extends AbsSavedState {
public BaseSavedState(Parcel source) {
super(source);
diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index 8ed2605..eab5937 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -97,34 +97,13 @@
* guide.</p>
* </div>
*
- * <a name="SampleCode"></a>
- * <h3>Sample Code</h3>
- *
- * <p>The following sample code shows a simple preference activity that
- * has two different sets of preferences. The implementation, consisting
- * of the activity itself as well as its two preference fragments is:</p>
- *
- * {@sample development/samples/ApiDemos/src/com/example/android/apis/preference/PreferenceWithHeaders.java
- * activity}
- *
- * <p>The preference_headers resource describes the headers to be displayed
- * and the fragments associated with them. It is:
- *
- * {@sample development/samples/ApiDemos/res/xml/preference_headers.xml headers}
- *
- * <p>The first header is shown by Prefs1Fragment, which populates itself
- * from the following XML resource:</p>
- *
- * {@sample development/samples/ApiDemos/res/xml/fragmented_preferences.xml preferences}
- *
- * <p>Note that this XML resource contains a preference screen holding another
- * fragment, the Prefs1FragmentInner implemented here. This allows the user
- * to traverse down a hierarchy of preferences; pressing back will pop each
- * fragment off the stack to return to the previous preferences.
- *
- * <p>See {@link PreferenceFragment} for information on implementing the
- * fragments themselves.
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public abstract class PreferenceActivity extends ListActivity implements
PreferenceManager.OnPreferenceTreeClickListener,
PreferenceFragment.OnPreferenceStartFragmentCallback {
@@ -337,7 +316,14 @@
/**
* Description of a single Header item that the user can select.
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices.
+ * For more information on using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+ @Deprecated
public static final class Header implements Parcelable {
/**
* Identifier for this header, to correlate with a new list when
diff --git a/core/java/android/preference/PreferenceCategory.java b/core/java/android/preference/PreferenceCategory.java
index 253481b..887e468 100644
--- a/core/java/android/preference/PreferenceCategory.java
+++ b/core/java/android/preference/PreferenceCategory.java
@@ -29,7 +29,14 @@
* read the <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>
* guide.</p>
* </div>
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public class PreferenceCategory extends PreferenceGroup {
private static final String TAG = "PreferenceCategory";
diff --git a/core/java/android/preference/PreferenceDataStore.java b/core/java/android/preference/PreferenceDataStore.java
index 8caa404..5171632 100644
--- a/core/java/android/preference/PreferenceDataStore.java
+++ b/core/java/android/preference/PreferenceDataStore.java
@@ -39,7 +39,14 @@
*
* @see Preference#setPreferenceDataStore(PreferenceDataStore)
* @see PreferenceManager#setPreferenceDataStore(PreferenceDataStore)
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public interface PreferenceDataStore {
/**
diff --git a/core/java/android/preference/PreferenceFragment.java b/core/java/android/preference/PreferenceFragment.java
index 548895e..d6c069f0 100644
--- a/core/java/android/preference/PreferenceFragment.java
+++ b/core/java/android/preference/PreferenceFragment.java
@@ -88,25 +88,14 @@
* guide.</p>
* </div>
*
- * <a name="SampleCode"></a>
- * <h3>Sample Code</h3>
- *
- * <p>The following sample code shows a simple preference fragment that is
- * populated from a resource. The resource it loads is:</p>
- *
- * {@sample development/samples/ApiDemos/res/xml/preferences.xml preferences}
- *
- * <p>The fragment implementation itself simply populates the preferences
- * when created. Note that the preferences framework takes care of loading
- * the current values out of the app preferences and writing them when changed:</p>
- *
- * {@sample development/samples/ApiDemos/src/com/example/android/apis/preference/FragmentPreferences.java
- * fragment}
- *
* @see Preference
* @see PreferenceScreen
*
- * @deprecated Use {@link android.support.v7.preference.PreferenceFragmentCompat}
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
@Deprecated
public abstract class PreferenceFragment extends Fragment implements
diff --git a/core/java/android/preference/PreferenceFrameLayout.java b/core/java/android/preference/PreferenceFrameLayout.java
index 886338f..c667824 100644
--- a/core/java/android/preference/PreferenceFrameLayout.java
+++ b/core/java/android/preference/PreferenceFrameLayout.java
@@ -24,7 +24,14 @@
/**
* @hide
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public class PreferenceFrameLayout extends FrameLayout {
private static final int DEFAULT_BORDER_TOP = 0;
private static final int DEFAULT_BORDER_BOTTOM = 0;
diff --git a/core/java/android/preference/PreferenceGroup.java b/core/java/android/preference/PreferenceGroup.java
index f135b26..b33ea4e 100644
--- a/core/java/android/preference/PreferenceGroup.java
+++ b/core/java/android/preference/PreferenceGroup.java
@@ -16,15 +16,16 @@
package android.preference;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.AttributeSet;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
/**
* A container for multiple
* {@link Preference} objects. It is a base class for Preference objects that are
@@ -38,7 +39,14 @@
* </div>
*
* @attr ref android.R.styleable#PreferenceGroup_orderingFromXml
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public abstract class PreferenceGroup extends Preference implements GenericInflater.Parent<Preference> {
/**
* The container for child {@link Preference}s. This is sorted based on the
diff --git a/core/java/android/preference/PreferenceGroupAdapter.java b/core/java/android/preference/PreferenceGroupAdapter.java
index bee45ab..fb41ea8 100644
--- a/core/java/android/preference/PreferenceGroupAdapter.java
+++ b/core/java/android/preference/PreferenceGroupAdapter.java
@@ -16,10 +16,6 @@
package android.preference;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.preference.Preference.OnPreferenceChangeInternalListener;
@@ -30,6 +26,10 @@
import android.widget.FrameLayout;
import android.widget.ListView;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
/**
* An adapter that returns the {@link Preference} contained in this group.
* In most cases, this adapter should be the base class for any custom
@@ -49,7 +49,14 @@
* @see PreferenceCategoryAdapter
*
* @hide
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public class PreferenceGroupAdapter extends BaseAdapter
implements OnPreferenceChangeInternalListener {
diff --git a/core/java/android/preference/PreferenceInflater.java b/core/java/android/preference/PreferenceInflater.java
index 727fbca..04ad107 100644
--- a/core/java/android/preference/PreferenceInflater.java
+++ b/core/java/android/preference/PreferenceInflater.java
@@ -16,16 +16,16 @@
package android.preference;
-import com.android.internal.util.XmlUtils;
+import android.content.Context;
+import android.content.Intent;
+import android.util.AttributeSet;
-import java.io.IOException;
+import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import android.content.Context;
-import android.content.Intent;
-import android.util.AttributeSet;
+import java.io.IOException;
/**
* The {@link PreferenceInflater} is used to inflate preference hierarchies from
@@ -34,7 +34,14 @@
* Do not construct this directly, instead use
* {@link Context#getSystemService(String)} with
* {@link Context#PREFERENCE_INFLATER_SERVICE}.
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
class PreferenceInflater extends GenericInflater<Preference, PreferenceGroup> {
private static final String TAG = "PreferenceInflater";
private static final String INTENT_TAG_NAME = "intent";
diff --git a/core/java/android/preference/PreferenceManager.java b/core/java/android/preference/PreferenceManager.java
index dfee1af..f741bd6 100644
--- a/core/java/android/preference/PreferenceManager.java
+++ b/core/java/android/preference/PreferenceManager.java
@@ -47,7 +47,14 @@
* {@link PreferenceActivity#addPreferencesFromResource(int)}.
*
* @see PreferenceActivity
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public class PreferenceManager {
private static final String TAG = "PreferenceManager";
@@ -1004,7 +1011,14 @@
* clicked.
*
* @hide
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices.
+ * For more information on using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+ @Deprecated
public interface OnPreferenceTreeClickListener {
/**
* Called when a preference in the tree rooted at this
@@ -1021,7 +1035,14 @@
/**
* Interface definition for a class that will be called when the container's activity
* receives an activity result.
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices.
+ * For more information on using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+ @Deprecated
public interface OnActivityResultListener {
/**
@@ -1036,7 +1057,14 @@
/**
* Interface definition for a class that will be called when the container's activity
* is stopped.
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices.
+ * For more information on using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+ @Deprecated
public interface OnActivityStopListener {
/**
@@ -1048,7 +1076,14 @@
/**
* Interface definition for a class that will be called when the container's activity
* is destroyed.
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices.
+ * For more information on using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+ @Deprecated
public interface OnActivityDestroyListener {
/**
diff --git a/core/java/android/preference/PreferenceScreen.java b/core/java/android/preference/PreferenceScreen.java
index dd00a53..c7653c8 100644
--- a/core/java/android/preference/PreferenceScreen.java
+++ b/core/java/android/preference/PreferenceScreen.java
@@ -86,7 +86,14 @@
* </div>
*
* @see PreferenceCategory
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public final class PreferenceScreen extends PreferenceGroup implements AdapterView.OnItemClickListener,
DialogInterface.OnDismissListener {
diff --git a/core/java/android/preference/RingtonePreference.java b/core/java/android/preference/RingtonePreference.java
index cd751cd..025aad0 100644
--- a/core/java/android/preference/RingtonePreference.java
+++ b/core/java/android/preference/RingtonePreference.java
@@ -40,7 +40,14 @@
* @attr ref android.R.styleable#RingtonePreference_ringtoneType
* @attr ref android.R.styleable#RingtonePreference_showDefault
* @attr ref android.R.styleable#RingtonePreference_showSilent
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public class RingtonePreference extends Preference implements
PreferenceManager.OnActivityResultListener {
diff --git a/core/java/android/preference/SeekBarDialogPreference.java b/core/java/android/preference/SeekBarDialogPreference.java
index a8e5992..32ef821 100644
--- a/core/java/android/preference/SeekBarDialogPreference.java
+++ b/core/java/android/preference/SeekBarDialogPreference.java
@@ -28,7 +28,14 @@
/**
* @hide
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public class SeekBarDialogPreference extends DialogPreference {
private final Drawable mMyIcon;
diff --git a/core/java/android/preference/SeekBarPreference.java b/core/java/android/preference/SeekBarPreference.java
index cd35f3d..f789e31 100644
--- a/core/java/android/preference/SeekBarPreference.java
+++ b/core/java/android/preference/SeekBarPreference.java
@@ -29,7 +29,14 @@
/**
* @hide
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public class SeekBarPreference extends Preference
implements OnSeekBarChangeListener {
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index a871425..f01d5b1 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -45,7 +45,14 @@
/**
* Turns a {@link SeekBar} into a volume control.
* @hide
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callback {
private static final String TAG = "SeekBarVolumizer";
diff --git a/core/java/android/preference/SwitchPreference.java b/core/java/android/preference/SwitchPreference.java
index 1ec18bb..9dea1c8 100644
--- a/core/java/android/preference/SwitchPreference.java
+++ b/core/java/android/preference/SwitchPreference.java
@@ -36,7 +36,14 @@
* @attr ref android.R.styleable#SwitchPreference_switchTextOff
* @attr ref android.R.styleable#SwitchPreference_switchTextOn
* @attr ref android.R.styleable#SwitchPreference_disableDependentsState
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public class SwitchPreference extends TwoStatePreference {
@UnsupportedAppUsage
private final Listener mListener = new Listener();
diff --git a/core/java/android/preference/TwoStatePreference.java b/core/java/android/preference/TwoStatePreference.java
index 2079a63..454472a 100644
--- a/core/java/android/preference/TwoStatePreference.java
+++ b/core/java/android/preference/TwoStatePreference.java
@@ -32,7 +32,14 @@
* Common base class for preferences that have two selectable states, persist a
* boolean value in SharedPreferences, and may have dependent preferences that are
* enabled/disabled based on the current state.
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public abstract class TwoStatePreference extends Preference {
private CharSequence mSummaryOn;
diff --git a/core/java/android/preference/VolumePreference.java b/core/java/android/preference/VolumePreference.java
index ea1d1eb..92d848a 100644
--- a/core/java/android/preference/VolumePreference.java
+++ b/core/java/android/preference/VolumePreference.java
@@ -31,7 +31,14 @@
/**
* @hide
+ *
+ * @deprecated Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/preference/package-summary.html">
+ * Preference Library</a> for consistent behavior across all devices. For more information on
+ * using the AndroidX Preference Library see
+ * <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
*/
+@Deprecated
public class VolumePreference extends SeekBarDialogPreference implements
PreferenceManager.OnActivityStopListener, View.OnKeyListener, SeekBarVolumizer.Callback {
@UnsupportedAppUsage
diff --git a/core/java/android/preference/package.html b/core/java/android/preference/package.html
index d24d5bb..382ed6e 100644
--- a/core/java/android/preference/package.html
+++ b/core/java/android/preference/package.html
@@ -1,23 +1,9 @@
<HTML>
<BODY>
-Provides classes that manage application preferences and implement the preferences UI.
-Using these ensures that all the preferences within each application are maintained
-in the same manner and the user experience is consistent with that of the system and
-other applications.
-<p>
-The preferences portion of an application
-should be ran as a separate {@link android.app.Activity} that extends
-the {@link android.preference.PreferenceActivity} class. In the PreferenceActivity, a
-{@link android.preference.PreferenceScreen} object should be the root element of the layout.
-The PreferenceScreen contains {@link android.preference.Preference} elements such as a
-{@link android.preference.CheckBoxPreference}, {@link android.preference.EditTextPreference},
-{@link android.preference.ListPreference}, {@link android.preference.PreferenceCategory},
-or {@link android.preference.RingtonePreference}. </p>
-<p>
-All settings made for a given {@link android.preference.Preference} will be automatically saved
-to the application's instance of {@link android.content.SharedPreferences}. Access to the
-SharedPreferences is simple with {@link android.preference.Preference#getSharedPreferences()}.</p>
-<p>
-Note that saved preferences are accessible only to the application that created them.</p>
+These classes are deprecated. Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+<a href="{@docRoot}reference/androidx/preference/package-summary.html">
+Preference Library</a> for consistent behavior across all devices. For more information on
+using the AndroidX Preference Library see
+<a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>.
</BODY>
</HTML>
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b266648..30bcbf4 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1692,6 +1692,15 @@
/** @hide - Private call() method to write to 'configuration' table */
public static final String CALL_METHOD_PUT_CONFIG = "PUT_config";
+ /** @hide - Private call() method to delete from the 'system' table */
+ public static final String CALL_METHOD_DELETE_SYSTEM = "DELETE_system";
+
+ /** @hide - Private call() method to delete from the 'secure' table */
+ public static final String CALL_METHOD_DELETE_SECURE = "DELETE_secure";
+
+ /** @hide - Private call() method to delete from the 'global' table */
+ public static final String CALL_METHOD_DELETE_GLOBAL = "DELETE_global";
+
/** @hide - Private call() method to reset to defaults the 'global' table */
public static final String CALL_METHOD_RESET_GLOBAL = "RESET_global";
diff --git a/core/java/android/service/carrier/CarrierIdentifier.java b/core/java/android/service/carrier/CarrierIdentifier.java
index 09bba4b..e930f40 100644
--- a/core/java/android/service/carrier/CarrierIdentifier.java
+++ b/core/java/android/service/carrier/CarrierIdentifier.java
@@ -19,6 +19,7 @@
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.TelephonyManager;
import com.android.internal.telephony.uicc.IccUtils;
@@ -26,7 +27,10 @@
/**
* Used to pass info to CarrierConfigService implementations so they can decide what values to
- * return.
+ * return. Instead of passing mcc, mnc, gid1, gid2, spn, imsi to locate carrier information,
+ * CarrierIdentifier also include carrier id {@link TelephonyManager#getSimCarrierId()},
+ * a platform-wide unique identifier for each carrier. CarrierConfigService can directly use
+ * carrier id as the key to look up the carrier info.
*/
public class CarrierIdentifier implements Parcelable {
@@ -49,15 +53,40 @@
private @Nullable String mImsi;
private @Nullable String mGid1;
private @Nullable String mGid2;
+ private int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
+ private int mPreciseCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
public CarrierIdentifier(String mcc, String mnc, @Nullable String spn, @Nullable String imsi,
@Nullable String gid1, @Nullable String gid2) {
+ this(mcc, mnc, spn, imsi, gid1, gid2, TelephonyManager.UNKNOWN_CARRIER_ID,
+ TelephonyManager.UNKNOWN_CARRIER_ID);
+ }
+
+ /**
+ * @param mcc mobile country code
+ * @param mnc mobile network code
+ * @param spn service provider name
+ * @param imsi International Mobile Subscriber Identity {@link TelephonyManager#getSubscriberId()}
+ * @param gid1 group id level 1 {@link TelephonyManager#getGroupIdLevel1()}
+ * @param gid2 group id level 2
+ * @param carrierid carrier unique identifier {@link TelephonyManager#getSimCarrierId()}, used
+ * to uniquely identify the carrier and look up the carrier configurations.
+ * @param preciseCarrierId precise carrier identifier {@link TelephonyManager#getSimPreciseCarrierId()}
+ * @hide
+ *
+ * TODO: expose this to public API
+ */
+ public CarrierIdentifier(String mcc, String mnc, @Nullable String spn,
+ @Nullable String imsi, @Nullable String gid1, @Nullable String gid2,
+ int carrierid, int preciseCarrierId) {
mMcc = mcc;
mMnc = mnc;
mSpn = spn;
mImsi = imsi;
mGid1 = gid1;
mGid2 = gid2;
+ mCarrierId = carrierid;
+ mPreciseCarrierId = preciseCarrierId;
}
/**
@@ -125,6 +154,22 @@
return mGid2;
}
+ /**
+ * Get the carrier id {@link TelephonyManager#getSimCarrierId() }
+ * @hide
+ */
+ public int getCarrierId() {
+ return mCarrierId;
+ }
+
+ /**
+ * Get the precise carrier id {@link TelephonyManager#getSimPreciseCarrierId()}
+ * @hide
+ */
+ public int getPreciseCarrierId() {
+ return mPreciseCarrierId;
+ }
+
@Override
public boolean equals(Object obj) {
if (this == obj) {
@@ -140,19 +185,14 @@
&& Objects.equals(mSpn, that.mSpn)
&& Objects.equals(mImsi, that.mImsi)
&& Objects.equals(mGid1, that.mGid1)
- && Objects.equals(mGid2, that.mGid2);
+ && Objects.equals(mGid2, that.mGid2)
+ && Objects.equals(mCarrierId, that.mCarrierId)
+ && Objects.equals(mPreciseCarrierId, that.mPreciseCarrierId);
}
@Override
- public int hashCode() {
- int result = 1;
- result = 31 * result + Objects.hashCode(mMcc);
- result = 31 * result + Objects.hashCode(mMnc);
- result = 31 * result + Objects.hashCode(mSpn);
- result = 31 * result + Objects.hashCode(mImsi);
- result = 31 * result + Objects.hashCode(mGid1);
- result = 31 * result + Objects.hashCode(mGid2);
- return result;
+ public int hashCode(){
+ return Objects.hash(mMcc, mMnc, mSpn, mImsi, mGid1, mGid2, mCarrierId, mPreciseCarrierId);
}
@Override
@@ -168,18 +208,22 @@
out.writeString(mImsi);
out.writeString(mGid1);
out.writeString(mGid2);
+ out.writeInt(mCarrierId);
+ out.writeInt(mPreciseCarrierId);
}
@Override
public String toString() {
return "CarrierIdentifier{"
- + "mcc=" + mMcc
- + ",mnc=" + mMnc
- + ",spn=" + mSpn
- + ",imsi=" + mImsi
- + ",gid1=" + mGid1
- + ",gid2=" + mGid2
- + "}";
+ + "mcc=" + mMcc
+ + ",mnc=" + mMnc
+ + ",spn=" + mSpn
+ + ",imsi=" + mImsi
+ + ",gid1=" + mGid1
+ + ",gid2=" + mGid2
+ + ",carrierid=" + mCarrierId
+ + ",mPreciseCarrierId=" + mPreciseCarrierId
+ + "}";
}
/** @hide */
@@ -190,6 +234,8 @@
mImsi = in.readString();
mGid1 = in.readString();
mGid2 = in.readString();
+ mCarrierId = in.readInt();
+ mPreciseCarrierId = in.readInt();
}
/** @hide */
diff --git a/core/java/android/service/carrier/CarrierService.java b/core/java/android/service/carrier/CarrierService.java
index b94ccf9..c351d89 100644
--- a/core/java/android/service/carrier/CarrierService.java
+++ b/core/java/android/service/carrier/CarrierService.java
@@ -93,7 +93,11 @@
* </p>
*
* @param id contains details about the current carrier that can be used do decide what
- * configuration values to return.
+ * configuration values to return. Instead of using details like MCCMNC to decide
+ * current carrier, it also contains subscription carrier id
+ * {@link android.telephony.TelephonyManager#getSimCarrierId()}, a platform-wide
+ * unique identifier for each carrier, CarrierConfigService can directly use carrier
+ * id as the key to look up the carrier info.
* @return a {@link PersistableBundle} object containing the configuration or null if default
* values should be used.
*/
diff --git a/core/java/android/service/wallpaper/IWallpaperConnection.aidl b/core/java/android/service/wallpaper/IWallpaperConnection.aidl
index 3c3ef0c..a976d0e 100644
--- a/core/java/android/service/wallpaper/IWallpaperConnection.aidl
+++ b/core/java/android/service/wallpaper/IWallpaperConnection.aidl
@@ -24,8 +24,8 @@
* @hide
*/
interface IWallpaperConnection {
- void attachEngine(IWallpaperEngine engine);
- void engineShown(IWallpaperEngine engine);
+ void attachEngine(IWallpaperEngine engine, int displayId);
+ void engineShown(IWallpaperEngine engine);
ParcelFileDescriptor setWallpaper(String name);
void onWallpaperColorsChanged(in WallpaperColors colors);
}
diff --git a/core/java/android/service/wallpaper/IWallpaperService.aidl b/core/java/android/service/wallpaper/IWallpaperService.aidl
index 5fd0157..99a81f5 100644
--- a/core/java/android/service/wallpaper/IWallpaperService.aidl
+++ b/core/java/android/service/wallpaper/IWallpaperService.aidl
@@ -24,6 +24,6 @@
*/
oneway interface IWallpaperService {
void attach(IWallpaperConnection connection,
- IBinder windowToken, int windowType, boolean isPreview,
- int reqWidth, int reqHeight, in Rect padding);
+ IBinder windowToken, int windowType, boolean isPreview,
+ int reqWidth, int reqHeight, in Rect padding, int displayId);
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 4bd86a4..6f51bec 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -24,7 +24,6 @@
import android.app.WallpaperColors;
import android.app.WallpaperInfo;
import android.app.WallpaperManager;
-import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
@@ -806,7 +805,7 @@
com.android.internal.R.style.Animation_Wallpaper;
mInputChannel = new InputChannel();
if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE,
- Display.DEFAULT_DISPLAY, mWinFrame, mContentInsets, mStableInsets,
+ mDisplay.getDisplayId(), mWinFrame, mContentInsets, mStableInsets,
mOutsets, mDisplayCutout, mInputChannel) < 0) {
Log.w(TAG, "Failed to add window while updating wallpaper surface.");
return;
@@ -1015,7 +1014,15 @@
if (mDestroyed) {
return;
}
-
+
+ mDisplayManager = getSystemService(DisplayManager.class);
+ mDisplay = mDisplayManager.getDisplay(wrapper.mDisplayId);
+ if (mDisplay == null) {
+ // TODO(b/115486823) Ignore this engine.
+ Log.e(TAG, "Attaching to a non-existent display: " + wrapper.mDisplayId);
+ return;
+ }
+
mIWallpaperEngine = wrapper;
mCaller = wrapper.mCaller;
mConnection = wrapper.mConnection;
@@ -1027,10 +1034,7 @@
mWindow.setSession(mSession);
mLayout.packageName = getPackageName();
-
- mDisplayManager = (DisplayManager)getSystemService(Context.DISPLAY_SERVICE);
mDisplayManager.registerDisplayListener(mDisplayListener, mCaller.getHandler());
- mDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
mDisplayState = mDisplay.getState();
if (DEBUG) Log.v(TAG, "onCreate(): " + this);
@@ -1268,12 +1272,14 @@
int mReqWidth;
int mReqHeight;
final Rect mDisplayPadding = new Rect();
+ int mDisplayId;
Engine mEngine;
IWallpaperEngineWrapper(WallpaperService context,
IWallpaperConnection conn, IBinder windowToken,
- int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding) {
+ int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding,
+ int displayId) {
mCaller = new HandlerCaller(context, context.getMainLooper(), this, true);
mConnection = conn;
mWindowToken = windowToken;
@@ -1282,6 +1288,7 @@
mReqWidth = reqWidth;
mReqHeight = reqHeight;
mDisplayPadding.set(padding);
+ mDisplayId = displayId;
Message msg = mCaller.obtainMessage(DO_ATTACH);
mCaller.sendMessage(msg);
@@ -1353,7 +1360,7 @@
switch (message.what) {
case DO_ATTACH: {
try {
- mConnection.attachEngine(this);
+ mConnection.attachEngine(this, mDisplayId);
} catch (RemoteException e) {
Log.w(TAG, "Wallpaper host disappeared", e);
return;
@@ -1453,9 +1460,10 @@
@Override
public void attach(IWallpaperConnection conn, IBinder windowToken,
- int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding) {
+ int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding,
+ int displayId) {
new IWallpaperEngineWrapper(mTarget, conn, windowToken,
- windowType, isPreview, reqWidth, reqHeight, padding);
+ windowType, isPreview, reqWidth, reqHeight, padding, displayId);
}
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 5b1544b..c6155ce 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -390,7 +390,8 @@
com.android.internal.R.bool.config_enableHapticTextHandle);
if (FLAG_USE_MAGNIFIER) {
- final Magnifier magnifier = new Magnifier.Builder(mTextView).build();
+ final Magnifier magnifier =
+ Magnifier.createBuilderWithOldMagnifierDefaults(mTextView).build();
mMagnifierAnimator = new MagnifierMotionAnimator(magnifier);
}
}
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 932f182..f4c25c3 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -145,7 +145,47 @@
*/
@Deprecated
public Magnifier(@NonNull View view) {
- this(new Builder(view));
+ this(createBuilderWithOldMagnifierDefaults(view));
+ }
+
+ static Builder createBuilderWithOldMagnifierDefaults(final View view) {
+ final Builder params = new Builder(view);
+ final Context context = view.getContext();
+ final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Magnifier,
+ R.attr.magnifierStyle, 0);
+ params.mWidth = a.getDimensionPixelSize(R.styleable.Magnifier_magnifierWidth, 0);
+ params.mHeight = a.getDimensionPixelSize(R.styleable.Magnifier_magnifierHeight, 0);
+ params.mElevation = a.getDimension(R.styleable.Magnifier_magnifierElevation, 0);
+ params.mCornerRadius = getDeviceDefaultDialogCornerRadius(context);
+ params.mZoom = a.getFloat(R.styleable.Magnifier_magnifierZoom, 0);
+ params.mHorizontalDefaultSourceToMagnifierOffset =
+ a.getDimensionPixelSize(R.styleable.Magnifier_magnifierHorizontalOffset, 0);
+ params.mVerticalDefaultSourceToMagnifierOffset =
+ a.getDimensionPixelSize(R.styleable.Magnifier_magnifierVerticalOffset, 0);
+ params.mOverlay = new ColorDrawable(a.getColor(
+ R.styleable.Magnifier_magnifierColorOverlay, Color.TRANSPARENT));
+ a.recycle();
+ params.mForcePositionWithinWindowSystemInsetsBounds = true;
+ params.mLeftContentBound = SOURCE_BOUND_MAX_VISIBLE;
+ params.mTopContentBound = SOURCE_BOUND_MAX_IN_SURFACE;
+ params.mRightContentBound = SOURCE_BOUND_MAX_VISIBLE;
+ params.mBottomContentBound = SOURCE_BOUND_MAX_IN_SURFACE;
+ return params;
+ }
+
+ /**
+ * Returns the device default theme dialog corner radius attribute.
+ * We retrieve this from the device default theme to avoid
+ * using the values set in the custom application themes.
+ */
+ private static float getDeviceDefaultDialogCornerRadius(final Context context) {
+ final Context deviceDefaultContext =
+ new ContextThemeWrapper(context, R.style.Theme_DeviceDefault);
+ final TypedArray ta = deviceDefaultContext.obtainStyledAttributes(
+ new int[]{android.R.attr.dialogCornerRadius});
+ final float dialogCornerRadius = ta.getDimension(0, 0);
+ ta.recycle();
+ return dialogCornerRadius;
}
private Magnifier(@NonNull Builder params) {
@@ -1105,41 +1145,23 @@
}
private void applyDefaults() {
- final Context context = mView.getContext();
- final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Magnifier,
- R.attr.magnifierStyle, 0);
- mWidth = a.getDimensionPixelSize(R.styleable.Magnifier_magnifierWidth, 0);
- mHeight = a.getDimensionPixelSize(R.styleable.Magnifier_magnifierHeight, 0);
- mElevation = a.getDimension(R.styleable.Magnifier_magnifierElevation, 0);
- mCornerRadius = getDeviceDefaultDialogCornerRadius();
- mZoom = a.getFloat(R.styleable.Magnifier_magnifierZoom, 0);
+ final Resources resources = mView.getContext().getResources();
+ mWidth = resources.getDimensionPixelSize(R.dimen.default_magnifier_width);
+ mHeight = resources.getDimensionPixelSize(R.dimen.default_magnifier_height);
+ mElevation = resources.getDimension(R.dimen.default_magnifier_elevation);
+ mCornerRadius = resources.getDimension(R.dimen.default_magnifier_corner_radius);
+ mZoom = resources.getFloat(R.dimen.default_magnifier_zoom);
mHorizontalDefaultSourceToMagnifierOffset =
- a.getDimensionPixelSize(R.styleable.Magnifier_magnifierHorizontalOffset, 0);
+ resources.getDimensionPixelSize(R.dimen.default_magnifier_horizontal_offset);
mVerticalDefaultSourceToMagnifierOffset =
- a.getDimensionPixelSize(R.styleable.Magnifier_magnifierVerticalOffset, 0);
- mOverlay = new ColorDrawable(a.getColor(
- R.styleable.Magnifier_magnifierColorOverlay, Color.TRANSPARENT));
- a.recycle();
+ resources.getDimensionPixelSize(R.dimen.default_magnifier_vertical_offset);
+ mOverlay = new ColorDrawable(resources.getColor(
+ R.color.default_magnifier_color_overlay, null));
mForcePositionWithinWindowSystemInsetsBounds = true;
mLeftContentBound = SOURCE_BOUND_MAX_VISIBLE;
- mTopContentBound = SOURCE_BOUND_MAX_IN_SURFACE;
+ mTopContentBound = SOURCE_BOUND_MAX_VISIBLE;
mRightContentBound = SOURCE_BOUND_MAX_VISIBLE;
- mBottomContentBound = SOURCE_BOUND_MAX_IN_SURFACE;
- }
-
- /**
- * Returns the device default theme dialog corner radius attribute.
- * We retrieve this from the device default theme to avoid
- * using the values set in the custom application themes.
- */
- private float getDeviceDefaultDialogCornerRadius() {
- final Context deviceDefaultContext =
- new ContextThemeWrapper(mView.getContext(), R.style.Theme_DeviceDefault);
- final TypedArray ta = deviceDefaultContext.obtainStyledAttributes(
- new int[]{android.R.attr.dialogCornerRadius});
- final float dialogCornerRadius = ta.getDimension(0, 0);
- ta.recycle();
- return dialogCornerRadius;
+ mBottomContentBound = SOURCE_BOUND_MAX_VISIBLE;
}
/**
@@ -1186,8 +1208,7 @@
}
/**
- * Sets the corner radius of the magnifier window, in pixels.
- * Defaults to the corner radius defined in the device default theme.
+ * Sets the corner radius of the magnifier window, in pixels. Defaults to 2dp.
* @param cornerRadius the corner radius to be set
*/
@NonNull
@@ -1201,10 +1222,11 @@
/**
* Sets an overlay that will be drawn on the top of the magnifier content.
* In general, the overlay should not be opaque, in order to let the expected magnifier
- * content be partially visible. The default overlay is a white {@link ColorDrawable},
- * with 5% alpha, aiming to make the magnifier distinguishable when shown in dark
- * application regions. To disable this default (or in general to have no overlay), the
- * parameter should be set to {@code null}. The overlay will be automatically redrawn
+ * content be partially visible. The default overlay is {@code null} (no overlay).
+ * As an example, TextView applies a white {@link ColorDrawable} overlay with
+ * 5% alpha, aiming to make the magnifier distinguishable when shown in dark
+ * application regions. To disable the overlay, the parameter should be set
+ * to {@code null}. If not null, the overlay will be automatically redrawn
* when the drawable is invalidated. To achieve this, the magnifier will set a new
* {@link android.graphics.drawable.Drawable.Callback} for the overlay drawable,
* so keep in mind that any existing one set by the application will be lost.
@@ -1220,7 +1242,7 @@
* Sets an offset that should be added to the content source center to obtain
* the position of the magnifier window, when the {@link #show(float, float)}
* method is called. The offset is ignored when {@link #show(float, float, float, float)}
- * is used. The offset can be negative, and it defaults to (0dp, -42dp).
+ * is used. The offset can be negative. It defaults to (0dp, 0dp).
* @param horizontalOffset the horizontal component of the offset
* @param verticalOffset the vertical component of the offset
*/
@@ -1406,8 +1428,8 @@
final Resources resources = Resources.getSystem();
final float density = resources.getDisplayMetrics().density;
final PointF size = new PointF();
- size.x = resources.getDimension(R.dimen.magnifier_width) / density;
- size.y = resources.getDimension(R.dimen.magnifier_height) / density;
+ size.x = resources.getDimension(R.dimen.default_magnifier_width) / density;
+ size.y = resources.getDimension(R.dimen.default_magnifier_height) / density;
return size;
}
diff --git a/core/java/com/android/internal/os/KernelCpuThreadReader.java b/core/java/com/android/internal/os/KernelCpuThreadReader.java
index ade5d05..3861739 100644
--- a/core/java/com/android/internal/os/KernelCpuThreadReader.java
+++ b/core/java/com/android/internal/os/KernelCpuThreadReader.java
@@ -29,6 +29,7 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
+import java.util.function.Predicate;
/**
* Given a process, will iterate over the child threads of the process, and return the CPU usage
@@ -70,6 +71,11 @@
private static final String THREAD_NAME_FILENAME = "comm";
/**
+ * Glob pattern for the process directory names under {@code proc}
+ */
+ private static final String PROCESS_DIRECTORY_FILTER = "[0-9]*";
+
+ /**
* Default process name when the name can't be read
*/
private static final String DEFAULT_PROCESS_NAME = "unknown_process";
@@ -96,6 +102,18 @@
private static final int NUM_BUCKETS = 8;
/**
+ * Default predicate for what UIDs to check for when getting processes. This filters to only
+ * select system UIDs (1000-1999)
+ */
+ private static final Predicate<Integer> DEFAULT_UID_PREDICATE =
+ uid -> 1000 <= uid && uid < 2000;
+
+ /**
+ * Value returned when there was an error getting an integer ID value (e.g. PID, UID)
+ */
+ private static final int ID_ERROR = -1;
+
+ /**
* Where the proc filesystem is mounted
*/
private final Path mProcPath;
@@ -116,8 +134,13 @@
*/
private final FrequencyBucketCreator mFrequencyBucketCreator;
+ private final Injector mInjector;
+
private KernelCpuThreadReader() throws IOException {
- this(DEFAULT_PROC_PATH, DEFAULT_INITIAL_TIME_IN_STATE_PATH);
+ this(
+ DEFAULT_PROC_PATH,
+ DEFAULT_INITIAL_TIME_IN_STATE_PATH,
+ new Injector());
}
/**
@@ -128,9 +151,13 @@
* format
*/
@VisibleForTesting
- public KernelCpuThreadReader(Path procPath, Path initialTimeInStatePath) throws IOException {
+ public KernelCpuThreadReader(
+ Path procPath,
+ Path initialTimeInStatePath,
+ Injector injector) throws IOException {
mProcPath = procPath;
mProcTimeInStateReader = new ProcTimeInStateReader(initialTimeInStatePath);
+ mInjector = injector;
// Copy mProcTimeInState's frequencies and initialize bucketing
final long[] frequenciesKhz = mProcTimeInStateReader.getFrequenciesKhz();
@@ -154,6 +181,67 @@
}
/**
+ * Get the per-thread CPU usage of all processes belonging to UIDs between {@code [1000, 2000)}
+ */
+ @Nullable
+ public ArrayList<ProcessCpuUsage> getProcessCpuUsageByUids() {
+ return getProcessCpuUsageByUids(DEFAULT_UID_PREDICATE);
+ }
+
+ /**
+ * Get the per-thread CPU usage of all processes belonging to a set of UIDs
+ *
+ * <p>This function will crawl through all process {@code proc} directories found by the pattern
+ * {@code /proc/[0-9]*}, and then check the UID using {@code /proc/$PID/status}. This takes
+ * approximately 500ms on a Pixel 2. Therefore, this method can be computationally expensive,
+ * and should not be called more than once an hour.
+ *
+ * @param uidPredicate only get usage from processes owned by UIDs that match this predicate
+ */
+ @Nullable
+ public ArrayList<ProcessCpuUsage> getProcessCpuUsageByUids(Predicate<Integer> uidPredicate) {
+ if (DEBUG) {
+ Slog.d(TAG, "Reading CPU thread usages for processes owned by UIDs");
+ }
+
+ final ArrayList<ProcessCpuUsage> processCpuUsages = new ArrayList<>();
+
+ try (DirectoryStream<Path> processPaths =
+ Files.newDirectoryStream(mProcPath, PROCESS_DIRECTORY_FILTER)) {
+ for (Path processPath : processPaths) {
+ final int processId = getProcessId(processPath);
+ final int uid = mInjector.getUidForPid(processId);
+ if (uid == ID_ERROR || processId == ID_ERROR) {
+ continue;
+ }
+ if (!uidPredicate.test(uid)) {
+ continue;
+ }
+
+ final ProcessCpuUsage processCpuUsage =
+ getProcessCpuUsage(processPath, processId, uid);
+ if (processCpuUsage != null) {
+ processCpuUsages.add(processCpuUsage);
+ }
+ }
+ } catch (IOException e) {
+ Slog.w("Failed to iterate over process paths", e);
+ return null;
+ }
+
+ if (processCpuUsages.isEmpty()) {
+ Slog.w(TAG, "Didn't successfully get any process CPU information for UIDs specified");
+ return null;
+ }
+
+ if (DEBUG) {
+ Slog.d(TAG, "Read usage for " + processCpuUsages.size() + " processes");
+ }
+
+ return processCpuUsages;
+ }
+
+ /**
* Read all of the CPU usage statistics for each child thread of the current process
*
* @return process CPU usage containing usage of all child threads
@@ -162,8 +250,8 @@
public ProcessCpuUsage getCurrentProcessCpuUsage() {
return getProcessCpuUsage(
mProcPath.resolve("self"),
- Process.myPid(),
- Process.myUid());
+ mInjector.myPid(),
+ mInjector.myUid());
}
/**
@@ -172,7 +260,8 @@
* @param processPath the {@code /proc} path of the thread
* @param processId the ID of the process
* @param uid the ID of the user who owns the process
- * @return process CPU usage containing usage of all child threads
+ * @return process CPU usage containing usage of all child threads. Null if the process exited
+ * and its {@code proc} directory was removed while collecting information
*/
@Nullable
private ProcessCpuUsage getProcessCpuUsage(Path processPath, int processId, int uid) {
@@ -224,7 +313,8 @@
* Get a thread's CPU usage
*
* @param threadDirectory the {@code /proc} directory of the thread
- * @return null in the case that the directory read failed
+ * @return thread CPU usage. Null if the thread exited and its {@code proc} directory was
+ * removed while collecting information
*/
@Nullable
private ThreadCpuUsage getThreadCpuUsage(Path threadDirectory) {
@@ -280,6 +370,22 @@
}
/**
+ * Get the ID of a process from its path
+ *
+ * @param processPath {@code proc} path of the process
+ * @return the ID, {@link #ID_ERROR} if the path could not be parsed
+ */
+ private int getProcessId(Path processPath) {
+ String fileName = processPath.getFileName().toString();
+ try {
+ return Integer.parseInt(fileName);
+ } catch (NumberFormatException e) {
+ Slog.w(TAG, "Failed to parse " + fileName + " as process ID", e);
+ return ID_ERROR;
+ }
+ }
+
+ /**
* Puts frequencies and usage times into buckets
*/
@VisibleForTesting
@@ -443,4 +549,31 @@
this.usageTimesMillis = usageTimesMillis;
}
}
+
+ /**
+ * Used to inject static methods from {@link Process}
+ */
+ @VisibleForTesting
+ public static class Injector {
+ /**
+ * Get the PID of the current process
+ */
+ public int myPid() {
+ return Process.myPid();
+ }
+
+ /**
+ * Get the UID that owns the current process
+ */
+ public int myUid() {
+ return Process.myUid();
+ }
+
+ /**
+ * Get the UID for the process with ID {@code pid}
+ */
+ public int getUidForPid(int pid) {
+ return Process.getUidForPid(pid);
+ }
+ }
}
diff --git a/core/java/com/android/internal/os/ProcTimeInStateReader.java b/core/java/com/android/internal/os/ProcTimeInStateReader.java
index 3a63498..2318473 100644
--- a/core/java/com/android/internal/os/ProcTimeInStateReader.java
+++ b/core/java/com/android/internal/os/ProcTimeInStateReader.java
@@ -19,9 +19,15 @@
import android.annotation.Nullable;
import android.os.Process;
+import com.android.internal.util.ArrayUtils;
+
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
/**
* Reads and parses {@code time_in_state} files in the {@code proc} filesystem.
@@ -43,6 +49,17 @@
*
* This file would indicate that the CPU has spent 30 milliseconds at frequency 300,000KHz (300Mhz)
* and 10 milliseconds at frequency 1,900,800KHz (1.9GHz).
+ *
+ * <p>This class will also read {@code time_in_state} files with headers, such as:
+ * <pre>
+ * cpu0
+ * 300000 3
+ * 364800 0
+ * ...
+ * cpu4
+ * 300000 1
+ * 364800 4
+ * </pre>
*/
public class ProcTimeInStateReader {
private static final String TAG = "ProcTimeInStateReader";
@@ -51,24 +68,28 @@
* The format of a single line of the {@code time_in_state} file that exports the frequency
* values
*/
- private static final int[] TIME_IN_STATE_LINE_FREQUENCY_FORMAT = {
+ private static final List<Integer> TIME_IN_STATE_LINE_FREQUENCY_FORMAT = Arrays.asList(
Process.PROC_OUT_LONG | Process.PROC_SPACE_TERM,
- Process.PROC_NEWLINE_TERM,
- };
+ Process.PROC_NEWLINE_TERM
+ );
/**
* The format of a single line of the {@code time_in_state} file that exports the time values
*/
- private static final int[] TIME_IN_STATE_LINE_TIME_FORMAT = {
+ private static final List<Integer> TIME_IN_STATE_LINE_TIME_FORMAT = Arrays.asList(
Process.PROC_SPACE_TERM,
- Process.PROC_OUT_LONG | Process.PROC_NEWLINE_TERM,
- };
+ Process.PROC_OUT_LONG | Process.PROC_NEWLINE_TERM
+ );
/**
- * The format of the {@code time_in_state} file, defined using {@link Process}'s {@code
- * PROC_OUT_LONG} and related variables
- *
- * Defined on first successful read of {@code time_in_state} file.
+ * The format of a header line of the {@code time_in_state} file
+ */
+ private static final List<Integer> TIME_IN_STATE_HEADER_LINE_FORMAT =
+ Collections.singletonList(Process.PROC_NEWLINE_TERM);
+
+ /**
+ * The format of the {@code time_in_state} file to extract times, defined using {@link
+ * Process}'s {@code PROC_OUT_LONG} and related variables
*/
private int[] mTimeInStateTimeFormat;
@@ -141,46 +162,44 @@
// Read the bytes of the `time_in_state` file
byte[] timeInStateBytes = Files.readAllBytes(timeInStatePath);
- // The number of lines in the `time_in_state` file is the number of frequencies available
+ // Iterate over the lines of the time_in_state file, for each one adding a line to the
+ // formats. These formats are used to extract either the frequencies or the times from a
+ // time_in_state file
+ // Also check if each line is a header, and handle this in the created format arrays
+ ArrayList<Integer> timeInStateFrequencyFormat = new ArrayList<>();
+ ArrayList<Integer> timeInStateTimeFormat = new ArrayList<>();
int numFrequencies = 0;
for (int i = 0; i < timeInStateBytes.length; i++) {
- if (timeInStateBytes[i] == '\n') {
+ // If the first character of the line is not a digit, we treat it as a header
+ if (!Character.isDigit(timeInStateBytes[i])) {
+ timeInStateFrequencyFormat.addAll(TIME_IN_STATE_HEADER_LINE_FORMAT);
+ timeInStateTimeFormat.addAll(TIME_IN_STATE_HEADER_LINE_FORMAT);
+ } else {
+ timeInStateFrequencyFormat.addAll(TIME_IN_STATE_LINE_FREQUENCY_FORMAT);
+ timeInStateTimeFormat.addAll(TIME_IN_STATE_LINE_TIME_FORMAT);
numFrequencies++;
}
- }
- if (numFrequencies == 0) {
- throw new IOException("Empty time_in_state file");
+ // Go to the next line
+ while (i < timeInStateBytes.length && timeInStateBytes[i] != '\n') {
+ i++;
+ }
}
- // Set `mTimeInStateTimeFormat` and `timeInStateFrequencyFormat` to the correct length, and
- // then copy in the `TIME_IN_STATE_{FREQUENCY,TIME}_LINE_FORMAT` until it's full. As we only
- // use the frequency format in this method, it is not an member variable.
- final int[] timeInStateTimeFormat =
- new int[numFrequencies * TIME_IN_STATE_LINE_TIME_FORMAT.length];
- final int[] timeInStateFrequencyFormat =
- new int[numFrequencies * TIME_IN_STATE_LINE_FREQUENCY_FORMAT.length];
- for (int i = 0; i < numFrequencies; i++) {
- System.arraycopy(
- TIME_IN_STATE_LINE_FREQUENCY_FORMAT, 0, timeInStateFrequencyFormat,
- i * TIME_IN_STATE_LINE_FREQUENCY_FORMAT.length,
- TIME_IN_STATE_LINE_FREQUENCY_FORMAT.length);
- System.arraycopy(
- TIME_IN_STATE_LINE_TIME_FORMAT, 0, timeInStateTimeFormat,
- i * TIME_IN_STATE_LINE_TIME_FORMAT.length,
- TIME_IN_STATE_LINE_TIME_FORMAT.length);
+ if (numFrequencies == 0) {
+ throw new IOException("Empty time_in_state file");
}
// Read the frequencies from the `time_in_state` file and store them, as they will be the
// same for every `time_in_state` file
final long[] readLongs = new long[numFrequencies];
final boolean readSuccess = Process.parseProcLine(
- timeInStateBytes, 0, timeInStateBytes.length, timeInStateFrequencyFormat,
- null, readLongs, null);
+ timeInStateBytes, 0, timeInStateBytes.length,
+ ArrayUtils.convertToIntArray(timeInStateFrequencyFormat), null, readLongs, null);
if (!readSuccess) {
throw new IOException("Failed to parse time_in_state file");
}
- mTimeInStateTimeFormat = timeInStateTimeFormat;
+ mTimeInStateTimeFormat = ArrayUtils.convertToIntArray(timeInStateTimeFormat);
mFrequenciesKhz = readLongs;
}
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 20d315f..e35b701 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4201,7 +4201,13 @@
<!-- @SystemApi Allows modifying accessibility state.
@hide -->
<permission android:name="android.permission.MANAGE_ACCESSIBILITY"
- android:protectionLevel="signature|setup" />
+ android:protectionLevel="signature|setup" />
+
+ <!-- @SystemApi Allows an app to grant a profile owner access to device identifiers.
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS"
+ android:protectionLevel="signature" />
<!-- Allows financial apps to read filtered sms messages. -->
<permission android:name="android.permission.SMS_FINANCIAL_TRANSACTIONS"
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index ffcd300..4122cf0 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -211,6 +211,6 @@
<color name="floating_popup_divider_light">#E9E9E9</color>
<!-- Magnifier -->
- <color name="magnifier_color_overlay">#0EFFFFFF</color>
+ <color name="default_magnifier_color_overlay">#00FFFFFF</color>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index b65c0fd..e902989 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -578,12 +578,13 @@
<dimen name="floating_toolbar_icon_text_spacing">8dp</dimen>
<!-- Magnifier dimensions -->
- <dimen name="magnifier_width">100dp</dimen>
- <dimen name="magnifier_height">48dp</dimen>
- <dimen name="magnifier_elevation">4dp</dimen>
- <dimen name="magnifier_vertical_offset">-42dp</dimen>
- <dimen name="magnifier_horizontal_offset">0dp</dimen>
- <item type="dimen" format="float" name="magnifier_zoom">1.25</item>
+ <dimen name="default_magnifier_width">100dp</dimen>
+ <dimen name="default_magnifier_height">48dp</dimen>
+ <dimen name="default_magnifier_elevation">4dp</dimen>
+ <dimen name="default_magnifier_corner_radius">2dp</dimen>
+ <dimen name="default_magnifier_vertical_offset">-42dp</dimen>
+ <dimen name="default_magnifier_horizontal_offset">0dp</dimen>
+ <item type="dimen" format="float" name="default_magnifier_zoom">1.25</item>
<dimen name="chooser_grid_padding">0dp</dimen>
<!-- Spacing around the background change frome service to non-service -->
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index ec22f42..bd53936 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -800,13 +800,13 @@
</style>
<style name="Widget.Magnifier">
- <item name="magnifierWidth">@dimen/magnifier_width</item>
- <item name="magnifierHeight">@dimen/magnifier_height</item>
- <item name="magnifierZoom">@dimen/magnifier_zoom</item>
- <item name="magnifierElevation">@dimen/magnifier_elevation</item>
- <item name="magnifierVerticalOffset">@dimen/magnifier_vertical_offset</item>
- <item name="magnifierHorizontalOffset">@dimen/magnifier_horizontal_offset</item>
- <item name="magnifierColorOverlay">@color/magnifier_color_overlay</item>
+ <item name="magnifierWidth">100dp</item>
+ <item name="magnifierHeight">48dp</item>
+ <item name="magnifierZoom">1.25</item>
+ <item name="magnifierElevation">4dp</item>
+ <item name="magnifierVerticalOffset">-42dp</item>
+ <item name="magnifierHorizontalOffset">0dp</item>
+ <item name="magnifierColorOverlay">#0EFFFFFF</item>
</style>
<!-- Text Appearances -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b25e7a8..82c9ff3 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2637,13 +2637,14 @@
<java-symbol type="attr" name="floatingToolbarDividerColor" />
<!-- Magnifier -->
- <java-symbol type="dimen" name="magnifier_width" />
- <java-symbol type="dimen" name="magnifier_height" />
- <java-symbol type="dimen" name="magnifier_elevation" />
- <java-symbol type="dimen" name="magnifier_zoom" />
- <java-symbol type="dimen" name="magnifier_vertical_offset" />
- <java-symbol type="dimen" name="magnifier_horizontal_offset" />
- <java-symbol type="color" name="magnifier_color_overlay" />
+ <java-symbol type="dimen" name="default_magnifier_width" />
+ <java-symbol type="dimen" name="default_magnifier_height" />
+ <java-symbol type="dimen" name="default_magnifier_elevation" />
+ <java-symbol type="dimen" name="default_magnifier_corner_radius" />
+ <java-symbol type="dimen" name="default_magnifier_zoom" />
+ <java-symbol type="dimen" name="default_magnifier_vertical_offset" />
+ <java-symbol type="dimen" name="default_magnifier_horizontal_offset" />
+ <java-symbol type="color" name="default_magnifier_color_overlay" />
<java-symbol type="attr" name="magnifierWidth" />
<java-symbol type="attr" name="magnifierHeight" />
<java-symbol type="attr" name="magnifierElevation" />
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
index c866bc4..b242a34 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
@@ -39,13 +39,16 @@
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Comparator;
+import java.util.function.Predicate;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class KernelCpuThreadReaderTest {
- private static final String PROCESS_NAME = "test_process";
+ private static final int UID = 1000;
+ private static final int PROCESS_ID = 1234;
private static final int[] THREAD_IDS = {0, 1000, 1235, 4321};
+ private static final String PROCESS_NAME = "test_process";
private static final String[] THREAD_NAMES = {
"test_thread_1", "test_thread_2", "test_thread_3", "test_thread_4"
};
@@ -73,49 +76,126 @@
}
@Test
- public void testSimple() throws IOException {
- // Make /proc/self
- final Path selfPath = mProcDirectory.toPath().resolve("self");
- assertTrue(selfPath.toFile().mkdirs());
+ public void testReader_currentProcess() throws IOException {
+ KernelCpuThreadReader.Injector processUtils =
+ new KernelCpuThreadReader.Injector() {
+ @Override
+ public int myPid() {
+ return PROCESS_ID;
+ }
- // Make /proc/self/task
- final Path selfThreadsPath = selfPath.resolve("task");
+ @Override
+ public int myUid() {
+ return UID;
+ }
+
+ @Override
+ public int getUidForPid(int pid) {
+ return 0;
+ }
+ };
+ setupDirectory(mProcDirectory.toPath().resolve("self"), THREAD_IDS, PROCESS_NAME,
+ THREAD_NAMES, THREAD_CPU_FREQUENCIES, THREAD_CPU_TIMES);
+
+ final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader(
+ mProcDirectory.toPath(),
+ mProcDirectory.toPath().resolve("self/task/" + THREAD_IDS[0] + "/time_in_state"),
+ processUtils);
+ final KernelCpuThreadReader.ProcessCpuUsage processCpuUsage =
+ kernelCpuThreadReader.getCurrentProcessCpuUsage();
+ checkResults(processCpuUsage, kernelCpuThreadReader.getCpuFrequenciesKhz(), UID, PROCESS_ID,
+ THREAD_IDS, PROCESS_NAME, THREAD_NAMES, THREAD_CPU_FREQUENCIES, THREAD_CPU_TIMES);
+ }
+
+ @Test
+ public void testReader_byUids() throws IOException {
+ int[] uids = new int[]{0, 2, 3, 4, 5, 6000};
+ Predicate<Integer> uidPredicate = uid -> uid == 0 || uid >= 4;
+ int[] expectedUids = new int[]{0, 4, 5, 6000};
+ KernelCpuThreadReader.Injector processUtils =
+ new KernelCpuThreadReader.Injector() {
+ @Override
+ public int myPid() {
+ return 0;
+ }
+
+ @Override
+ public int myUid() {
+ return 0;
+ }
+
+ @Override
+ public int getUidForPid(int pid) {
+ return pid;
+ }
+ };
+
+ for (int uid : uids) {
+ setupDirectory(mProcDirectory.toPath().resolve(String.valueOf(uid)),
+ new int[]{uid * 10},
+ "process" + uid, new String[]{"thread" + uid}, new int[]{1000},
+ new int[][]{{uid}});
+ }
+ final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader(
+ mProcDirectory.toPath(),
+ mProcDirectory.toPath().resolve(uids[0] + "/task/" + uids[0] + "/time_in_state"),
+ processUtils);
+ ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsageByUids =
+ kernelCpuThreadReader.getProcessCpuUsageByUids(uidPredicate);
+ processCpuUsageByUids.sort(Comparator.comparing(usage -> usage.processId));
+
+ assertEquals(expectedUids.length, processCpuUsageByUids.size());
+ for (int i = 0; i < expectedUids.length; i++) {
+ KernelCpuThreadReader.ProcessCpuUsage processCpuUsage =
+ processCpuUsageByUids.get(i);
+ int uid = expectedUids[i];
+ checkResults(processCpuUsage, kernelCpuThreadReader.getCpuFrequenciesKhz(),
+ uid, uid, new int[]{uid * 10}, "process" + uid, new String[]{"thread" + uid},
+ new int[]{1000}, new int[][]{{uid}});
+ }
+ }
+
+ private void setupDirectory(Path processPath, int[] threadIds, String processName,
+ String[] threadNames, int[] cpuFrequencies, int[][] cpuTimes) throws IOException {
+ // Make /proc/$PID
+ assertTrue(processPath.toFile().mkdirs());
+
+ // Make /proc/$PID/task
+ final Path selfThreadsPath = processPath.resolve("task");
assertTrue(selfThreadsPath.toFile().mkdirs());
- // Make /proc/self/cmdline
- Files.write(selfPath.resolve("cmdline"), PROCESS_NAME.getBytes());
+ // Make /proc/$PID/cmdline
+ Files.write(processPath.resolve("cmdline"), processName.getBytes());
// Make thread directories in reverse order, as they are read in order of creation by
// CpuThreadProcReader
- for (int i = 0; i < THREAD_IDS.length; i++) {
- // Make /proc/self/task/$TID
- final Path threadPath = selfThreadsPath.resolve(String.valueOf(THREAD_IDS[i]));
+ for (int i = 0; i < threadIds.length; i++) {
+ // Make /proc/$PID/task/$TID
+ final Path threadPath = selfThreadsPath.resolve(String.valueOf(threadIds[i]));
assertTrue(threadPath.toFile().mkdirs());
- // Make /proc/self/task/$TID/comm
- Files.write(threadPath.resolve("comm"), THREAD_NAMES[i].getBytes());
+ // Make /proc/$PID/task/$TID/comm
+ Files.write(threadPath.resolve("comm"), threadNames[i].getBytes());
- // Make /proc/self/task/$TID/time_in_state
+ // Make /proc/$PID/task/$TID/time_in_state
final OutputStream timeInStateStream =
Files.newOutputStream(threadPath.resolve("time_in_state"));
- for (int j = 0; j < THREAD_CPU_FREQUENCIES.length; j++) {
- final String line = String.valueOf(THREAD_CPU_FREQUENCIES[j]) + " "
- + String.valueOf(THREAD_CPU_TIMES[i][j]) + "\n";
+ for (int j = 0; j < cpuFrequencies.length; j++) {
+ final String line = String.valueOf(cpuFrequencies[j]) + " "
+ + String.valueOf(cpuTimes[i][j]) + "\n";
timeInStateStream.write(line.getBytes());
}
timeInStateStream.close();
}
+ }
- final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader(
- mProcDirectory.toPath(),
- mProcDirectory.toPath().resolve("self/task/" + THREAD_IDS[0] + "/time_in_state"));
- final KernelCpuThreadReader.ProcessCpuUsage processCpuUsage =
- kernelCpuThreadReader.getCurrentProcessCpuUsage();
-
+ private void checkResults(KernelCpuThreadReader.ProcessCpuUsage processCpuUsage,
+ int[] readerCpuFrequencies, int uid, int processId, int[] threadIds, String processName,
+ String[] threadNames, int[] cpuFrequencies, int[][] cpuTimes) {
assertNotNull(processCpuUsage);
- assertEquals(android.os.Process.myPid(), processCpuUsage.processId);
- assertEquals(android.os.Process.myUid(), processCpuUsage.uid);
- assertEquals(PROCESS_NAME, processCpuUsage.processName);
+ assertEquals(processId, processCpuUsage.processId);
+ assertEquals(uid, processCpuUsage.uid);
+ assertEquals(processName, processCpuUsage.processName);
// Sort the thread CPU usages to compare with test case
final ArrayList<KernelCpuThreadReader.ThreadCpuUsage> threadCpuUsages =
@@ -124,21 +204,21 @@
int threadCount = 0;
for (KernelCpuThreadReader.ThreadCpuUsage threadCpuUsage : threadCpuUsages) {
- assertEquals(THREAD_IDS[threadCount], threadCpuUsage.threadId);
- assertEquals(THREAD_NAMES[threadCount], threadCpuUsage.threadName);
+ assertEquals(threadIds[threadCount], threadCpuUsage.threadId);
+ assertEquals(threadNames[threadCount], threadCpuUsage.threadName);
for (int i = 0; i < threadCpuUsage.usageTimesMillis.length; i++) {
assertEquals(
- THREAD_CPU_TIMES[threadCount][i] * 10,
+ cpuTimes[threadCount][i] * 10,
threadCpuUsage.usageTimesMillis[i]);
assertEquals(
- THREAD_CPU_FREQUENCIES[i],
- kernelCpuThreadReader.getCpuFrequenciesKhz()[i]);
+ cpuFrequencies[i],
+ readerCpuFrequencies[i]);
}
threadCount++;
}
- assertEquals(threadCount, THREAD_IDS.length);
+ assertEquals(threadCount, threadIds.length);
}
@Test
diff --git a/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java b/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java
index f2a531f..2893066 100644
--- a/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/ProcTimeInStateReaderTest.java
@@ -70,6 +70,23 @@
}
@Test
+ public void testHeaderFormat() throws IOException {
+ final Path initialTimeInStateFile = mProcDirectory.toPath().resolve(
+ "initial-time-in-state");
+ Files.write(initialTimeInStateFile, "header1\n1 2\nheader2:\n3 4\n5 6\n7 8\n".getBytes());
+ final ProcTimeInStateReader reader = new ProcTimeInStateReader(initialTimeInStateFile);
+
+ assertArrayEquals(
+ "Reported frequencies are correct",
+ new long[]{1, 3, 5, 7},
+ reader.getFrequenciesKhz());
+ assertArrayEquals(
+ "Reported usage times are correct",
+ new long[]{20, 40, 60, 80},
+ reader.getUsageTimesMillis(initialTimeInStateFile));
+ }
+
+ @Test
public void testDifferentFile() throws IOException {
Path initialTimeInStateFile = mProcDirectory.toPath().resolve("initial-time-in-state");
Files.write(initialTimeInStateFile, "1 2\n3 4\n5 6\n7 8\n".getBytes());
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 009e042..e3b165c 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -1869,8 +1869,8 @@
}
float scale = (float) dstDensity / srcDensity;
- int scaledWidth = (int) (mWidth * scale + 0.5f);
- int scaledHeight = (int) (mHeight * scale + 0.5f);
+ int scaledWidth = Math.max((int) (mWidth * scale + 0.5f), 1);
+ int scaledHeight = Math.max((int) (mHeight * scale + 0.5f), 1);
this.setTargetSize(scaledWidth, scaledHeight);
return dstDensity;
}
diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java
index 7216a22..072fe73 100644
--- a/keystore/java/android/security/Credentials.java
+++ b/keystore/java/android/security/Credentials.java
@@ -282,8 +282,11 @@
* Returns {@code true} if the entry no longer exists.
*/
public static boolean deleteUserKeyTypeForAlias(KeyStore keystore, String alias, int uid) {
- return keystore.delete(Credentials.USER_PRIVATE_KEY + alias, uid) ||
- keystore.delete(Credentials.USER_SECRET_KEY + alias, uid);
+ int ret = keystore.delete2(Credentials.USER_PRIVATE_KEY + alias, uid);
+ if (ret == KeyStore.KEY_NOT_FOUND) {
+ return keystore.delete(Credentials.USER_SECRET_KEY + alias, uid);
+ }
+ return ret == KeyStore.NO_ERROR;
}
/**
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 6d58d95..6e6ed30 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -267,16 +267,20 @@
}
}
- public boolean delete(String key, int uid) {
+ int delete2(String key, int uid) {
try {
- int ret = mBinder.del(key, uid);
- return (ret == NO_ERROR || ret == KEY_NOT_FOUND);
+ return mBinder.del(key, uid);
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
- return false;
+ return SYSTEM_ERROR;
}
}
+ public boolean delete(String key, int uid) {
+ int ret = delete2(key, uid);
+ return ret == NO_ERROR || ret == KEY_NOT_FOUND;
+ }
+
@UnsupportedAppUsage
public boolean delete(String key) {
return delete(key, UID_SELF);
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 9c707bab..9a15ff2 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -30,6 +30,7 @@
#include <SkColorSpaceXformCanvas.h>
#include <SkDeque.h>
#include <SkDrawable.h>
+#include <SkFont.h>
#include <SkGraphics.h>
#include <SkImage.h>
#include <SkImagePriv.h>
@@ -732,6 +733,7 @@
float y, float boundsLeft, float boundsTop, float boundsRight,
float boundsBottom, float totalAdvance) {
if (count <= 0 || paint.nothingToDraw()) return;
+ SkFont font = SkFont::LEGACY_ExtractFromPaint(paint);
SkPaint paintCopy(paint);
if (mPaintFilter) {
mPaintFilter->filter(&paintCopy);
@@ -748,7 +750,7 @@
SkRect::MakeLTRB(boundsLeft + x, boundsTop + y, boundsRight + x, boundsBottom + y);
SkTextBlobBuilder builder;
- const SkTextBlobBuilder::RunBuffer& buffer = builder.allocRunPos(paintCopy, count, &bounds);
+ const SkTextBlobBuilder::RunBuffer& buffer = builder.allocRunPos(font, count, &bounds);
glyphFunc(buffer.glyphs, buffer.pos);
sk_sp<SkTextBlob> textBlob(builder.make());
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 2c73940..0331581 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -461,7 +461,7 @@
ProjectionLayer(int* drawCounter)
: SkSurface_Base(SkImageInfo::MakeN32Premul(LAYER_WIDTH, LAYER_HEIGHT), nullptr)
, mDrawCounter(drawCounter) {}
- virtual sk_sp<SkImage> onNewImageSnapshot() override {
+ virtual sk_sp<SkImage> onNewImageSnapshot(const SkIRect* bounds) override {
EXPECT_EQ(3, (*mDrawCounter)++);
EXPECT_EQ(SkRect::MakeLTRB(100 - SCROLL_X, 100 - SCROLL_Y, 300 - SCROLL_X,
300 - SCROLL_Y),
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 65b4e26..d16b8be 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -247,7 +247,7 @@
SkCanvas* onNewCanvas() override { return new T(); }
sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override { return nullptr; }
- sk_sp<SkImage> onNewImageSnapshot() override { return nullptr; }
+ sk_sp<SkImage> onNewImageSnapshot(const SkIRect* bounds) override { return nullptr; }
T* canvas() { return static_cast<T*>(getCanvas()); }
void onCopyOnWrite(ContentChangeMode) override {}
void onWritePixels(const SkPixmap&, int x, int y) override {}
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index c5a6ec5..f1d9397 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -28,10 +28,13 @@
"libgui",
"libui",
"libinput",
- "libinputflinger",
"libnativewindow",
],
+ header_libs: [
+ "libinputflinger_headers",
+ ],
+
include_dirs: ["frameworks/native/services"],
cflags: [
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index eb3469e..7f4e5a5 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -24,7 +24,7 @@
#include <ui/DisplayInfo.h>
#include <input/Input.h>
-#include <inputflinger/PointerControllerInterface.h>
+#include <PointerControllerInterface.h>
#include <utils/BitSet.h>
#include <utils/RefBase.h>
#include <utils/Looper.h>
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index 548dc88..8288976 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -1763,10 +1763,13 @@
*/
// This is an asynchronous call.
public Object setAudioSessionId(int sessionId) {
- keepAudioSessionIdAlive(sessionId);
+ final AudioTrack dummyAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 44100,
+ AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, 2,
+ AudioTrack.MODE_STATIC, sessionId);
return addTask(new Task(CALL_COMPLETED_SET_AUDIO_SESSION_ID, false) {
@Override
void process() {
+ keepAudioSessionIdAlive(dummyAudioTrack);
native_setAudioSessionId(sessionId);
}
});
@@ -4621,4 +4624,17 @@
AudioTrack.MODE_STATIC, sessionId);
}
}
+
+ private void keepAudioSessionIdAlive(AudioTrack at) {
+ synchronized (mSessionIdLock) {
+ if (mDummyAudioTrack != null) {
+ if (mDummyAudioTrack.getAudioSessionId() == at.getAudioSessionId()) {
+ at.release();
+ return;
+ }
+ mDummyAudioTrack.release();
+ }
+ mDummyAudioTrack = at;
+ }
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
index 47e51f3..08a75ab 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
@@ -22,12 +22,11 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import static org.robolectric.Shadows.shadowOf;
import android.app.ActivityManager;
import android.content.ContentResolver;
@@ -54,7 +53,6 @@
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
-import org.robolectric.shadows.ShadowAudioManager;
import org.robolectric.shadows.ShadowSettings;
import java.util.HashMap;
@@ -72,7 +70,7 @@
private static final String PERCENTAGE_50 = "50%";
private static final String PERCENTAGE_100 = "100%";
- private ShadowAudioManager mShadowAudioManager;
+ private AudioManager mAudioManager;
private Context mContext;
@Mock
private LocationManager mLocationManager;
@@ -85,7 +83,7 @@
mContext = spy(RuntimeEnvironment.application);
when(mContext.getSystemService(Context.LOCATION_SERVICE)).thenReturn(mLocationManager);
ShadowSecure.reset();
- mShadowAudioManager = shadowOf(mContext.getSystemService(AudioManager.class));
+ mAudioManager = mContext.getSystemService(AudioManager.class);
}
@Test
@@ -205,28 +203,28 @@
@Test
public void isAudioModeOngoingCall_modeInCommunication_returnTrue() {
- mShadowAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
+ mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
assertThat(Utils.isAudioModeOngoingCall(mContext)).isTrue();
}
@Test
public void isAudioModeOngoingCall_modeInCall_returnTrue() {
- mShadowAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ mAudioManager.setMode(AudioManager.MODE_IN_CALL);
assertThat(Utils.isAudioModeOngoingCall(mContext)).isTrue();
}
@Test
public void isAudioModeOngoingCall_modeRingtone_returnTrue() {
- mShadowAudioManager.setMode(AudioManager.MODE_RINGTONE);
+ mAudioManager.setMode(AudioManager.MODE_RINGTONE);
assertThat(Utils.isAudioModeOngoingCall(mContext)).isTrue();
}
@Test
public void isAudioModeOngoingCall_modeNormal_returnFalse() {
- mShadowAudioManager.setMode(AudioManager.MODE_NORMAL);
+ mAudioManager.setMode(AudioManager.MODE_NORMAL);
assertThat(Utils.isAudioModeOngoingCall(mContext)).isFalse();
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 49520c0..5ceede1 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -17,15 +17,14 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyString;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import static org.robolectric.Shadows.shadowOf;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
@@ -40,7 +39,6 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
-import org.robolectric.shadows.ShadowAudioManager;
@RunWith(SettingsLibRobolectricTestRunner.class)
public class CachedBluetoothDeviceTest {
@@ -67,7 +65,7 @@
@Mock
private BluetoothDevice mSubDevice;
private CachedBluetoothDevice mCachedDevice;
- private ShadowAudioManager mShadowAudioManager;
+ private AudioManager mAudioManager;
private Context mContext;
private int mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
@@ -75,7 +73,7 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
- mShadowAudioManager = shadowOf(mContext.getSystemService(AudioManager.class));
+ mAudioManager = mContext.getSystemService(AudioManager.class);
when(mDevice.getAddress()).thenReturn(DEVICE_ADDRESS);
when(mHfpProfile.isProfileReady()).thenReturn(true);
when(mA2dpProfile.isProfileReady()).thenReturn(true);
@@ -212,7 +210,7 @@
// 2. Audio Manager: In Call
updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
- mShadowAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ mAudioManager.setMode(AudioManager.MODE_IN_CALL);
// Act & Assert:
// Get null result without Battery Level.
@@ -228,7 +226,7 @@
updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
mBatteryLevel = 10;
- mShadowAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ mAudioManager.setMode(AudioManager.MODE_IN_CALL);
// Act & Assert:
// Get "10% battery" result with Battery Level 10.
@@ -244,14 +242,13 @@
// Set device as Active for HFP and test connection state summary
mCachedDevice.onAudioModeChanged();
- mShadowAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ mAudioManager.setMode(AudioManager.MODE_IN_CALL);
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active");
// Test with battery level
mBatteryLevel = 10;
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
- "Active, 10% battery");
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active, 10% battery");
// Set HFP profile to be disconnected and test connection state summary
updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
@@ -363,7 +360,7 @@
// 2. Audio Manager: In Call
updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEARING_AID);
- mShadowAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ mAudioManager.setMode(AudioManager.MODE_IN_CALL);
// Act & Assert:
// Get "Active" result without Battery Level.
@@ -378,8 +375,8 @@
// 3. Audio Manager: In Call
updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEARING_AID);
+ mAudioManager.setMode(AudioManager.MODE_IN_CALL);
mBatteryLevel = 10;
- mShadowAudioManager.setMode(AudioManager.MODE_IN_CALL);
// Act & Assert:
// Get "Active, 10% battery" result with Battery Level 10.
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index e0c4d72..2227642 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -185,6 +185,8 @@
private static final Bundle NULL_SETTING_BUNDLE = Bundle.forPair(
Settings.NameValueTable.VALUE, null);
+ public static final String RESULT_ROWS_DELETED = "result_rows_deleted";
+
// Overlay specified settings whitelisted for Instant Apps
private static final Set<String> OVERLAY_ALLOWED_GLOBAL_INSTANT_APP_SETTINGS = new ArraySet<>();
private static final Set<String> OVERLAY_ALLOWED_SYSTEM_INSTANT_APP_SETTINGS = new ArraySet<>();
@@ -460,6 +462,27 @@
break;
}
+ case Settings.CALL_METHOD_DELETE_SYSTEM: {
+ int rows = deleteSystemSetting(name, requestingUserId) ? 1 : 0;
+ Bundle result = new Bundle();
+ result.putInt(RESULT_ROWS_DELETED, rows);
+ return result;
+ }
+
+ case Settings.CALL_METHOD_DELETE_SECURE: {
+ int rows = deleteSecureSetting(name, requestingUserId, false) ? 1 : 0;
+ Bundle result = new Bundle();
+ result.putInt(RESULT_ROWS_DELETED, rows);
+ return result;
+ }
+
+ case Settings.CALL_METHOD_DELETE_GLOBAL: {
+ int rows = deleteGlobalSetting(name, requestingUserId, false) ? 1 : 0;
+ Bundle result = new Bundle();
+ result.putInt(RESULT_ROWS_DELETED, rows);
+ return result;
+ }
+
default: {
Slog.w(LOG_TAG, "call() with invalid method: " + method);
} break;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
index 379cfc7..f8445fd 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsService.java
@@ -265,8 +265,8 @@
}
if (mUser < 0) {
mUser = UserHandle.USER_SYSTEM;
- } else if (mVerb == CommandVerb.DELETE || mVerb == CommandVerb.LIST) {
- perr.println("--user not supported for delete and list.");
+ } else if (mVerb == CommandVerb.LIST) {
+ perr.println("--user not supported for list.");
return -1;
}
UserManager userManager = UserManager.get(mProvider.getContext());
@@ -392,22 +392,27 @@
int deleteForUser(IContentProvider provider, int userHandle,
final String table, final String key) {
- Uri targetUri;
- if ("system".equals(table)) targetUri = Settings.System.getUriFor(key);
- else if ("secure".equals(table)) targetUri = Settings.Secure.getUriFor(key);
- else if ("global".equals(table)) targetUri = Settings.Global.getUriFor(key);
- else {
+ final String callDeleteCommand;
+ if ("system".equals(table)) {
+ callDeleteCommand = Settings.CALL_METHOD_DELETE_SYSTEM;
+ } else if ("secure".equals(table)) {
+ callDeleteCommand = Settings.CALL_METHOD_DELETE_SECURE;
+ } else if ("global".equals(table)) {
+ callDeleteCommand = Settings.CALL_METHOD_DELETE_GLOBAL;
+ } else {
getErrPrintWriter().println("Invalid table; no delete performed");
throw new IllegalArgumentException("Invalid table " + table);
}
- int num = 0;
try {
- num = provider.delete(resolveCallingPackage(), targetUri, null, null);
+ Bundle arg = new Bundle();
+ arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
+ Bundle result =
+ provider.call(resolveCallingPackage(), callDeleteCommand, key, arg);
+ return result.getInt(SettingsProvider.RESULT_ROWS_DELETED);
} catch (RemoteException e) {
throw new RuntimeException("Failed in IPC", e);
}
- return num;
}
void resetForUser(IContentProvider provider, int userHandle,
@@ -473,7 +478,7 @@
pw.println(" Change the contents of KEY to VALUE.");
pw.println(" TAG to associate with the setting.");
pw.println(" {default} to set as the default, case-insensitive only for global/secure namespace");
- pw.println(" delete NAMESPACE KEY");
+ pw.println(" delete [--user <USER_ID> | current] NAMESPACE KEY");
pw.println(" Delete the entry for KEY.");
pw.println(" reset [--user <USER_ID> | current] NAMESPACE {PACKAGE_NAME | RESET_MODE}");
pw.println(" Reset the global/secure table for a package with mode.");
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java
index 572a924..183f599 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java
@@ -32,9 +32,11 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
-import java.util.concurrent.atomic.AtomicBoolean;
+
import org.junit.Test;
+import java.util.concurrent.atomic.AtomicBoolean;
+
/**
* Tests for the SettingContentProvider.
*
@@ -258,7 +260,7 @@
FAKE_SETTING_VALUE, false);
// Reset the changes made by the "shell/root" package
- resetToDefaultsViaShell(type, "shell");
+ resetToDefaultsViaShell(type, "com.android.shell");
resetToDefaultsViaShell(type, "root");
// Make sure the old APIs don't set defaults
@@ -272,7 +274,7 @@
FAKE_SETTING_VALUE_2, false);
// Reset the changes made by this package
- resetToDefaultsViaShell(type, "shell");
+ resetToDefaultsViaShell(type, "com.android.shell");
resetToDefaultsViaShell(type, "root");
// Make sure the old APIs don't set defaults
@@ -313,7 +315,7 @@
FAKE_SETTING_VALUE_2, "TOKEN2", false);
// Reset settings associated with TOKEN1
- resetToDefaultsViaShell(type, "shell", "TOKEN1");
+ resetToDefaultsViaShell(type, "com.android.shell", "TOKEN1");
resetToDefaultsViaShell(type, "root", "TOKEN1");
// Make sure TOKEN1 settings are reset
@@ -325,7 +327,7 @@
FAKE_SETTING_NAME_1));
// Reset settings associated with TOKEN2
- resetToDefaultsViaShell(type, "shell", "TOKEN2");
+ resetToDefaultsViaShell(type, "com.android.shell", "TOKEN2");
resetToDefaultsViaShell(type, "root", "TOKEN2");
// Make sure TOKEN2 settings are reset
diff --git a/packages/Shell/OWNERS b/packages/Shell/OWNERS
index 75c0391..6ba1fcb 100644
--- a/packages/Shell/OWNERS
+++ b/packages/Shell/OWNERS
@@ -1,10 +1,12 @@
+set noparent
+
+jsharkey@android.com
+felipeal@google.com
+nandana@google.com
svetoslavganov@google.com
hackbod@google.com
yamasani@google.com
moltmann@google.com
toddke@google.com
-jsharkey@google.com
cbrubaker@google.com
omakoto@google.com
-nandana@google.com
-felipeal@google.com
diff --git a/packages/SystemUI/res/layout/smart_action_button.xml b/packages/SystemUI/res/layout/smart_action_button.xml
new file mode 100644
index 0000000..2716034
--- /dev/null
+++ b/packages/SystemUI/res/layout/smart_action_button.xml
@@ -0,0 +1,34 @@
+<!--
+ ~ 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
+ -->
+
+<!-- android:paddingHorizontal is set dynamically in SmartReplyView. -->
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@android:style/Widget.Material.Button"
+ android:stateListAnimator="@null"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="0dp"
+ android:minHeight="@dimen/smart_reply_button_min_height"
+ android:paddingVertical="@dimen/smart_reply_button_padding_vertical"
+ android:background="@drawable/smart_reply_button_background"
+ android:gravity="center"
+ android:fontFamily="roboto-medium"
+ android:textSize="@dimen/smart_reply_button_font_size"
+ android:lineSpacingExtra="@dimen/smart_reply_button_line_spacing_extra"
+ android:textColor="@color/smart_reply_button_text"
+ android:drawablePadding="@dimen/smart_action_button_icon_padding"
+ android:textStyle="normal"
+ android:ellipsize="none"/>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 07628c6..0997c5b1 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -881,6 +881,7 @@
<dimen name="smart_reply_button_stroke_width">1dp</dimen>
<dimen name="smart_reply_button_font_size">14sp</dimen>
<dimen name="smart_reply_button_line_spacing_extra">6sp</dimen> <!-- Total line height 20sp. -->
+ <dimen name="smart_action_button_icon_padding">10dp</dimen>
<!-- A reasonable upper bound for the height of the smart reply button. The measuring code
needs to start with a guess for the maximum size. Currently two-line smart reply buttons
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index c7910f9..46ed715b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -54,7 +54,6 @@
import android.util.Log;
import android.view.IRecentsAnimationController;
import android.view.IRecentsAnimationRunner;
-
import android.view.RemoteAnimationTarget;
import com.android.internal.app.IVoiceInteractionManagerService;
@@ -480,4 +479,16 @@
return false;
}
}
+
+ /**
+ * Returns true if the system supports freeform multi-window.
+ */
+ public boolean supportsFreeformMultiWindow(Context context) {
+ final boolean freeformDevOption = Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;
+ return ActivityTaskManager.supportsMultiWindow(context)
+ && (context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT)
+ || freeformDevOption);
+ }
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
index 7154f53..a6b66e7 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
@@ -18,6 +18,7 @@
import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import android.app.ActivityOptions;
@@ -41,6 +42,15 @@
return options;
}
+ /**
+ * @return ActivityOptions for starting a task in freeform.
+ */
+ public static ActivityOptions makeFreeformOptions() {
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
+ return options;
+ }
+
public static ActivityOptions makeRemoteAnimation(
RemoteAnimationAdapterCompat remoteAnimationAdapter) {
return ActivityOptions.makeRemoteAnimation(remoteAnimationAdapter.getWrapped());
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 35ae899..5d33ffd 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -90,7 +90,7 @@
private float[] mRecentTemps = new float[MAX_RECENT_TEMPS];
private int mNumTemps;
private long mNextLogTime;
- private IThermalService mThermalService;
+ @VisibleForTesting IThermalService mThermalService;
@VisibleForTesting int mBatteryLevel = 100;
@VisibleForTesting int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;
@@ -394,7 +394,7 @@
// Enable push notifications of throttling from vendor thermal
// management subsystem via thermalservice, in addition to our
// usual polling, to react to temperature jumps more quickly.
- IBinder b = ServiceManager.getService("thermalservice");
+ IBinder b = ServiceManager.getService(Context.THERMAL_SERVICE);
if (b != null) {
mThermalService = IThermalService.Stub.asInterface(b);
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 fa3fa5b..bb9a341 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
@@ -55,6 +55,8 @@
import com.android.systemui.statusbar.policy.SmartReplyConstants;
import com.android.systemui.statusbar.policy.SmartReplyView;
+import java.util.List;
+
/**
* A frame layout containing the actual payload of the notification, including the contracted,
* expanded and heads up layout. This class is responsible for clipping the content and and
@@ -1285,38 +1287,88 @@
return;
}
- Notification notification = entry.notification.getNotification();
+ SmartRepliesAndActions smartRepliesAndActions = chooseSmartRepliesAndActions(
+ mSmartReplyConstants, entry);
- Pair<RemoteInput, Notification.Action> remoteInputActionPair =
- entry.notification.getNotification().findRemoteInputActionPair(false /*freeform */);
- Pair<RemoteInput, Notification.Action> freeformRemoteInputActionPair =
- notification.findRemoteInputActionPair(true /*freeform */);
+ applyRemoteInput(entry, smartRepliesAndActions.freeformRemoteInputActionPair != null);
+ applySmartReplyView(smartRepliesAndActions, entry);
+ }
- boolean enableAppGeneratedSmartReplies = (mSmartReplyConstants.isEnabled()
- && (!mSmartReplyConstants.requiresTargetingP()
+ /**
+ * Chose what smart replies and smart actions to display. App generated suggestions take
+ * precedence. So if the app provides any smart replies, we don't show any
+ * replies or actions generated by the NotificationAssistantService (NAS), and if the app
+ * provides any smart actions we also don't show any NAS-generated replies or actions.
+ */
+ @VisibleForTesting
+ static SmartRepliesAndActions chooseSmartRepliesAndActions(
+ SmartReplyConstants smartReplyConstants,
+ final NotificationData.Entry entry) {
+ boolean enableAppGeneratedSmartReplies = (smartReplyConstants.isEnabled()
+ && (!smartReplyConstants.requiresTargetingP()
|| entry.targetSdk >= Build.VERSION_CODES.P));
- RemoteInput remoteInputWithChoices = null;
- PendingIntent pendingIntentWithChoices= null;
- CharSequence[] choices = null;
- if (enableAppGeneratedSmartReplies
- && remoteInputActionPair != null
- && !ArrayUtils.isEmpty(remoteInputActionPair.first.getChoices())) {
- // app generated smart replies
- remoteInputWithChoices = remoteInputActionPair.first;
- pendingIntentWithChoices = remoteInputActionPair.second.actionIntent;
- choices = remoteInputActionPair.first.getChoices();
+ Notification notification = entry.notification.getNotification();
+ Pair<RemoteInput, Notification.Action> remoteInputActionPair =
+ notification.findRemoteInputActionPair(false /* freeform */);
+ Pair<RemoteInput, Notification.Action> freeformRemoteInputActionPair =
+ notification.findRemoteInputActionPair(true /* freeform */);
+
+ boolean appGeneratedSmartRepliesExist =
+ enableAppGeneratedSmartReplies
+ && remoteInputActionPair != null
+ && !ArrayUtils.isEmpty(remoteInputActionPair.first.getChoices());
+
+ List<Notification.Action> appGeneratedSmartActions = notification.getContextualActions();
+ boolean appGeneratedSmartActionsExist = !appGeneratedSmartActions.isEmpty();
+
+ if (appGeneratedSmartRepliesExist) {
+ return new SmartRepliesAndActions(remoteInputActionPair.first,
+ remoteInputActionPair.second.actionIntent,
+ remoteInputActionPair.first.getChoices(),
+ appGeneratedSmartActions,
+ freeformRemoteInputActionPair);
+ } else if (appGeneratedSmartActionsExist) {
+ return new SmartRepliesAndActions(null, null, null, appGeneratedSmartActions,
+ freeformRemoteInputActionPair);
} else if (!ArrayUtils.isEmpty(entry.smartReplies)
&& freeformRemoteInputActionPair != null
&& freeformRemoteInputActionPair.second.getAllowGeneratedReplies()) {
- // system generated smart replies
- remoteInputWithChoices = freeformRemoteInputActionPair.first;
- pendingIntentWithChoices = freeformRemoteInputActionPair.second.actionIntent;
- choices = entry.smartReplies;
+ // App didn't generate anything, use NAS-generated replies and actions
+ return new SmartRepliesAndActions(freeformRemoteInputActionPair.first,
+ freeformRemoteInputActionPair.second.actionIntent,
+ entry.smartReplies,
+ entry.systemGeneratedSmartActions,
+ freeformRemoteInputActionPair);
+ }
+ // App didn't generate anything, and there are no NAS-generated smart replies.
+ return new SmartRepliesAndActions(null, null, null, entry.systemGeneratedSmartActions,
+ freeformRemoteInputActionPair);
+ }
+
+ @VisibleForTesting
+ static class SmartRepliesAndActions {
+ public final RemoteInput remoteInputWithChoices;
+ public final PendingIntent pendingIntentForSmartReplies;
+ public final CharSequence[] smartReplies;
+ public final List<Notification.Action> smartActions;
+ public final Pair<RemoteInput, Notification.Action> freeformRemoteInputActionPair;
+
+ SmartRepliesAndActions(RemoteInput remoteInput, PendingIntent pendingIntent,
+ CharSequence[] choices, List<Notification.Action> smartActions,
+ Pair<RemoteInput, Notification.Action> freeformRemoteInputActionPair) {
+ this.remoteInputWithChoices = remoteInput;
+ this.pendingIntentForSmartReplies = pendingIntent;
+ this.smartReplies = choices;
+ this.smartActions = smartActions;
+ this.freeformRemoteInputActionPair = freeformRemoteInputActionPair;
}
- applyRemoteInput(entry, freeformRemoteInputActionPair != null);
- applySmartReplyView(remoteInputWithChoices, pendingIntentWithChoices, entry, choices);
+ boolean smartRepliesExist() {
+ return remoteInputWithChoices != null
+ && pendingIntentForSmartReplies != null
+ && !ArrayUtils.isEmpty(smartReplies);
+ }
}
private void applyRemoteInput(NotificationData.Entry entry, boolean hasFreeformRemoteInput) {
@@ -1418,28 +1470,32 @@
return null;
}
- private void applySmartReplyView(RemoteInput remoteInput, PendingIntent pendingIntent,
- NotificationData.Entry entry, CharSequence[] choices) {
+ private void applySmartReplyView(SmartRepliesAndActions smartRepliesAndActions,
+ NotificationData.Entry entry) {
if (mExpandedChild != null) {
mExpandedSmartReplyView =
- applySmartReplyView(mExpandedChild, remoteInput, pendingIntent, entry, choices);
- if (mExpandedSmartReplyView != null && remoteInput != null
- && choices != null && choices.length > 0) {
- mSmartReplyController.smartRepliesAdded(entry, choices.length);
+ applySmartReplyView(mExpandedChild, smartRepliesAndActions, entry);
+ if (mExpandedSmartReplyView != null
+ && smartRepliesAndActions.remoteInputWithChoices != null
+ && smartRepliesAndActions.smartReplies != null
+ && smartRepliesAndActions.smartReplies.length > 0) {
+ mSmartReplyController.smartRepliesAdded(entry,
+ smartRepliesAndActions.smartReplies.length);
}
}
}
- private SmartReplyView applySmartReplyView(
- View view, RemoteInput remoteInput, PendingIntent pendingIntent,
- NotificationData.Entry entry, CharSequence[] choices) {
+ private SmartReplyView applySmartReplyView(View view,
+ SmartRepliesAndActions smartRepliesAndActions, NotificationData.Entry entry) {
View smartReplyContainerCandidate = view.findViewById(
com.android.internal.R.id.smart_reply_container);
if (!(smartReplyContainerCandidate instanceof LinearLayout)) {
return null;
}
LinearLayout smartReplyContainer = (LinearLayout) smartReplyContainerCandidate;
- if (remoteInput == null || pendingIntent == null) {
+ // If there are no smart replies and no smart actions - early out.
+ if (!smartRepliesAndActions.smartRepliesExist()
+ && smartRepliesAndActions.smartActions.isEmpty()) {
smartReplyContainer.setVisibility(View.GONE);
return null;
}
@@ -1468,9 +1524,11 @@
}
}
if (smartReplyView != null) {
- smartReplyView.setRepliesFromRemoteInput(remoteInput, pendingIntent,
- mSmartReplyController, entry, smartReplyContainer, choices
- );
+ smartReplyView.resetSmartSuggestions(smartReplyContainer);
+ smartReplyView.addRepliesFromRemoteInput(smartRepliesAndActions.remoteInputWithChoices,
+ smartRepliesAndActions.pendingIntentForSmartReplies, mSmartReplyController,
+ entry, smartRepliesAndActions.smartReplies);
+ smartReplyView.addSmartActions(smartRepliesAndActions.smartActions);
smartReplyContainer.setVisibility(View.VISIBLE);
}
return smartReplyView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index c16b28f..b6ff6fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -367,6 +367,9 @@
public void onSnapOpen() {
mMenuSnapped = true;
mMenuSnappedOnLeft = isMenuOnLeft();
+ if (mAlpha == 0f && mParent != null) {
+ fadeInMenu(mParent.getWidth());
+ }
if (mMenuListener != null) {
mMenuListener.onMenuShown(getParent());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
index 42f1378..0186683 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -1,6 +1,7 @@
package com.android.systemui.statusbar.policy;
import android.annotation.ColorInt;
+import android.app.Notification;
import android.app.PendingIntent;
import android.app.RemoteInput;
import android.content.Context;
@@ -19,6 +20,7 @@
import android.text.method.TransformationMethod;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.Size;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -30,6 +32,7 @@
import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.notification.NotificationData;
@@ -38,14 +41,15 @@
import java.text.BreakIterator;
import java.util.Comparator;
+import java.util.List;
import java.util.PriorityQueue;
-/** View which displays smart reply buttons in notifications. */
+/** View which displays smart reply and smart actions buttons in notifications. */
public class SmartReplyView extends ViewGroup {
private static final String TAG = "SmartReplyView";
- private static final int MEASURE_SPEC_ANY_WIDTH =
+ private static final int MEASURE_SPEC_ANY_LENGTH =
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
private static final Comparator<View> DECREASING_MEASURED_WIDTH_WITHOUT_PADDING_COMPARATOR =
@@ -98,6 +102,8 @@
private final int mStrokeWidth;
private final double mMinStrokeContrast;
+ private ActivityStarter mActivityStarter;
+
public SmartReplyView(Context context, AttributeSet attrs) {
super(context, attrs);
mConstants = Dependency.get(SmartReplyConstants.class);
@@ -168,13 +174,24 @@
Math.max(getChildCount(), 1), DECREASING_MEASURED_WIDTH_WITHOUT_PADDING_COMPARATOR);
}
- public void setRepliesFromRemoteInput(
- RemoteInput remoteInput, PendingIntent pendingIntent,
- SmartReplyController smartReplyController, NotificationData.Entry entry,
- View smartReplyContainer, CharSequence[] choices) {
- mSmartReplyContainer = smartReplyContainer;
+ /**
+ * Reset the smart suggestions view to allow adding new replies and actions.
+ */
+ public void resetSmartSuggestions(View newSmartReplyContainer) {
+ mSmartReplyContainer = newSmartReplyContainer;
removeAllViews();
mCurrentBackgroundColor = mDefaultBackgroundColor;
+ }
+
+ /**
+ * Add smart replies to this view, using the provided {@link RemoteInput} and
+ * {@link PendingIntent} to respond when the user taps a smart reply. Only the replies that fit
+ * into the notification are shown.
+ */
+ public void addRepliesFromRemoteInput(
+ RemoteInput remoteInput, PendingIntent pendingIntent,
+ SmartReplyController smartReplyController, NotificationData.Entry entry,
+ CharSequence[] choices) {
if (remoteInput != null && pendingIntent != null) {
if (choices != null) {
for (int i = 0; i < choices.length; ++i) {
@@ -188,6 +205,22 @@
reallocateCandidateButtonQueueForSqueezing();
}
+ /**
+ * Add smart actions to be shown next to smart replies. Only the actions that fit into the
+ * notification are shown.
+ */
+ public void addSmartActions(List<Notification.Action> smartActions) {
+ int numSmartActions = smartActions.size();
+ for (int n = 0; n < numSmartActions; n++) {
+ Notification.Action action = smartActions.get(n);
+ if (action.actionIntent != null) {
+ Button actionButton = inflateActionButton(getContext(), this, action);
+ addView(actionButton);
+ }
+ }
+ reallocateCandidateButtonQueueForSqueezing();
+ }
+
public static SmartReplyView inflate(Context context, ViewGroup root) {
return (SmartReplyView)
LayoutInflater.from(context).inflate(R.layout.smart_reply_view, root, false);
@@ -234,6 +267,48 @@
return b;
}
+ @VisibleForTesting
+ Button inflateActionButton(Context context, ViewGroup root, Notification.Action action) {
+ Button button = (Button) LayoutInflater.from(context).inflate(
+ R.layout.smart_action_button, root, false);
+ button.setText(action.title);
+
+ Drawable iconDrawable = action.getIcon().loadDrawable(context);
+ // Add the action icon to the Smart Action button.
+ Size newIconSize = calculateIconSizeFromSingleLineButton(context, root,
+ new Size(iconDrawable.getIntrinsicWidth(), iconDrawable.getIntrinsicHeight()));
+ iconDrawable.setBounds(0, 0, newIconSize.getWidth(), newIconSize.getHeight());
+ button.setCompoundDrawables(iconDrawable, null, null, null);
+
+ button.setOnClickListener(view ->
+ getActivityStarter().startPendingIntentDismissingKeyguard(action.actionIntent));
+
+ // TODO(b/119010281): handle accessibility
+
+ return button;
+ }
+
+ private static Size calculateIconSizeFromSingleLineButton(Context context, ViewGroup root,
+ Size originalIconSize) {
+ Button button = (Button) LayoutInflater.from(context).inflate(
+ R.layout.smart_action_button, root, false);
+ // Add simple text here to ensure the button displays one line of text.
+ button.setText("a");
+ return calculateIconSizeFromButtonHeight(button, originalIconSize);
+ }
+
+ // Given a button with text on a single line - we want to add an icon to that button. This
+ // method calculates the icon height to use to avoid making the button grow in height.
+ private static Size calculateIconSizeFromButtonHeight(Button button, Size originalIconSize) {
+ // A completely permissive measure spec should make the button text single-line.
+ button.measure(MEASURE_SPEC_ANY_LENGTH, MEASURE_SPEC_ANY_LENGTH);
+ int buttonHeight = button.getMeasuredHeight();
+ int newIconHeight = buttonHeight / 2;
+ int newIconWidth = (int) (originalIconSize.getWidth()
+ * ((double) newIconHeight) / originalIconSize.getHeight());
+ return new Size(newIconWidth, newIconHeight);
+ }
+
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(mContext, attrs);
@@ -277,7 +352,7 @@
child.setPadding(buttonPaddingHorizontal, child.getPaddingTop(),
buttonPaddingHorizontal, child.getPaddingBottom());
- child.measure(MEASURE_SPEC_ANY_WIDTH, heightMeasureSpec);
+ child.measure(MEASURE_SPEC_ANY_LENGTH, heightMeasureSpec);
final int lineCount = ((Button) child).getLineCount();
if (lineCount < 1 || lineCount > 2) {
@@ -437,6 +512,18 @@
return (int) Math.ceil(optimalTextWidth);
}
+ /**
+ * Returns the combined width of the left drawable (the action icon) and the padding between the
+ * drawable and the button text.
+ */
+ private int getLeftCompoundDrawableWidthWithPadding(Button button) {
+ Drawable[] drawables = button.getCompoundDrawables();
+ Drawable leftDrawable = drawables[0];
+ if (leftDrawable == null) return 0;
+
+ return leftDrawable.getBounds().width() + button.getCompoundDrawablePadding();
+ }
+
private int squeezeButtonToTextWidth(Button button, int heightMeasureSpec, int textWidth) {
int oldWidth = button.getMeasuredWidth();
if (button.getPaddingLeft() != mDoubleLineButtonPaddingHorizontal) {
@@ -449,7 +536,8 @@
button.setPadding(mDoubleLineButtonPaddingHorizontal, button.getPaddingTop(),
mDoubleLineButtonPaddingHorizontal, button.getPaddingBottom());
final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(
- 2 * mDoubleLineButtonPaddingHorizontal + textWidth, MeasureSpec.AT_MOST);
+ 2 * mDoubleLineButtonPaddingHorizontal + textWidth
+ + getLeftCompoundDrawableWidthWithPadding(button), MeasureSpec.AT_MOST);
button.measure(widthMeasureSpec, heightMeasureSpec);
final int newWidth = button.getMeasuredWidth();
@@ -607,6 +695,13 @@
button.setTextColor(textColor);
}
+ private ActivityStarter getActivityStarter() {
+ if (mActivityStarter == null) {
+ mActivityStarter = Dependency.get(ActivityStarter.class);
+ }
+ return mActivityStarter;
+ }
+
@VisibleForTesting
static class LayoutParams extends ViewGroup.LayoutParams {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index b44630a..221cbe9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -32,12 +32,13 @@
import android.content.Intent;
import android.os.BatteryManager;
import android.os.HardwarePropertiesManager;
+import android.os.IThermalService;
import android.os.PowerManager;
import android.provider.Settings;
+import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.testing.TestableResources;
-import android.test.suitebuilder.annotation.SmallTest;
import com.android.settingslib.utils.ThreadUtils;
import com.android.systemui.R;
@@ -45,15 +46,16 @@
import com.android.systemui.power.PowerUI.WarningsUI;
import com.android.systemui.statusbar.phone.StatusBar;
-import java.time.Duration;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.time.Duration;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@SmallTest
@@ -76,6 +78,7 @@
private PowerUI mPowerUI;
private EnhancedEstimates mEnhancedEstimates;
@Mock private PowerManager mPowerManager;
+ @Mock private IThermalService mThermalServiceMock;
@Before
public void setup() {
@@ -541,5 +544,6 @@
mPowerUI = new PowerUI();
mPowerUI.mContext = mContext;
mPowerUI.mComponents = mContext.getComponents();
+ mPowerUI.mThermalService = mThermalServiceMock;
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
index f59bfae..b3b45eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
@@ -39,6 +39,7 @@
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.PendingIntent;
+import android.app.Person;
import android.content.Intent;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
@@ -421,6 +422,33 @@
assertEquals(snoozeCriterions, entry.snoozeCriteria);
}
+ @Test
+ public void notificationDataEntry_testIsLastMessageFromReply() {
+ Person.Builder person = new Person.Builder()
+ .setName("name")
+ .setKey("abc")
+ .setUri("uri")
+ .setBot(true);
+
+ // EXTRA_MESSAGING_PERSON is the same Person as the sender in last message in EXTRA_MESSAGES
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(Notification.EXTRA_MESSAGING_PERSON, person.build());
+ Bundle[] messagesBundle = new Bundle[]{ new Notification.MessagingStyle.Message(
+ "text", 0, person.build()).toBundle() };
+ bundle.putParcelableArray(Notification.EXTRA_MESSAGES, messagesBundle);
+
+ Notification notification = new Notification.Builder(mContext, "test")
+ .addExtras(bundle)
+ .build();
+ StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
+ notification, mContext.getUser(), "", 0);
+
+ NotificationData.Entry entry = new NotificationData.Entry(sbn);
+ entry.setHasSentReply();
+
+ assertTrue(entry.isLastMessageFromReply());
+ }
+
private void initStatusBarNotification(boolean allowDuringSetup) {
Bundle bundle = new Bundle();
bundle.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, allowDuringSetup);
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 c189c95..a6725b8 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,6 +16,11 @@
package com.android.systemui.statusbar.notification.row;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.Mockito.doNothing;
@@ -28,29 +33,62 @@
import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.RemoteInput;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
+import android.service.notification.StatusBarNotification;
import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.util.ArraySet;
+import android.util.Pair;
import android.view.NotificationHeaderView;
import android.view.View;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.policy.SmartReplyConstants;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class NotificationContentViewTest extends SysuiTestCase {
+ private static final String TEST_ACTION = "com.android.SMART_REPLY_VIEW_ACTION";
+
NotificationContentView mView;
+ @Mock
+ SmartReplyConstants mSmartReplyConstants;
+ @Mock
+ StatusBarNotification mStatusBarNotification;
+ @Mock
+ Notification mNotification;
+ NotificationData.Entry mEntry;
+ @Mock
+ RemoteInput mRemoteInput;
+ @Mock
+ RemoteInput mFreeFormRemoteInput;
+
+ private Icon mActionIcon;
+
+
@Before
@UiThreadTest
public void setup() {
+ MockitoAnnotations.initMocks(this);
+
mView = new NotificationContentView(mContext, null);
ExpandableNotificationRow row = new ExpandableNotificationRow(mContext, null);
ExpandableNotificationRow mockRow = spy(row);
@@ -67,6 +105,12 @@
mView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
+
+ // Smart replies
+ when(mStatusBarNotification.getNotification()).thenReturn(mNotification);
+ mEntry = new NotificationData.Entry(mStatusBarNotification);
+ when(mSmartReplyConstants.isEnabled()).thenReturn(true);
+ mActionIcon = Icon.createWithResource(mContext, R.drawable.ic_person);
}
private View createViewWithHeight(int height) {
@@ -82,7 +126,7 @@
mView.setDark(true, false, 0);
mView.setDark(false, true, 0);
mView.setHeadsUpAnimatingAway(true);
- Assert.assertFalse(mView.isAnimatingVisibleType());
+ assertFalse(mView.isAnimatingVisibleType());
}
@Test
@@ -115,4 +159,161 @@
verify(mockAmbient, never()).showAppOpsIcons(ops);
verify(mockHeadsUp, times(1)).showAppOpsIcons(any());
}
+
+ private void setupAppGeneratedReplies(CharSequence[] smartReplyTitles) {
+ Notification.Action freeFormAction =
+ new Notification.Action.Builder(null, "Freeform Test Action", null).build();
+ setupAppGeneratedReplies(smartReplyTitles, freeFormAction);
+ }
+
+ private void setupAppGeneratedReplies(
+ CharSequence[] smartReplyTitles,
+ Notification.Action freeFormRemoteInputAction) {
+ Notification.Action action =
+ new Notification.Action.Builder(null, "Test Action", null).build();
+ when(mRemoteInput.getChoices()).thenReturn(smartReplyTitles);
+ Pair<RemoteInput, Notification.Action> remoteInputActionPair =
+ Pair.create(mRemoteInput, action);
+ when(mNotification.findRemoteInputActionPair(false)).thenReturn(remoteInputActionPair);
+
+ Pair<RemoteInput, Notification.Action> freeFormRemoteInputActionPair =
+ Pair.create(mFreeFormRemoteInput, freeFormRemoteInputAction);
+ when(mNotification.findRemoteInputActionPair(true)).thenReturn(
+ freeFormRemoteInputActionPair);
+ }
+
+ @Test
+ public void chooseSmartRepliesAndActions_smartRepliesOff_noAppGeneratedSmartReplies() {
+ setupAppGeneratedReplies(new String[] {"Reply1", "Reply2"});
+ when(mSmartReplyConstants.isEnabled()).thenReturn(false);
+
+ NotificationContentView.SmartRepliesAndActions repliesAndActions =
+ NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+
+ assertFalse(repliesAndActions.smartRepliesExist());
+ }
+
+ @Test
+ public void chooseSmartRepliesAndActions_appGeneratedSmartReplies() {
+ CharSequence[] smartReplies = new String[] {"Reply1", "Reply2"};
+ setupAppGeneratedReplies(smartReplies);
+ when(mSmartReplyConstants.requiresTargetingP()).thenReturn(false);
+
+ NotificationContentView.SmartRepliesAndActions repliesAndActions =
+ NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+
+ assertThat(repliesAndActions.smartReplies, equalTo(smartReplies));
+ }
+
+ @Test
+ public void chooseSmartRepliesAndActions_appGeneratedSmartRepliesAndActions() {
+ CharSequence[] smartReplies = new String[] {"Reply1", "Reply2"};
+ setupAppGeneratedReplies(smartReplies);
+ when(mSmartReplyConstants.requiresTargetingP()).thenReturn(false);
+
+ List<Notification.Action> smartActions =
+ createActions(new String[] {"Test Action 1", "Test Action 2"});
+ when(mNotification.getContextualActions()).thenReturn(smartActions);
+
+ NotificationContentView.SmartRepliesAndActions repliesAndActions =
+ NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+
+ assertThat(repliesAndActions.smartReplies, equalTo(smartReplies));
+ assertThat(repliesAndActions.smartActions, equalTo(smartActions));
+ }
+
+ @Test
+ public void chooseSmartRepliesAndActions_sysGeneratedSmartReplies() {
+ Notification.Action freeFormAction = createActionBuilder("Freeform Action")
+ .setAllowGeneratedReplies(true)
+ .build();
+ // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
+ // replies.
+ setupAppGeneratedReplies(null, freeFormAction);
+
+ mEntry.smartReplies =
+ new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
+ NotificationContentView.SmartRepliesAndActions repliesAndActions =
+ NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+
+ assertThat(repliesAndActions.smartReplies, equalTo(mEntry.smartReplies));
+ assertThat(repliesAndActions.smartActions, is(empty()));
+ }
+
+ @Test
+ public void chooseSmartRepliesAndActions_noSysGeneratedSmartRepliesIfNotAllowed() {
+ Notification.Action freeFormAction = createActionBuilder("Freeform Action")
+ .setAllowGeneratedReplies(false)
+ .build();
+ // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
+ // replies.
+ setupAppGeneratedReplies(null, freeFormAction);
+
+ mEntry.smartReplies =
+ new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
+ NotificationContentView.SmartRepliesAndActions repliesAndActions =
+ NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+
+ assertThat(repliesAndActions.smartReplies, equalTo(null));
+ assertThat(repliesAndActions.smartActions, is(empty()));
+ }
+
+ @Test
+ public void chooseSmartRepliesAndActions_sysGeneratedSmartActions() {
+ // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
+ // actions.
+ setupAppGeneratedReplies(null);
+
+ mEntry.systemGeneratedSmartActions =
+ createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"});
+ NotificationContentView.SmartRepliesAndActions repliesAndActions =
+ NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+
+ assertThat(repliesAndActions.smartReplies, equalTo(null));
+ assertThat(repliesAndActions.smartActions, equalTo(mEntry.systemGeneratedSmartActions));
+ }
+
+ @Test
+ public void chooseSmartRepliesAndActions_appGenPreferredOverSysGen() {
+ Notification.Action freeFormAction = createActionBuilder("Freeform Action")
+ .setAllowGeneratedReplies(true)
+ .build();
+ CharSequence[] appGenSmartReplies = new String[] {"Reply1", "Reply2"};
+ // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart
+ // replies.
+ setupAppGeneratedReplies(appGenSmartReplies, freeFormAction);
+ when(mSmartReplyConstants.requiresTargetingP()).thenReturn(false);
+
+ List<Notification.Action> appGenSmartActions =
+ createActions(new String[] {"Test Action 1", "Test Action 2"});
+ when(mNotification.getContextualActions()).thenReturn(appGenSmartActions);
+
+ mEntry.smartReplies = new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"};
+ mEntry.systemGeneratedSmartActions =
+ createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"});
+
+ NotificationContentView.SmartRepliesAndActions repliesAndActions =
+ NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry);
+
+ assertThat(repliesAndActions.smartReplies, equalTo(appGenSmartReplies));
+ assertThat(repliesAndActions.smartActions, equalTo(appGenSmartActions));
+ }
+
+ private Notification.Action.Builder createActionBuilder(String actionTitle) {
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
+ new Intent(TEST_ACTION), 0);
+ return new Notification.Action.Builder(mActionIcon, actionTitle, pendingIntent);
+ }
+
+ private Notification.Action createAction(String actionTitle) {
+ return createActionBuilder(actionTitle).build();
+ }
+
+ private List<Notification.Action> createActions(String[] actionTitles) {
+ List<Notification.Action> actions = new ArrayList<>();
+ for (String title : actionTitles) {
+ actions.add(createAction(title));
+ }
+ return actions;
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
index 4534ebe..9e659c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
@@ -22,7 +22,9 @@
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -32,6 +34,8 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.service.notification.StatusBarNotification;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -41,14 +45,14 @@
import android.widget.Button;
import android.widget.LinearLayout;
-import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.statusbar.SmartReplyController;
+import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
-
-import java.util.concurrent.atomic.AtomicReference;
+import com.android.systemui.statusbar.phone.ShadeController;
import org.junit.After;
import org.junit.Before;
@@ -57,6 +61,10 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
@@ -67,6 +75,10 @@
private static final String[] TEST_CHOICES = new String[]{"Hello", "What's up?", "I'm here"};
private static final String TEST_NOTIFICATION_KEY = "akey";
+ private static final String[] TEST_ACTION_TITLES = new String[]{
+ "First action", "Open something", "Action"
+ };
+
private static final int WIDTH_SPEC = MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY);
private static final int HEIGHT_SPEC = MeasureSpec.makeMeasureSpec(400, MeasureSpec.AT_MOST);
@@ -74,6 +86,8 @@
private SmartReplyView mView;
private View mContainer;
+ private Icon mActionIcon;
+
private int mSingleLinePaddingHorizontal;
private int mDoubleLinePaddingHorizontal;
private int mSpacing;
@@ -82,12 +96,16 @@
private NotificationData.Entry mEntry;
private Notification mNotification;
+ @Mock ActivityStarter mActivityStarter;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mReceiver = new BlockingQueueIntentReceiver();
mContext.registerReceiver(mReceiver, new IntentFilter(TEST_ACTION));
mDependency.get(KeyguardDismissUtil.class).setDismissHandler(action -> action.onDismiss());
+ mDependency.injectMockDependency(ShadeController.class);
+ mDependency.injectTestDependency(ActivityStarter.class, mActivityStarter);
mContainer = new View(mContext, null);
mView = SmartReplyView.inflate(mContext, null);
@@ -108,6 +126,8 @@
when(sbn.getNotification()).thenReturn(mNotification);
when(sbn.getKey()).thenReturn(TEST_NOTIFICATION_KEY);
mEntry = new NotificationData.Entry(sbn);
+
+ mActionIcon = Icon.createWithResource(mContext, R.drawable.ic_person);
}
@After
@@ -117,7 +137,7 @@
@Test
public void testSendSmartReply_intentContainsResultsAndSource() throws InterruptedException {
- setRepliesFromRemoteInput(TEST_CHOICES);
+ setSmartReplies(TEST_CHOICES);
mView.getChildAt(2).performClick();
@@ -130,7 +150,7 @@
@Test
public void testSendSmartReply_keyguardCancelled() throws InterruptedException {
mDependency.get(KeyguardDismissUtil.class).setDismissHandler(action -> {});
- setRepliesFromRemoteInput(TEST_CHOICES);
+ setSmartReplies(TEST_CHOICES);
mView.getChildAt(2).performClick();
@@ -141,7 +161,7 @@
public void testSendSmartReply_waitsForKeyguard() throws InterruptedException {
AtomicReference<OnDismissAction> actionRef = new AtomicReference<>();
mDependency.get(KeyguardDismissUtil.class).setDismissHandler(actionRef::set);
- setRepliesFromRemoteInput(TEST_CHOICES);
+ setSmartReplies(TEST_CHOICES);
mView.getChildAt(2).performClick();
@@ -159,7 +179,7 @@
@Test
public void testSendSmartReply_controllerCalled() {
- setRepliesFromRemoteInput(TEST_CHOICES);
+ setSmartReplies(TEST_CHOICES);
mView.getChildAt(2).performClick();
verify(mLogger).smartReplySent(mEntry, 2, TEST_CHOICES[2]);
}
@@ -167,7 +187,7 @@
@Test
public void testSendSmartReply_hidesContainer() {
mContainer.setVisibility(View.VISIBLE);
- setRepliesFromRemoteInput(TEST_CHOICES);
+ setSmartReplies(TEST_CHOICES);
mView.getChildAt(0).performClick();
assertEquals(View.GONE, mContainer.getVisibility());
}
@@ -198,7 +218,7 @@
ViewGroup expectedView = buildExpectedView(choices, 1);
expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
- setRepliesFromRemoteInput(choices);
+ setSmartReplies(choices);
mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
assertEqualMeasures(expectedView, mView);
@@ -217,7 +237,7 @@
expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
10 + expectedView.getMeasuredHeight());
- setRepliesFromRemoteInput(choices);
+ setSmartReplies(choices);
mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
@@ -235,7 +255,7 @@
ViewGroup expectedView = buildExpectedView(choices, 2);
expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
- setRepliesFromRemoteInput(choices);
+ setSmartReplies(choices);
mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
assertEqualMeasures(expectedView, mView);
@@ -254,7 +274,7 @@
expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
10 + expectedView.getMeasuredHeight());
- setRepliesFromRemoteInput(choices);
+ setSmartReplies(choices);
mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
@@ -273,7 +293,7 @@
ViewGroup expectedView = buildExpectedView(new CharSequence[]{"Hi", "Bye"}, 1);
expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
- setRepliesFromRemoteInput(choices);
+ setSmartReplies(choices);
mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
assertEqualMeasures(expectedView, mView);
@@ -293,7 +313,7 @@
expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
10 + expectedView.getMeasuredHeight());
- setRepliesFromRemoteInput(choices);
+ setSmartReplies(choices);
mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
@@ -313,7 +333,7 @@
new CharSequence[]{"Short", "Short", "Looooooong \nreplyyyyy"}, 2);
expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
- setRepliesFromRemoteInput(choices);
+ setSmartReplies(choices);
mView.measure(
MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
MeasureSpec.UNSPECIFIED);
@@ -335,7 +355,7 @@
expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
10 + expectedView.getMeasuredHeight());
- setRepliesFromRemoteInput(choices);
+ setSmartReplies(choices);
mView.measure(
MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
MeasureSpec.UNSPECIFIED);
@@ -359,7 +379,7 @@
expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
10 + expectedView.getMeasuredHeight());
- setRepliesFromRemoteInput(choices);
+ setSmartReplies(choices);
mView.measure(
MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
MeasureSpec.UNSPECIFIED);
@@ -371,15 +391,45 @@
assertReplyButtonHidden(mView.getChildAt(2));
}
- private void setRepliesFromRemoteInput(CharSequence[] choices) {
+ private void setSmartReplies(CharSequence[] choices) {
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
new Intent(TEST_ACTION), 0);
RemoteInput input = new RemoteInput.Builder(TEST_RESULT_KEY).setChoices(choices).build();
- mView.setRepliesFromRemoteInput(input, pendingIntent, mLogger, mEntry, mContainer, choices);
+ mView.resetSmartSuggestions(mContainer);
+ mView.addRepliesFromRemoteInput(input, pendingIntent, mLogger, mEntry, choices);
+ }
+
+ private Notification.Action createAction(String actionTitle) {
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
+ new Intent(TEST_ACTION), 0);
+ return new Notification.Action.Builder(mActionIcon, actionTitle, pendingIntent).build();
+ }
+
+ private List<Notification.Action> createActions(String[] actionTitles) {
+ List<Notification.Action> actions = new ArrayList<>();
+ for (String title : actionTitles) {
+ actions.add(createAction(title));
+ }
+ return actions;
+ }
+
+ private void setSmartActions(String[] actionTitles) {
+ mView.resetSmartSuggestions(mContainer);
+ mView.addSmartActions(createActions(actionTitles));
+ }
+
+ private void setSmartRepliesAndActions(CharSequence[] choices, String[] actionTitles) {
+ setSmartReplies(choices);
+ mView.addSmartActions(createActions(actionTitles));
+ }
+
+ private ViewGroup buildExpectedView(CharSequence[] choices, int lineCount) {
+ return buildExpectedView(choices, lineCount, new ArrayList<>());
}
/** Builds a {@link ViewGroup} whose measures and layout mirror a {@link SmartReplyView}. */
- private ViewGroup buildExpectedView(CharSequence[] choices, int lineCount) {
+ private ViewGroup buildExpectedView(
+ CharSequence[] choices, int lineCount, List<Notification.Action> actions) {
LinearLayout layout = new LinearLayout(mContext);
layout.setOrientation(LinearLayout.HORIZONTAL);
@@ -401,6 +451,7 @@
return null;
}
+ // Add smart replies
Button previous = null;
for (int i = 0; i < choices.length; ++i) {
Button current = mView.inflateReplyButton(mContext, mView, i, choices[i],
@@ -420,6 +471,24 @@
previous = current;
}
+ // Add smart actions
+ for (int i = 0; i < actions.size(); ++i) {
+ Button current = inflateActionButton(actions.get(i));
+ current.setPadding(paddingHorizontal, current.getPaddingTop(), paddingHorizontal,
+ current.getPaddingBottom());
+ if (previous != null) {
+ ViewGroup.MarginLayoutParams lp =
+ (ViewGroup.MarginLayoutParams) previous.getLayoutParams();
+ if (isRtl) {
+ lp.leftMargin = mSpacing;
+ } else {
+ lp.rightMargin = mSpacing;
+ }
+ }
+ layout.addView(current);
+ previous = current;
+ }
+
return layout;
}
@@ -455,4 +524,255 @@
assertEquals(expected.getPaddingRight(), actual.getPaddingRight());
assertEquals(expected.getPaddingBottom(), actual.getPaddingBottom());
}
+
+
+ // =============================================================================================
+ // ============================= Smart Action tests ============================================
+ // =============================================================================================
+
+ @Test
+ public void testTapSmartAction_waitsForKeyguard() throws InterruptedException {
+ setSmartActions(TEST_ACTION_TITLES);
+
+ mView.getChildAt(2).performClick();
+
+ verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any());
+ }
+
+ @Test
+ public void testMeasure_shortSmartActions() {
+ String[] actions = new String[] {"Hi", "Hello", "Bye"};
+ // All choices should be displayed as SINGLE-line smart action buttons.
+ ViewGroup expectedView = buildExpectedView(new CharSequence[0], 1, createActions(actions));
+ expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ setSmartActions(actions);
+ mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ assertEqualMeasures(expectedView, mView);
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
+ }
+
+ @Test
+ public void testLayout_shortSmartActions() {
+ String[] actions = new String[] {"Hi", "Hello", "Bye"};
+ // All choices should be displayed as SINGLE-line smart action buttons.
+ ViewGroup expectedView = buildExpectedView(new CharSequence[0], 1, createActions(actions));
+ expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
+ 10 + expectedView.getMeasuredHeight());
+
+ setSmartActions(actions);
+ mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
+
+ assertEqualLayouts(expectedView, mView);
+ assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
+ assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(1));
+ assertEqualLayouts(expectedView.getChildAt(2), mView.getChildAt(2));
+ }
+
+ @Test
+ public void testMeasure_smartActionWithTwoLines() {
+ String[] actions = new String[] {"Hi", "Hello\neveryone", "Bye"};
+
+ // All actions should be displayed as DOUBLE-line smart action buttons.
+ ViewGroup expectedView = buildExpectedView(new CharSequence[0], 2, createActions(actions));
+ expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ setSmartActions(actions);
+ mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ assertEqualMeasures(expectedView, mView);
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
+ }
+
+ @Test
+ public void testLayout_smartActionWithTwoLines() {
+ String[] actions = new String[] {"Hi", "Hello\neveryone", "Bye"};
+
+ // All actions should be displayed as DOUBLE-line smart action buttons.
+ ViewGroup expectedView = buildExpectedView(new CharSequence[0], 2, createActions(actions));
+ expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
+ 10 + expectedView.getMeasuredHeight());
+
+ setSmartActions(actions);
+ mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
+
+ assertEqualLayouts(expectedView, mView);
+ assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
+ assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(1));
+ assertEqualLayouts(expectedView.getChildAt(2), mView.getChildAt(2));
+ }
+
+ @Test
+ public void testMeasure_smartActionWithThreeLines() {
+ String[] actions = new String[] {"Hi", "Hello\nevery\nbody", "Bye"};
+
+ // The action with three lines should NOT be displayed. All other actions should be
+ // displayed as SINGLE-line smart action buttons.
+ ViewGroup expectedView = buildExpectedView(new CharSequence[0], 1,
+ createActions(new String[]{"Hi", "Bye"}));
+ expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ setSmartActions(actions);
+ mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ assertEqualMeasures(expectedView, mView);
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
+ assertReplyButtonHidden(mView.getChildAt(1));
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(2));
+ }
+
+ @Test
+ public void testLayout_smartActionWithThreeLines() {
+ String[] actions = new String[] {"Hi", "Hello\nevery\nbody", "Bye"};
+
+ // The action with three lines should NOT be displayed. All other actions should be
+ // displayed as SINGLE-line smart action buttons.
+ ViewGroup expectedView = buildExpectedView(new CharSequence[0], 1,
+ createActions(new String[]{"Hi", "Bye"}));
+ expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
+ 10 + expectedView.getMeasuredHeight());
+
+ setSmartActions(actions);
+ mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
+
+ assertEqualLayouts(expectedView, mView);
+ assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
+ // We don't care about mView.getChildAt(1)'s layout because it's hidden (see
+ // testMeasure_smartActionWithThreeLines).
+ assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(2));
+ }
+
+ @Test
+ public void testMeasure_squeezeLongestSmartAction() {
+ String[] actions = new String[] {"Short", "Short", "Looooooong replyyyyy"};
+
+ // All actions should be displayed as DOUBLE-line smart action buttons.
+ ViewGroup expectedView = buildExpectedView(new CharSequence[0], 2,
+ createActions(new String[] {"Short", "Short", "Looooooong \nreplyyyyy"}));
+ expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ setSmartActions(actions);
+ mView.measure(
+ MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
+ MeasureSpec.UNSPECIFIED);
+
+ assertEqualMeasures(expectedView, mView);
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
+ }
+
+ @Test
+ public void testLayout_squeezeLongestSmartAction() {
+ String[] actions = new String[] {"Short", "Short", "Looooooong replyyyyy"};
+
+ // All actions should be displayed as DOUBLE-line smart action buttons.
+ ViewGroup expectedView = buildExpectedView(new CharSequence[0], 2,
+ createActions(new String[] {"Short", "Short", "Looooooong \nreplyyyyy"}));
+ expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
+ 10 + expectedView.getMeasuredHeight());
+
+ setSmartActions(actions);
+ mView.measure(
+ MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
+ MeasureSpec.UNSPECIFIED);
+ mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
+
+ assertEqualLayouts(expectedView, mView);
+ assertEqualLayouts(expectedView.getChildAt(0), mView.getChildAt(0));
+ assertEqualLayouts(expectedView.getChildAt(1), mView.getChildAt(1));
+ assertEqualLayouts(expectedView.getChildAt(2), mView.getChildAt(2));
+ }
+
+ @Test
+ public void testMeasure_dropLongestSmartAction() {
+ String[] actions = new String[] {"Short", "Short", "LooooooongUnbreakableReplyyyyy"};
+
+ // Short actions should be shown as single line views
+ ViewGroup expectedView = buildExpectedView(
+ new CharSequence[0], 1, createActions(new String[] {"Short", "Short"}));
+ expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ expectedView.layout(10, 10, 10 + expectedView.getMeasuredWidth(),
+ 10 + expectedView.getMeasuredHeight());
+
+ setSmartActions(actions);
+ mView.measure(
+ MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
+ MeasureSpec.UNSPECIFIED);
+ mView.layout(10, 10, 10 + mView.getMeasuredWidth(), 10 + mView.getMeasuredHeight());
+
+ assertEqualLayouts(expectedView, mView);
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
+ assertReplyButtonHidden(mView.getChildAt(2));
+ }
+
+ private Button inflateActionButton(Notification.Action action) {
+ return mView.inflateActionButton(getContext(), mView, action);
+ }
+
+ @Test
+ public void testInflateActionButton_smartActionIconSingleLineSizeForTwoLineButton() {
+ // Ensure smart action icons are the same size regardless of the number of text rows in the
+ // button.
+ Button singleLineButton = inflateActionButton(createAction("One line"));
+ Button doubleLineButton = inflateActionButton(createAction("Two\nlines"));
+ Drawable singleLineDrawable = singleLineButton.getCompoundDrawables()[0]; // left drawable
+ Drawable doubleLineDrawable = doubleLineButton.getCompoundDrawables()[0]; // left drawable
+ assertEquals(singleLineDrawable.getBounds().width(),
+ doubleLineDrawable.getBounds().width());
+ assertEquals(singleLineDrawable.getBounds().height(),
+ doubleLineDrawable.getBounds().height());
+ }
+
+ @Test
+ public void testMeasure_shortChoicesAndActions() {
+ CharSequence[] choices = new String[] {"Hi", "Hello"};
+ String[] actions = new String[] {"Bye"};
+ // All choices should be displayed as SINGLE-line smart action buttons.
+ ViewGroup expectedView = buildExpectedView(choices, 1, createActions(actions));
+ expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ setSmartRepliesAndActions(choices, actions);
+ mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ assertEqualMeasures(expectedView, mView);
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
+ }
+
+ @Test
+ public void testMeasure_choicesAndActionsSqueezeLongestAction() {
+ CharSequence[] choices = new String[] {"Short", "Short"};
+ String[] actions = new String[] {"Looooooong replyyyyy"};
+
+ // All actions should be displayed as DOUBLE-line smart action buttons.
+ ViewGroup expectedView = buildExpectedView(choices, 2,
+ createActions(new String[] {"Looooooong \nreplyyyyy"}));
+ expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ setSmartRepliesAndActions(choices, actions);
+ mView.measure(
+ MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
+ MeasureSpec.UNSPECIFIED);
+
+ assertEqualMeasures(expectedView, mView);
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 5c189ce..d5decce 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1832,8 +1832,8 @@
updateMagnificationLocked(userState);
scheduleUpdateFingerprintGestureHandling(userState);
scheduleUpdateInputFilter(userState);
- scheduleUpdateClientsIfNeededLocked(userState);
updateRelevantEventsLocked(userState);
+ scheduleUpdateClientsIfNeededLocked(userState);
updateAccessibilityButtonTargetsLocked(userState);
}
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 92d8d73..87a42fa 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -1328,40 +1328,12 @@
modifyInterfaceForward(false, fromIface, toIface);
}
- private void modifyNat(String action, String internalInterface, String externalInterface)
- throws SocketException {
- final Command cmd = new Command("nat", action, internalInterface, externalInterface);
-
- final NetworkInterface internalNetworkInterface = NetworkInterface.getByName(
- internalInterface);
- if (internalNetworkInterface == null) {
- cmd.appendArg("0");
- } else {
- // Don't touch link-local routes, as link-local addresses aren't routable,
- // kernel creates link-local routes on all interfaces automatically
- List<InterfaceAddress> interfaceAddresses = excludeLinkLocal(
- internalNetworkInterface.getInterfaceAddresses());
- cmd.appendArg(interfaceAddresses.size());
- for (InterfaceAddress ia : interfaceAddresses) {
- InetAddress addr = NetworkUtils.getNetworkPart(
- ia.getAddress(), ia.getNetworkPrefixLength());
- cmd.appendArg(addr.getHostAddress() + "/" + ia.getNetworkPrefixLength());
- }
- }
-
- try {
- mConnector.execute(cmd);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
- }
- }
-
@Override
public void enableNat(String internalInterface, String externalInterface) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- modifyNat("enable", internalInterface, externalInterface);
- } catch (SocketException e) {
+ mNetdService.tetherAddForward(internalInterface, externalInterface);
+ } catch (RemoteException | ServiceSpecificException e) {
throw new IllegalStateException(e);
}
}
@@ -1370,8 +1342,8 @@
public void disableNat(String internalInterface, String externalInterface) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- modifyNat("disable", internalInterface, externalInterface);
- } catch (SocketException e) {
+ mNetdService.tetherRemoveForward(internalInterface, externalInterface);
+ } catch (RemoteException | ServiceSpecificException e) {
throw new IllegalStateException(e);
}
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index c660cc6..a19e928 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1702,8 +1702,11 @@
s.app.whitelistManager = true;
}
// This could have made the service more important.
- mAm.updateLruProcessLocked(s.app, s.app.hasClientActivities()
- || s.app.treatLikeActivity, b.client);
+ mAm.updateLruProcessLocked(s.app,
+ (callerApp.hasActivitiesOrRecentTasks() && s.app.hasClientActivities())
+ || (callerApp.getCurProcState() <= ActivityManager.PROCESS_STATE_TOP
+ && (flags & Context.BIND_TREAT_LIKE_ACTIVITY) != 0),
+ b.client);
mAm.updateOomAdjLocked(s.app, true);
}
@@ -1787,6 +1790,32 @@
}
}
+ void updateServiceGroupLocked(IServiceConnection connection, int group, int importance) {
+ final IBinder binder = connection.asBinder();
+ if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "updateServiceGroup: conn=" + binder);
+ final ArrayList<ConnectionRecord> clist = mServiceConnections.get(binder);
+ if (clist == null) {
+ throw new IllegalArgumentException("Could not find connection for "
+ + connection.asBinder());
+ }
+ for (int i = clist.size() - 1; i >= 0; i--) {
+ final ConnectionRecord crec = clist.get(i);
+ final ServiceRecord srec = crec.binding.service;
+ if (srec != null && srec.app != null
+ && (srec.serviceInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0) {
+ if (group > 0) {
+ srec.app.connectionService = srec;
+ srec.app.connectionGroup = group;
+ srec.app.connectionImportance = importance;
+ } else {
+ srec.app.connectionService = null;
+ srec.app.connectionGroup = 0;
+ srec.app.connectionImportance = 0;
+ }
+ }
+ }
+ }
+
boolean unbindServiceLocked(IServiceConnection connection) {
IBinder binder = connection.asBinder();
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "unbindService: conn=" + binder);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4e417ba..7e9e83c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -9207,26 +9207,33 @@
}
dumpAssociationsLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage);
}
+ if (dumpPackage == null) {
+ pw.println();
+ if (dumpAll) {
+ pw.println("-------------------------------------------------------------------------------");
+ }
+ mOomAdjProfiler.dump(pw);
+ pw.println();
+ if (dumpAll) {
+ pw.println("-------------------------------------------------------------------------------");
+ }
+ dumpBinderProxies(pw);
+ pw.println();
+ if (dumpAll) {
+ pw.println("-------------------------------------------------------------------------------");
+ }
+ dumpLmkLocked(pw);
+ }
+ pw.println();
+ if (dumpAll) {
+ pw.println("-------------------------------------------------------------------------------");
+ }
+ dumpLruLocked(pw, dumpPackage);
pw.println();
if (dumpAll) {
pw.println("-------------------------------------------------------------------------------");
}
dumpProcessesLocked(fd, pw, args, opti, dumpAll, dumpPackage, dumpAppId);
- pw.println();
- if (dumpAll) {
- pw.println("-------------------------------------------------------------------------------");
- }
- mOomAdjProfiler.dump(pw);
- pw.println();
- if (dumpAll) {
- pw.println("-------------------------------------------------------------------------------");
- }
- dumpBinderProxies(pw);
- pw.println();
- if (dumpAll) {
- pw.println("-------------------------------------------------------------------------------");
- }
- dumpLmkLocked(pw);
}
}
@@ -9421,6 +9428,10 @@
synchronized (this) {
dumpLmkLocked(pw);
}
+ } else if ("lru".equals(cmd)) {
+ synchronized (this) {
+ dumpLruLocked(pw, null);
+ }
} else if ("permissions".equals(cmd) || "perm".equals(cmd)) {
synchronized (this) {
dumpPermissionsLocked(fd, pw, args, opti, true, null);
@@ -9698,17 +9709,102 @@
}
pw.println();
}
- pw.println();
return true;
}
return false;
}
void dumpBinderProxies(PrintWriter pw) {
+ pw.println("ACTIVITY MANAGER BINDER PROXY STATE (dumpsys activity binder-proxies)");
dumpBinderProxyInterfaceCounts(pw,
- "Top proxy interface names held by SYSTEM");
+ " Top proxy interface names held by SYSTEM");
dumpBinderProxiesCounts(pw,
- "Counts of Binder Proxies held by SYSTEM");
+ " Counts of Binder Proxies held by SYSTEM");
+ }
+
+ void dumpLruEntryLocked(PrintWriter pw, int index, ProcessRecord proc) {
+ pw.print(" #");
+ pw.print(index);
+ pw.print(": ");
+ pw.print(ProcessList.makeOomAdjString(proc.setAdj));
+ pw.print(" ");
+ pw.print(ProcessList.makeProcStateString(proc.getCurProcState()));
+ pw.print(" ");
+ pw.print(proc.toShortString());
+ pw.print(" ");
+ if (proc.hasActivitiesOrRecentTasks() || proc.hasClientActivities()
+ || proc.treatLikeActivity) {
+ pw.print(" activity=");
+ boolean printed = false;
+ if (proc.hasActivities()) {
+ pw.print("activities");
+ printed = true;
+ }
+ if (proc.hasRecentTasks()) {
+ if (printed) {
+ pw.print("|");
+ }
+ pw.print("recents");
+ printed = true;
+ }
+ if (proc.hasClientActivities()) {
+ if (printed) {
+ pw.print("|");
+ }
+ pw.print("client");
+ printed = true;
+ }
+ if (proc.treatLikeActivity) {
+ if (printed) {
+ pw.print("|");
+ }
+ pw.print("treated");
+ }
+ }
+ pw.println();
+ }
+
+ // TODO: Move to ProcessList?
+ void dumpLruLocked(PrintWriter pw, String dumpPackage) {
+ pw.println("ACTIVITY MANAGER LRU PROCESSES (dumpsys activity lru)");
+ final int N = mProcessList.mLruProcesses.size();
+ int i;
+ boolean first = true;
+ for (i = N - 1; i >= mProcessList.mLruProcessActivityStart; i--) {
+ final ProcessRecord r = mProcessList.mLruProcesses.get(i);
+ if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
+ continue;
+ }
+ if (first) {
+ pw.println(" Activities:");
+ first = false;
+ }
+ dumpLruEntryLocked(pw, i, r);
+ }
+ first = true;
+ for (; i >= mProcessList.mLruProcessServiceStart; i--) {
+ final ProcessRecord r = mProcessList.mLruProcesses.get(i);
+ if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
+ continue;
+ }
+ if (first) {
+ pw.println(" Services:");
+ first = false;
+ }
+ dumpLruEntryLocked(pw, i, r);
+ }
+ first = true;
+ for (; i >= 0; i--) {
+ final ProcessRecord r = mProcessList.mLruProcesses.get(i);
+ if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
+ continue;
+ }
+ if (first) {
+ pw.println(" Other:");
+ first = false;
+ }
+ dumpLruEntryLocked(pw, i, r);
+ }
}
// TODO: Move to ProcessList?
@@ -13214,6 +13310,12 @@
}
}
+ public void updateServiceGroup(IServiceConnection connection, int group, int importance) {
+ synchronized (this) {
+ mServices.updateServiceGroupLocked(connection, group, importance);
+ }
+ }
+
public boolean unbindService(IServiceConnection connection) {
synchronized (this) {
return mServices.unbindServiceLocked(connection);
@@ -17398,8 +17500,11 @@
int stepCached = 0;
int stepEmpty = 0;
int numCached = 0;
+ int numCachedExtraGroup = 0;
int numEmpty = 0;
int numTrimming = 0;
+ int lastCachedGroup = 0;
+ int lastCachedGroupUid = 0;
mNumNonCachedProcs = 0;
mNumCachedHiddenProcs = 0;
@@ -17523,7 +17628,21 @@
case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
mNumCachedHiddenProcs++;
numCached++;
- if (numCached > cachedProcessLimit) {
+ if (app.connectionGroup != 0) {
+ if (lastCachedGroupUid == app.uid
+ && lastCachedGroup == app.connectionGroup) {
+ // If this process is the next in the same group, we don't
+ // want it to count against our limit of the number of cached
+ // processes, so bump up the group count to account for it.
+ numCachedExtraGroup++;
+ } else {
+ lastCachedGroupUid = app.uid;
+ lastCachedGroup = app.connectionGroup;
+ }
+ } else {
+ lastCachedGroupUid = lastCachedGroup = 0;
+ }
+ if ((numCached - numCachedExtraGroup) > cachedProcessLimit) {
app.kill("cached #" + numCached, true);
}
break;
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 8f8d5ab..67a4d14 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2853,6 +2853,9 @@
pw.println(" provider [COMP_SPEC]: provider client-side state");
pw.println(" s[ervices] [COMP_SPEC ...]: service state");
pw.println(" as[sociations]: tracked app associations");
+ pw.println(" lmk: stats on low memory killer");
+ pw.println(" lru: raw LRU process list");
+ pw.println(" binder-proxies: stats on binder objects and IPCs");
pw.println(" settings: currently applied config settings");
pw.println(" service [COMP_SPEC]: service client-side state");
pw.println(" package [PACKAGE_NAME]: all state related to given package");
diff --git a/services/core/java/com/android/server/am/ConnectionRecord.java b/services/core/java/com/android/server/am/ConnectionRecord.java
index bfa3f66..aa76b3d 100644
--- a/services/core/java/com/android/server/am/ConnectionRecord.java
+++ b/services/core/java/com/android/server/am/ConnectionRecord.java
@@ -48,7 +48,7 @@
boolean serviceDead; // Well is it?
// Please keep the following two enum list synced.
- private static int[] BIND_ORIG_ENUMS = new int[] {
+ private static final int[] BIND_ORIG_ENUMS = new int[] {
Context.BIND_AUTO_CREATE,
Context.BIND_DEBUG_UNBIND,
Context.BIND_NOT_FOREGROUND,
@@ -65,7 +65,7 @@
Context.BIND_SHOWING_UI,
Context.BIND_NOT_VISIBLE,
};
- private static int[] BIND_PROTO_ENUMS = new int[] {
+ private static final int[] BIND_PROTO_ENUMS = new int[] {
ConnectionRecordProto.AUTO_CREATE,
ConnectionRecordProto.DEBUG_UNBIND,
ConnectionRecordProto.NOT_FG,
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 84b364b..4b19398 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -50,6 +50,7 @@
import android.app.AppProtoEnums;
import android.app.IApplicationThread;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
@@ -2204,7 +2205,7 @@
@GuardedBy("mService")
int updateLruProcessInternalLocked(ProcessRecord app, long now, int index,
- String what, Object obj, ProcessRecord srcApp) {
+ int lruSeq, String what, Object obj, ProcessRecord srcApp) {
app.lastActivityTime = now;
if (app.hasActivitiesOrRecentTasks()) {
@@ -2225,7 +2226,7 @@
return index;
}
- if (lrui >= mLruProcessActivityStart) {
+ if (lrui >= mLruProcessActivityStart && index < mLruProcessActivityStart) {
// Don't want to touch dependent processes that are hosting activities.
return index;
}
@@ -2237,6 +2238,7 @@
if (DEBUG_LRU) Slog.d(TAG_LRU, "Moving dep from " + lrui + " to " + index
+ " in LRU list: " + app);
mLruProcesses.add(index, app);
+ app.lruSeq = lruSeq;
return index;
}
@@ -2345,9 +2347,11 @@
*/
int nextIndex;
+ int nextActivityIndex = -1;
if (hasActivity) {
final int N = mLruProcesses.size();
- if ((!app.hasActivities() || app.hasRecentTasks())
+ nextIndex = mLruProcessServiceStart;
+ if (!app.hasActivitiesOrRecentTasks() && !app.treatLikeActivity
&& mLruProcessActivityStart < (N - 1)) {
// Process doesn't have activities, but has clients with
// activities... move it up, but one below the top (the top
@@ -2355,36 +2359,92 @@
if (DEBUG_LRU) Slog.d(TAG_LRU,
"Adding to second-top of LRU activity list: " + app);
mLruProcesses.add(N - 1, app);
- // To keep it from spamming the LRU list (by making a bunch of clients),
- // we will push down any other entries owned by the app.
+ // If this process is part of a group, need to pull up any other processes
+ // in that group to be with it.
final int uid = app.info.uid;
- for (int i = N - 2; i > mLruProcessActivityStart; i--) {
- ProcessRecord subProc = mLruProcesses.get(i);
- if (subProc.info.uid == uid) {
- // We want to push this one down the list. If the process after
- // it is for the same uid, however, don't do so, because we don't
- // want them internally to be re-ordered.
- if (mLruProcesses.get(i - 1).info.uid != uid) {
- if (DEBUG_LRU) Slog.d(TAG_LRU,
- "Pushing uid " + uid + " swapping at " + i + ": "
- + mLruProcesses.get(i) + " : "
- + mLruProcesses.get(i - 1));
- ProcessRecord tmp = mLruProcesses.get(i);
- mLruProcesses.set(i, mLruProcesses.get(i - 1));
- mLruProcesses.set(i - 1, tmp);
- i--;
+ int endIndex = N - 2;
+ nextActivityIndex = N - 2;
+ if (app.connectionGroup > 0) {
+ int endImportance = app.connectionImportance;
+ for (int i = endIndex; i >= mLruProcessActivityStart; i--) {
+ final ProcessRecord subProc = mLruProcesses.get(i);
+ if (subProc.info.uid == uid
+ && subProc.connectionGroup == subProc.connectionGroup) {
+ if (i == endIndex && subProc.connectionImportance >= endImportance) {
+ // This process is already in the group, and its importance
+ // is not as strong as the process before it, so it keep it
+ // correctly positioned in the group.
+ endIndex--;
+ endImportance = subProc.connectionImportance;
+ } else {
+ // We want to pull this up to be with the rest of the group,
+ // and order within the group by importance.
+ boolean moved = false;
+ for (int pos = N - 1; pos > endIndex; pos--) {
+ final ProcessRecord posProc = mLruProcesses.get(pos);
+ if (subProc.connectionImportance
+ <= posProc.connectionImportance) {
+ mLruProcesses.remove(i);
+ mLruProcesses.add(pos, subProc);
+ moved = true;
+ endIndex--;
+ break;
+ }
+ }
+ if (!moved) {
+ // Goes to the end of the group.
+ mLruProcesses.remove(i);
+ mLruProcesses.add(endIndex - 1, subProc);
+ endIndex--;
+ endImportance = subProc.connectionImportance;
+ }
+ }
}
- } else {
- // A gap, we can stop here.
- break;
+ }
+
+ }
+ // To keep it from spamming the LRU list (by making a bunch of clients),
+ // we will distribute other entries owned by it to be in-between other apps.
+ for (int i = endIndex; i >= mLruProcessActivityStart; i--) {
+ final ProcessRecord subProc = mLruProcesses.get(i);
+ if (subProc.info.uid != uid) {
+ // This is a different app... if we have gone through some of the
+ // target app, pull this up to be before them.
+ if (i < endIndex) {
+ mLruProcesses.remove(i);
+ mLruProcesses.add(endIndex, subProc);
+ }
+ // Find the end of the next group of processes for target app. This
+ // is after any entries of different apps (so we don't change the existing
+ // relative order of apps) and then after the next last group of processes
+ // of the target app.
+ for (endIndex--; endIndex >= mLruProcessActivityStart; endIndex--) {
+ final ProcessRecord endProc = mLruProcesses.get(endIndex);
+ if (endProc.info.uid == uid) {
+ break;
+ }
+ }
+ if (endIndex >= mLruProcessActivityStart) {
+ final ProcessRecord endProc = mLruProcesses.get(endIndex);
+ for (endIndex--; endIndex >= mLruProcessActivityStart; endIndex--) {
+ final ProcessRecord nextEndProc = mLruProcesses.get(endIndex);
+ if (nextEndProc.info.uid != uid
+ || nextEndProc.connectionGroup != endProc.connectionGroup) {
+ break;
+ }
+ }
+ }
+ if (i > endIndex) {
+ i = endIndex;
+ }
}
}
} else {
// Process has activities, put it at the very tipsy-top.
if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU activity list: " + app);
mLruProcesses.add(app);
+ nextActivityIndex = mLruProcesses.size() - 1;
}
- nextIndex = mLruProcessServiceStart;
} else if (hasService) {
// Process has services, put it at the top of the service list.
if (DEBUG_LRU) Slog.d(TAG_LRU, "Adding to top of LRU service list: " + app);
@@ -2416,6 +2476,8 @@
mLruProcessServiceStart++;
}
+ app.lruSeq = mLruSeq;
+
// If the app is currently using a content provider or service,
// bump those processes as well.
for (int j = app.connections.size() - 1; j >= 0; j--) {
@@ -2423,17 +2485,27 @@
if (cr.binding != null && !cr.serviceDead && cr.binding.service != null
&& cr.binding.service.app != null
&& cr.binding.service.app.lruSeq != mLruSeq
+ && (cr.flags & Context.BIND_REDUCTION_FLAGS) == 0
&& !cr.binding.service.app.isPersistent()) {
- nextIndex = updateLruProcessInternalLocked(cr.binding.service.app,
- now,
- nextIndex,
- "service connection", cr, app);
+ if (cr.binding.service.app.hasClientActivities()) {
+ if (nextActivityIndex >= 0) {
+ nextActivityIndex = updateLruProcessInternalLocked(cr.binding.service.app,
+ now,
+ nextActivityIndex, mLruSeq,
+ "service connection", cr, app);
+ }
+ } else {
+ nextIndex = updateLruProcessInternalLocked(cr.binding.service.app,
+ now,
+ nextIndex, mLruSeq,
+ "service connection", cr, app);
+ }
}
}
for (int j = app.conProviders.size() - 1; j >= 0; j--) {
ContentProviderRecord cpr = app.conProviders.get(j).provider;
if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq && !cpr.proc.isPersistent()) {
- nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex,
+ nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex, mLruSeq,
"provider reference", cpr, app);
}
}
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index faf8561..013de93 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -155,6 +155,9 @@
int pssStatType; // The type of stat collection that we are currently requesting
int savedPriority; // Previous priority value if we're switching to non-SCHED_OTHER
int renderThreadTid; // TID for RenderThread
+ ServiceRecord connectionService; // Service that applied current connectionGroup/Importance
+ int connectionGroup; // Last group set by a connection
+ int connectionImportance; // Last importance set by a connection
boolean serviceb; // Process currently is on the service B list
boolean serviceHighRam; // We are forcing to service B list due to its RAM use
boolean notCachedSinceIdle; // Has this process not been in a cached state since last idle?
@@ -396,6 +399,11 @@
pw.print(" hasAboveClient="); pw.print(hasAboveClient);
pw.print(" treatLikeActivity="); pw.println(treatLikeActivity);
}
+ if (connectionService != null || connectionGroup != 0) {
+ pw.print(prefix); pw.print("connectionGroup="); pw.print(connectionGroup);
+ pw.print(" Importance="); pw.print(connectionImportance);
+ pw.print(" Service="); pw.println(connectionService);
+ }
if (hasTopUi() || hasOverlayUi() || runningRemoteAnimation) {
pw.print(prefix); pw.print("hasTopUi="); pw.print(hasTopUi());
pw.print(" hasOverlayUi="); pw.print(hasOverlayUi());
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 9d5d65d..1dfb86a 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -94,6 +94,7 @@
import android.service.vr.IVrStateCallbacks;
import android.text.TextUtils;
import android.text.style.SuggestionSpan;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.EventLog;
@@ -179,7 +180,6 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
-import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.WeakHashMap;
@@ -321,7 +321,7 @@
// All known input methods. mMethodMap also serves as the global
// lock for this class.
final ArrayList<InputMethodInfo> mMethodList = new ArrayList<>();
- final HashMap<String, InputMethodInfo> mMethodMap = new HashMap<>();
+ final ArrayMap<String, InputMethodInfo> mMethodMap = new ArrayMap<>();
private final LruCache<SuggestionSpan, InputMethodInfo> mSecureSuggestionSpans =
new LruCache<>(SECURE_SUGGESTION_SPANS_MAX_SIZE);
private final InputMethodSubtypeSwitchingController mSwitchingController;
@@ -457,7 +457,7 @@
}
}
- final HashMap<IBinder, ClientState> mClients = new HashMap<>();
+ final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<>();
/**
* Set once the system is ready to run third party code.
@@ -553,8 +553,8 @@
private InputMethodSubtype mCurrentSubtype;
// This list contains the pairs of InputMethodInfo and InputMethodSubtype.
- private final HashMap<InputMethodInfo, ArrayList<InputMethodSubtype>>
- mShortcutInputMethodsAndSubtypes = new HashMap<>();
+ private final ArrayMap<InputMethodInfo, ArrayList<InputMethodSubtype>>
+ mShortcutInputMethodsAndSubtypes = new ArrayMap<>();
// Was the keyguard locked when this client became current?
private boolean mCurClientInKeyguard;
@@ -1781,7 +1781,9 @@
final int callerPid = Binder.getCallingPid();
synchronized (mMethodMap) {
// TODO: Optimize this linear search.
- for (ClientState state : mClients.values()) {
+ final int numClients = mClients.size();
+ for (int i = 0; i < numClients; ++i) {
+ final ClientState state = mClients.valueAt(i);
if (state.uid == callerUid && state.pid == callerPid
&& state.selfReportedDisplayId == selfReportedDisplayId) {
throw new SecurityException("uid=" + callerUid + "/pid=" + callerPid
@@ -2192,8 +2194,9 @@
void clearCurMethodLocked() {
if (mCurMethod != null) {
- for (ClientState cs : mClients.values()) {
- clearClientSessionLocked(cs);
+ final int numClients = mClients.size();
+ for (int i = 0; i < numClients; ++i) {
+ clearClientSessionLocked(mClients.valueAt(i));
}
finishSessionLocked(mEnabledSession);
@@ -3670,7 +3673,7 @@
| PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS),
mSettings.getCurrentUserId());
- final HashMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
+ final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
mFileManager.getAllAdditionalInputMethodSubtypes();
for (int i = 0; i < services.size(); ++i) {
ResolveInfo ri = services.get(i);
@@ -3824,19 +3827,15 @@
if (DEBUG) Slog.v(TAG, "Current IME: " + lastInputMethodId);
synchronized (mMethodMap) {
- final HashMap<InputMethodInfo, List<InputMethodSubtype>> immis =
- mSettings.getExplicitlyOrImplicitlyEnabledInputMethodsAndSubtypeListLocked(
- mContext);
- if (immis == null || immis.size() == 0) {
+ final List<ImeSubtypeListItem> imList =
+ mSwitchingController.getSortedInputMethodAndSubtypeListLocked(
+ showAuxSubtypes, isScreenLocked);
+ if (imList.isEmpty()) {
return;
}
hideInputMethodMenuLocked();
- final List<ImeSubtypeListItem> imList =
- mSwitchingController.getSortedInputMethodAndSubtypeListLocked(
- showAuxSubtypes, isScreenLocked);
-
if (lastInputMethodSubtypeId == NOT_A_SUBTYPE_ID) {
final InputMethodSubtype currentSubtype = getCurrentInputMethodSubtypeLocked();
if (currentSubtype != null) {
@@ -4310,10 +4309,10 @@
private static final String ATTR_IS_AUXILIARY = "isAuxiliary";
private static final String ATTR_IS_ASCII_CAPABLE = "isAsciiCapable";
private final AtomicFile mAdditionalInputMethodSubtypeFile;
- private final HashMap<String, InputMethodInfo> mMethodMap;
- private final HashMap<String, List<InputMethodSubtype>> mAdditionalSubtypesMap =
- new HashMap<>();
- public InputMethodFileManager(HashMap<String, InputMethodInfo> methodMap, int userId) {
+ private final ArrayMap<String, InputMethodInfo> mMethodMap;
+ private final ArrayMap<String, List<InputMethodSubtype>> mAdditionalSubtypesMap =
+ new ArrayMap<>();
+ InputMethodFileManager(ArrayMap<String, InputMethodInfo> methodMap, int userId) {
if (methodMap == null) {
throw new NullPointerException("methodMap is null");
}
@@ -4365,15 +4364,15 @@
}
}
- public HashMap<String, List<InputMethodSubtype>> getAllAdditionalInputMethodSubtypes() {
+ public ArrayMap<String, List<InputMethodSubtype>> getAllAdditionalInputMethodSubtypes() {
synchronized (mMethodMap) {
return mAdditionalSubtypesMap;
}
}
private static void writeAdditionalInputMethodSubtypes(
- HashMap<String, List<InputMethodSubtype>> allSubtypes, AtomicFile subtypesFile,
- HashMap<String, InputMethodInfo> methodMap) {
+ ArrayMap<String, List<InputMethodSubtype>> allSubtypes, AtomicFile subtypesFile,
+ ArrayMap<String, InputMethodInfo> methodMap) {
// Safety net for the case that this function is called before methodMap is set.
final boolean isSetMethodMap = methodMap != null && methodMap.size() > 0;
FileOutputStream fos = null;
@@ -4427,7 +4426,7 @@
}
private static void readAdditionalInputMethodSubtypes(
- HashMap<String, List<InputMethodSubtype>> allSubtypes, AtomicFile subtypesFile) {
+ ArrayMap<String, List<InputMethodSubtype>> allSubtypes, AtomicFile subtypesFile) {
if (allSubtypes == null || subtypesFile == null) return;
allSubtypes.clear();
try (final FileInputStream fis = subtypesFile.openRead()) {
@@ -4625,7 +4624,9 @@
info.dump(p, " ");
}
p.println(" Clients:");
- for (ClientState ci : mClients.values()) {
+ final int numClients = mClients.size();
+ for (int i = 0; i < numClients; ++i) {
+ final ClientState ci = mClients.valueAt(i);
p.println(" Client " + ci + ":");
p.println(" client=" + ci.client);
p.println(" inputContext=" + ci.inputContext);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
index 27c59d4..b13c307 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.Log;
import android.util.Printer;
import android.util.Slog;
@@ -31,8 +32,6 @@
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
@@ -184,11 +183,8 @@
public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList(
boolean includeAuxiliarySubtypes, boolean isScreenLocked) {
- final ArrayList<ImeSubtypeListItem> imList = new ArrayList<>();
- final HashMap<InputMethodInfo, List<InputMethodSubtype>> immis =
- mSettings.getExplicitlyOrImplicitlyEnabledInputMethodsAndSubtypeListLocked(
- mContext);
- if (immis == null || immis.size() == 0) {
+ final ArrayList<InputMethodInfo> imis = mSettings.getEnabledInputMethodListLocked();
+ if (imis.isEmpty()) {
return Collections.emptyList();
}
if (isScreenLocked && includeAuxiliarySubtypes) {
@@ -197,12 +193,13 @@
}
includeAuxiliarySubtypes = false;
}
- for (InputMethodInfo imi : immis.keySet()) {
- if (imi == null) {
- continue;
- }
- List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypeList = immis.get(imi);
- HashSet<String> enabledSubtypeSet = new HashSet<>();
+ final ArrayList<ImeSubtypeListItem> imList = new ArrayList<>();
+ final int numImes = imis.size();
+ for (int i = 0; i < numImes; ++i) {
+ final InputMethodInfo imi = imis.get(i);
+ final List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypeList =
+ mSettings.getEnabledInputMethodSubtypeListLocked(mContext, imi, true);
+ final ArraySet<String> enabledSubtypeSet = new ArraySet<>();
for (InputMethodSubtype subtype : explicitlyOrImplicitlyEnabledSubtypeList) {
enabledSubtypeSet.add(String.valueOf(subtype.hashCode()));
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
index 154e8b3..8e3f351 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
@@ -31,6 +31,7 @@
import android.os.RemoteException;
import android.provider.Settings;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.Pair;
import android.util.Printer;
import android.util.Slog;
@@ -45,7 +46,6 @@
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
@@ -473,7 +473,7 @@
final int numSubtypes = subtypes.size();
// Handle overridesImplicitlyEnabledSubtype mechanism.
- final HashMap<String, InputMethodSubtype> applicableModeAndSubtypesMap = new HashMap<>();
+ final ArrayMap<String, InputMethodSubtype> applicableModeAndSubtypesMap = new ArrayMap<>();
for (int i = 0; i < numSubtypes; ++i) {
// scan overriding implicitly enabled subtypes.
final InputMethodSubtype subtype = subtypes.get(i);
@@ -488,8 +488,8 @@
return new ArrayList<>(applicableModeAndSubtypesMap.values());
}
- final HashMap<String, ArrayList<InputMethodSubtype>> nonKeyboardSubtypesMap =
- new HashMap<>();
+ final ArrayMap<String, ArrayList<InputMethodSubtype>> nonKeyboardSubtypesMap =
+ new ArrayMap<>();
final ArrayList<InputMethodSubtype> keyboardSubtypes = new ArrayList<>();
for (int i = 0; i < numSubtypes; ++i) {
@@ -761,12 +761,12 @@
private final Resources mRes;
private final ContentResolver mResolver;
- private final HashMap<String, InputMethodInfo> mMethodMap;
+ private final ArrayMap<String, InputMethodInfo> mMethodMap;
/**
* On-memory data store to emulate when {@link #mCopyOnWrite} is {@code true}.
*/
- private final HashMap<String, String> mCopyOnWriteDataStore = new HashMap<>();
+ private final ArrayMap<String, String> mCopyOnWriteDataStore = new ArrayMap<>();
private boolean mCopyOnWrite = false;
@NonNull
@@ -812,7 +812,7 @@
public InputMethodSettings(
Resources res, ContentResolver resolver,
- HashMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList,
+ ArrayMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList,
@UserIdInt int userId, boolean copyOnWrite) {
mRes = res;
mResolver = resolver;
@@ -1277,17 +1277,6 @@
}
}
- public HashMap<InputMethodInfo, List<InputMethodSubtype>>
- getExplicitlyOrImplicitlyEnabledInputMethodsAndSubtypeListLocked(Context context) {
- HashMap<InputMethodInfo, List<InputMethodSubtype>> enabledInputMethodAndSubtypes =
- new HashMap<>();
- for (InputMethodInfo imi: getEnabledInputMethodListLocked()) {
- enabledInputMethodAndSubtypes.put(
- imi, getEnabledInputMethodSubtypeListLocked(context, imi, true));
- }
- return enabledInputMethodAndSubtypes;
- }
-
public void dumpLocked(final Printer pw, final String prefix) {
pw.println(prefix + "mCurrentUserId=" + mCurrentUserId);
pw.println(prefix + "mCurrentProfileIds=" + Arrays.toString(mCurrentProfileIds));
diff --git a/services/core/java/com/android/server/inputmethod/LocaleUtils.java b/services/core/java/com/android/server/inputmethod/LocaleUtils.java
index 4958ece..7a6853a 100644
--- a/services/core/java/com/android/server/inputmethod/LocaleUtils.java
+++ b/services/core/java/com/android/server/inputmethod/LocaleUtils.java
@@ -22,10 +22,10 @@
import android.icu.util.ULocale;
import android.os.LocaleList;
import android.text.TextUtils;
+import android.util.ArrayMap;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashMap;
import java.util.List;
import java.util.Locale;
@@ -155,7 +155,7 @@
}
final int numPreferredLocales = preferredLocales.size();
- final HashMap<String, ScoreEntry> scoreboard = new HashMap<>();
+ final ArrayMap<String, ScoreEntry> scoreboard = new ArrayMap<>();
final byte[] score = new byte[numPreferredLocales];
final ULocale[] preferredULocaleCache = new ULocale[numPreferredLocales];
@@ -197,7 +197,11 @@
}
}
- final ScoreEntry[] result = scoreboard.values().toArray(new ScoreEntry[scoreboard.size()]);
+ final int numEntries = scoreboard.size();
+ final ScoreEntry[] result = new ScoreEntry[numEntries];
+ for (int i = 0; i < numEntries; ++i) {
+ result[i] = scoreboard.valueAt(i);
+ }
Arrays.sort(result);
for (final ScoreEntry entry : result) {
dest.add(sources.get(entry.mIndex));
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 32990ce..4da29e4 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -411,7 +411,7 @@
private NotificationAssistants mAssistants;
private ConditionProviders mConditionProviders;
private NotificationUsageStats mUsageStats;
- private boolean mLockScreenAllowSecureNotifications;
+ private boolean mLockScreenAllowSecureNotifications = true;
private static final int MY_UID = Process.myUid();
private static final int MY_PID = Process.myPid();
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 31711e5..6f275ec 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2821,7 +2821,8 @@
pw.println(" [--install-reason 0/1/2/3/4] [--originating-uri URI]");
pw.println(" [--referrer URI] [--abi ABI_NAME] [--force-sdk]");
pw.println(" [--preload] [--instantapp] [--full] [--dont-kill]");
- pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES] [PATH|-]");
+ pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES] [--apex]");
+ pw.println(" [PATH|-]");
pw.println(" Install an application. Must provide the apk data to install, either as a");
pw.println(" file path or '-' to read from stdin. Options are:");
pw.println(" -l: forward lock application");
@@ -2850,13 +2851,14 @@
pw.println(" --force-uuid: force install on to disk volume with given UUID");
pw.println(" --force-sdk: allow install even when existing app targets platform");
pw.println(" codename but new one targets a final API level");
+ pw.println(" --apex: install an .apex file, not an .apk");
pw.println("");
pw.println(" install-create [-lrtsfdg] [-i PACKAGE] [--user USER_ID|all|current]");
pw.println(" [-p INHERIT_PACKAGE] [--install-location 0/1/2]");
pw.println(" [--install-reason 0/1/2/3/4] [--originating-uri URI]");
pw.println(" [--referrer URI] [--abi ABI_NAME] [--force-sdk]");
pw.println(" [--preload] [--instantapp] [--full] [--dont-kill]");
- pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES]");
+ pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [--apex] [-S BYTES]");
pw.println(" [--multi-package]");
pw.println(" Like \"install\", but starts an install session. Use \"install-write\"");
pw.println(" to push data into the session, and \"install-commit\" to finish.");
diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java
index 812fd82..79e2688 100644
--- a/services/core/java/com/android/server/power/ThermalManagerService.java
+++ b/services/core/java/com/android/server/power/ThermalManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.power;
+import android.annotation.Nullable;
import android.content.Context;
import android.hardware.thermal.V1_0.ThermalStatus;
import android.hardware.thermal.V1_0.ThermalStatusCode;
@@ -26,12 +27,16 @@
import android.os.HwBinder;
import android.os.IThermalEventListener;
import android.os.IThermalService;
+import android.os.IThermalStatusListener;
import android.os.PowerManager;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.os.Temperature;
+import android.util.ArrayMap;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
import com.android.server.FgThread;
import com.android.server.SystemService;
@@ -39,6 +44,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import java.util.NoSuchElementException;
@@ -51,210 +57,154 @@
public class ThermalManagerService extends SystemService {
private static final String TAG = ThermalManagerService.class.getSimpleName();
- /** Registered observers of the thermal changed events. Cookie is used to store type */
+ /** Lock to protect listen list. */
+ private final Object mLock = new Object();
+
+ /**
+ * Registered observers of the thermal events. Cookie is used to store type as Integer, null
+ * means no filter.
+ */
@GuardedBy("mLock")
private final RemoteCallbackList<IThermalEventListener> mThermalEventListeners =
new RemoteCallbackList<>();
- /** Lock to protect HAL handles and listen list. */
- private final Object mLock = new Object();
-
- /** Newly registered callback. */
+ /** Registered observers of the thermal status. */
@GuardedBy("mLock")
- private IThermalEventListener mNewListenerCallback = null;
+ private final RemoteCallbackList<IThermalStatusListener> mThermalStatusListeners =
+ new RemoteCallbackList<>();
- /** Newly registered callback type, null means not filter type. */
+ /** Current thermal status */
@GuardedBy("mLock")
- private Integer mNewListenerType = null;
+ private int mStatus;
+
+ /** Current thermal map, key as name */
+ @GuardedBy("mLock")
+ private ArrayMap<String, Temperature> mTemperatureMap = new ArrayMap<>();
/** Local PMS handle. */
private final PowerManager mPowerManager;
- /** Proxy object for the Thermal HAL 2.0 service. */
+ /** HAL wrapper. */
+ private ThermalHalWrapper mHalWrapper;
+
+ /** Hal ready. */
@GuardedBy("mLock")
- private android.hardware.thermal.V2_0.IThermal mThermalHal20 = null;
+ private boolean mHalReady;
- /** Proxy object for the Thermal HAL 1.1 service. */
- @GuardedBy("mLock")
- private android.hardware.thermal.V1_1.IThermal mThermalHal11 = null;
-
- /** Cookie for matching the right end point. */
- private static final int THERMAL_HAL_DEATH_COOKIE = 5612;
-
- /** HWbinder callback for Thermal HAL 2.0. */
- private final IThermalChangedCallback.Stub mThermalCallback20 =
- new IThermalChangedCallback.Stub() {
- @Override
- public void notifyThrottling(
- android.hardware.thermal.V2_0.Temperature temperature) {
- android.os.Temperature thermalSvcTemp = new android.os.Temperature(
- temperature.value, temperature.type, temperature.name,
- temperature.throttlingStatus);
- final long token = Binder.clearCallingIdentity();
- try {
- notifyThrottlingImpl(thermalSvcTemp);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
- };
-
- /** HWbinder callback for Thermal HAL 1.1. */
- private final IThermalCallback.Stub mThermalCallback11 =
- new IThermalCallback.Stub() {
- @Override
- public void notifyThrottling(boolean isThrottling,
- android.hardware.thermal.V1_0.Temperature temperature) {
- android.os.Temperature thermalSvcTemp = new android.os.Temperature(
- temperature.currentValue, temperature.type, temperature.name,
- isThrottling ? ThrottlingSeverity.SEVERE : ThrottlingSeverity.NONE);
- final long token = Binder.clearCallingIdentity();
- try {
- notifyThrottlingImpl(thermalSvcTemp);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
- };
+ /** Invalid throttling status */
+ private static final int INVALID_THROTTLING = Integer.MIN_VALUE;
public ThermalManagerService(Context context) {
+ this(context, null);
+ }
+
+ @VisibleForTesting
+ ThermalManagerService(Context context, @Nullable ThermalHalWrapper halWrapper) {
super(context);
mPowerManager = context.getSystemService(PowerManager.class);
+ mHalWrapper = halWrapper;
+ // Initialize to invalid to send status onActivityManagerReady
+ mStatus = INVALID_THROTTLING;
}
- private void setNewListener(IThermalEventListener listener, Integer type) {
- synchronized (mLock) {
- mNewListenerCallback = listener;
- mNewListenerType = type;
+ @Override
+ public void onStart() {
+ publishBinderService(Context.THERMAL_SERVICE, mService);
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+ onActivityManagerReady();
}
}
- private void clearNewListener() {
+ private void onActivityManagerReady() {
synchronized (mLock) {
- mNewListenerCallback = null;
- mNewListenerType = null;
- }
- }
-
- private final IThermalService.Stub mService = new IThermalService.Stub() {
- @Override
- public void registerThermalEventListener(IThermalEventListener listener) {
- synchronized (mLock) {
- mThermalEventListeners.register(listener, null);
- // Notify its callback after new client registered.
- setNewListener(listener, null);
- long token = Binder.clearCallingIdentity();
- try {
- notifyCurrentTemperaturesLocked();
- } finally {
- Binder.restoreCallingIdentity(token);
- clearNewListener();
+ // Connect to HAL and post to listeners.
+ boolean halConnected = (mHalWrapper != null);
+ if (!halConnected) {
+ mHalWrapper = new ThermalHal20Wrapper();
+ halConnected = mHalWrapper.connectToHal();
+ if (!halConnected) {
+ mHalWrapper = new ThermalHal11Wrapper();
+ halConnected = mHalWrapper.connectToHal();
}
}
- }
-
- @Override
- public void registerThermalEventListenerWithType(IThermalEventListener listener, int type) {
- synchronized (mLock) {
- mThermalEventListeners.register(listener, new Integer(type));
- setNewListener(listener, new Integer(type));
- // Notify its callback after new client registered.
- long token = Binder.clearCallingIdentity();
- try {
- notifyCurrentTemperaturesLocked();
- } finally {
- Binder.restoreCallingIdentity(token);
- clearNewListener();
- }
+ mHalWrapper.setCallback(this::onTemperatureChangedCallback);
+ if (!halConnected) {
+ return;
}
- }
-
- @Override
- public void unregisterThermalEventListener(IThermalEventListener listener) {
- synchronized (mLock) {
- long token = Binder.clearCallingIdentity();
- try {
- mThermalEventListeners.unregister(listener);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ List<Temperature> temperatures = mHalWrapper.getCurrentTemperatures(false,
+ 0);
+ final int count = temperatures.size();
+ for (int i = 0; i < count; i++) {
+ onTemperatureChanged(temperatures.get(i), false);
}
+ onTemperatureMapChangedLocked();
+ mHalReady = halConnected /* true */;
}
-
- @Override
- public List<android.os.Temperature> getCurrentTemperatures() {
- List<android.os.Temperature> ret;
- long token = Binder.clearCallingIdentity();
- try {
- ret = getCurrentTemperaturesInternal(false, 0 /* not used */);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- return ret;
- }
-
- @Override
- public List<android.os.Temperature> getCurrentTemperaturesWithType(int type) {
- List<android.os.Temperature> ret;
- long token = Binder.clearCallingIdentity();
- try {
- ret = getCurrentTemperaturesInternal(true, type);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- return ret;
- }
-
- @Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
- pw.println("ThermalEventListeners dump:");
- synchronized (mLock) {
- mThermalEventListeners.dump(pw, "\t");
- pw.println("ThermalHAL 1.1 connected: " + (mThermalHal11 != null ? "yes" : "no"));
- pw.println("ThermalHAL 2.0 connected: " + (mThermalHal20 != null ? "yes" : "no"));
- }
- }
- };
-
- private List<android.os.Temperature> getCurrentTemperaturesInternal(boolean shouldFilter,
- int type) {
- List<android.os.Temperature> ret = new ArrayList<>();
- synchronized (mLock) {
- if (mThermalHal20 == null) {
- return ret;
- }
- try {
- mThermalHal20.getCurrentTemperatures(shouldFilter, type,
- (ThermalStatus status,
- ArrayList<android.hardware.thermal.V2_0.Temperature>
- temperatures) -> {
- if (ThermalStatusCode.SUCCESS == status.code) {
- for (android.hardware.thermal.V2_0.Temperature
- temperature : temperatures) {
- ret.add(new android.os.Temperature(
- temperature.value, temperature.type, temperature.name,
- temperature.throttlingStatus));
- }
- } else {
- Slog.e(TAG,
- "Couldn't get temperatures because of HAL error: "
- + status.debugMessage);
- }
-
- });
- } catch (RemoteException e) {
- Slog.e(TAG, "Couldn't getCurrentTemperatures, reconnecting...", e);
- connectToHalLocked();
- // Post to listeners after reconnect to HAL.
- notifyCurrentTemperaturesLocked();
- }
- }
- return ret;
}
- private void notifyListener(android.os.Temperature temperature, IThermalEventListener listener,
- Integer type) {
+ private void postStatusListener(IThermalStatusListener listener) {
+ final boolean thermalCallbackQueued = FgThread.getHandler().post(() -> {
+ try {
+ listener.onStatusChange(mStatus);
+ } catch (RemoteException | RuntimeException e) {
+ Slog.e(TAG, "Thermal callback failed to call", e);
+ }
+ });
+ if (!thermalCallbackQueued) {
+ Slog.e(TAG, "Thermal callback failed to queue");
+ }
+ }
+
+ private void notifyStatusListenersLocked() {
+ if (!Temperature.isValidStatus(mStatus)) {
+ return;
+ }
+ final int length = mThermalStatusListeners.beginBroadcast();
+ try {
+ for (int i = 0; i < length; i++) {
+ final IThermalStatusListener listener =
+ mThermalStatusListeners.getBroadcastItem(i);
+ postStatusListener(listener);
+ }
+ } finally {
+ mThermalStatusListeners.finishBroadcast();
+ }
+ }
+
+ private void onTemperatureMapChangedLocked() {
+ int newStatus = INVALID_THROTTLING;
+ final int count = mTemperatureMap.size();
+ for (int i = 0; i < count; i++) {
+ Temperature t = mTemperatureMap.valueAt(i);
+ if (t.getStatus() >= newStatus) {
+ newStatus = t.getStatus();
+ }
+ }
+ if (newStatus != mStatus) {
+ mStatus = newStatus;
+ notifyStatusListenersLocked();
+ }
+ }
+
+
+ private void postEventListenerCurrentTemperatures(IThermalEventListener listener,
+ @Nullable Integer type) {
+ synchronized (mLock) {
+ final int count = mTemperatureMap.size();
+ for (int i = 0; i < count; i++) {
+ postEventListener(mTemperatureMap.valueAt(i), listener,
+ type);
+ }
+ }
+ }
+
+ private void postEventListener(Temperature temperature,
+ IThermalEventListener listener,
+ @Nullable Integer type) {
// Skip if listener registered with a different type
if (type != null && type != temperature.getType()) {
return;
@@ -271,11 +221,26 @@
}
}
- private void notifyThrottlingImpl(android.os.Temperature temperature) {
+ private void notifyEventListenersLocked(Temperature temperature) {
+ final int length = mThermalEventListeners.beginBroadcast();
+ try {
+ for (int i = 0; i < length; i++) {
+ final IThermalEventListener listener =
+ mThermalEventListeners.getBroadcastItem(i);
+ final Integer type =
+ (Integer) mThermalEventListeners.getBroadcastCookie(i);
+ postEventListener(temperature, listener, type);
+ }
+ } finally {
+ mThermalEventListeners.finishBroadcast();
+ }
+ }
+
+ private void onTemperatureChanged(Temperature temperature, boolean sendStatus) {
synchronized (mLock) {
// Thermal Shutdown for Skin temperature
- if (temperature.getStatus() == android.os.Temperature.THROTTLING_SHUTDOWN
- && temperature.getType() == android.os.Temperature.TYPE_SKIN) {
+ if (temperature.getStatus() == Temperature.THROTTLING_SHUTDOWN
+ && temperature.getType() == Temperature.TYPE_SKIN) {
final long token = Binder.clearCallingIdentity();
try {
mPowerManager.shutdown(false, PowerManager.SHUTDOWN_THERMAL_STATE, false);
@@ -284,107 +249,420 @@
}
}
- if (mNewListenerCallback != null) {
- // Only notify current newly added callback.
- notifyListener(temperature, mNewListenerCallback, mNewListenerType);
+ Temperature old = mTemperatureMap.put(temperature.getName(), temperature);
+ if (old != null) {
+ if (old.getStatus() != temperature.getStatus()) {
+ notifyEventListenersLocked(temperature);
+ }
} else {
- final int length = mThermalEventListeners.beginBroadcast();
- try {
- for (int i = 0; i < length; i++) {
- final IThermalEventListener listener =
- mThermalEventListeners.getBroadcastItem(i);
- final Integer type = (Integer) mThermalEventListeners.getBroadcastCookie(i);
- notifyListener(temperature, listener, type);
- }
- } finally {
- mThermalEventListeners.finishBroadcast();
- }
+ notifyEventListenersLocked(temperature);
+ }
+ if (sendStatus) {
+ onTemperatureMapChangedLocked();
}
}
}
- @Override
- public void onStart() {
- publishBinderService(Context.THERMAL_SERVICE, mService);
+ private void onTemperatureChangedCallback(Temperature temperature) {
+ onTemperatureChanged(temperature, true);
}
- @Override
- public void onBootPhase(int phase) {
- if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
- onActivityManagerReady();
+ private void dumpTemperaturesLocked(PrintWriter pw, String prefix,
+ Collection<Temperature> temperatures) {
+ for (Temperature t : temperatures) {
+ pw.print(prefix);
+ String out = String.format("Name: %s, Type: %d, Status: %d, Value: %f",
+ t.getName(),
+ t.getType(),
+ t.getStatus(),
+ t.getValue()
+ );
+ pw.println(out);
}
}
- private void notifyCurrentTemperaturesCallbackLocked(ThermalStatus status,
- ArrayList<android.hardware.thermal.V2_0.Temperature> temperatures) {
- if (ThermalStatusCode.SUCCESS != status.code) {
- Slog.e(TAG, "Couldn't get temperatures because of HAL error: "
- + status.debugMessage);
- return;
- }
- for (android.hardware.thermal.V2_0.Temperature temperature : temperatures) {
- android.os.Temperature thermal_svc_temp =
- new android.os.Temperature(
- temperature.value, temperature.type,
- temperature.name,
- temperature.throttlingStatus);
- notifyThrottlingImpl(thermal_svc_temp);
- }
- }
-
- private void notifyCurrentTemperaturesLocked() {
- if (mThermalHal20 == null) {
- return;
- }
- try {
- mThermalHal20.getCurrentTemperatures(false, 0,
- this::notifyCurrentTemperaturesCallbackLocked);
- } catch (RemoteException e) {
- Slog.e(TAG, "Couldn't get temperatures, reconnecting...", e);
- connectToHalLocked();
- }
- }
-
- private void onActivityManagerReady() {
- synchronized (mLock) {
- connectToHalLocked();
- // Post to listeners after connect to HAL.
- notifyCurrentTemperaturesLocked();
- }
- }
-
- final class DeathRecipient implements HwBinder.DeathRecipient {
+ @VisibleForTesting
+ final IThermalService.Stub mService = new IThermalService.Stub() {
@Override
- public void serviceDied(long cookie) {
- if (cookie == THERMAL_HAL_DEATH_COOKIE) {
- Slog.e(TAG, "Thermal HAL service died cookie: " + cookie);
+ public boolean registerThermalEventListener(IThermalEventListener listener) {
+ synchronized (mLock) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (!mThermalEventListeners.register(listener, null)) {
+ return false;
+ }
+ if (mHalReady) {
+ // Notify its callback after new client registered.
+ postEventListenerCurrentTemperatures(listener, null);
+ }
+ return true;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
+ @Override
+ public boolean registerThermalEventListenerWithType(IThermalEventListener listener,
+ int type) {
+ synchronized (mLock) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (!mThermalEventListeners.register(listener, new Integer(type))) {
+ return false;
+ }
+ if (mHalReady) {
+ // Notify its callback after new client registered.
+ postEventListenerCurrentTemperatures(listener, new Integer(type));
+ }
+ return true;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
+ @Override
+ public boolean unregisterThermalEventListener(IThermalEventListener listener) {
+ synchronized (mLock) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return mThermalEventListeners.unregister(listener);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
+ @Override
+ public List<Temperature> getCurrentTemperatures() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (!mHalReady) {
+ return new ArrayList<>();
+ }
+ return mHalWrapper.getCurrentTemperatures(false, 0 /* not used */);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public List<Temperature> getCurrentTemperaturesWithType(int type) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (!mHalReady) {
+ return new ArrayList<>();
+ }
+ return mHalWrapper.getCurrentTemperatures(true, type);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public boolean registerThermalStatusListener(IThermalStatusListener listener) {
+ synchronized (mLock) {
+ // Notify its callback after new client registered.
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (!mThermalStatusListeners.register(listener)) {
+ return false;
+ }
+ if (mHalReady) {
+ // Notify its callback after new client registered.
+ postStatusListener(listener);
+ }
+ return true;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
+ @Override
+ public boolean unregisterThermalStatusListener(IThermalStatusListener listener) {
+ synchronized (mLock) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return mThermalStatusListeners.unregister(listener);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
+ @Override
+ public int getCurrentStatus() {
+ synchronized (mLock) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return Temperature.isValidStatus(mStatus) ? mStatus
+ : Temperature.THROTTLING_NONE;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
+ return;
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
synchronized (mLock) {
- connectToHalLocked();
- // Post to listeners after reconnect to HAL.
- notifyCurrentTemperaturesLocked();
+ pw.println("ThermalEventListeners:");
+ mThermalEventListeners.dump(pw, "\t");
+ pw.println("ThermalStatusListeners:");
+ mThermalStatusListeners.dump(pw, "\t");
+ pw.println("Thermal Status: " + Integer.toString(mStatus));
+ pw.println("Cached temperatures:");
+ dumpTemperaturesLocked(pw, "\t", mTemperatureMap.values());
+ pw.println("HAL Ready: " + Boolean.toString(mHalReady));
+ if (mHalReady) {
+ pw.println("HAL connection:");
+ mHalWrapper.dump(pw, "\t");
+ pw.println("Current temperatures from HAL:");
+ dumpTemperaturesLocked(pw, "\t",
+ mHalWrapper.getCurrentTemperatures(false, 0));
+ }
+ }
+
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ };
+
+ abstract static class ThermalHalWrapper {
+ protected static final String TAG = ThermalHalWrapper.class.getSimpleName();
+
+ /** Lock to protect HAL handle. */
+ protected final Object mHalLock = new Object();
+
+ @FunctionalInterface
+ interface TemperatureChangedCallback {
+ void onValues(Temperature temperature);
+ }
+
+ /** Temperature callback. */
+ protected TemperatureChangedCallback mCallback;
+
+ /** Cookie for matching the right end point. */
+ protected static final int THERMAL_HAL_DEATH_COOKIE = 5612;
+
+ @VisibleForTesting
+ protected void setCallback(TemperatureChangedCallback cb) {
+ mCallback = cb;
+ }
+
+ protected abstract List<Temperature> getCurrentTemperatures(boolean shouldFilter,
+ int type);
+
+ protected abstract boolean connectToHal();
+
+ protected abstract void dump(PrintWriter pw, String prefix);
+
+ protected void resendCurrentTemperatures() {
+ synchronized (mHalLock) {
+ List<Temperature> temperatures = getCurrentTemperatures(false, 0);
+ final int count = temperatures.size();
+ for (int i = 0; i < count; i++) {
+ mCallback.onValues(temperatures.get(i));
+ }
+ }
+ }
+
+ final class DeathRecipient implements HwBinder.DeathRecipient {
+ @Override
+ public void serviceDied(long cookie) {
+ if (cookie == THERMAL_HAL_DEATH_COOKIE) {
+ Slog.e(TAG, "Thermal HAL service died cookie: " + cookie);
+ synchronized (mHalLock) {
+ connectToHal();
+ // Post to listeners after reconnect to HAL.
+ resendCurrentTemperatures();
+ }
}
}
}
}
- private void connectToHalLocked() {
- try {
- mThermalHal20 = android.hardware.thermal.V2_0.IThermal.getService();
- mThermalHal20.linkToDeath(new DeathRecipient(), THERMAL_HAL_DEATH_COOKIE);
- mThermalHal20.registerThermalChangedCallback(mThermalCallback20, false,
- 0 /* not used */);
- } catch (NoSuchElementException | RemoteException e) {
- Slog.e(TAG, "Thermal HAL 2.0 service not connected, trying 1.1.");
- mThermalHal20 = null;
- try {
- mThermalHal11 = android.hardware.thermal.V1_1.IThermal.getService();
- mThermalHal11.linkToDeath(new DeathRecipient(), THERMAL_HAL_DEATH_COOKIE);
- mThermalHal11.registerThermalCallback(mThermalCallback11);
- } catch (NoSuchElementException | RemoteException e2) {
- Slog.e(TAG,
- "Thermal HAL 1.1 service not connected, no thermal call back "
- + "will be called.");
- mThermalHal11 = null;
+ static class ThermalHal11Wrapper extends ThermalHalWrapper {
+ /** Proxy object for the Thermal HAL 1.1 service. */
+ @GuardedBy("mHalLock")
+ private android.hardware.thermal.V1_1.IThermal mThermalHal11 = null;
+
+ /** HWbinder callback for Thermal HAL 1.1. */
+ private final IThermalCallback.Stub mThermalCallback11 =
+ new IThermalCallback.Stub() {
+ @Override
+ public void notifyThrottling(boolean isThrottling,
+ android.hardware.thermal.V1_0.Temperature temperature) {
+ Temperature thermalSvcTemp = new Temperature(
+ temperature.currentValue, temperature.type, temperature.name,
+ isThrottling ? ThrottlingSeverity.SEVERE
+ : ThrottlingSeverity.NONE);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mCallback.onValues(thermalSvcTemp);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ };
+
+ @Override
+ protected List<Temperature> getCurrentTemperatures(boolean shouldFilter,
+ int type) {
+ synchronized (mHalLock) {
+ List<Temperature> ret = new ArrayList<>();
+ if (mThermalHal11 == null) {
+ return ret;
+ }
+ try {
+ mThermalHal11.getTemperatures(
+ (ThermalStatus status,
+ ArrayList<android.hardware.thermal.V1_0.Temperature>
+ temperatures) -> {
+ if (ThermalStatusCode.SUCCESS == status.code) {
+ for (android.hardware.thermal.V1_0.Temperature
+ temperature : temperatures) {
+ if (shouldFilter && type != temperature.type) {
+ continue;
+ }
+ // Thermal HAL 1.1 doesn't report current throttling status
+ ret.add(new Temperature(
+ temperature.currentValue, temperature.type,
+ temperature.name,
+ Temperature.THROTTLING_NONE));
+ }
+ } else {
+ Slog.e(TAG,
+ "Couldn't get temperatures because of HAL error: "
+ + status.debugMessage);
+ }
+
+ });
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Couldn't getCurrentTemperatures, reconnecting...", e);
+ connectToHal();
+ }
+ return ret;
+ }
+ }
+
+ @Override
+ protected boolean connectToHal() {
+ synchronized (mHalLock) {
+ try {
+ mThermalHal11 = android.hardware.thermal.V1_1.IThermal.getService();
+ mThermalHal11.linkToDeath(new DeathRecipient(),
+ THERMAL_HAL_DEATH_COOKIE);
+ mThermalHal11.registerThermalCallback(mThermalCallback11);
+ } catch (NoSuchElementException | RemoteException e) {
+ Slog.e(TAG,
+ "Thermal HAL 1.1 service not connected, no thermal call back will be "
+ + "called.");
+ mThermalHal11 = null;
+ }
+ return (mThermalHal11 != null);
+ }
+ }
+
+ @Override
+ protected void dump(PrintWriter pw, String prefix) {
+ synchronized (mHalLock) {
+ pw.print(prefix);
+ pw.println("ThermalHAL 1.1 connected: " + (mThermalHal11 != null ? "yes"
+ : "no"));
+ }
+ }
+ }
+
+ static class ThermalHal20Wrapper extends ThermalHalWrapper {
+ /** Proxy object for the Thermal HAL 2.0 service. */
+ @GuardedBy("mHalLock")
+ private android.hardware.thermal.V2_0.IThermal mThermalHal20 = null;
+
+ /** HWbinder callback for Thermal HAL 2.0. */
+ private final IThermalChangedCallback.Stub mThermalCallback20 =
+ new IThermalChangedCallback.Stub() {
+ @Override
+ public void notifyThrottling(
+ android.hardware.thermal.V2_0.Temperature temperature) {
+ Temperature thermalSvcTemp = new Temperature(
+ temperature.value, temperature.type, temperature.name,
+ temperature.throttlingStatus);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mCallback.onValues(thermalSvcTemp);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ };
+
+ @Override
+ protected List<Temperature> getCurrentTemperatures(boolean shouldFilter,
+ int type) {
+ synchronized (mHalLock) {
+ List<Temperature> ret = new ArrayList<>();
+ if (mThermalHal20 == null) {
+ return ret;
+ }
+ try {
+ mThermalHal20.getCurrentTemperatures(shouldFilter, type,
+ (ThermalStatus status,
+ ArrayList<android.hardware.thermal.V2_0.Temperature>
+ temperatures) -> {
+ if (ThermalStatusCode.SUCCESS == status.code) {
+ for (android.hardware.thermal.V2_0.Temperature
+ temperature : temperatures) {
+ ret.add(new Temperature(
+ temperature.value, temperature.type,
+ temperature.name,
+ temperature.throttlingStatus));
+ }
+ } else {
+ Slog.e(TAG,
+ "Couldn't get temperatures because of HAL error: "
+ + status.debugMessage);
+ }
+
+ });
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Couldn't getCurrentTemperatures, reconnecting...", e);
+ connectToHal();
+ }
+ return ret;
+ }
+ }
+
+ @Override
+ protected boolean connectToHal() {
+ synchronized (mHalLock) {
+ try {
+ mThermalHal20 = android.hardware.thermal.V2_0.IThermal.getService();
+ mThermalHal20.linkToDeath(new DeathRecipient(), THERMAL_HAL_DEATH_COOKIE);
+ mThermalHal20.registerThermalChangedCallback(mThermalCallback20, false,
+ 0 /* not used */);
+ } catch (NoSuchElementException | RemoteException e) {
+ Slog.e(TAG, "Thermal HAL 2.0 service not connected, trying 1.1.");
+ mThermalHal20 = null;
+ }
+ return (mThermalHal20 != null);
+ }
+ }
+
+ @Override
+ protected void dump(PrintWriter pw, String prefix) {
+ synchronized (mHalLock) {
+ pw.print(prefix);
+ pw.println("ThermalHAL 2.0 connected: " + (mThermalHal20 != null ? "yes"
+ : "no"));
}
}
}
diff --git a/services/core/java/com/android/server/role/RemoteRoleControllerService.java b/services/core/java/com/android/server/role/RemoteRoleControllerService.java
index 538f4cf..cb89780 100644
--- a/services/core/java/com/android/server/role/RemoteRoleControllerService.java
+++ b/services/core/java/com/android/server/role/RemoteRoleControllerService.java
@@ -48,6 +48,7 @@
static final boolean DEBUG = false;
private static final String LOG_TAG = RemoteRoleControllerService.class.getSimpleName();
+ // TODO: STOPSHIP: This isn't the right thread, as we are also using it to write to disk.
@NonNull
private static final Handler sCallbackHandler = BackgroundThread.getHandler();
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index d01e762..4124210 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -284,12 +284,26 @@
}
@Override
+ public void setRoleNamesFromController(@NonNull List<String> roleNames) {
+ Preconditions.checkNotNull(roleNames, "roleNames cannot be null");
+ getContext().enforceCallingOrSelfPermission(
+ RoleManager.PERMISSION_MANAGE_ROLES_FROM_CONTROLLER,
+ "setRoleNamesFromController");
+
+ int userId = UserHandle.getCallingUserId();
+ synchronized (mLock) {
+ RoleUserState userState = getUserStateLocked(userId);
+ userState.setRoleNamesLocked(roleNames);
+ }
+ }
+
+ @Override
public boolean addRoleHolderFromController(@NonNull String roleName,
@NonNull String packageName) {
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
getContext().enforceCallingOrSelfPermission(
- RoleManager.PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER,
+ RoleManager.PERMISSION_MANAGE_ROLES_FROM_CONTROLLER,
"addRoleHolderFromController");
int userId = UserHandle.getCallingUserId();
@@ -305,7 +319,7 @@
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
getContext().enforceCallingOrSelfPermission(
- RoleManager.PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER,
+ RoleManager.PERMISSION_MANAGE_ROLES_FROM_CONTROLLER,
"removeRoleHolderFromController");
int userId = UserHandle.getCallingUserId();
diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java
index 68e737e..9c43f4d 100644
--- a/services/core/java/com/android/server/role/RoleUserState.java
+++ b/services/core/java/com/android/server/role/RoleUserState.java
@@ -45,6 +45,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
+import java.util.List;
/**
* Stores the state of roles for a user.
@@ -101,7 +102,11 @@
@GuardedBy("RoleManagerService.mLock")
public void setVersionLocked(int version) {
throwIfDestroyedLocked();
+ if (mVersion == version) {
+ return;
+ }
mVersion = version;
+ writeAsyncLocked();
}
/**
@@ -132,6 +137,41 @@
}
/**
+ * Set the names of all available roles.
+ *
+ * @param roleNames the names of all the available roles
+ */
+ @GuardedBy("RoleManagerService.mLock")
+ public void setRoleNamesLocked(@NonNull List<String> roleNames) {
+ throwIfDestroyedLocked();
+ boolean changed = false;
+ for (int i = mRoles.size() - 1; i >= 0; i--) {
+ String roleName = mRoles.keyAt(i);
+ if (!roleNames.contains(roleName)) {
+ ArraySet<String> packageNames = mRoles.valueAt(i);
+ if (!packageNames.isEmpty()) {
+ Slog.e(LOG_TAG, "Holders of a removed role should have been cleaned up, role: "
+ + roleName + ", holders: " + packageNames);
+ }
+ mRoles.removeAt(i);
+ changed = true;
+ }
+ }
+ int roleNamesSize = roleNames.size();
+ for (int i = 0; i < roleNamesSize; i++) {
+ String roleName = roleNames.get(i);
+ if (!mRoles.containsKey(roleName)) {
+ mRoles.put(roleName, new ArraySet<>());
+ Slog.i(LOG_TAG, "Added new role: " + roleName);
+ changed = true;
+ }
+ }
+ if (changed) {
+ writeAsyncLocked();
+ }
+ }
+
+ /**
* Add a holder to a role.
*
* @param roleName the name of the role to add the holder to
@@ -150,7 +190,10 @@
+ ", package: " + packageName);
return false;
}
- roleHolders.add(packageName);
+ boolean changed = roleHolders.add(packageName);
+ if (changed) {
+ writeAsyncLocked();
+ }
return true;
}
@@ -173,7 +216,10 @@
+ ", package: " + packageName);
return false;
}
- roleHolders.remove(packageName);
+ boolean changed = roleHolders.remove(packageName);
+ if (changed) {
+ writeAsyncLocked();
+ }
return true;
}
@@ -181,7 +227,7 @@
* Schedule writing the state to file.
*/
@GuardedBy("RoleManagerService.mLock")
- public void writeAsyncLocked() {
+ private void writeAsyncLocked() {
throwIfDestroyedLocked();
int version = mVersion;
ArrayMap<String, ArraySet<String>> roles = new ArrayMap<>();
@@ -192,6 +238,7 @@
roles.put(roleName, roleHolders);
}
mWriteHandler.removeCallbacksAndMessages(null);
+ // TODO: Throttle writes.
mWriteHandler.sendMessage(PooledLambda.obtainMessage(
RoleUserState::writeSync, this, version, roles));
}
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index d2ca850..6f6846d 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -1630,37 +1630,42 @@
if (this.mKernelCpuThreadReader == null) {
return;
}
- KernelCpuThreadReader.ProcessCpuUsage processCpuUsage = this.mKernelCpuThreadReader
- .getCurrentProcessCpuUsage();
- if (processCpuUsage == null) {
+ ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsages =
+ this.mKernelCpuThreadReader.getProcessCpuUsageByUids();
+ if (processCpuUsages == null) {
return;
}
int[] cpuFrequencies = mKernelCpuThreadReader.getCpuFrequenciesKhz();
- for (KernelCpuThreadReader.ThreadCpuUsage threadCpuUsage
- : processCpuUsage.threadCpuUsages) {
- if (threadCpuUsage.usageTimesMillis.length != cpuFrequencies.length) {
- Slog.w(TAG, "Unexpected number of usage times,"
- + " expected " + cpuFrequencies.length
- + " but got " + threadCpuUsage.usageTimesMillis.length);
- continue;
- }
-
- for (int i = 0; i < threadCpuUsage.usageTimesMillis.length; i++) {
- // Do not report CPU usage at a frequency when it's zero
- if (threadCpuUsage.usageTimesMillis[i] == 0) {
+ for (int i = 0; i < processCpuUsages.size(); i++) {
+ KernelCpuThreadReader.ProcessCpuUsage processCpuUsage = processCpuUsages.get(i);
+ ArrayList<KernelCpuThreadReader.ThreadCpuUsage> threadCpuUsages =
+ processCpuUsage.threadCpuUsages;
+ for (int j = 0; j < threadCpuUsages.size(); j++) {
+ KernelCpuThreadReader.ThreadCpuUsage threadCpuUsage = threadCpuUsages.get(j);
+ if (threadCpuUsage.usageTimesMillis.length != cpuFrequencies.length) {
+ Slog.w(TAG, "Unexpected number of usage times,"
+ + " expected " + cpuFrequencies.length
+ + " but got " + threadCpuUsage.usageTimesMillis.length);
continue;
}
- StatsLogEventWrapper e =
- new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(processCpuUsage.uid);
- e.writeInt(processCpuUsage.processId);
- e.writeInt(threadCpuUsage.threadId);
- e.writeString(processCpuUsage.processName);
- e.writeString(threadCpuUsage.threadName);
- e.writeInt(cpuFrequencies[i]);
- e.writeInt(threadCpuUsage.usageTimesMillis[i]);
- pulledData.add(e);
+ for (int k = 0; k < threadCpuUsage.usageTimesMillis.length; k++) {
+ // Do not report CPU usage at a frequency when it's zero
+ if (threadCpuUsage.usageTimesMillis[k] == 0) {
+ continue;
+ }
+
+ StatsLogEventWrapper e =
+ new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(processCpuUsage.uid);
+ e.writeInt(processCpuUsage.processId);
+ e.writeInt(threadCpuUsage.threadId);
+ e.writeString(processCpuUsage.processName);
+ e.writeString(threadCpuUsage.threadName);
+ e.writeInt(cpuFrequencies[k]);
+ e.writeInt(threadCpuUsage.usageTimesMillis[k]);
+ pulledData.add(e);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 479f427..409d2b4 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -18,6 +18,7 @@
import static android.app.WallpaperManager.FLAG_LOCK;
import static android.app.WallpaperManager.FLAG_SYSTEM;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AUTO;
import static android.os.ParcelFileDescriptor.MODE_CREATE;
import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
@@ -56,6 +57,7 @@
import android.graphics.BitmapRegionDecoder;
import android.graphics.Color;
import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.Environment;
@@ -118,6 +120,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
+import java.util.function.Consumer;
public class WallpaperManagerService extends IWallpaperManager.Stub
implements IWallpaperManagerService {
@@ -640,6 +643,43 @@
final IPackageManager mIPackageManager;
final MyPackageMonitor mMonitor;
final AppOpsManager mAppOpsManager;
+
+ private final DisplayManager mDisplayManager;
+ private final DisplayManager.DisplayListener mDisplayListener =
+ new DisplayManager.DisplayListener() {
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ synchronized (mLock) {
+ if (mLastWallpaper != null) {
+ final WallpaperConnection.DisplayConnector connector =
+ mLastWallpaper.connection.getDisplayConnectorOrCreate(displayId);
+ if (connector == null) return;
+
+ connector.connectLocked(mLastWallpaper.connection, mLastWallpaper);
+ }
+ }
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ synchronized (mLock) {
+ if (mLastWallpaper != null) {
+ final WallpaperConnection.DisplayConnector connector =
+ mLastWallpaper.connection.getDisplayConnectorOrCreate(displayId);
+ if (connector == null) return;
+ connector.disconnectLocked();
+ mLastWallpaper.connection.removeDisplayConnector(displayId);
+ }
+ }
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ // TODO(b/115486823) Review that do we need to handle display changes.
+ }
+ };
+
/**
* Map of color listeners per user id.
* The key will be the id of a user or UserHandle.USER_ALL - for wildcard listeners.
@@ -775,20 +815,96 @@
class WallpaperConnection extends IWallpaperConnection.Stub
implements ServiceConnection {
+ /**
+ * Collect needed info for a display.
+ */
+ private final class DisplayConnector {
+ final int mDisplayId;
+ final Binder mToken = new Binder();
+ IWallpaperEngine mEngine;
+ boolean mDimensionsChanged;
+ boolean mPaddingChanged;
+
+ DisplayConnector(int displayId) {
+ mDisplayId = displayId;
+ }
+
+ void ensureStatusHandled() {
+ if (mDimensionsChanged) {
+ try {
+ mEngine.setDesiredSize(mWallpaper.width, mWallpaper.height);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to set wallpaper dimensions", e);
+ }
+ mDimensionsChanged = false;
+ }
+ if (mPaddingChanged) {
+ try {
+ mEngine.setDisplayPadding(mWallpaper.padding);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to set wallpaper padding", e);
+ }
+ mPaddingChanged = false;
+ }
+ }
+
+ void connectLocked(WallpaperConnection connection, WallpaperData wallpaper) {
+ if (DEBUG) Slog.v(TAG, "Adding window token: " + mToken);
+ try {
+ mIWindowManager.addWindowToken(mToken, TYPE_WALLPAPER, mDisplayId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed add wallpaper window token on display " + mDisplayId, e);
+ return;
+ }
+
+ try {
+ // TODO(b/115486823) Consider the size of non-default display
+ connection.mService.attach(connection, mToken, TYPE_WALLPAPER, false,
+ wallpaper.width, wallpaper.height,
+ wallpaper.padding, mDisplayId);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed attaching wallpaper on display", e);
+ // TODO(b/115486823) Failed when attaching a new engine, however, other engines
+ // may still working. Should we abandon them all or just ignore this one.
+ if (mLastWallpaper != null && !mLastWallpaper.wallpaperUpdating) {
+ bindWallpaperComponentLocked(null /* componentName */, false /* force */,
+ false /* fromUser */, wallpaper, null /* reply */);
+ }
+ }
+ }
+
+ void disconnectLocked() {
+ if (DEBUG) Slog.v(TAG, "Removing window token: " + mToken);
+ try {
+ mIWindowManager.removeWindowToken(mToken, mDisplayId);
+ } catch (RemoteException e) {
+ }
+ try {
+ if (mEngine != null) {
+ mEngine.destroy();
+ }
+ } catch (RemoteException e) {
+ }
+ mEngine = null;
+ }
+ }
+
+ /**
+ * A map for each display.
+ * Use {@link #getDisplayConnectorOrCreate(int displayId)} to ensure the display is usable.
+ */
+ private SparseArray<DisplayConnector> mDisplayConnector = new SparseArray<>();
+
/** Time in milliseconds until we expect the wallpaper to reconnect (unless we're in the
* middle of an update). If exceeded, the wallpaper gets reset to the system default. */
private static final long WALLPAPER_RECONNECT_TIMEOUT_MS = 10000;
final WallpaperInfo mInfo;
- final Binder mToken = new Binder();
IWallpaperService mService;
- IWallpaperEngine mEngine;
WallpaperData mWallpaper;
+ final int mClientUid;
IRemoteCallback mReply;
- boolean mDimensionsChanged = false;
- boolean mPaddingChanged = false;
-
private Runnable mResetRunnable = () -> {
synchronized (mLock) {
if (mShuttingDown) {
@@ -809,9 +925,55 @@
}
};
- public WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper) {
+ WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper, int clientUid) {
mInfo = info;
mWallpaper = wallpaper;
+ mClientUid = clientUid;
+ initDisplayState();
+ }
+
+ private void initDisplayState() {
+ final Display[] displays = mDisplayManager.getDisplays();
+ for (Display display : displays) {
+ if (isUsableDisplay(display)) {
+ final int displayId = display.getDisplayId();
+ mDisplayConnector.append(displayId, new DisplayConnector(displayId));
+ }
+ }
+ }
+
+ // TODO(b/115486823) Support the system decorations change at runtime.
+ private boolean isUsableDisplay(Display display) {
+ return display != null && display.hasAccess(mClientUid)
+ // TODO(b/114338689) Use WindowManager.supportsSystemDecorations when ready
+ && (display.supportsSystemDecorations()
+ || display.getDisplayId() == DEFAULT_DISPLAY);
+ }
+
+ void forEachDisplayConnector(Consumer<DisplayConnector> action) {
+ for (int i = mDisplayConnector.size() - 1; i >= 0; i--) {
+ final DisplayConnector connector = mDisplayConnector.get(i);
+ action.accept(connector);
+ }
+ }
+
+ DisplayConnector getDisplayConnectorOrCreate(int displayId) {
+ DisplayConnector connector = mDisplayConnector.get(displayId);
+ if (connector == null) {
+ final Display display = mDisplayManager.getDisplay(displayId);
+ if (isUsableDisplay(display)) {
+ connector = new DisplayConnector(displayId);
+ mDisplayConnector.append(displayId, connector);
+ }
+ }
+ return connector;
+ }
+
+ void removeDisplayConnector(int displayId) {
+ final DisplayConnector connector = mDisplayConnector.get(displayId);
+ if (connector != null) {
+ mDisplayConnector.remove(displayId);
+ }
}
@Override
@@ -839,7 +1001,7 @@
+ mWallpaper.wallpaperComponent);
}
mService = null;
- mEngine = null;
+ forEachDisplayConnector(connector -> connector.mEngine = null);
if (mWallpaper.connection == this) {
// There is an inherent ordering race between this callback and the
// package monitor that receives notice that a package is being updated,
@@ -863,7 +1025,8 @@
fgHandler.removeCallbacks(mResetRunnable);
fgHandler.postDelayed(mResetRunnable, WALLPAPER_RECONNECT_TIMEOUT_MS);
if (DEBUG_LIVE) {
- Slog.i(TAG, "Started wallpaper reconnect timeout for " + mWallpaper.wallpaperComponent);
+ Slog.i(TAG,
+ "Started wallpaper reconnect timeout for " + mWallpaper.wallpaperComponent);
}
}
@@ -943,38 +1106,37 @@
}
@Override
- public void attachEngine(IWallpaperEngine engine) {
+ public void attachEngine(IWallpaperEngine engine, int displayId) {
synchronized (mLock) {
- mEngine = engine;
- if (mDimensionsChanged) {
+ final DisplayConnector connector = getDisplayConnectorOrCreate(displayId);
+ if (connector == null) {
try {
- mEngine.setDesiredSize(mWallpaper.width, mWallpaper.height);
+ engine.destroy();
} catch (RemoteException e) {
- Slog.w(TAG, "Failed to set wallpaper dimensions", e);
+ Slog.w(TAG, "Failed to destroy engine", e);
}
- mDimensionsChanged = false;
+ return;
}
- if (mPaddingChanged) {
+ connector.mEngine = engine;
+ connector.ensureStatusHandled();
+
+ // TODO(multi-display) TBD.
+ if (mInfo != null && mInfo.supportsAmbientMode() && displayId == DEFAULT_DISPLAY) {
try {
- mEngine.setDisplayPadding(mWallpaper.padding);
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to set wallpaper padding", e);
- }
- mPaddingChanged = false;
- }
- if (mInfo != null && mInfo.supportsAmbientMode()) {
- try {
- mEngine.setInAmbientMode(mInAmbientMode, false /* animated */);
+ connector.mEngine.setInAmbientMode(mInAmbientMode, false /* animated */);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to set ambient mode state", e);
}
}
- try {
- // This will trigger onComputeColors in the wallpaper engine.
- // It's fine to be locked in here since the binder is oneway.
- mEngine.requestWallpaperColors();
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to request wallpaper colors", e);
+ // TODO(b/115486823) Extends for secondary display.
+ if (displayId == DEFAULT_DISPLAY) {
+ try {
+ // This will trigger onComputeColors in the wallpaper engine.
+ // It's fine to be locked in here since the binder is oneway.
+ connector.mEngine.requestWallpaperColors();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to request wallpaper colors", e);
+ }
}
}
}
@@ -1162,6 +1324,8 @@
ServiceManager.getService(Context.WINDOW_SERVICE));
mIPackageManager = AppGlobals.getPackageManager();
mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+ mDisplayManager = mContext.getSystemService(DisplayManager.class);
+ mDisplayManager.registerDisplayListener(mDisplayListener, null /* handler */);
mMonitor = new MyPackageMonitor();
mColorsChangedListeners = new SparseArray<>();
}
@@ -1541,6 +1705,7 @@
return false;
}
+ // TODO(b/115486823) Extends this method with specific display.
public void setDimensionHints(int width, int height, String callingPackage)
throws RemoteException {
checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
@@ -1560,10 +1725,12 @@
saveSettingsLocked(userId);
if (mCurrentUserId != userId) return; // Don't change the properties now
if (wallpaper.connection != null) {
- if (wallpaper.connection.mEngine != null) {
+ // TODO(b/115486823) Extends this method with specific display.
+ final IWallpaperEngine engine = wallpaper.connection
+ .getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine;
+ if (engine != null) {
try {
- wallpaper.connection.mEngine.setDesiredSize(
- width, height);
+ engine.setDesiredSize(width, height);
} catch (RemoteException e) {
}
notifyCallbacksLocked(wallpaper);
@@ -1571,7 +1738,9 @@
// We've attached to the service but the engine hasn't attached back to us
// yet. This means it will be created with the previous dimensions, so we
// need to update it to the new dimensions once it attaches.
- wallpaper.connection.mDimensionsChanged = true;
+ // TODO(b/115486823) Extends this method with specific display.
+ wallpaper.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY)
+ .mDimensionsChanged = true;
}
}
}
@@ -1600,6 +1769,7 @@
}
}
+ // TODO(b/115486823) Extends this method with specific display.
public void setDisplayPadding(Rect padding, String callingPackage) {
checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
if (!isWallpaperSupported(callingPackage)) {
@@ -1617,9 +1787,12 @@
saveSettingsLocked(userId);
if (mCurrentUserId != userId) return; // Don't change the properties now
if (wallpaper.connection != null) {
- if (wallpaper.connection.mEngine != null) {
+ // TODO(b/115486823) Extends this method with specific display.
+ final IWallpaperEngine engine = wallpaper.connection
+ .getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine;
+ if (engine != null) {
try {
- wallpaper.connection.mEngine.setDisplayPadding(padding);
+ engine.setDisplayPadding(padding);
} catch (RemoteException e) {
}
notifyCallbacksLocked(wallpaper);
@@ -1627,7 +1800,9 @@
// We've attached to the service but the engine hasn't attached back to us
// yet. This means it will be created with the previous dimensions, so we
// need to update it to the new dimensions once it attaches.
- wallpaper.connection.mPaddingChanged = true;
+ // TODO(b/115486823) Extends this method with specific display.
+ wallpaper.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY)
+ .mPaddingChanged = true;
}
}
}
@@ -1756,6 +1931,7 @@
}
}
+ // TODO(b/115486823) Extends this method with specific display.
public void setInAmbientMode(boolean inAmbienMode, boolean animated) {
final IWallpaperEngine engine;
synchronized (mLock) {
@@ -1763,7 +1939,8 @@
final WallpaperData data = mWallpaperMap.get(mCurrentUserId);
if (data != null && data.connection != null && data.connection.mInfo != null
&& data.connection.mInfo.supportsAmbientMode()) {
- engine = data.connection.mEngine;
+ // TODO(b/115486823) Extends this method with specific display.
+ engine = data.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine;
} else {
engine = null;
}
@@ -2125,7 +2302,9 @@
// Bind the service!
if (DEBUG) Slog.v(TAG, "Binding to:" + componentName);
- WallpaperConnection newConn = new WallpaperConnection(wi, wallpaper);
+ final int componentUid = mIPackageManager.getPackageUid(componentName.getPackageName(),
+ MATCH_DIRECT_BOOT_AUTO, wallpaper.userId);
+ WallpaperConnection newConn = new WallpaperConnection(wi, wallpaper, componentUid);
intent.setComponent(componentName);
intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
com.android.internal.R.string.wallpaper_binding_label);
@@ -2152,14 +2331,8 @@
wallpaper.wallpaperComponent = componentName;
wallpaper.connection = newConn;
newConn.mReply = reply;
- try {
- if (wallpaper.userId == mCurrentUserId) {
- if (DEBUG)
- Slog.v(TAG, "Adding window token: " + newConn.mToken);
- mIWindowManager.addWindowToken(newConn.mToken, TYPE_WALLPAPER, DEFAULT_DISPLAY);
- mLastWallpaper = wallpaper;
- }
- } catch (RemoteException e) {
+ if (wallpaper.userId == mCurrentUserId) {
+ mLastWallpaper = wallpaper;
}
} catch (RemoteException e) {
String msg = "Remote exception for " + componentName + "\n" + e;
@@ -2181,22 +2354,12 @@
}
wallpaper.connection.mReply = null;
}
- if (wallpaper.connection.mEngine != null) {
- try {
- wallpaper.connection.mEngine.destroy();
- } catch (RemoteException e) {
- }
- }
mContext.unbindService(wallpaper.connection);
- try {
- if (DEBUG)
- Slog.v(TAG, "Removing window token: " + wallpaper.connection.mToken);
- mIWindowManager.removeWindowToken(wallpaper.connection.mToken, DEFAULT_DISPLAY);
- } catch (RemoteException e) {
- }
+ wallpaper.connection.forEachDisplayConnector(connector -> connector.disconnectLocked());
wallpaper.connection.mService = null;
- wallpaper.connection.mEngine = null;
+ wallpaper.connection.mDisplayConnector.clear();
wallpaper.connection = null;
+ if (wallpaper == mLastWallpaper) mLastWallpaper = null;
}
}
@@ -2206,16 +2369,7 @@
}
void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) {
- try {
- conn.mService.attach(conn, conn.mToken,
- TYPE_WALLPAPER, false,
- wallpaper.width, wallpaper.height, wallpaper.padding);
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed attaching wallpaper; clearing", e);
- if (!wallpaper.wallpaperUpdating) {
- bindWallpaperComponentLocked(null, false, false, wallpaper, null);
- }
- }
+ conn.forEachDisplayConnector(connector-> connector.connectLocked(conn, wallpaper));
}
private void notifyCallbacksLocked(WallpaperData wallpaper) {
@@ -2801,12 +2955,16 @@
pw.print(" mInfo.component=");
pw.println(conn.mInfo.getComponent());
}
- pw.print(" mToken=");
- pw.println(conn.mToken);
+ conn.forEachDisplayConnector(connector -> {
+ pw.print(" mDisplayId=");
+ pw.println(connector.mDisplayId);
+ pw.print(" mToken=");
+ pw.println(connector.mToken);
+ pw.print(" mEngine=");
+ pw.println(connector.mEngine);
+ });
pw.print(" mService=");
pw.println(conn.mService);
- pw.print(" mEngine=");
- pw.println(conn.mEngine);
pw.print(" mLastDiedTime=");
pw.println(wallpaper.lastDiedTime - SystemClock.uptimeMillis());
}
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 94a47dd..e65f3b4 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -51,7 +51,6 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.H.NOTIFY_APP_TRANSITION_STARTING;
-import android.app.WindowConfiguration;
import android.os.Trace;
import android.util.ArraySet;
import android.util.Slog;
@@ -83,7 +82,7 @@
AppTransitionController(WindowManagerService service, DisplayContent displayContent) {
mService = service;
mDisplayContent = displayContent;
- mWallpaperControllerLocked = new WallpaperController(mService);
+ mWallpaperControllerLocked = mDisplayContent.mWallpaperController;
}
/**
@@ -106,7 +105,7 @@
mDisplayContent.mAppTransition.removeAppTransitionTimeoutCallbacks();
- mService.mRoot.mWallpaperMayChange = false;
+ mDisplayContent.mWallpaperMayChange = false;
int i;
for (i = 0; i < appsCount; i++) {
@@ -121,7 +120,7 @@
// Adjust wallpaper before we pull the lower/upper target, since pending changes
// (like the clearAnimatingFlags() above) might affect wallpaper target result.
// Or, the opening app window should be a wallpaper target.
- mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(mDisplayContent,
+ mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(
mDisplayContent.mOpeningApps);
// Determine if closing and opening app token sets are wallpaper targets, in which case
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index ab7d259..2f7956e 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -414,6 +414,8 @@
WallpaperController mWallpaperController;
+ boolean mWallpaperMayChange = false;
+
private final SurfaceSession mSession = new SurfaceSession();
/**
@@ -751,8 +753,7 @@
}
}
- if (isDefaultDisplay && obscuredChanged && w.isVisibleLw()
- && mWallpaperController.isWallpaperTarget(w)) {
+ if (obscuredChanged && w.isVisibleLw() && mWallpaperController.isWallpaperTarget(w)) {
// This is the wallpaper target and its obscured state changed... make sure the
// current wallpaper's visibility has been updated accordingly.
mWallpaperController.updateWallpaperVisibility();
@@ -784,7 +785,7 @@
if ((w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) {
if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
"First draw done in potential wallpaper target " + w);
- root.mWallpaperMayChange = true;
+ mWallpaperMayChange = true;
pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
if (DEBUG_LAYOUT_REPEATS) {
surfacePlacer.debugLayoutRepeats(
@@ -816,11 +817,10 @@
* initialize direct children.
* @param display May not be null.
* @param service You know.
- * @param wallpaperController wallpaper windows controller used to adjust the positioning of the
- * wallpaper windows in the window list.
+ * @param controller The controller for the display container.
*/
DisplayContent(Display display, WindowManagerService service,
- WallpaperController wallpaperController, DisplayWindowController controller) {
+ DisplayWindowController controller) {
super(service);
setController(controller);
if (service.mRoot.getDisplayContent(display.getDisplayId()) != null) {
@@ -831,7 +831,7 @@
mDisplay = display;
mDisplayId = display.getDisplayId();
- mWallpaperController = wallpaperController;
+ mWallpaperController = new WallpaperController(mService, this);
display.getDisplayInfo(mDisplayInfo);
display.getMetrics(mDisplayMetrics);
isDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
@@ -2661,6 +2661,9 @@
pw.print(" mFocusedApp="); pw.println(mFocusedApp);
pw.println();
+ mWallpaperController.dump(pw, " ");
+
+ pw.println();
pw.println(prefix + "Application tokens in top down Z order:");
for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final TaskStack stack = mTaskStackContainers.getChildAt(stackNdx);
@@ -3388,12 +3391,8 @@
if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats("On entry to LockedInner",
pendingLayoutChanges);
- // TODO(multi-display): For now adjusting wallpaper only on primary display to avoid
- // the wallpaper window jumping across displays.
- // Remove check for default display when there will be support for multiple wallpaper
- // targets (on different displays).
- if (isDefaultDisplay && (pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
- mWallpaperController.adjustWallpaperWindows(this);
+ if ((pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
+ mWallpaperController.adjustWallpaperWindows();
}
if ((pendingLayoutChanges & FINISH_LAYOUT_REDO_CONFIG) != 0) {
@@ -4673,7 +4672,7 @@
Slog.v(TAG_WM, "Wallpaper layer changed: assigning layers + relayout");
}
computeImeTarget(true /* updateImeTarget */);
- mService.mRoot.mWallpaperMayChange = true;
+ mWallpaperMayChange = true;
// Since the window list has been rebuilt, focus might have to be recomputed since the
// actual order of windows might have changed again.
mService.mFocusMayChange = true;
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 61bc4e4..83d32c8ad 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -40,14 +40,11 @@
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
+import android.view.InputApplicationHandle;
import android.view.InputChannel;
import android.view.InputEventReceiver;
-import android.view.KeyEvent;
-import android.view.WindowManager;
-import android.view.InputApplicationHandle;
import android.view.InputWindowHandle;
-import com.android.server.input.InputManagerService;
import com.android.server.policy.WindowManagerPolicy;
import java.io.PrintWriter;
@@ -401,10 +398,10 @@
mTmpRect.setEmpty();
mDisableWallpaperTouchEvents = false;
this.inDrag = inDrag;
- wallpaperController = mService.mRoot.mWallpaperController;
+ final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId);
+ wallpaperController = dc.mWallpaperController;
- mService.mRoot.getDisplayContent(mDisplayId).forAllWindows(this,
- true /* traverseTopToBottom */);
+ dc.forAllWindows(this, true /* traverseTopToBottom */);
if (mAddWallpaperInputConsumerHandle) {
// No visible wallpaper found, add the wallpaper input consumer at the end.
addInputWindowHandle(wallpaperInputConsumer.mWindowHandle);
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 2399716..72e3562 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -51,7 +51,6 @@
import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION;
import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING;
-import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE;
import android.annotation.CallSuper;
import android.annotation.NonNull;
@@ -106,7 +105,6 @@
private boolean mSustainedPerformanceModeEnabled = false;
private boolean mSustainedPerformanceModeCurrent = false;
- boolean mWallpaperMayChange = false;
// During an orientation change, we track whether all windows have rendered
// at the new orientation, and this will be false from changing orientation until that occurs.
// For seamless rotation cases this always stays true, as the windows complete their orientation
@@ -114,8 +112,6 @@
boolean mOrientationChangeComplete = true;
boolean mWallpaperActionPending = false;
- final WallpaperController mWallpaperController;
-
private final Handler mHandler;
private String mCloseSystemDialogsReason;
@@ -150,7 +146,6 @@
RootWindowContainer(WindowManagerService service) {
super(service);
mHandler = new MyHandler(service.mH.getLooper());
- mWallpaperController = new WallpaperController(mService);
}
boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
@@ -236,8 +231,7 @@
return existing;
}
- final DisplayContent dc =
- new DisplayContent(display, mService, mWallpaperController, controller);
+ final DisplayContent dc = new DisplayContent(display, mService, controller);
if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Adding display=" + display);
@@ -579,14 +573,19 @@
final RecentsAnimationController recentsAnimationController =
mService.getRecentsAnimationController();
if (recentsAnimationController != null) {
- recentsAnimationController.checkAnimationReady(mWallpaperController);
+ recentsAnimationController.checkAnimationReady(defaultDisplay.mWallpaperController);
}
- if (mWallpaperMayChange) {
- if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Wallpaper may change! Adjusting");
- defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
- if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats("WallpaperMayChange",
- defaultDisplay.pendingLayoutChanges);
+ for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+ final DisplayContent displayContent = mChildren.get(displayNdx);
+ if (displayContent.mWallpaperMayChange) {
+ if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Wallpaper may change! Adjusting");
+ displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+ if (DEBUG_LAYOUT_REPEATS) {
+ surfacePlacer.debugLayoutRepeats("WallpaperMayChange",
+ displayContent.pendingLayoutChanges);
+ }
+ }
}
if (mService.mFocusMayChange) {
@@ -617,7 +616,6 @@
}
// Destroy the surface of any windows that are no longer visible.
- boolean wallpaperDestroyed = false;
i = mService.mDestroySurface.size();
if (i > 0) {
do {
@@ -629,7 +627,7 @@
displayContent.setInputMethodWindowLocked(null);
}
if (displayContent.mWallpaperController.isWallpaperTarget(win)) {
- wallpaperDestroyed = true;
+ displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
win.destroySurfaceUnchecked();
win.mWinAnimator.destroyPreservedSurfaceLocked();
@@ -643,11 +641,6 @@
displayContent.removeExistingTokensIfPossible();
}
- if (wallpaperDestroyed) {
- defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
- defaultDisplay.setLayoutNeeded();
- }
-
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
final DisplayContent displayContent = mChildren.get(displayNdx);
if (displayContent.pendingLayoutChanges != 0) {
@@ -905,10 +898,6 @@
mUpdateRotation = true;
doRequest = true;
}
- if ((bulkUpdateParams & SET_WALLPAPER_MAY_CHANGE) != 0) {
- mWallpaperMayChange = true;
- doRequest = true;
- }
if ((bulkUpdateParams & SET_ORIENTATION_CHANGE_COMPLETE) == 0) {
mOrientationChangeComplete = false;
} else {
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index b411fad..6838c55 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -29,7 +29,6 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.content.ClipData;
-import android.content.Context;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Binder;
@@ -42,7 +41,6 @@
import android.os.UserHandle;
import android.util.MergedConfiguration;
import android.util.Slog;
-import android.view.Display;
import android.view.DisplayCutout;
import android.view.IWindow;
import android.view.IWindowId;
@@ -60,6 +58,7 @@
import java.io.PrintWriter;
import java.util.HashSet;
import java.util.Set;
+import java.util.function.BiConsumer;
/**
* This class represents an active client session. There is generally one
@@ -315,14 +314,19 @@
}
}
+ private void actionOnWallpaper(IBinder window,
+ BiConsumer<WallpaperController, WindowState> action) {
+ final WindowState windowState = mService.windowForClientLocked(this, window, true);
+ action.accept(windowState.getDisplayContent().mWallpaperController, windowState);
+ }
+
@Override
public void setWallpaperPosition(IBinder window, float x, float y, float xStep, float yStep) {
synchronized (mService.mGlobalLock) {
long ident = Binder.clearCallingIdentity();
try {
- mService.mRoot.mWallpaperController.setWindowWallpaperPosition(
- mService.windowForClientLocked(this, window, true),
- x, y, xStep, yStep);
+ actionOnWallpaper(window, (wpController, windowState) ->
+ wpController.setWindowWallpaperPosition(windowState, x, y, xStep, yStep));
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -332,7 +336,8 @@
@Override
public void wallpaperOffsetsComplete(IBinder window) {
synchronized (mService.mGlobalLock) {
- mService.mRoot.mWallpaperController.wallpaperOffsetsComplete(window);
+ actionOnWallpaper(window, (wpController, windowState) ->
+ wpController.wallpaperOffsetsComplete(window));
}
}
@@ -341,8 +346,8 @@
synchronized (mService.mGlobalLock) {
long ident = Binder.clearCallingIdentity();
try {
- mService.mRoot.mWallpaperController.setWindowWallpaperDisplayOffset(
- mService.windowForClientLocked(this, window, true), x, y);
+ actionOnWallpaper(window, (wpController, windowState) ->
+ wpController.setWindowWallpaperDisplayOffset(windowState, x, y));
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -355,9 +360,9 @@
synchronized (mService.mGlobalLock) {
long ident = Binder.clearCallingIdentity();
try {
- return mService.mRoot.mWallpaperController.sendWindowWallpaperCommand(
- mService.windowForClientLocked(this, window, true),
- action, x, y, z, extras, sync);
+ final WindowState windowState = mService.windowForClientLocked(this, window, true);
+ return windowState.getDisplayContent().mWallpaperController
+ .sendWindowWallpaperCommand(windowState, action, x, y, z, extras, sync);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -367,7 +372,8 @@
@Override
public void wallpaperCommandComplete(IBinder window, Bundle result) {
synchronized (mService.mGlobalLock) {
- mService.mRoot.mWallpaperController.wallpaperCommandComplete(window);
+ actionOnWallpaper(window, (wpController, windowState) ->
+ wpController.wallpaperCommandComplete(window));
}
}
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 29e1b20..15239c7 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -60,6 +60,7 @@
class WallpaperController {
private static final String TAG = TAG_WITH_CLASS_NAME ? "WallpaperController" : TAG_WM;
private WindowManagerService mService;
+ private final DisplayContent mDisplayContent;
private final ArrayList<WallpaperWindowToken> mWallpaperTokens = new ArrayList<>();
@@ -187,8 +188,9 @@
return false;
};
- public WallpaperController(WindowManagerService service) {
+ WallpaperController(WindowManagerService service, DisplayContent displayContent) {
mService = service;
+ mDisplayContent = displayContent;
}
WindowState getWallpaperTarget() {
@@ -397,11 +399,7 @@
}
private void updateWallpaperOffsetLocked(WindowState changingTarget, boolean sync) {
- final DisplayContent displayContent = changingTarget.getDisplayContent();
- if (displayContent == null) {
- return;
- }
- final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+ final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
final int dw = displayInfo.logicalWidth;
final int dh = displayInfo.logicalHeight;
@@ -464,15 +462,15 @@
}
}
- private void findWallpaperTarget(DisplayContent dc) {
+ private void findWallpaperTarget() {
mFindResults.reset();
- if (dc.isStackVisible(WINDOWING_MODE_FREEFORM)) {
+ if (mDisplayContent.isStackVisible(WINDOWING_MODE_FREEFORM)) {
// In freeform mode we set the wallpaper as its own target, so we don't need an
// additional window to make it visible.
mFindResults.setUseTopWallpaperAsTarget(true);
}
- dc.forAllWindows(mFindWallpaperTargetFunction, true /* traverseTopToBottom */);
+ mDisplayContent.forAllWindows(mFindWallpaperTargetFunction, true /* traverseTopToBottom */);
if (mFindResults.wallpaperTarget == null && mFindResults.useTopWallpaperAsTarget) {
mFindResults.setWallpaperTarget(mFindResults.topWallpaper);
@@ -485,8 +483,7 @@
}
/** Updates the target wallpaper if needed and returns true if an update happened. */
- private void updateWallpaperWindowsTarget(DisplayContent dc,
- FindWallpaperTargetResult result) {
+ private void updateWallpaperWindowsTarget(FindWallpaperTargetResult result) {
WindowState wallpaperTarget = result.wallpaperTarget;
@@ -529,7 +526,7 @@
return;
}
- if (dc.getWindow(w -> w == prevWallpaperTarget) == null) {
+ if (mDisplayContent.getWindow(w -> w == prevWallpaperTarget) == null) {
return;
}
@@ -550,9 +547,9 @@
// is not. If they're both hidden, still use the new target.
mWallpaperTarget = prevWallpaperTarget;
} else if (newTargetHidden == oldTargetHidden
- && !dc.mOpeningApps.contains(wallpaperTarget.mAppToken)
- && (dc.mOpeningApps.contains(prevWallpaperTarget.mAppToken)
- || dc.mClosingApps.contains(prevWallpaperTarget.mAppToken))) {
+ && !mDisplayContent.mOpeningApps.contains(wallpaperTarget.mAppToken)
+ && (mDisplayContent.mOpeningApps.contains(prevWallpaperTarget.mAppToken)
+ || mDisplayContent.mClosingApps.contains(prevWallpaperTarget.mAppToken))) {
// If they're both hidden (or both not hidden), prefer the one that's currently in
// opening or closing app list, this allows transition selection logic to better
// determine the wallpaper status of opening/closing apps.
@@ -570,18 +567,21 @@
}
}
- void adjustWallpaperWindows(DisplayContent dc) {
- mService.mRoot.mWallpaperMayChange = false;
+ void adjustWallpaperWindows() {
+ mDisplayContent.mWallpaperMayChange = false;
// First find top-most window that has asked to be on top of the wallpaper;
// all wallpapers go behind it.
- findWallpaperTarget(dc);
- updateWallpaperWindowsTarget(dc, mFindResults);
+ findWallpaperTarget();
+ updateWallpaperWindowsTarget(mFindResults);
// The window is visible to the compositor...but is it visible to the user?
// That is what the wallpaper cares about.
final boolean visible = mWallpaperTarget != null && isWallpaperVisible(mWallpaperTarget);
- if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper visibility: " + visible);
+ if (DEBUG_WALLPAPER) {
+ Slog.v(TAG, "Wallpaper visibility: " + visible + " at display "
+ + mDisplayContent.getDisplayId());
+ }
if (visible) {
if (mWallpaperTarget.mWallpaperX >= 0) {
@@ -637,9 +637,11 @@
}
if (mWallpaperDrawState == WALLPAPER_DRAW_NORMAL) {
mWallpaperDrawState = WALLPAPER_DRAW_PENDING;
- mService.mH.removeMessages(WALLPAPER_DRAW_PENDING_TIMEOUT);
- mService.mH.sendEmptyMessageDelayed(WALLPAPER_DRAW_PENDING_TIMEOUT,
- WALLPAPER_DRAW_PENDING_TIMEOUT_DURATION);
+ mService.mH.removeMessages(WALLPAPER_DRAW_PENDING_TIMEOUT, this);
+ mService.mH.sendMessageDelayed(
+ mService.mH.obtainMessage(WALLPAPER_DRAW_PENDING_TIMEOUT, this),
+ WALLPAPER_DRAW_PENDING_TIMEOUT_DURATION);
+
}
if (DEBUG_APP_TRANSITIONS || DEBUG_WALLPAPER) Slog.v(TAG,
"Wallpaper should be visible but has not been drawn yet. " +
@@ -649,7 +651,7 @@
}
if (wallpaperReady) {
mWallpaperDrawState = WALLPAPER_DRAW_NORMAL;
- mService.mH.removeMessages(WALLPAPER_DRAW_PENDING_TIMEOUT);
+ mService.mH.removeMessages(WALLPAPER_DRAW_PENDING_TIMEOUT, this);
}
return transitionReady;
@@ -659,10 +661,9 @@
* Adjusts the wallpaper windows if the input display has a pending wallpaper layout or one of
* the opening apps should be a wallpaper target.
*/
- void adjustWallpaperWindowsForAppTransitionIfNeeded(DisplayContent dc,
- ArraySet<AppWindowToken> openingApps) {
+ void adjustWallpaperWindowsForAppTransitionIfNeeded(ArraySet<AppWindowToken> openingApps) {
boolean adjust = false;
- if ((dc.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
+ if ((mDisplayContent.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
adjust = true;
} else {
for (int i = openingApps.size() - 1; i >= 0; --i) {
@@ -675,7 +676,7 @@
}
if (adjust) {
- adjustWallpaperWindows(dc);
+ adjustWallpaperWindows();
}
}
@@ -740,6 +741,7 @@
}
void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix); pw.print("displayId="); pw.println(mDisplayContent.getDisplayId());
pw.print(prefix); pw.print("mWallpaperTarget="); pw.println(mWallpaperTarget);
if (mPrevWallpaperTarget != null) {
pw.print(prefix); pw.print("mPrevWallpaperTarget="); pw.println(mPrevWallpaperTarget);
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 46bb981..449c409 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.view.Display.DEFAULT_DISPLAY;
+
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -270,9 +271,6 @@
if ((bulkUpdateParams & WindowSurfacePlacer.SET_UPDATE_ROTATION) != 0) {
builder.append(" UPDATE_ROTATION");
}
- if ((bulkUpdateParams & WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE) != 0) {
- builder.append(" WALLPAPER_MAY_CHANGE");
- }
if ((bulkUpdateParams & WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE) != 0) {
builder.append(" ORIENTATION_CHANGE_COMPLETE");
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c47b22f..8d5cd78 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2286,7 +2286,7 @@
}
synchronized (mGlobalLock) {
- final DisplayContent dc = mRoot.getDisplayContent(displayId);
+ final DisplayContent dc = getDisplayContentOrCreate(displayId, null /* token */);
if (dc == null) {
Slog.w(TAG_WM, "addWindowToken: Attempted to add token: " + binder
+ " for non-exiting displayId=" + displayId);
@@ -3462,7 +3462,9 @@
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "screenshotWallpaper");
synchronized (mGlobalLock) {
- return mRoot.mWallpaperController.screenshotWallpaperLocked();
+ // TODO(b/115486823) Screenshot at secondary displays if needed.
+ final DisplayContent dc = mRoot.getDisplayContent(DEFAULT_DISPLAY);
+ return dc.mWallpaperController.screenshotWallpaperLocked();
}
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
@@ -4694,7 +4696,10 @@
break;
case WALLPAPER_DRAW_PENDING_TIMEOUT: {
synchronized (mGlobalLock) {
- if (mRoot.mWallpaperController.processWallpaperDrawPendingTimeout()) {
+ final WallpaperController wallpaperController =
+ (WallpaperController) msg.obj;
+ if (wallpaperController != null
+ && wallpaperController.processWallpaperDrawPendingTimeout()) {
mWindowPlacerLocked.performSurfacePlacement();
}
}
@@ -5975,7 +5980,6 @@
pw.print(" mInputMethodWindow="); pw.println(imeWindow);
}
mWindowPlacerLocked.dump(pw, " ");
- mRoot.mWallpaperController.dump(pw, " ");
pw.print(" mSystemBooted="); pw.print(mSystemBooted);
pw.print(" mDisplayEnabled="); pw.println(mDisplayEnabled);
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 838d2a1..15ea5b5 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -245,7 +245,7 @@
mSession = win.mSession;
mAttrType = win.mAttrs.type;
mIsWallpaper = win.mIsWallpaper;
- mWallpaperControllerLocked = mService.mRoot.mWallpaperController;
+ mWallpaperControllerLocked = win.getDisplayContent().mWallpaperController;
}
void cancelExitAnimationForNextAnimationLocked() {
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 7d25b8c..7193dd7 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -38,7 +38,6 @@
class WindowSurfacePlacer {
private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowSurfacePlacer" : TAG_WM;
private final WindowManagerService mService;
- private final WallpaperController mWallpaperControllerLocked;
private boolean mInLayout = false;
@@ -46,7 +45,6 @@
private int mLayoutRepeatCount;
static final int SET_UPDATE_ROTATION = 1 << 0;
- static final int SET_WALLPAPER_MAY_CHANGE = 1 << 1;
static final int SET_ORIENTATION_CHANGE_COMPLETE = 1 << 2;
static final int SET_WALLPAPER_ACTION_PENDING = 1 << 3;
@@ -59,7 +57,6 @@
public WindowSurfacePlacer(WindowManagerService service) {
mService = service;
- mWallpaperControllerLocked = mService.mRoot.mWallpaperController;
mPerformSurfacePlacement = () -> {
synchronized (mService.mGlobalLock) {
performSurfacePlacement();
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 04a526f..bf83ac13 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -83,6 +83,7 @@
"libui",
"libinput",
"libinputflinger",
+ "libinputflinger_base",
"libinputservice",
"libschedulerservicehidl",
"libsensorservice",
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index e2db807..ee8a08b 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -341,8 +341,7 @@
}
mInteractive = true;
- sp<EventHub> eventHub = new EventHub();
- mInputManager = new InputManager(eventHub, this, this);
+ mInputManager = new InputManager(this, this);
}
NativeInputManager::~NativeInputManager() {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 2dbbf55..5926bdd 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -88,4 +88,7 @@
public String getGlobalPrivateDnsHost(ComponentName who) {
return null;
}
+
+ @Override
+ public void grantDeviceIdsAccessToProfileOwner(ComponentName who, int userId) { }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index a7542d7..bbbc40c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -55,16 +55,15 @@
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
-import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_UNKNOWN;
import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_OFF;
import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
+import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_UNKNOWN;
import static android.app.admin.DevicePolicyManager.PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
-
import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
import static android.provider.Telephony.Carriers.DPC_URI;
@@ -75,11 +74,8 @@
.PROVISIONING_ENTRY_POINT_ADB;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker
.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
-
import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_DEVICE_OWNER;
import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_PROFILE_OWNER;
-
-
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
@@ -228,19 +224,19 @@
import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
import com.android.internal.util.JournaledFile;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.StatLogger;
import com.android.internal.util.XmlUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.LocalServices;
import com.android.server.LockGuard;
-import com.android.internal.util.StatLogger;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;
import com.android.server.devicepolicy.DevicePolicyManagerService.ActiveAdmin.TrustAgentInfo;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.pm.UserRestrictionsUtils;
import com.android.server.storage.DeviceStorageMonitorInternal;
-
import com.android.server.uri.UriGrantsManagerInternal;
+
import com.google.android.collect.Sets;
import org.xmlpull.v1.XmlPullParser;
@@ -267,7 +263,6 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
-import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -2701,7 +2696,7 @@
final DevicePolicyData policy = getUserData(userId);
ActiveAdmin admin = policy.mAdminMap.get(who);
if (admin == null) {
- throw new SecurityException("No active admin " + who);
+ throw new SecurityException("No active admin " + who + " for UID " + uid);
}
if (admin.getUid() != uid) {
throw new SecurityException("Admin " + who + " is not owned by uid " + uid);
@@ -2709,6 +2704,16 @@
return admin;
}
+ /**
+ * Returns the active admin for the user of the caller as denoted by uid, which implements
+ * the {@code reqPolicy}.
+ *
+ * The {@code who} parameter is used as a hint:
+ * If provided, it must be the component name of the active admin for that user and the caller
+ * uid must match the uid of the admin.
+ * If not provided, iterate over all of the active admins in the DevicePolicyData for that user
+ * and return the one with the uid specified as parameter, and has the policy specified.
+ */
private ActiveAdmin getActiveAdminWithPolicyForUidLocked(ComponentName who, int reqPolicy,
int uid) {
ensureLocked();
@@ -5435,23 +5440,54 @@
return false;
}
- private void enforceIsDeviceOwnerOrCertInstallerOfDeviceOwner(
+ /**
+ * Enforce one the following conditions are met:
+ * (1) The device has a Device Owner, and one of the following holds:
+ * (1.1) The caller is the Device Owner
+ * (1.2) The caller is another app in the same user as the device owner, AND
+ * The caller is the delegated certificate installer.
+ * (2) The user has a profile owner, AND:
+ * (2.1) The profile owner has been granted access to Device IDs and one of the following
+ * holds:
+ * (2.1.1) The caller is the profile owner.
+ * (2.1.2) The caller is from another app in the same user as the profile owner, AND
+ * (2.1.2.1) The caller is the delegated cert installer.
+ *
+ * For the device owner case, simply check that the caller is the device owner or the
+ * delegated certificate installer.
+ *
+ * For the profile owner case, first check that the caller is the profile owner or can
+ * manage the DELEGATION_CERT_INSTALL scope.
+ * If that check succeeds, ensure the profile owner was granted access to device
+ * identifiers. The grant is transitive: The delegated cert installer is implicitly allowed
+ * access to device identifiers in this case as part of the delegation.
+ */
+ @VisibleForTesting
+ public void enforceCallerCanRequestDeviceIdAttestation(
ComponentName who, String callerPackage, int callerUid) throws SecurityException {
- if (who == null) {
- if (!mOwners.hasDeviceOwner()) {
- throw new SecurityException("Not in Device Owner mode.");
+ final int userId = UserHandle.getUserId(callerUid);
+
+ /**
+ * First check if there's a profile owner because the device could be in COMP mode (where
+ * there's a device owner and profile owner on the same device).
+ * If the caller is from the work profile, then it must be the PO or the delegate, and
+ * it must have the right permission to access device identifiers.
+ */
+ if (hasProfileOwner(userId)) {
+ // Make sure that the caller is the profile owner or delegate.
+ enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
+ DELEGATION_CERT_INSTALL);
+ // Verify that the profile owner was granted access to Device IDs.
+ if (canProfileOwnerAccessDeviceIds(userId)) {
+ return;
}
- if (UserHandle.getUserId(callerUid) != mOwners.getDeviceOwnerUserId()) {
- throw new SecurityException("Caller not from device owner user");
- }
- if (!isCallerDelegate(callerPackage, DELEGATION_CERT_INSTALL)) {
- throw new SecurityException("Caller with uid " + mInjector.binderGetCallingUid() +
- "has no permission to generate keys.");
- }
- } else {
- // Caller provided - check it is the device owner.
- enforceDeviceOwner(who);
+ throw new SecurityException(
+ "Profile Owner is not allowed to access Device IDs.");
}
+
+ // If not, fall back to the device owner check.
+ enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER,
+ DELEGATION_CERT_INSTALL);
}
@VisibleForTesting
@@ -5499,7 +5535,7 @@
final int callingUid = mInjector.binderGetCallingUid();
if (deviceIdAttestationRequired && attestationUtilsFlags.length > 0) {
- enforceIsDeviceOwnerOrCertInstallerOfDeviceOwner(who, callerPackage, callingUid);
+ enforceCallerCanRequestDeviceIdAttestation(who, callerPackage, callingUid);
} else {
enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
DELEGATION_CERT_INSTALL);
@@ -7365,6 +7401,18 @@
return who != null && who.equals(profileOwner);
}
+ private boolean hasProfileOwner(int userId) {
+ synchronized (getLockObject()) {
+ return mOwners.hasProfileOwner(userId);
+ }
+ }
+
+ private boolean canProfileOwnerAccessDeviceIds(int userId) {
+ synchronized (getLockObject()) {
+ return mOwners.canProfileOwnerAccessDeviceIds(userId);
+ }
+ }
+
@Override
public ComponentName getDeviceOwnerComponent(boolean callingUserOnly) {
if (!mHasFeature) {
@@ -11583,6 +11631,53 @@
return false;
}
+ private boolean hasGrantProfileOwnerDevcieIdAccessPermission() {
+ return mContext.checkCallingPermission(
+ android.Manifest.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ @Override
+ public void grantDeviceIdsAccessToProfileOwner(ComponentName who, int userId) {
+ // As the caller is the system, it must specify the component name of the profile owner
+ // as a sanity / safety check.
+ Preconditions.checkNotNull(who);
+
+ if (!mHasFeature) {
+ return;
+ }
+
+ // Only privileged system apps can grant the Profile Owner access to Device IDs.
+ if (!(isCallerWithSystemUid() || isAdb()
+ || hasGrantProfileOwnerDevcieIdAccessPermission())) {
+ throw new SecurityException(
+ "Only the system can grant Device IDs access for a profile owner.");
+ }
+
+ if (isAdb() && hasIncompatibleAccountsOrNonAdbNoLock(userId, who)) {
+ throw new SecurityException(
+ "Can only be called from ADB if the device has no accounts.");
+ }
+
+ // Grant access under lock.
+ synchronized (getLockObject()) {
+ // Sanity check: Make sure that the user has a profile owner and that the specified
+ // component is the profile owner of that user.
+ if (!isProfileOwner(who, userId)) {
+ throw new IllegalArgumentException(String.format(
+ "Component %s is not a Profile Owner of user %d",
+ who.flattenToString(), userId));
+ }
+
+ Slog.i(LOG_TAG, String.format("Granting Device ID access to %s, for user %d",
+ who.flattenToString(), userId));
+
+ // setProfileOwnerCanAccessDeviceIds will trigger writing of the profile owner
+ // data, no need to do it manually.
+ mOwners.setProfileOwnerCanAccessDeviceIds(userId);
+ }
+ }
+
private void pushMeteredDisabledPackagesLocked(int userId) {
mInjector.getNetworkPolicyManagerInternal().setMeteredRestrictedPackages(
getMeteredDisabledPackagesLocked(userId), userId);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 632f0aa..ee1c1df3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -42,6 +42,8 @@
import com.android.internal.util.FastXmlSerializer;
import com.android.server.LocalServices;
+import libcore.io.IoUtils;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -58,8 +60,6 @@
import java.util.Objects;
import java.util.Set;
-import libcore.io.IoUtils;
-
/**
* Stores and restores state for the Device and Profile owners and related device-wide information.
* By definition there can be only one device owner, but there may be a profile owner for each user.
@@ -99,6 +99,7 @@
private static final String ATTR_USER_RESTRICTIONS_MIGRATED = "userRestrictionsMigrated";
private static final String ATTR_FREEZE_RECORD_START = "start";
private static final String ATTR_FREEZE_RECORD_END = "end";
+ private static final String ATTR_CAN_ACCESS_DEVICE_IDS = "canAccessDeviceIds";
private final UserManager mUserManager;
private final UserManagerInternal mUserManagerInternal;
@@ -264,8 +265,12 @@
void setDeviceOwnerWithRestrictionsMigrated(ComponentName admin, String ownerName, int userId,
boolean userRestrictionsMigrated) {
synchronized (mLock) {
+ // A device owner is allowed to access device identifiers. Even though this flag
+ // is not currently checked for device owner, it is set to true here so that it is
+ // semantically compatible with the meaning of this flag.
mDeviceOwner = new OwnerInfo(ownerName, admin, userRestrictionsMigrated,
- /* remoteBugreportUri =*/ null, /* remoteBugreportHash =*/ null);
+ /* remoteBugreportUri =*/ null, /* remoteBugreportHash =*/
+ null, /* canAccessDeviceIds =*/true);
mDeviceOwnerUserId = userId;
mUserManagerInternal.setDeviceManaged(true);
@@ -290,7 +295,7 @@
// For a newly set PO, there's no need for migration.
mProfileOwners.put(userId, new OwnerInfo(ownerName, admin,
/* userRestrictionsMigrated =*/ true, /* remoteBugreportUri =*/ null,
- /* remoteBugreportHash =*/ null));
+ /* remoteBugreportHash =*/ null, /* canAccessDeviceIds =*/ false));
mUserManagerInternal.setUserManaged(userId, true);
pushToPackageManagerLocked();
pushToAppOpsLocked();
@@ -311,7 +316,8 @@
final OwnerInfo ownerInfo = mProfileOwners.get(userId);
final OwnerInfo newOwnerInfo = new OwnerInfo(target.getPackageName(), target,
ownerInfo.userRestrictionsMigrated, ownerInfo.remoteBugreportUri,
- ownerInfo.remoteBugreportHash);
+ ownerInfo.remoteBugreportHash, /* canAccessDeviceIds =*/
+ ownerInfo.canAccessDeviceIds);
mProfileOwners.put(userId, newOwnerInfo);
pushToPackageManagerLocked();
pushToAppOpsLocked();
@@ -324,7 +330,8 @@
// See DevicePolicyManagerService#getDeviceOwnerName
mDeviceOwner = new OwnerInfo(null, target,
mDeviceOwner.userRestrictionsMigrated, mDeviceOwner.remoteBugreportUri,
- mDeviceOwner.remoteBugreportHash);
+ mDeviceOwner.remoteBugreportHash, /* canAccessDeviceIds =*/
+ mDeviceOwner.canAccessDeviceIds);
pushToPackageManagerLocked();
pushToAppOpsLocked();
}
@@ -351,6 +358,17 @@
}
}
+ /**
+ * Returns true if {@code userId} has a profile owner and that profile owner was granted
+ * the ability to access device identifiers.
+ */
+ boolean canProfileOwnerAccessDeviceIds(int userId) {
+ synchronized (mLock) {
+ OwnerInfo profileOwner = mProfileOwners.get(userId);
+ return profileOwner != null ? profileOwner.canAccessDeviceIds : false;
+ }
+ }
+
Set<Integer> getProfileOwnerKeys() {
synchronized (mLock) {
return mProfileOwners.keySet();
@@ -486,6 +504,20 @@
}
}
+ /** Sets the grant to access device IDs, and also writes to file. */
+ void setProfileOwnerCanAccessDeviceIds(int userId) {
+ synchronized (mLock) {
+ OwnerInfo profileOwner = mProfileOwners.get(userId);
+ if (profileOwner != null) {
+ profileOwner.canAccessDeviceIds = true;
+ } else {
+ Slog.e(TAG, String.format(
+ "Cannot grant Device IDs access for user %d, no profile owner.", userId));
+ }
+ writeProfileOwner(userId);
+ }
+ }
+
private boolean readLegacyOwnerFileLocked(File file) {
if (!file.exists()) {
// Already migrated or the device has no owners.
@@ -507,7 +539,7 @@
String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
mDeviceOwner = new OwnerInfo(name, packageName,
/* userRestrictionsMigrated =*/ false, /* remoteBugreportUri =*/ null,
- /* remoteBugreportHash =*/ null);
+ /* remoteBugreportHash =*/ null, /* canAccessDeviceIds =*/ true);
mDeviceOwnerUserId = UserHandle.USER_SYSTEM;
} else if (tag.equals(TAG_DEVICE_INITIALIZER)) {
// Deprecated tag
@@ -523,7 +555,8 @@
profileOwnerComponentStr);
if (admin != null) {
profileOwnerInfo = new OwnerInfo(profileOwnerName, admin,
- /* userRestrictionsMigrated =*/ false, null, null);
+ /* userRestrictionsMigrated =*/ false, null,
+ null, /* canAccessDeviceIds =*/ false);
} else {
// This shouldn't happen but switch from package name -> component name
// might have written bad device owner files. b/17652534
@@ -534,7 +567,8 @@
if (profileOwnerInfo == null) {
profileOwnerInfo = new OwnerInfo(profileOwnerName, profileOwnerPackageName,
/* userRestrictionsMigrated =*/ false,
- /* remoteBugreportUri =*/ null, /* remoteBugreportHash =*/ null);
+ /* remoteBugreportUri =*/ null, /* remoteBugreportHash =*/
+ null, /* canAccessDeviceIds =*/ false);
}
mProfileOwners.put(userId, profileOwnerInfo);
} else if (TAG_SYSTEM_UPDATE_POLICY.equals(tag)) {
@@ -894,25 +928,28 @@
public boolean userRestrictionsMigrated;
public String remoteBugreportUri;
public String remoteBugreportHash;
+ public boolean canAccessDeviceIds;
public OwnerInfo(String name, String packageName, boolean userRestrictionsMigrated,
- String remoteBugreportUri, String remoteBugreportHash) {
+ String remoteBugreportUri, String remoteBugreportHash, boolean canAccessDeviceIds) {
this.name = name;
this.packageName = packageName;
this.admin = new ComponentName(packageName, "");
this.userRestrictionsMigrated = userRestrictionsMigrated;
this.remoteBugreportUri = remoteBugreportUri;
this.remoteBugreportHash = remoteBugreportHash;
+ this.canAccessDeviceIds = canAccessDeviceIds;
}
public OwnerInfo(String name, ComponentName admin, boolean userRestrictionsMigrated,
- String remoteBugreportUri, String remoteBugreportHash) {
+ String remoteBugreportUri, String remoteBugreportHash, boolean canAccessDeviceIds) {
this.name = name;
this.admin = admin;
this.packageName = admin.getPackageName();
this.userRestrictionsMigrated = userRestrictionsMigrated;
this.remoteBugreportUri = remoteBugreportUri;
this.remoteBugreportHash = remoteBugreportHash;
+ this.canAccessDeviceIds = canAccessDeviceIds;
}
public void writeToXml(XmlSerializer out, String tag) throws IOException {
@@ -932,6 +969,10 @@
if (remoteBugreportHash != null) {
out.attribute(null, ATTR_REMOTE_BUGREPORT_HASH, remoteBugreportHash);
}
+ if (canAccessDeviceIds) {
+ out.attribute(null, ATTR_CAN_ACCESS_DEVICE_IDS,
+ String.valueOf(canAccessDeviceIds));
+ }
out.endTag(null, tag);
}
@@ -948,13 +989,17 @@
ATTR_REMOTE_BUGREPORT_URI);
final String remoteBugreportHash = parser.getAttributeValue(null,
ATTR_REMOTE_BUGREPORT_HASH);
+ final String canAccessDeviceIdsStr =
+ parser.getAttributeValue(null, ATTR_CAN_ACCESS_DEVICE_IDS);
+ final boolean canAccessDeviceIds =
+ ("true".equals(canAccessDeviceIdsStr));
// Has component name? If so, return [name, component]
if (componentName != null) {
final ComponentName admin = ComponentName.unflattenFromString(componentName);
if (admin != null) {
return new OwnerInfo(name, admin, userRestrictionsMigrated,
- remoteBugreportUri, remoteBugreportHash);
+ remoteBugreportUri, remoteBugreportHash, canAccessDeviceIds);
} else {
// This shouldn't happen but switch from package name -> component name
// might have written bad device owner files. b/17652534
@@ -965,13 +1010,14 @@
// Else, build with [name, package]
return new OwnerInfo(name, packageName, userRestrictionsMigrated, remoteBugreportUri,
- remoteBugreportHash);
+ remoteBugreportHash, canAccessDeviceIds);
}
public void dump(String prefix, PrintWriter pw) {
pw.println(prefix + "admin=" + admin);
pw.println(prefix + "name=" + name);
pw.println(prefix + "package=" + packageName);
+ pw.println(prefix + "canAccessDeviceIds=" + canAccessDeviceIds);
}
}
diff --git a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
index 25d1cc7..f7bb68c 100644
--- a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -42,6 +42,7 @@
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
+import android.os.Binder;
import android.os.HandlerThread;
import android.os.PowerManager;
import android.os.PowerSaveState;
@@ -139,7 +140,7 @@
* specifically to prevent overloading the logs in production.
*/
@Test
- public void testMoreDebug_isFalse() throws Exception {
+ public void testMoreDebug_isFalse() {
boolean moreDebug = BackupManagerService.MORE_DEBUG;
assertThat(moreDebug).isFalse();
@@ -943,7 +944,7 @@
backupManagerService.backupNow();
assertThat(ShadowKeyValueBackupJob.getCallingUid()).isEqualTo(ShadowBinder.LOCAL_UID);
- assertThat(ShadowBinder.getCallingUid()).isEqualTo(1);
+ assertThat(Binder.getCallingUid()).isEqualTo(1);
}
/**
@@ -961,7 +962,7 @@
expectThrows(IllegalArgumentException.class, backupManagerService::backupNow);
assertThat(ShadowKeyValueBackupJobException.getCallingUid())
.isEqualTo(ShadowBinder.LOCAL_UID);
- assertThat(ShadowBinder.getCallingUid()).isEqualTo(1);
+ assertThat(Binder.getCallingUid()).isEqualTo(1);
}
private BackupManagerService createBackupManagerServiceForRequestBackup() {
diff --git a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java b/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
index 603a471..f307730 100644
--- a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
+++ b/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
@@ -18,9 +18,7 @@
import static com.android.server.backup.testing.TestUtils.runToEndOfTasks;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -36,18 +34,16 @@
import android.os.Looper;
import android.os.PowerManager;
import android.os.Process;
-import android.util.SparseArray;
+import android.util.Log;
import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupManagerService;
import com.android.server.backup.Trampoline;
import com.android.server.backup.TransportManager;
-import com.android.server.backup.internal.Operation;
import org.mockito.stubbing.Answer;
import org.robolectric.shadows.ShadowApplication;
import org.robolectric.shadows.ShadowBinder;
-import org.robolectric.shadows.ShadowLog;
import java.io.File;
import java.lang.Thread.UncaughtExceptionHandler;
@@ -192,8 +188,7 @@
public static HandlerThread startSilentBackupThread(String tag) {
return startBackupThread(
(thread, e) ->
- ShadowLog.e(
- tag, "Uncaught exception in test thread " + thread.getName(), e));
+ Log.e(tag, "Uncaught exception in test thread " + thread.getName(), e));
}
private BackupManagerServiceTestUtils() {}
diff --git a/services/robotests/src/com/android/server/backup/testing/TestUtils.java b/services/robotests/src/com/android/server/backup/testing/TestUtils.java
index 2f54513..3fe1f3f 100644
--- a/services/robotests/src/com/android/server/backup/testing/TestUtils.java
+++ b/services/robotests/src/com/android/server/backup/testing/TestUtils.java
@@ -25,12 +25,12 @@
import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;
+import android.os.SystemClock;
import com.android.server.testing.shadows.ShadowEventLog;
import org.robolectric.shadows.ShadowLog;
import org.robolectric.shadows.ShadowLooper;
-import org.robolectric.shadows.ShadowSystemClock;
import java.util.Arrays;
import java.util.concurrent.Callable;
@@ -87,7 +87,7 @@
// specific time to the looper the time of those messages will be before the looper's time.
// To fix this we advance SystemClock as well since that is from where the handlers read
// time.
- ShadowSystemClock.setCurrentTimeMillis(shadowLooper.getScheduler().getCurrentTime());
+ SystemClock.setCurrentTimeMillis(shadowLooper.getScheduler().getCurrentTime());
}
/**
diff --git a/services/robotests/src/com/android/server/location/NtpTimeHelperTest.java b/services/robotests/src/com/android/server/location/NtpTimeHelperTest.java
index aac0a34..a8a258f 100644
--- a/services/robotests/src/com/android/server/location/NtpTimeHelperTest.java
+++ b/services/robotests/src/com/android/server/location/NtpTimeHelperTest.java
@@ -5,6 +5,7 @@
import static org.mockito.Mockito.doReturn;
import android.os.Looper;
+import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
import android.util.NtpTrustedTime;
@@ -18,7 +19,6 @@
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.shadows.ShadowLooper;
-import org.robolectric.shadows.ShadowSystemClock;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -74,7 +74,7 @@
doReturn(true).when(mMockNtpTrustedTime).forceRefresh();
doReturn(1L).when(mMockNtpTrustedTime).getCacheAge();
doReturn(MOCK_NTP_TIME).when(mMockNtpTrustedTime).getCachedNtpTime();
- ShadowSystemClock.sleep(NtpTimeHelper.RETRY_INTERVAL);
+ SystemClock.sleep(NtpTimeHelper.RETRY_INTERVAL);
waitForTasksToBePostedOnHandlerAndRunThem();
assertThat(mCountDownLatch.await(2, TimeUnit.SECONDS)).isTrue();
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 16b127c..5dc6d83 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -4973,6 +4973,176 @@
assertProfileOwnershipRevertedWithFakeTransferMetadata();
}
+ public void testGrantDeviceIdsAccess_notToProfileOwner() throws Exception {
+ setupProfileOwner();
+ configureContextForAccess(mContext, false);
+
+ assertExpectException(SecurityException.class, /* messageRegex= */ null,
+ () -> dpm.setProfileOwnerCanAccessDeviceIdsForUser(admin2,
+ UserHandle.of(DpmMockContext.CALLER_UID)));
+ }
+
+ public void testGrantDeviceIdsAccess_notByAuthorizedCaller() throws Exception {
+ setupProfileOwner();
+ configureContextForAccess(mContext, false);
+
+ assertExpectException(SecurityException.class, /* messageRegex= */ null,
+ () -> dpm.setProfileOwnerCanAccessDeviceIdsForUser(admin1,
+ UserHandle.of(DpmMockContext.CALLER_UID)));
+ }
+
+ public void testGrantDeviceIdsAccess_byAuthorizedSystemCaller() throws Exception {
+ setupProfileOwner();
+
+ // This method will throw if the system context could not call
+ // setProfileOwnerCanAccessDeviceIds successfully.
+ configureProfileOwnerForDeviceIdAccess(admin1, DpmMockContext.CALLER_USER_HANDLE);
+ }
+
+ private static void configureContextForAccess(DpmMockContext context, boolean granted) {
+ when(context.spiedContext.checkCallingPermission(
+ android.Manifest.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS))
+ .thenReturn(granted ? PackageManager.PERMISSION_GRANTED
+ : PackageManager.PERMISSION_DENIED);
+ }
+
+ public void testGrantDeviceIdsAccess_byAuthorizedManagedProvisioning() throws Exception {
+ setupProfileOwner();
+
+ final long ident = mServiceContext.binder.clearCallingIdentity();
+ configureContextForAccess(mServiceContext, true);
+
+ mServiceContext.binder.callingUid =
+ UserHandle.getUid(DpmMockContext.CALLER_USER_HANDLE,
+ DpmMockContext.CALLER_MANAGED_PROVISIONING_UID);
+ try {
+ runAsCaller(mServiceContext, dpms, dpm -> {
+ dpm.setProfileOwnerCanAccessDeviceIdsForUser(admin1,
+ UserHandle.of(DpmMockContext.CALLER_USER_HANDLE));
+ });
+ } finally {
+ mServiceContext.binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ public void testEnforceCallerCanRequestDeviceIdAttestation_deviceOwnerCaller()
+ throws Exception {
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ setupDeviceOwner();
+ configureContextForAccess(mContext, false);
+
+ // Device owner should be allowed to request Device ID attestation.
+ dpms.enforceCallerCanRequestDeviceIdAttestation(admin1, admin1.getPackageName(),
+ DpmMockContext.CALLER_SYSTEM_USER_UID);
+
+ // Another package must not be allowed to request Device ID attestation.
+ assertExpectException(SecurityException.class, null,
+ () -> dpms.enforceCallerCanRequestDeviceIdAttestation(null,
+ admin2.getPackageName(), DpmMockContext.CALLER_UID));
+ // Another component that is not the admin must not be allowed to request Device ID
+ // attestation.
+ assertExpectException(SecurityException.class, null,
+ () -> dpms.enforceCallerCanRequestDeviceIdAttestation(admin2,
+ admin1.getPackageName(), DpmMockContext.CALLER_UID));
+ }
+
+ public void testEnforceCallerCanRequestDeviceIdAttestation_profileOwnerCaller()
+ throws Exception {
+ configureContextForAccess(mContext, false);
+
+ // Make sure a security exception is thrown if the device has no profile owner.
+ assertExpectException(SecurityException.class, null,
+ () -> dpms.enforceCallerCanRequestDeviceIdAttestation(admin1,
+ admin1.getPackageName(), DpmMockContext.CALLER_SYSTEM_USER_UID));
+
+ setupProfileOwner();
+ configureProfileOwnerForDeviceIdAccess(admin1, DpmMockContext.CALLER_USER_HANDLE);
+
+ // The profile owner is allowed to request Device ID attestation.
+ mServiceContext.binder.callingUid = DpmMockContext.CALLER_UID;
+ dpms.enforceCallerCanRequestDeviceIdAttestation(admin1, admin1.getPackageName(),
+ DpmMockContext.CALLER_UID);
+ // But not another package.
+ assertExpectException(SecurityException.class, null,
+ () -> dpms.enforceCallerCanRequestDeviceIdAttestation(null,
+ admin2.getPackageName(), DpmMockContext.CALLER_UID));
+ // Or another component which is not the admin.
+ assertExpectException(SecurityException.class, null,
+ () -> dpms.enforceCallerCanRequestDeviceIdAttestation(admin2,
+ admin2.getPackageName(), DpmMockContext.CALLER_UID));
+ }
+
+ public void runAsDelegatedCertInstaller(DpmRunnable action) throws Exception {
+ final long ident = mServiceContext.binder.clearCallingIdentity();
+
+ mServiceContext.binder.callingUid = UserHandle.getUid(DpmMockContext.CALLER_USER_HANDLE,
+ DpmMockContext.DELEGATE_CERT_INSTALLER_UID);
+ try {
+ runAsCaller(mServiceContext, dpms, action);
+ } finally {
+ mServiceContext.binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ public void testEnforceCallerCanRequestDeviceIdAttestation_delegateCaller() throws Exception {
+ setupProfileOwner();
+ markDelegatedCertInstallerAsInstalled();
+
+ // Configure a delegated cert installer.
+ runAsCaller(mServiceContext, dpms,
+ dpm -> dpm.setDelegatedScopes(admin1, DpmMockContext.DELEGATE_PACKAGE_NAME,
+ Arrays.asList(DELEGATION_CERT_INSTALL)));
+
+ configureProfileOwnerForDeviceIdAccess(admin1, DpmMockContext.CALLER_USER_HANDLE);
+
+ // Make sure that the profile owner can still request Device ID attestation.
+ mServiceContext.binder.callingUid = DpmMockContext.CALLER_UID;
+ dpms.enforceCallerCanRequestDeviceIdAttestation(admin1, admin1.getPackageName(),
+ DpmMockContext.CALLER_UID);
+
+ runAsDelegatedCertInstaller(dpm -> {
+ dpms.enforceCallerCanRequestDeviceIdAttestation(null,
+ DpmMockContext.DELEGATE_PACKAGE_NAME,
+ UserHandle.getUid(DpmMockContext.CALLER_USER_HANDLE,
+ DpmMockContext.DELEGATE_CERT_INSTALLER_UID));
+ });
+ }
+
+ public void testEnforceCallerCanRequestDeviceIdAttestation_delegateCallerWithoutPermissions()
+ throws Exception {
+ setupProfileOwner();
+ markDelegatedCertInstallerAsInstalled();
+
+ // Configure a delegated cert installer.
+ runAsCaller(mServiceContext, dpms,
+ dpm -> dpm.setDelegatedScopes(admin1, DpmMockContext.DELEGATE_PACKAGE_NAME,
+ Arrays.asList(DELEGATION_CERT_INSTALL)));
+
+
+ assertExpectException(SecurityException.class, null,
+ () -> dpms.enforceCallerCanRequestDeviceIdAttestation(admin1,
+ admin1.getPackageName(),
+ DpmMockContext.CALLER_UID));
+
+ runAsDelegatedCertInstaller(dpm -> {
+ assertExpectException(SecurityException.class, /* messageRegex= */ null,
+ () -> dpms.enforceCallerCanRequestDeviceIdAttestation(null,
+ DpmMockContext.DELEGATE_PACKAGE_NAME,
+ UserHandle.getUid(DpmMockContext.CALLER_USER_HANDLE,
+ DpmMockContext.DELEGATE_CERT_INSTALLER_UID)));
+ });
+ }
+
+ private void configureProfileOwnerForDeviceIdAccess(ComponentName who, int userId) {
+ final long ident = mServiceContext.binder.clearCallingIdentity();
+ mServiceContext.binder.callingUid =
+ UserHandle.getUid(DpmMockContext.CALLER_USER_HANDLE, DpmMockContext.SYSTEM_UID);
+ runAsCaller(mServiceContext, dpms, dpm -> {
+ dpm.setProfileOwnerCanAccessDeviceIdsForUser(who, UserHandle.of(userId));
+ });
+ mServiceContext.binder.restoreCallingIdentity(ident);
+ }
+
// admin1 is the outgoing DPC, adminAnotherPakcage is the incoming one.
private void assertDeviceOwnershipRevertedWithFakeTransferMetadata() throws Exception {
writeFakeTransferMetadataFile(UserHandle.USER_SYSTEM,
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index be00bb6..e411fb5 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -59,6 +59,12 @@
public static final int CALLER_UID = UserHandle.getUid(CALLER_USER_HANDLE, 20123);
/**
+ * UID corresponding to {@link #CALLER_USER_HANDLE}.
+ */
+ public static final int CALLER_MANAGED_PROVISIONING_UID = UserHandle.getUid(CALLER_USER_HANDLE,
+ 20125);
+
+ /**
* UID used when a caller is on the system user.
*/
public static final int CALLER_SYSTEM_USER_UID = 20321;
@@ -81,6 +87,10 @@
public static final String ANOTHER_PACKAGE_NAME = "com.another.package.name";
public static final int ANOTHER_UID = UserHandle.getUid(UserHandle.USER_SYSTEM, 18434);
+ public static final String DELEGATE_PACKAGE_NAME = "com.delegate.package.name";
+ public static final int DELEGATE_CERT_INSTALLER_UID = UserHandle.getUid(UserHandle.USER_SYSTEM,
+ 18437);
+
private final MockSystemServices mMockSystemServices;
public static class MockBinder {
@@ -427,4 +437,9 @@
public int getUserId() {
return UserHandle.getUserId(binder.getCallingUid());
}
+
+ @Override
+ public int checkCallingPermission(String permission) {
+ return spiedContext.checkCallingPermission(permission);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
index 0c8a787..a34c2ff 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
@@ -50,6 +50,7 @@
public ComponentName admin3;
public ComponentName adminAnotherPackage;
public ComponentName adminNoPerm;
+ public ComponentName delegateCertInstaller;
@Override
protected void setUp() throws Exception {
@@ -66,6 +67,8 @@
adminAnotherPackage = new ComponentName(DpmMockContext.ANOTHER_PACKAGE_NAME,
"whatever.random.class");
adminNoPerm = new ComponentName(mRealTestContext, DummyDeviceAdmins.AdminNoPerm.class);
+ delegateCertInstaller = new ComponentName(DpmMockContext.DELEGATE_PACKAGE_NAME,
+ "some.random.class");
mockSystemPropertiesToReturnDefault();
}
@@ -130,6 +133,20 @@
eq(userId));
}
+ protected void markDelegatedCertInstallerAsInstalled() throws Exception {
+ final ApplicationInfo ai = new ApplicationInfo();
+ ai.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+ ai.flags = ApplicationInfo.FLAG_HAS_CODE;
+ // Mark the package as installed on the work profile.
+ ai.uid = UserHandle.getUid(DpmMockContext.CALLER_USER_HANDLE,
+ DpmMockContext.DELEGATE_CERT_INSTALLER_UID);
+ ai.packageName = delegateCertInstaller.getPackageName();
+ ai.name = delegateCertInstaller.getClassName();
+
+ markPackageAsInstalled(delegateCertInstaller.getPackageName(), ai,
+ DpmMockContext.CALLER_USER_HANDLE);
+ }
+
protected void setUpPackageManagerForAdmin(ComponentName admin, int packageUid)
throws Exception {
setUpPackageManagerForAdmin(admin, packageUid,
diff --git a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
new file mode 100644
index 0000000..7cf7df13
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.IBinder;
+import android.os.IPowerManager;
+import android.os.IThermalEventListener;
+import android.os.IThermalStatusListener;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.Temperature;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.SystemService;
+import com.android.server.power.ThermalManagerService.ThermalHalWrapper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server
+ * /power/ThermalManagerServiceTest.java
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ThermalManagerServiceTest {
+ private static final long CALLBACK_TIMEOUT_MILLI_SEC = 5000;
+ private ThermalManagerService mService;
+ private ThermalHalFake mFakeHal;
+ private PowerManager mPowerManager;
+ @Mock
+ private Context mContext;
+ @Mock
+ private IPowerManager mIPowerManagerMock;
+ @Mock
+ private IThermalEventListener mEventListener1;
+ @Mock
+ private IThermalEventListener mEventListener2;
+ @Mock
+ private IThermalStatusListener mStatusListener1;
+ @Mock
+ private IThermalStatusListener mStatusListener2;
+
+ /**
+ * Fake Hal class.
+ */
+ private class ThermalHalFake extends ThermalHalWrapper {
+ private static final int INIT_STATUS = Temperature.THROTTLING_NONE;
+ private ArrayList<Temperature> mTemperatureList = new ArrayList<>();
+ private Temperature mSkin1 = new Temperature(0, Temperature.TYPE_SKIN, "skin1",
+ INIT_STATUS);
+ private Temperature mSkin2 = new Temperature(0, Temperature.TYPE_SKIN, "skin2",
+ INIT_STATUS);
+ private Temperature mBattery = new Temperature(0, Temperature.TYPE_BATTERY, "batt",
+ INIT_STATUS);
+ private Temperature mUsbPort = new Temperature(0, Temperature.TYPE_USB_PORT, "usbport",
+ INIT_STATUS);
+
+ ThermalHalFake() {
+ mTemperatureList.add(mSkin1);
+ mTemperatureList.add(mSkin2);
+ mTemperatureList.add(mBattery);
+ mTemperatureList.add(mUsbPort);
+ }
+
+ @Override
+ protected List<Temperature> getCurrentTemperatures(boolean shouldFilter, int type) {
+ return mTemperatureList;
+ }
+
+ @Override
+ protected boolean connectToHal() {
+ return true;
+ }
+
+ @Override
+ protected void dump(PrintWriter pw, String prefix) {
+ return;
+ }
+ }
+
+ private void assertTemperatureEquals(List<Temperature> expected, List<Temperature> value) {
+ assertEquals(new HashSet<>(expected), new HashSet<>(value));
+ }
+
+ @Before
+ public void setUp() throws RemoteException {
+ MockitoAnnotations.initMocks(this);
+ mFakeHal = new ThermalHalFake();
+ mPowerManager = new PowerManager(mContext, mIPowerManagerMock, null);
+ when(mContext.getSystemServiceName(PowerManager.class)).thenReturn(Context.POWER_SERVICE);
+ when(mContext.getSystemService(PowerManager.class)).thenReturn(mPowerManager);
+ resetListenerMock();
+ mService = new ThermalManagerService(mContext, mFakeHal);
+ // Register callbacks before AMS ready and no callback sent
+ assertTrue(mService.mService.registerThermalEventListener(mEventListener1));
+ assertTrue(mService.mService.registerThermalStatusListener(mStatusListener1));
+ assertTrue(mService.mService.registerThermalEventListenerWithType(mEventListener2,
+ Temperature.TYPE_SKIN));
+ assertTrue(mService.mService.registerThermalStatusListener(mStatusListener2));
+ verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(0)).notifyThrottling(any(Temperature.class));
+ verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(0)).onStatusChange(anyInt());
+ verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(0)).notifyThrottling(any(Temperature.class));
+ verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(0)).onStatusChange(anyInt());
+ mService.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY);
+ ArgumentCaptor<Temperature> captor = ArgumentCaptor.forClass(Temperature.class);
+ verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(4)).notifyThrottling(captor.capture());
+ assertTemperatureEquals(mFakeHal.mTemperatureList, captor.getAllValues());
+ verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).onStatusChange(Temperature.THROTTLING_NONE);
+ captor = ArgumentCaptor.forClass(Temperature.class);
+ verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(2)).notifyThrottling(captor.capture());
+ assertTemperatureEquals(new ArrayList<>(Arrays.asList(mFakeHal.mSkin1, mFakeHal.mSkin2)),
+ captor.getAllValues());
+ verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).onStatusChange(Temperature.THROTTLING_NONE);
+ }
+
+ private void resetListenerMock() {
+ reset(mEventListener1);
+ reset(mStatusListener1);
+ reset(mEventListener2);
+ reset(mStatusListener2);
+ doReturn(mock(IBinder.class)).when(mEventListener1).asBinder();
+ doReturn(mock(IBinder.class)).when(mStatusListener1).asBinder();
+ doReturn(mock(IBinder.class)).when(mEventListener2).asBinder();
+ doReturn(mock(IBinder.class)).when(mStatusListener2).asBinder();
+ }
+
+ @Test
+ public void testRegister() throws RemoteException {
+ // Unregister all
+ assertTrue(mService.mService.unregisterThermalEventListener(mEventListener1));
+ assertTrue(mService.mService.unregisterThermalStatusListener(mStatusListener1));
+ assertTrue(mService.mService.unregisterThermalEventListener(mEventListener2));
+ assertTrue(mService.mService.unregisterThermalStatusListener(mStatusListener2));
+ resetListenerMock();
+ // Register callbacks and verify they are called
+ assertTrue(mService.mService.registerThermalEventListener(mEventListener1));
+ assertTrue(mService.mService.registerThermalStatusListener(mStatusListener1));
+ ArgumentCaptor<Temperature> captor = ArgumentCaptor.forClass(Temperature.class);
+ verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(4)).notifyThrottling(captor.capture());
+ assertTemperatureEquals(mFakeHal.mTemperatureList, captor.getAllValues());
+ verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).onStatusChange(Temperature.THROTTLING_NONE);
+ // Register new callbacks and verify old ones are not called (remained same) while new
+ // ones are called
+ assertTrue(mService.mService.registerThermalEventListenerWithType(mEventListener2,
+ Temperature.TYPE_SKIN));
+ assertTrue(mService.mService.registerThermalStatusListener(mStatusListener2));
+ verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(4)).notifyThrottling(any(Temperature.class));
+ verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).onStatusChange(Temperature.THROTTLING_NONE);
+ captor = ArgumentCaptor.forClass(Temperature.class);
+ verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(2)).notifyThrottling(captor.capture());
+ assertTemperatureEquals(new ArrayList<>(Arrays.asList(mFakeHal.mSkin1, mFakeHal.mSkin2)),
+ captor.getAllValues());
+ verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).onStatusChange(Temperature.THROTTLING_NONE);
+ }
+
+ @Test
+ public void testNotify() throws RemoteException {
+ int status = Temperature.THROTTLING_SEVERE;
+ Temperature newBattery = new Temperature(50, Temperature.TYPE_BATTERY, "batt", status);
+ mFakeHal.mCallback.onValues(newBattery);
+ verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).notifyThrottling(newBattery);
+ verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).onStatusChange(status);
+ verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(0)).notifyThrottling(newBattery);
+ verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).onStatusChange(status);
+ resetListenerMock();
+ // Should only notify event not status
+ Temperature newSkin = new Temperature(50, Temperature.TYPE_SKIN, "skin1", status);
+ mFakeHal.mCallback.onValues(newSkin);
+ verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).notifyThrottling(newSkin);
+ verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(0)).onStatusChange(anyInt());
+ verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).notifyThrottling(newSkin);
+ verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(0)).onStatusChange(anyInt());
+ resetListenerMock();
+ // Back to None, should only notify event not status
+ status = Temperature.THROTTLING_NONE;
+ newBattery = new Temperature(50, Temperature.TYPE_BATTERY, "batt", status);
+ mFakeHal.mCallback.onValues(newBattery);
+ verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).notifyThrottling(newBattery);
+ verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(0)).onStatusChange(anyInt());
+ verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(0)).notifyThrottling(newBattery);
+ verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(0)).onStatusChange(anyInt());
+ resetListenerMock();
+ // Should also notify status
+ newSkin = new Temperature(50, Temperature.TYPE_SKIN, "skin1", status);
+ mFakeHal.mCallback.onValues(newSkin);
+ verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).notifyThrottling(newSkin);
+ verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).onStatusChange(status);
+ verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).notifyThrottling(newSkin);
+ verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).onStatusChange(status);
+ }
+
+ @Test
+ public void testGetCurrentTemperatures() throws RemoteException {
+ assertTemperatureEquals(mFakeHal.getCurrentTemperatures(false, 0),
+ mService.mService.getCurrentTemperatures());
+ assertTemperatureEquals(mFakeHal.getCurrentTemperatures(true, Temperature.TYPE_SKIN),
+ mService.mService.getCurrentTemperaturesWithType(Temperature.TYPE_SKIN));
+ }
+
+ @Test
+ public void testGetCurrentStatus() throws RemoteException {
+ int status = Temperature.THROTTLING_WARNING;
+ Temperature newSkin = new Temperature(100, Temperature.TYPE_SKIN, "skin1", status);
+ mFakeHal.mCallback.onValues(newSkin);
+ assertEquals(status, mService.mService.getCurrentStatus());
+ }
+
+ @Test
+ public void testThermalShutdown() throws RemoteException {
+ int status = Temperature.THROTTLING_SHUTDOWN;
+ Temperature newSkin = new Temperature(100, Temperature.TYPE_SKIN, "skin1", status);
+ mFakeHal.mCallback.onValues(newSkin);
+ verify(mIPowerManagerMock, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).shutdown(false, PowerManager.SHUTDOWN_THERMAL_STATE, false);
+ }
+
+ @Test
+ public void testNoHal() throws RemoteException {
+ mService = new ThermalManagerService(mContext);
+ // Do no call onActivityManagerReady to skip connect HAL
+ assertTrue(mService.mService.registerThermalEventListener(mEventListener1));
+ assertTrue(mService.mService.registerThermalStatusListener(mStatusListener1));
+ assertTrue(mService.mService.unregisterThermalEventListener(mEventListener1));
+ assertTrue(mService.mService.unregisterThermalStatusListener(mStatusListener1));
+ assertEquals(0, mService.mService.getCurrentTemperatures().size());
+ assertEquals(0,
+ mService.mService.getCurrentTemperaturesWithType(Temperature.TYPE_SKIN).size());
+ assertEquals(Temperature.THROTTLING_NONE, mService.mService.getCurrentStatus());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java
index 25e73e3..4ea6b39 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -48,7 +48,7 @@
synchronized (mWm.mGlobalLock) {
// No wallpaper
final DisplayContent dc = createNewDisplay();
- Bitmap wallpaperBitmap = mWm.mRoot.mWallpaperController.screenshotWallpaperLocked();
+ Bitmap wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked();
assertNull(wallpaperBitmap);
// No wallpaper WSA Surface
@@ -56,25 +56,25 @@
true, dc, true /* ownerCanManageAppTokens */);
WindowState wallpaperWindow = createWindow(null /* parent */, TYPE_WALLPAPER,
wallpaperWindowToken, "wallpaperWindow");
- wallpaperBitmap = mWallpaperController.screenshotWallpaperLocked();
+ wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked();
assertNull(wallpaperBitmap);
// Wallpaper with not visible WSA surface.
wallpaperWindow.mWinAnimator.mSurfaceController = windowSurfaceController;
wallpaperWindow.mWinAnimator.mLastAlpha = 1;
- wallpaperBitmap = mWallpaperController.screenshotWallpaperLocked();
+ wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked();
assertNull(wallpaperBitmap);
when(windowSurfaceController.getShown()).thenReturn(true);
// Wallpaper with WSA alpha set to 0.
wallpaperWindow.mWinAnimator.mLastAlpha = 0;
- wallpaperBitmap = mWallpaperController.screenshotWallpaperLocked();
+ wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked();
assertNull(wallpaperBitmap);
// Wallpaper window with WSA Surface
wallpaperWindow.mWinAnimator.mLastAlpha = 1;
- wallpaperBitmap = mWallpaperController.screenshotWallpaperLocked();
+ wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked();
assertNotNull(wallpaperBitmap);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
index 80bb936..d6fea09 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
@@ -27,7 +27,6 @@
import static org.mockito.Mockito.anyFloat;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -58,8 +57,8 @@
public static class TestDisplayContent extends DisplayContent {
private TestDisplayContent(Display display, WindowManagerService service,
- WallpaperController wallpaperController, DisplayWindowController controller) {
- super(display, service, wallpaperController, controller);
+ DisplayWindowController controller) {
+ super(display, service, controller);
}
/** Create a mocked default {@link DisplayContent}. */
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index 53858c7..1eb46fb 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -90,7 +90,6 @@
WindowState mChildAppWindowAbove;
WindowState mChildAppWindowBelow;
HashSet<WindowState> mCommonWindows;
- WallpaperController mWallpaperController;
@Rule
public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
@@ -119,8 +118,6 @@
mWm = mWmRule.getWindowManagerService();
beforeCreateDisplay();
- mWallpaperController = new WallpaperController(mWm);
-
context.getDisplay().getDisplayInfo(mDisplayInfo);
mDisplayContent = createNewDisplay();
mWm.mDisplayEnabled = true;
@@ -363,8 +360,7 @@
final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
displayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
synchronized (mWm.mGlobalLock) {
- return new DisplayContent(display, mWm, mWallpaperController,
- mock(DisplayWindowController.class));
+ return new DisplayContent(display, mWm, mock(DisplayWindowController.class));
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index 72d7c90..630a8bf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -47,12 +47,10 @@
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.FileReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
-import java.io.OutputStream;
+import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
@@ -79,15 +77,10 @@
final TaskRecord expected = createTaskRecord(64);
expected.mLastNonFullscreenBounds = new Rect(50, 50, 100, 100);
- final File serializedFile = serializeToFile(expected);
-
- try {
- final TaskRecord actual = restoreFromFile(serializedFile);
- assertEquals(expected.taskId, actual.taskId);
- assertEquals(expected.mLastNonFullscreenBounds, actual.mLastNonFullscreenBounds);
- } finally {
- serializedFile.delete();
- }
+ final byte[] serializedBytes = serializeToBytes(expected);
+ final TaskRecord actual = restoreFromBytes(serializedBytes);
+ assertEquals(expected.taskId, actual.taskId);
+ assertEquals(expected.mLastNonFullscreenBounds, actual.mLastNonFullscreenBounds);
}
@Test
@@ -131,10 +124,8 @@
assertTrue(task.returnsToHomeStack());
}
- private File serializeToFile(TaskRecord r) throws IOException, XmlPullParserException {
- final File tmpFile = File.createTempFile(r.taskId + "_task_", "xml");
-
- try (OutputStream os = new FileOutputStream(tmpFile)) {
+ private byte[] serializeToBytes(TaskRecord r) throws IOException, XmlPullParserException {
+ try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
final XmlSerializer serializer = Xml.newSerializer();
serializer.setOutput(os, "UTF-8");
serializer.startDocument(null, true);
@@ -142,13 +133,14 @@
r.saveToXml(serializer);
serializer.endTag(null, TASK_TAG);
serializer.endDocument();
- }
- return tmpFile;
+ os.flush();
+ return os.toByteArray();
+ }
}
- private TaskRecord restoreFromFile(File file) throws IOException, XmlPullParserException {
- try (Reader reader = new BufferedReader(new FileReader(file))) {
+ private TaskRecord restoreFromBytes(byte[] in) throws IOException, XmlPullParserException {
+ try (Reader reader = new InputStreamReader(new ByteArrayInputStream(in))) {
final XmlPullParser parser = Xml.newPullParser();
parser.setInput(reader);
assertEquals(XmlPullParser.START_TAG, parser.next());
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
index cf34fe7..e56edab 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
@@ -42,8 +42,8 @@
public static class TestDisplayContent extends DisplayContent {
private TestDisplayContent(Display display, WindowManagerService service,
- WallpaperController wallpaperController, DisplayWindowController controller) {
- super(display, service, wallpaperController, controller);
+ DisplayWindowController controller) {
+ super(display, service, controller);
}
/**
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index bcc0e6b..5ad7c30 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1023,9 +1023,9 @@
public static final String KEY_HIDE_ENHANCED_4G_LTE_BOOL = "hide_enhanced_4g_lte_bool";
/**
- * Default Enhanced 4G LTE mode enabled. When this is {@code true}, Enhanced 4G LTE mode by
- * default is on, otherwise if {@code false}, Enhanced 4G LTE mode by default is off.
- * @hide
+ * Sets the default state for the "Enhanced 4G LTE" or "Advanced Calling" mode toggle set by the
+ * user. When this is {@code true}, this mode by default is on, otherwise if {@code false},
+ * this mode by default is off.
*/
public static final String KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL =
"enhanced_4g_lte_on_by_default_bool";
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 8324f00..79ed93e 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -7696,7 +7696,7 @@
try {
return getITelephony().isAvailable(getSubId(),
MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
- ImsRegistrationImplBase.REGISTRATION_TECH_LTE, getOpPackageName());
+ ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
} catch (RemoteException | NullPointerException ex) {
return false;
}
@@ -8557,6 +8557,26 @@
return UNKNOWN_CARRIER_ID;
}
+ /**
+ * Returns carrier id based on MCCMNC only. This is for fallback when exact carrier id
+ * {@link #getSimCarrierId()} configurations are not found
+ *
+ * @return matching carrier id from passing mccmnc.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public int getCarrierIdFromMccMnc(String mccmnc) {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.getCarrierIdFromMccMnc(getSlotIndex(), mccmnc);
+ }
+ } catch (RemoteException ex) {
+ // This could happen if binder process crashes.
+ }
+ return UNKNOWN_CARRIER_ID;
+ }
+
/**
* Return the application ID for the uicc application type like {@link #APPTYPE_CSIM}.
* All uicc applications are uniquely identified by application ID. See ETSI 102.221 and 101.220
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index e06c372..122626f 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -28,17 +28,22 @@
import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.telephony.AccessNetworkConstants;
import android.telephony.SubscriptionManager;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.ITelephony;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.Map;
import java.util.concurrent.Executor;
/**
@@ -52,6 +57,7 @@
* @see #createForSubscriptionId(Context, int)
* @hide
*/
+@SystemApi
public class ImsMmTelManager {
private static final String TAG = "ImsMmTelManager";
@@ -70,16 +76,12 @@
/**
* Register for IMS over IWLAN if WiFi signal quality is high enough. Do not hand over to LTE
* registration if signal quality degrades.
- * @hide
*/
- @SystemApi
public static final int WIFI_MODE_WIFI_ONLY = 0;
/**
* Prefer registering for IMS over LTE if LTE signal quality is high enough.
- * @hide
*/
- @SystemApi
public static final int WIFI_MODE_CELLULAR_PREFERRED = 1;
/**
@@ -91,13 +93,26 @@
/**
* Callback class for receiving Registration callback events.
- * @see #addImsRegistrationCallback(Executor, RegistrationCallback) (RegistrationCallback)
- * @see #removeImsRegistrationCallback(RegistrationCallback)
+ * @see #registerImsRegistrationCallback(Executor, RegistrationCallback) (RegistrationCallback)
+ * @see #unregisterImsRegistrationCallback(RegistrationCallback)
*/
public static class RegistrationCallback {
private static class RegistrationBinder extends IImsRegistrationCallback.Stub {
+ // Translate ImsRegistrationImplBase API to new AccessNetworkConstant because WLAN
+ // and WWAN are more accurate constants.
+ private static final Map<Integer, Integer> IMS_REG_TO_ACCESS_TYPE_MAP =
+ new HashMap<Integer, Integer>() {{
+ // Map NONE to -1 to make sure that we handle the REGISTRATION_TECH_NONE
+ // case, since it is defined.
+ put(ImsRegistrationImplBase.REGISTRATION_TECH_NONE, -1);
+ put(ImsRegistrationImplBase.REGISTRATION_TECH_LTE,
+ AccessNetworkConstants.TransportType.WWAN);
+ put(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
+ AccessNetworkConstants.TransportType.WLAN);
+ }};
+
private final RegistrationCallback mLocalCallback;
private Executor mExecutor;
@@ -109,16 +124,16 @@
public void onRegistered(int imsRadioTech) {
if (mLocalCallback == null) return;
- Binder.withCleanCallingIdentity(() ->
- mExecutor.execute(() -> mLocalCallback.onRegistered(imsRadioTech)));
+ Binder.withCleanCallingIdentity(() -> mExecutor.execute(() ->
+ mLocalCallback.onRegistered(getAccessType(imsRadioTech))));
}
@Override
public void onRegistering(int imsRadioTech) {
if (mLocalCallback == null) return;
- Binder.withCleanCallingIdentity(() ->
- mExecutor.execute(() -> mLocalCallback.onRegistering(imsRadioTech)));
+ Binder.withCleanCallingIdentity(() -> mExecutor.execute(() ->
+ mLocalCallback.onRegistering(getAccessType(imsRadioTech))));
}
@Override
@@ -134,8 +149,8 @@
if (mLocalCallback == null) return;
Binder.withCleanCallingIdentity(() ->
- mExecutor.execute(() ->
- mLocalCallback.onTechnologyChangeFailed(imsRadioTech, info)));
+ mExecutor.execute(() -> mLocalCallback.onTechnologyChangeFailed(
+ getAccessType(imsRadioTech), info)));
}
@Override
@@ -150,6 +165,15 @@
private void setExecutor(Executor executor) {
mExecutor = executor;
}
+
+ private static int getAccessType(int regType) {
+ if (!IMS_REG_TO_ACCESS_TYPE_MAP.containsKey(regType)) {
+ Log.w("ImsMmTelManager", "RegistrationBinder - invalid regType returned: "
+ + regType);
+ return -1;
+ }
+ return IMS_REG_TO_ACCESS_TYPE_MAP.get(regType);
+ }
}
private final RegistrationBinder mBinder = new RegistrationBinder(this);
@@ -157,19 +181,19 @@
/**
* Notifies the framework when the IMS Provider is registered to the IMS network.
*
- * @param imsRadioTech the radio access technology. Valid values are defined in
- * {@link ImsRegistrationImplBase.ImsRegistrationTech}.
+ * @param imsTransportType the radio access technology. Valid values are defined in
+ * {@link android.telephony.AccessNetworkConstants.TransportType}.
*/
- public void onRegistered(@ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) {
+ public void onRegistered(int imsTransportType) {
}
/**
* Notifies the framework when the IMS Provider is trying to register the IMS network.
*
- * @param imsRadioTech the radio access technology. Valid values are defined in
- * {@link ImsRegistrationImplBase.ImsRegistrationTech}.
+ * @param imsTransportType the radio access technology. Valid values are defined in
+ * {@link android.telephony.AccessNetworkConstants.TransportType}.
*/
- public void onRegistering(@ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) {
+ public void onRegistering(int imsTransportType) {
}
/**
@@ -182,14 +206,14 @@
/**
* A failure has occurred when trying to handover registration to another technology type,
- * defined in {@link ImsRegistrationImplBase.ImsRegistrationTech}
+ * defined in {@link android.telephony.AccessNetworkConstants.TransportType}
*
- * @param imsRadioTech The {@link ImsRegistrationImplBase.ImsRegistrationTech} type that has
- * failed
+ * @param imsTransportType The
+ * {@link android.telephony.AccessNetworkConstants.TransportType}
+ * transport type that has failed to handover registration to.
* @param info A {@link ImsReasonInfo} that identifies the reason for failure.
*/
- public void onTechnologyChangeFailed(
- @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech, ImsReasonInfo info) {
+ public void onTechnologyChangeFailed(int imsTransportType, ImsReasonInfo info) {
}
/**
@@ -219,8 +243,8 @@
/**
* Receives IMS capability status updates from the ImsService.
*
- * @see #addMmTelCapabilityCallback(Executor, CapabilityCallback) (CapabilityCallback)
- * @see #removeMmTelCapabilityCallback(CapabilityCallback)
+ * @see #registerMmTelCapabilityCallback(Executor, CapabilityCallback) (CapabilityCallback)
+ * @see #unregisterMmTelCapabilityCallback(CapabilityCallback)
*/
public static class CapabilityCallback {
@@ -285,13 +309,12 @@
}
}
- private Context mContext;
private int mSubId;
/**
* Create an instance of ImsManager for the subscription id specified.
*
- * @param context
+ * @param context The context to create this ImsMmTelManager instance within.
* @param subId The ID of the subscription that this ImsMmTelManager will use.
* @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList()
* @throws IllegalArgumentException if the subscription is invalid or
@@ -303,11 +326,15 @@
throw new IllegalArgumentException("Invalid subscription ID");
}
- return new ImsMmTelManager(context, subId);
+ return new ImsMmTelManager(subId);
}
- private ImsMmTelManager(Context context, int subId) {
- mContext = context;
+ /**
+ * Only visible for testing, use {@link #createForSubscriptionId(Context, int)} instead.
+ * @hide
+ */
+ @VisibleForTesting
+ public ImsMmTelManager(int subId) {
mSubId = subId;
}
@@ -315,14 +342,18 @@
* Registers a {@link RegistrationCallback} with the system, which will provide registration
* updates for the subscription specified in {@link #createForSubscriptionId(Context, int)}. Use
* {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to Subscription changed
- * events and call {@link #removeImsRegistrationCallback(RegistrationCallback)} to clean up
+ * events and call {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up
* after a subscription is removed.
+ *
+ * When the callback is registered, it will initiate the callback c to be called with the
+ * current registration state.
+ *
* @param executor The executor the callback events should be run on.
* @param c The {@link RegistrationCallback} to be added.
- * @see #removeImsRegistrationCallback(RegistrationCallback)
+ * @see #unregisterImsRegistrationCallback(RegistrationCallback)
*/
- @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
- public void addImsRegistrationCallback(@CallbackExecutor Executor executor,
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void registerImsRegistrationCallback(@CallbackExecutor Executor executor,
@NonNull RegistrationCallback c) {
if (c == null) {
throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
@@ -332,8 +363,7 @@
}
c.setExecutor(executor);
try {
- getITelephony().addImsRegistrationCallback(mSubId, c.getBinder(),
- mContext.getOpPackageName());
+ getITelephony().registerImsRegistrationCallback(mSubId, c.getBinder());
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -344,16 +374,15 @@
* up to avoid memory leaks or when the subscription is removed.
* @param c The {@link RegistrationCallback} to be removed.
* @see SubscriptionManager.OnSubscriptionsChangedListener
- * @see #addImsRegistrationCallback(Executor, RegistrationCallback)
+ * @see #registerImsRegistrationCallback(Executor, RegistrationCallback)
*/
- @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
- public void removeImsRegistrationCallback(@NonNull RegistrationCallback c) {
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void unregisterImsRegistrationCallback(@NonNull RegistrationCallback c) {
if (c == null) {
throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
}
try {
- getITelephony().removeImsRegistrationCallback(mSubId, c.getBinder(),
- mContext.getOpPackageName());
+ getITelephony().unregisterImsRegistrationCallback(mSubId, c.getBinder());
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -364,14 +393,18 @@
* updates for the subscription specified in {@link #createForSubscriptionId(Context, int)}.
* Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to
* subscription changed events and call
- * {@link #removeImsRegistrationCallback(RegistrationCallback)} to clean up after a subscription
- * is removed.
+ * {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up after a
+ * subscription is removed.
+ *
+ * When the callback is registered, it will initiate the callback c to be called with the
+ * current capabilities.
+ *
* @param executor The executor the callback events should be run on.
* @param c The MmTel {@link CapabilityCallback} to be registered.
- * @see #removeMmTelCapabilityCallback(CapabilityCallback)
+ * @see #unregisterMmTelCapabilityCallback(CapabilityCallback)
*/
- @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
- public void addMmTelCapabilityCallback(@CallbackExecutor Executor executor,
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void registerMmTelCapabilityCallback(@CallbackExecutor Executor executor,
@NonNull CapabilityCallback c) {
if (c == null) {
throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
@@ -381,8 +414,7 @@
}
c.setExecutor(executor);
try {
- getITelephony().addMmTelCapabilityCallback(mSubId, c.getBinder(),
- mContext.getOpPackageName());
+ getITelephony().registerMmTelCapabilityCallback(mSubId, c.getBinder());
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -392,29 +424,42 @@
* Removes an existing MmTel {@link CapabilityCallback}. Be sure to call this when cleaning
* up to avoid memory leaks.
* @param c The MmTel {@link CapabilityCallback} to be removed.
- * @see #addMmTelCapabilityCallback(Executor, CapabilityCallback)
+ * @see #registerMmTelCapabilityCallback(Executor, CapabilityCallback)
*/
- @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
- public void removeMmTelCapabilityCallback(@NonNull CapabilityCallback c) {
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void unregisterMmTelCapabilityCallback(@NonNull CapabilityCallback c) {
if (c == null) {
throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
}
try {
- getITelephony().removeMmTelCapabilityCallback(mSubId, c.getBinder(),
- mContext.getOpPackageName());
+ getITelephony().unregisterMmTelCapabilityCallback(mSubId, c.getBinder());
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
/**
- * Query the user's setting for whether or not to use MmTel capabilities over IMS,
- * such as voice and video, depending on carrier configuration for the current subscription.
+ * Query the user’s setting for “Advanced Calling” or "Enhanced 4G LTE", which is used to
+ * enable MmTel IMS features, depending on the carrier configuration for the current
+ * subscription. If this setting is enabled, IMS voice and video telephony over IWLAN/LTE will
+ * be enabled as long as the carrier has provisioned these services for the specified
+ * subscription. Other IMS services (SMS/UT) are not affected by this user setting and depend on
+ * carrier requirements.
+ *
+ * Modifying this value may also trigger an IMS registration or deregistration, depending on
+ * whether or not the new value is enabled or disabled.
+ *
+ * Note: If the carrier configuration for advanced calling is not editable or hidden, this
+ * method will do nothing and will instead always use the default value.
+ *
+ * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
+ * @see android.telephony.CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL
+ * @see android.telephony.CarrierConfigManager#KEY_HIDE_ENHANCED_4G_LTE_BOOL
+ * @see android.telephony.CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL
+ * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL
* @see #setAdvancedCallingSetting(boolean)
- * @return true if the user’s setting for advanced calling is enabled and false otherwise.
- * @hide
+ * @return true if the user's setting for advanced calling is enabled, false otherwise.
*/
- @SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isAdvancedCallingSettingEnabled() {
try {
@@ -426,13 +471,25 @@
/**
* Modify the user’s setting for “Advanced Calling” or "Enhanced 4G LTE", which is used to
- * enable MmTel IMS features, such as voice and video calling, depending on the carrier
- * configuration for the current subscription. Modifying this value may also trigger an IMS
- * registration or deregistration, depending on the new value.
- * @see #isAdvancedCallingEnabled()
- * @hide
+ * enable MmTel IMS features, depending on the carrier configuration for the current
+ * subscription. If this setting is enabled, IMS voice and video telephony over IWLAN/LTE will
+ * be enabled as long as the carrier has provisioned these services for the specified
+ * subscription. Other IMS services (SMS/UT) are not affected by this user setting and depend on
+ * carrier requirements.
+ *
+ * Modifying this value may also trigger an IMS registration or deregistration, depending on
+ * whether or not the new value is enabled or disabled.
+ *
+ * Note: If the carrier configuration for advanced calling is not editable or hidden, this
+ * method will do nothing and will instead always use the default value.
+ *
+ * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
+ * @see android.telephony.CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL
+ * @see android.telephony.CarrierConfigManager#KEY_HIDE_ENHANCED_4G_LTE_BOOL
+ * @see android.telephony.CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL
+ * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL
+ * @see #isAdvancedCallingSettingEnabled()
*/
- @SystemApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setAdvancedCallingSetting(boolean isEnabled) {
try {
@@ -464,12 +521,11 @@
* @return {@code true} if the MmTel IMS capability is capable for this subscription, false
* otherwise.
*/
- @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isCapable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
@ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) {
try {
- return getITelephony().isCapable(mSubId, capability, imsRegTech,
- mContext.getOpPackageName());
+ return getITelephony().isCapable(mSubId, capability, imsRegTech);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -492,12 +548,11 @@
* @return {@code true} if the MmTel IMS capability is available for this subscription, false
* otherwise.
*/
- @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isAvailable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
@ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) {
try {
- return getITelephony().isAvailable(mSubId, capability, imsRegTech,
- mContext.getOpPackageName());
+ return getITelephony().isAvailable(mSubId, capability, imsRegTech);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -508,11 +563,10 @@
* @return true if the user’s “Video Calling” setting is currently enabled.
* @see #setVtSetting(boolean)
*/
- @SystemApi
- @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isVtSettingEnabled() {
try {
- return getITelephony().isVtSettingEnabled(mSubId, mContext.getOpPackageName());
+ return getITelephony().isVtSettingEnabled(mSubId);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -521,9 +575,7 @@
/**
* Change the user's setting for Video Telephony and enable the Video Telephony capability.
* @see #isVtSettingEnabled()
- * @hide
*/
- @SystemApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setVtSetting(boolean isEnabled) {
try {
@@ -537,9 +589,7 @@
/**
* @return true if the user's setting for Voice over WiFi is enabled and false if it is not.
* @see #setVoWiFiSetting(boolean)
- * @hide
*/
- @SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isVoWiFiSettingEnabled() {
try {
@@ -553,9 +603,7 @@
* Sets the user's setting for whether or not Voice over WiFi is enabled.
* @param isEnabled true if the user's setting for Voice over WiFi is enabled, false otherwise=
* @see #isVoWiFiSettingEnabled()
- * @hide
*/
- @SystemApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setVoWiFiSetting(boolean isEnabled) {
try {
@@ -570,9 +618,7 @@
* @return true if the user's setting for Voice over WiFi while roaming is enabled, false
* if disabled.
* @see #setVoWiFiRoamingSetting(boolean)
- * @hide
*/
- @SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isVoWiFiRoamingSettingEnabled() {
try {
@@ -587,9 +633,7 @@
* @param isEnabled true if the user's setting for Voice over WiFi while roaming is enabled,
* false otherwise.
* @see #isVoWiFiRoamingSettingEnabled()
- * @hide
*/
- @SystemApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setVoWiFiRoamingSetting(boolean isEnabled) {
try {
@@ -611,9 +655,7 @@
* - {@link #WIFI_MODE_CELLULAR_PREFERRED}
* - {@link #WIFI_MODE_WIFI_PREFERRED}
* @see #setVoWiFiSetting(boolean)
- * @hide
*/
- @SystemApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setVoWiFiNonPersistent(boolean isCapable, int mode) {
try {
@@ -631,9 +673,7 @@
* - {@link #WIFI_MODE_CELLULAR_PREFERRED}
* - {@link #WIFI_MODE_WIFI_PREFERRED}
* @see #setVoWiFiSetting(boolean)
- * @hide
*/
- @SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public @WiFiCallingMode int getVoWiFiModeSetting() {
try {
@@ -651,9 +691,7 @@
* - {@link #WIFI_MODE_CELLULAR_PREFERRED}
* - {@link #WIFI_MODE_WIFI_PREFERRED}
* @see #getVoWiFiModeSetting()
- * @hide
*/
- @SystemApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setVoWiFiModeSetting(@WiFiCallingMode int mode) {
try {
@@ -674,9 +712,7 @@
* - {@link #WIFI_MODE_CELLULAR_PREFERRED}
* - {@link #WIFI_MODE_WIFI_PREFERRED}
* @see #setVoWiFiRoamingSetting(boolean)
- * @hide
*/
- @SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@WiFiCallingMode int getVoWiFiRoamingModeSetting() {
try {
@@ -696,9 +732,7 @@
* - {@link #WIFI_MODE_CELLULAR_PREFERRED}
* - {@link #WIFI_MODE_WIFI_PREFERRED}
* @see #getVoWiFiRoamingModeSetting()
- * @hide
*/
- @SystemApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setVoWiFiRoamingModeSetting(@WiFiCallingMode int mode) {
try {
@@ -712,9 +746,7 @@
/**
* Change the user's setting for RTT capability of this device.
* @param isEnabled if true RTT will be enabled during calls.
- * @hide
*/
- @SystemApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setRttCapabilitySetting(boolean isEnabled) {
try {
@@ -729,9 +761,7 @@
* @return true if TTY over VoLTE is supported
* @see android.telecom.TelecomManager#getCurrentTtyMode
* @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL
- * @hide
*/
- @SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
boolean isTtyOverVolteEnabled() {
try {
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index 3f22f98..b55866b 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -425,6 +425,12 @@
*/
public final void addCapabilityCallback(IImsCapabilityCallback c) {
mCapabilityCallbacks.register(c);
+ try {
+ // Notify the Capability callback that was just registered of the current capabilities.
+ c.onCapabilitiesStatusChanged(queryCapabilityStatus().mCapabilities);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "addCapabilityCallback: error accessing callback: " + e.getMessage());
+ }
}
/**
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index f0e8586..fc42de5 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1372,6 +1372,15 @@
String getSubscriptionPreciseCarrierName(int subId);
/**
+ * Returns carrier id based on MCCMNC only. This will return a MNO carrier id used for fallback
+ * check when exact carrier id {@link #getSimCarrierId()} configurations are not found
+ *
+ * @return carrier id from passing mccmnc.
+ * @hide
+ */
+ int getCarrierIdFromMccMnc(int slotIndex, String mccmnc);
+
+ /**
* Action set from carrier signalling broadcast receivers to enable/disable metered apns
* Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
* @param subId the subscription ID that this action applies to.
@@ -1587,35 +1596,31 @@
/**
* Adds an IMS registration status callback for the subscription id specified.
*/
- void addImsRegistrationCallback(int subId, IImsRegistrationCallback c,
- String callingPackage);
+ void registerImsRegistrationCallback(int subId, IImsRegistrationCallback c);
/**
* Removes an existing IMS registration status callback for the subscription specified.
*/
- void removeImsRegistrationCallback(int subId, IImsRegistrationCallback c,
- String callingPackage);
+ void unregisterImsRegistrationCallback(int subId, IImsRegistrationCallback c);
/**
* Adds an IMS MmTel capabilities callback for the subscription specified.
*/
- void addMmTelCapabilityCallback(int subId, IImsCapabilityCallback c,
- String callingPackage);
+ void registerMmTelCapabilityCallback(int subId, IImsCapabilityCallback c);
/**
* Removes an existing IMS MmTel capabilities callback for the subscription specified.
*/
- void removeMmTelCapabilityCallback(int subId, IImsCapabilityCallback c,
- String callingPackage);
+ void unregisterMmTelCapabilityCallback(int subId, IImsCapabilityCallback c);
/**
* return true if the IMS MmTel capability for the given registration tech is capable.
*/
- boolean isCapable(int subId, int capability, int regTech, String callingPackage);
+ boolean isCapable(int subId, int capability, int regTech);
/**
* return true if the IMS MmTel capability for the given registration tech is available.
*/
- boolean isAvailable(int subId, int capability, int regTech, String callingPackage);
+ boolean isAvailable(int subId, int capability, int regTech);
/**
* Returns true if the user's setting for 4G LTE is enabled, for the subscription specified.
@@ -1630,7 +1635,7 @@
/**
* return true if the user's setting for VT is enabled for the subscription.
*/
- boolean isVtSettingEnabled(int subId, String callingPackage);
+ boolean isVtSettingEnabled(int subId);
/**
* Modify the user's setting for whether or not VT is available for the subscrption specified.
@@ -1704,7 +1709,7 @@
* Identify if the number is emergency number, based on all the active subscriptions.
*/
boolean isCurrentEmergencyNumber(String number);
-
+
/**
* Return a list of certs in hex string from loaded carrier privileges access rules.
*/
diff --git a/test-mock/api/current.txt b/test-mock/api/current.txt
index f91d74a..7842a1c 100644
--- a/test-mock/api/current.txt
+++ b/test-mock/api/current.txt
@@ -135,6 +135,7 @@
method public boolean stopService(android.content.Intent);
method public void unbindService(android.content.ServiceConnection);
method public void unregisterReceiver(android.content.BroadcastReceiver);
+ method public void updateServiceGroup(android.content.ServiceConnection, int, int);
}
public deprecated class MockCursor implements android.database.Cursor {
diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java
index 66be6d9..ae6cd29 100644
--- a/test-mock/src/android/test/mock/MockContext.java
+++ b/test-mock/src/android/test/mock/MockContext.java
@@ -591,6 +591,11 @@
}
@Override
+ public void updateServiceGroup(ServiceConnection conn, int group, int importance) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public void unbindService(ServiceConnection conn) {
throw new UnsupportedOperationException();
}