Merge "Fix typo in debug message."
diff --git a/Android.bp b/Android.bp
index 3b8ef61..acd3b34 100644
--- a/Android.bp
+++ b/Android.bp
@@ -25,6 +25,18 @@
//
// READ ME: ########################################################
+// TODO(b/21090328): Remove filter after we are ready to.
+soong_config_module_type {
+ name: "java_library_with_nonpublic_deps",
+ module_type: "java_library",
+ config_namespace: "ANDROID",
+ bool_variables: ["include_nonpublic_framework_api"],
+ properties: [
+ "static_libs",
+ "libs",
+ ],
+}
+
package {
default_applicable_licenses: ["frameworks_base_license"],
}
@@ -142,7 +154,7 @@
],
}
-java_library {
+java_library_with_nonpublic_deps {
name: "framework-updatable-stubs-module_libs_api",
static_libs: [
"android.net.ipsec.ike.stubs.module_lib",
@@ -161,11 +173,18 @@
"framework-uwb.stubs.module_lib",
"framework-wifi.stubs.module_lib",
],
+ soong_config_variables: {
+ include_nonpublic_framework_api: {
+ static_libs: [
+ "framework-supplementalapi.stubs.module_lib",
+ ],
+ },
+ },
sdk_version: "module_current",
visibility: ["//visibility:private"],
}
-java_library {
+java_library_with_nonpublic_deps {
name: "framework-all",
installable: false,
static_libs: [
@@ -186,6 +205,13 @@
"framework-wifi.impl",
"updatable-media",
],
+ soong_config_variables: {
+ include_nonpublic_framework_api: {
+ static_libs: [
+ "framework-supplementalapi.stubs.module_lib",
+ ],
+ },
+ },
apex_available: ["//apex_available:platform"],
sdk_version: "core_platform",
visibility: [
@@ -362,6 +388,7 @@
"tv_tuner_resource_manager_aidl_interface-java",
"soundtrigger_middleware-aidl-java",
"modules-utils-preconditions",
+ "modules-utils-synchronous-result-receiver",
"modules-utils-os",
"framework-permission-aidl-java",
"spatializer-aidl-java",
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 3b11036..9543fbd 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -23,6 +23,14 @@
// and comparing them against the checked in API signature, and also checking compatibility
// with the latest frozen API signature.
+// TODO(b/21090328): Remove filter after we are ready to.
+soong_config_module_type_import {
+ from: "frameworks/base/Android.bp",
+ module_types: [
+ "java_library_with_nonpublic_deps",
+ ],
+}
+
/////////////////////////////////////////////////////////////////////
// Common metalava configs
/////////////////////////////////////////////////////////////////////
@@ -299,21 +307,35 @@
visibility: ["//visibility:private"],
}
-java_library {
+java_library_with_nonpublic_deps {
name: "android-non-updatable.stubs",
defaults: ["android-non-updatable_defaults_stubs_current"],
srcs: [":api-stubs-docs-non-updatable"],
libs: modules_public_stubs,
+ soong_config_variables: {
+ include_nonpublic_framework_api: {
+ libs: [
+ "framework-supplementalapi.stubs",
+ ],
+ },
+ },
dist: {
dir: "apistubs/android/public",
},
}
-java_library {
+java_library_with_nonpublic_deps {
name: "android-non-updatable.stubs.system",
defaults: ["android-non-updatable_defaults_stubs_current"],
srcs: [":system-api-stubs-docs-non-updatable"],
libs: modules_system_stubs,
+ soong_config_variables: {
+ include_nonpublic_framework_api: {
+ libs: [
+ "framework-supplementalapi.stubs",
+ ],
+ },
+ },
dist: {
dir: "apistubs/android/system",
},
@@ -334,11 +356,18 @@
},
}
-java_library {
+java_library_with_nonpublic_deps {
name: "android-non-updatable.stubs.test",
defaults: ["android-non-updatable_defaults_stubs_current"],
srcs: [":test-api-stubs-docs-non-updatable"],
libs: modules_system_stubs,
+ soong_config_variables: {
+ include_nonpublic_framework_api: {
+ libs: [
+ "framework-supplementalapi.stubs",
+ ],
+ },
+ },
dist: {
dir: "apistubs/android/test",
},
@@ -357,21 +386,35 @@
defaults_visibility: ["//frameworks/base/services"],
}
-java_library {
+java_library_with_nonpublic_deps {
name: "android_stubs_current",
static_libs: modules_public_stubs + [
"android-non-updatable.stubs",
"private-stub-annotations-jar",
],
+ soong_config_variables: {
+ include_nonpublic_framework_api: {
+ static_libs: [
+ "framework-supplementalapi.stubs",
+ ],
+ },
+ },
defaults: ["android.jar_defaults"],
}
-java_library {
+java_library_with_nonpublic_deps {
name: "android_system_stubs_current",
static_libs: modules_system_stubs + [
"android-non-updatable.stubs.system",
"private-stub-annotations-jar",
],
+ soong_config_variables: {
+ include_nonpublic_framework_api: {
+ static_libs: [
+ "framework-supplementalapi.stubs",
+ ],
+ },
+ },
defaults: [
"android.jar_defaults",
"android_stubs_dists_default",
@@ -392,7 +435,7 @@
],
}
-java_library {
+java_library_with_nonpublic_deps {
name: "android_test_stubs_current",
// Modules do not have test APIs, but we want to include their SystemApis, like we include
// the SystemApi of framework-non-updatable-sources.
@@ -400,6 +443,13 @@
"android-non-updatable.stubs.test",
"private-stub-annotations-jar",
],
+ soong_config_variables: {
+ include_nonpublic_framework_api: {
+ static_libs: [
+ "framework-supplementalapi.stubs",
+ ],
+ },
+ },
defaults: [
"android.jar_defaults",
"android_stubs_dists_default",
diff --git a/core/api/current.txt b/core/api/current.txt
index 49689e4..f84322e 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1420,6 +1420,7 @@
field public static final int supportsMultipleDisplays = 16844182; // 0x1010596
field public static final int supportsPictureInPicture = 16844023; // 0x10104f7
field public static final int supportsRtl = 16843695; // 0x10103af
+ field public static final int supportsStylusHandwriting;
field public static final int supportsSwitchingToNextInputMethod = 16843755; // 0x10103eb
field public static final int supportsUploading = 16843419; // 0x101029b
field public static final int suppressesSpellChecker = 16844355; // 0x1010643
@@ -10249,6 +10250,7 @@
method @Nullable public String getPackageName();
method public int getUid();
method public boolean isTrusted(@NonNull android.content.Context);
+ method @NonNull public static android.content.AttributionSource myAttributionSource();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.content.AttributionSource> CREATOR;
}
@@ -11566,6 +11568,7 @@
field public static final String EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME";
field public static final String EXTRA_PERMISSION_GROUP_NAME = "android.intent.extra.PERMISSION_GROUP_NAME";
field public static final String EXTRA_PHONE_NUMBER = "android.intent.extra.PHONE_NUMBER";
+ field public static final String EXTRA_PREVIOUS_UID = "android.intent.extra.PREVIOUS_UID";
field public static final String EXTRA_PROCESS_TEXT = "android.intent.extra.PROCESS_TEXT";
field public static final String EXTRA_PROCESS_TEXT_READONLY = "android.intent.extra.PROCESS_TEXT_READONLY";
field public static final String EXTRA_QUICK_VIEW_FEATURES = "android.intent.extra.QUICK_VIEW_FEATURES";
@@ -11597,6 +11600,7 @@
field public static final String EXTRA_TIMEZONE = "time-zone";
field public static final String EXTRA_TITLE = "android.intent.extra.TITLE";
field public static final String EXTRA_UID = "android.intent.extra.UID";
+ field public static final String EXTRA_UID_CHANGING = "android.intent.extra.UID_CHANGING";
field public static final String EXTRA_USER = "android.intent.extra.USER";
field public static final String EXTRA_USER_INITIATED = "android.intent.extra.USER_INITIATED";
field public static final int FILL_IN_ACTION = 1; // 0x1
@@ -26146,11 +26150,14 @@
field public static final String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
field public static final String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
field public static final String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+ field public static final String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
field public static final String COLUMN_LONG_DESCRIPTION = "long_description";
+ field public static final String COLUMN_MULTI_SERIES_ID = "multi_series_id";
field public static final String COLUMN_POSTER_ART_URI = "poster_art_uri";
field public static final String COLUMN_RECORDING_PROHIBITED = "recording_prohibited";
field public static final String COLUMN_REVIEW_RATING = "review_rating";
field public static final String COLUMN_REVIEW_RATING_STYLE = "review_rating_style";
+ field public static final String COLUMN_SCRAMBLED = "scrambled";
field public static final String COLUMN_SEARCHABLE = "searchable";
field public static final String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number";
field @Deprecated public static final String COLUMN_SEASON_NUMBER = "season_number";
@@ -26210,7 +26217,9 @@
field public static final String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
field public static final String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
field public static final String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+ field public static final String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
field public static final String COLUMN_LONG_DESCRIPTION = "long_description";
+ field public static final String COLUMN_MULTI_SERIES_ID = "multi_series_id";
field public static final String COLUMN_POSTER_ART_URI = "poster_art_uri";
field public static final String COLUMN_RECORDING_DATA_BYTES = "recording_data_bytes";
field public static final String COLUMN_RECORDING_DATA_URI = "recording_data_uri";
@@ -41469,6 +41478,7 @@
field public static final String KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY = "cdma_nonroaming_networks_string_array";
field public static final String KEY_CDMA_ROAMING_MODE_INT = "cdma_roaming_mode_int";
field public static final String KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY = "cdma_roaming_networks_string_array";
+ field public static final String KEY_CELLULAR_USAGE_SETTING_INT = "cellular_usage_setting_int";
field public static final String KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL = "check_pricing_with_carrier_data_roaming_bool";
field public static final String KEY_CI_ACTION_ON_SYS_UPDATE_BOOL = "ci_action_on_sys_update_bool";
field public static final String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING = "ci_action_on_sys_update_extra_string";
@@ -43085,6 +43095,7 @@
method public int getSimSlotIndex();
method public int getSubscriptionId();
method public int getSubscriptionType();
+ method public int getUsageSetting();
method public boolean isEmbedded();
method public boolean isOpportunistic();
method public void writeToParcel(android.os.Parcel, int);
@@ -43159,6 +43170,10 @@
field public static final int PHONE_NUMBER_SOURCE_UICC = 1; // 0x1
field public static final int SUBSCRIPTION_TYPE_LOCAL_SIM = 0; // 0x0
field public static final int SUBSCRIPTION_TYPE_REMOTE_SIM = 1; // 0x1
+ field public static final int USAGE_SETTING_DATA_CENTRIC = 2; // 0x2
+ field public static final int USAGE_SETTING_DEFAULT = 0; // 0x0
+ field public static final int USAGE_SETTING_UNKNOWN = -1; // 0xffffffff
+ field public static final int USAGE_SETTING_VOICE_CENTRIC = 1; // 0x1
}
public static class SubscriptionManager.OnOpportunisticSubscriptionsChangedListener {
@@ -52852,6 +52867,7 @@
method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager);
method public CharSequence loadLabel(android.content.pm.PackageManager);
method public boolean shouldShowInInputMethodPicker();
+ method public boolean supportsStylusHandwriting();
method public boolean suppressesSpellChecker();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.InputMethodInfo> CREATOR;
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index c78e50f..99e47b1 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -311,6 +311,7 @@
field public static final String TV_VIRTUAL_REMOTE_CONTROLLER = "android.permission.TV_VIRTUAL_REMOTE_CONTROLLER";
field public static final String UNLIMITED_SHORTCUTS_API_CALLS = "android.permission.UNLIMITED_SHORTCUTS_API_CALLS";
field public static final String UPDATE_APP_OPS_STATS = "android.permission.UPDATE_APP_OPS_STATS";
+ field public static final String UPDATE_DEVICE_MANAGEMENT_RESOURCES = "android.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES";
field public static final String UPDATE_DOMAIN_VERIFICATION_USER_SELECTION = "android.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION";
field public static final String UPDATE_FONTS = "android.permission.UPDATE_FONTS";
field public static final String UPDATE_LOCK = "android.permission.UPDATE_LOCK";
@@ -9302,6 +9303,7 @@
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setUserIcon(@NonNull android.graphics.Bitmap) throws android.os.UserManager.UserOperationException;
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setUserName(@Nullable String);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean someUserHasAccount(@NonNull String, @NonNull String);
+ field @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public static final String ACTION_CREATE_SUPERVISED_USER = "android.os.action.CREATE_SUPERVISED_USER";
field public static final String ACTION_USER_RESTRICTIONS_CHANGED = "android.os.action.USER_RESTRICTIONS_CHANGED";
field @Deprecated public static final String DISALLOW_OEM_UNLOCK = "no_oem_unlock";
field public static final String DISALLOW_RUN_IN_BACKGROUND = "no_run_in_background";
@@ -12616,6 +12618,7 @@
}
public class TelephonyManager {
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void addCarrierPrivilegesListener(int, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CarrierPrivilegesListener);
method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) @WorkerThread public void bootstrapAuthenticationRequest(int, @NonNull android.net.Uri, @NonNull android.telephony.gba.UaSecurityProtocolIdentifier, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.BootstrapAuthenticationCallback);
method @Deprecated @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void call(String, String);
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.PinResult changeIccLockPin(@NonNull String, @NonNull String);
@@ -12717,6 +12720,7 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyOtaEmergencyNumberDbInstalled();
method @RequiresPermission(android.Manifest.permission.REBOOT) public int prepareForUnattendedReboot();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean rebootRadio();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void removeCarrierPrivilegesListener(@NonNull android.telephony.TelephonyManager.CarrierPrivilegesListener);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void reportDefaultNetworkStatus(boolean);
method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull android.os.WorkSource, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestModemActivityInfo(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.ModemActivityInfo,android.telephony.TelephonyManager.ModemActivityInfoException>);
@@ -12893,6 +12897,10 @@
field public static final int RESULT_SUCCESS = 0; // 0x0
}
+ public static interface TelephonyManager.CarrierPrivilegesListener {
+ method public void onCarrierPrivilegesChanged(@NonNull java.util.List<java.lang.String>, @NonNull int[]);
+ }
+
public static class TelephonyManager.ModemActivityInfoException extends java.lang.Exception {
method public int getErrorCode();
field public static final int ERROR_INVALID_INFO_RECEIVED = 2; // 0x2
@@ -13140,6 +13148,7 @@
method public final int getSlotIndex();
method public final void notifyApnUnthrottled(@NonNull String);
method public final void notifyDataCallListChanged(java.util.List<android.telephony.data.DataCallResponse>);
+ method public final void notifyDataProfileUnthrottled(@NonNull android.telephony.data.DataProfile);
method public void requestDataCallList(@NonNull android.telephony.data.DataServiceCallback);
method public void setDataProfile(@NonNull java.util.List<android.telephony.data.DataProfile>, boolean, @NonNull android.telephony.data.DataServiceCallback);
method public void setInitialAttachApn(@NonNull android.telephony.data.DataProfile, boolean, @NonNull android.telephony.data.DataServiceCallback);
@@ -13150,6 +13159,7 @@
public class DataServiceCallback {
method public void onApnUnthrottled(@NonNull String);
method public void onDataCallListChanged(@NonNull java.util.List<android.telephony.data.DataCallResponse>);
+ method public void onDataProfileUnthrottled(@NonNull android.telephony.data.DataProfile);
method public void onDeactivateDataCallComplete(int);
method public void onRequestDataCallListComplete(int, @NonNull java.util.List<android.telephony.data.DataCallResponse>);
method public void onSetDataProfileComplete(int);
diff --git a/core/java/android/app/cloudsearch/OWNERS b/core/java/android/app/cloudsearch/OWNERS
new file mode 100644
index 0000000..aa4da3b
--- /dev/null
+++ b/core/java/android/app/cloudsearch/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 758286
+
+huiwu@google.com
+srazdan@google.com
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index d66dc63..8b9cec1 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -30,17 +32,19 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.ParcelUuid;
import android.os.RemoteException;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
@@ -271,7 +275,7 @@
IBluetoothA2dp.class.getName()) {
@Override
public IBluetoothA2dp getServiceInterface(IBinder service) {
- return IBluetoothA2dp.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothA2dp.Stub.asInterface(service);
}
};
@@ -322,17 +326,21 @@
@UnsupportedAppUsage
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- return service.connect(device);
+ final IBluetoothA2dp service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connectWithAttribution(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -364,17 +372,21 @@
@UnsupportedAppUsage
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- return service.disconnect(device);
+ final IBluetoothA2dp service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnectWithAttribution(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -385,19 +397,24 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()) {
+ final IBluetoothA2dp service = getService();
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevicesWithAttribution(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevicesWithAttribution(mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
}
+ return defaultValue;
}
/**
@@ -408,20 +425,25 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()) {
+ final IBluetoothA2dp service = getService();
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStatesWithAttribution(states,
+ mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStatesWithAttribution(states,
- mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
}
+ return defaultValue;
}
/**
@@ -432,18 +454,21 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @BtProfileState int getConnectionState(BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()
- && isValidDevice(device)) {
- return service.getConnectionState(device);
+ final IBluetoothA2dp service = getService();
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionStateWithAttribution(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
}
+ return defaultValue;
}
/**
@@ -471,18 +496,21 @@
@UnsupportedAppUsage(trackingBug = 171933273)
public boolean setActiveDevice(@Nullable BluetoothDevice device) {
if (DBG) log("setActiveDevice(" + device + ")");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()
- && ((device == null) || isValidDevice(device))) {
- return service.setActiveDevice(device, mAttributionSource);
+ final IBluetoothA2dp service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && ((device == null) || isValidDevice(device))) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setActiveDevice(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -499,18 +527,24 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothDevice getActiveDevice() {
if (VDBG) log("getActiveDevice()");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()) {
+ final IBluetoothA2dp service = getService();
+ final BluetoothDevice defaultValue = null;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<BluetoothDevice> recv =
+ new SynchronousResultReceiver();
+ service.getActiveDevice(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getActiveDevice(mAttributionSource), mAttributionSource);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return null;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return null;
}
+ return defaultValue;
}
/**
@@ -555,22 +589,23 @@
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()
- && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
+ final IBluetoothA2dp service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -589,19 +624,7 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()
- && isValidDevice(device)) {
- return BluetoothAdapter.connectionPolicyToPriority(
- service.getPriority(device, mAttributionSource));
- }
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.PRIORITY_OFF;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.PRIORITY_OFF;
- }
+ return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
}
/**
@@ -623,18 +646,21 @@
})
public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()
- && isValidDevice(device)) {
- return service.getConnectionPolicy(device, mAttributionSource);
+ final IBluetoothA2dp service = getService();
+ final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
}
+ return defaultValue;
}
/**
@@ -646,17 +672,21 @@
@RequiresNoPermission
public boolean isAvrcpAbsoluteVolumeSupported() {
if (DBG) Log.d(TAG, "isAvrcpAbsoluteVolumeSupported");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()) {
- return service.isAvrcpAbsoluteVolumeSupported();
+ final IBluetoothA2dp service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.isAvrcpAbsoluteVolumeSupported(recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Error talking to BT service in isAvrcpAbsoluteVolumeSupported()", e);
- return false;
}
+ return defaultValue;
}
/**
@@ -669,14 +699,16 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void setAvrcpAbsoluteVolume(int volume) {
if (DBG) Log.d(TAG, "setAvrcpAbsoluteVolume");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()) {
+ final IBluetoothA2dp service = getService();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
service.setAvrcpAbsoluteVolume(volume, mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- } catch (RemoteException e) {
- Log.e(TAG, "Error talking to BT service in setAvrcpAbsoluteVolume()", e);
}
}
@@ -689,18 +721,22 @@
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean isA2dpPlaying(BluetoothDevice device) {
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()
- && isValidDevice(device)) {
- return service.isA2dpPlaying(device, mAttributionSource);
+ if (DBG) log("isA2dpPlaying(" + device + ")");
+ final IBluetoothA2dp service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.isA2dpPlaying(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -729,8 +765,7 @@
/**
* Gets the current codec status (configuration and capability).
*
- * @param device the remote Bluetooth device. If null, use the current
- * active A2DP Bluetooth device.
+ * @param device the remote Bluetooth device.
* @return the current codec status
* @hide
*/
@@ -742,26 +777,28 @@
public BluetoothCodecStatus getCodecStatus(@NonNull BluetoothDevice device) {
if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")");
verifyDeviceNotNull(device, "getCodecStatus");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()) {
- return service.getCodecStatus(device, mAttributionSource);
+ final IBluetoothA2dp service = getService();
+ final BluetoothCodecStatus defaultValue = null;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<BluetoothCodecStatus> recv =
+ new SynchronousResultReceiver();
+ service.getCodecStatus(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- }
- return null;
- } catch (RemoteException e) {
- Log.e(TAG, "Error talking to BT service in getCodecStatus()", e);
- return null;
}
+ return defaultValue;
}
/**
* Sets the codec configuration preference.
*
- * @param device the remote Bluetooth device. If null, use the current
- * active A2DP Bluetooth device.
+ * @param device the remote Bluetooth device.
* @param codecConfig the codec configuration preference
* @hide
*/
@@ -777,24 +814,23 @@
Log.e(TAG, "setCodecConfigPreference: Codec config can't be null");
throw new IllegalArgumentException("codecConfig cannot be null");
}
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()) {
+ final IBluetoothA2dp service = getService();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
service.setCodecConfigPreference(device, codecConfig, mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return;
- } catch (RemoteException e) {
- Log.e(TAG, "Error talking to BT service in setCodecConfigPreference()", e);
- return;
}
}
/**
* Enables the optional codecs.
*
- * @param device the remote Bluetooth device. If null, use the currect
- * active A2DP Bluetooth device.
+ * @param device the remote Bluetooth device.
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -810,8 +846,7 @@
/**
* Disables the optional codecs.
*
- * @param device the remote Bluetooth device. If null, use the currect
- * active A2DP Bluetooth device.
+ * @param device the remote Bluetooth device.
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -827,26 +862,25 @@
/**
* Enables or disables the optional codecs.
*
- * @param device the remote Bluetooth device. If null, use the currect
- * active A2DP Bluetooth device.
+ * @param device the remote Bluetooth device.
* @param enable if true, enable the optional codecs, other disable them
*/
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
private void enableDisableOptionalCodecs(BluetoothDevice device, boolean enable) {
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()) {
+ final IBluetoothA2dp service = getService();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
if (enable) {
service.enableOptionalCodecs(device, mAttributionSource);
} else {
service.disableOptionalCodecs(device, mAttributionSource);
}
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return;
- } catch (RemoteException e) {
- Log.e(TAG, "Error talking to BT service in enableDisableOptionalCodecs()", e);
- return;
}
}
@@ -864,18 +898,23 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@OptionalCodecsSupportStatus
public int isOptionalCodecsSupported(@NonNull BluetoothDevice device) {
+ if (DBG) log("isOptionalCodecsSupported(" + device + ")");
verifyDeviceNotNull(device, "isOptionalCodecsSupported");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- return service.supportsOptionalCodecs(device, mAttributionSource);
+ final IBluetoothA2dp service = getService();
+ final int defaultValue = OPTIONAL_CODECS_SUPPORT_UNKNOWN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.supportsOptionalCodecs(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return OPTIONAL_CODECS_SUPPORT_UNKNOWN;
- } catch (RemoteException e) {
- Log.e(TAG, "Error talking to BT service in supportsOptionalCodecs()", e);
- return OPTIONAL_CODECS_SUPPORT_UNKNOWN;
}
+ return defaultValue;
}
/**
@@ -892,18 +931,23 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@OptionalCodecsPreferenceStatus
public int isOptionalCodecsEnabled(@NonNull BluetoothDevice device) {
+ if (DBG) log("isOptionalCodecsEnabled(" + device + ")");
verifyDeviceNotNull(device, "isOptionalCodecsEnabled");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- return service.getOptionalCodecsEnabled(device, mAttributionSource);
+ final IBluetoothA2dp service = getService();
+ final int defaultValue = OPTIONAL_CODECS_PREF_UNKNOWN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getOptionalCodecsEnabled(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return OPTIONAL_CODECS_PREF_UNKNOWN;
- } catch (RemoteException e) {
- Log.e(TAG, "Error talking to BT service in getOptionalCodecsEnabled()", e);
- return OPTIONAL_CODECS_PREF_UNKNOWN;
}
+ return defaultValue;
}
/**
@@ -921,24 +965,24 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void setOptionalCodecsEnabled(@NonNull BluetoothDevice device,
@OptionalCodecsPreferenceStatus int value) {
+ if (DBG) log("setOptionalCodecsEnabled(" + device + ")");
verifyDeviceNotNull(device, "setOptionalCodecsEnabled");
- try {
- if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN
- && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED
- && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) {
- Log.e(TAG, "Invalid value passed to setOptionalCodecsEnabled: " + value);
- return;
- }
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()
- && isValidDevice(device)) {
+ if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN
+ && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED
+ && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) {
+ Log.e(TAG, "Invalid value passed to setOptionalCodecsEnabled: " + value);
+ return;
+ }
+ final IBluetoothA2dp service = getService();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
service.setOptionalCodecsEnabled(device, value, mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return;
}
}
@@ -961,17 +1005,21 @@
})
public @Type int getDynamicBufferSupport() {
if (VDBG) log("getDynamicBufferSupport()");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()) {
- return service.getDynamicBufferSupport(mAttributionSource);
+ final IBluetoothA2dp service = getService();
+ final int defaultValue = DYNAMIC_BUFFER_SUPPORT_NONE;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getDynamicBufferSupport(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return DYNAMIC_BUFFER_SUPPORT_NONE;
- } catch (RemoteException e) {
- Log.e(TAG, "failed to get getDynamicBufferSupport, error: ", e);
- return DYNAMIC_BUFFER_SUPPORT_NONE;
}
+ return defaultValue;
}
/**
@@ -992,17 +1040,22 @@
})
public @Nullable BufferConstraints getBufferConstraints() {
if (VDBG) log("getBufferConstraints()");
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()) {
- return service.getBufferConstraints(mAttributionSource);
+ final IBluetoothA2dp service = getService();
+ final BufferConstraints defaultValue = null;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<BufferConstraints> recv =
+ new SynchronousResultReceiver();
+ service.getBufferConstraints(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return null;
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- return null;
}
+ return defaultValue;
}
/**
@@ -1027,17 +1080,21 @@
Log.e(TAG, "Trying to set audio buffer length to a negative value: " + value);
return false;
}
- try {
- final IBluetoothA2dp service = getService();
- if (service != null && isEnabled()) {
- return service.setBufferLengthMillis(codec, value, mAttributionSource);
+ final IBluetoothA2dp service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setBufferLengthMillis(codec, value, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- return false;
}
+ return defaultValue;
}
/**
diff --git a/core/java/android/bluetooth/BluetoothA2dpSink.java b/core/java/android/bluetooth/BluetoothA2dpSink.java
index 924dc55..5941681 100755
--- a/core/java/android/bluetooth/BluetoothA2dpSink.java
+++ b/core/java/android/bluetooth/BluetoothA2dpSink.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
@@ -29,14 +31,16 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
* This class provides the public APIs to control the Bluetooth A2DP Sink
@@ -86,7 +90,7 @@
"BluetoothA2dpSink", IBluetoothA2dpSink.class.getName()) {
@Override
public IBluetoothA2dpSink getServiceInterface(IBinder service) {
- return IBluetoothA2dpSink.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothA2dpSink.Stub.asInterface(service);
}
};
@@ -140,16 +144,20 @@
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
final IBluetoothA2dpSink service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.connect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -181,16 +189,20 @@
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
final IBluetoothA2dpSink service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.disconnect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -204,17 +216,23 @@
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
final IBluetoothA2dpSink service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -228,18 +246,23 @@
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
final IBluetoothA2dpSink service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -251,18 +274,22 @@
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getConnectionState(BluetoothDevice device) {
- if (VDBG) log("getState(" + device + ")");
+ if (VDBG) log("getConnectionState(" + device + ")");
final IBluetoothA2dpSink service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -282,16 +309,21 @@
public BluetoothAudioConfig getAudioConfig(BluetoothDevice device) {
if (VDBG) log("getAudioConfig(" + device + ")");
final IBluetoothA2dpSink service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final BluetoothAudioConfig defaultValue = null;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getAudioConfig(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return null;
+ final SynchronousResultReceiver<BluetoothAudioConfig> recv =
+ new SynchronousResultReceiver();
+ service.getAudioConfig(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return null;
+ return defaultValue;
}
/**
@@ -337,20 +369,22 @@
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
final IBluetoothA2dpSink service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
try {
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -393,16 +427,20 @@
public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothA2dpSink service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionPolicy(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return defaultValue;
}
/**
@@ -420,17 +458,22 @@
android.Manifest.permission.BLUETOOTH_PRIVILEGED,
})
public boolean isAudioPlaying(@NonNull BluetoothDevice device) {
+ if (VDBG) log("isAudioPlaying(" + device + ")");
final IBluetoothA2dpSink service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.isA2dpPlaying(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.isA2dpPlaying(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 4297512..2d1ecfb 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -797,7 +797,7 @@
@RequiresNoPermission
public static synchronized BluetoothAdapter getDefaultAdapter() {
if (sAdapter == null) {
- sAdapter = createAdapter(BluetoothManager.resolveAttributionSource(null));
+ sAdapter = createAdapter(AttributionSource.myAttributionSource());
}
return sAdapter;
}
diff --git a/core/java/android/bluetooth/BluetoothAvrcpController.java b/core/java/android/bluetooth/BluetoothAvrcpController.java
index 536dfb0..81fc3e1 100644
--- a/core/java/android/bluetooth/BluetoothAvrcpController.java
+++ b/core/java/android/bluetooth/BluetoothAvrcpController.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
@@ -23,13 +25,15 @@
import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
* This class provides the public APIs to control the Bluetooth AVRCP Controller. It currently
@@ -93,8 +97,7 @@
"BluetoothAvrcpController", IBluetoothAvrcpController.class.getName()) {
@Override
public IBluetoothAvrcpController getServiceInterface(IBinder service) {
- return IBluetoothAvrcpController.Stub.asInterface(
- Binder.allowBlocking(service));
+ return IBluetoothAvrcpController.Stub.asInterface(service);
}
};
@@ -130,19 +133,24 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
- final IBluetoothAvrcpController service =
- getService();
- if (service != null && isEnabled()) {
+ final IBluetoothAvrcpController service = getService();
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -153,20 +161,24 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
- final IBluetoothAvrcpController service =
- getService();
- if (service != null && isEnabled()) {
+ final IBluetoothAvrcpController service = getService();
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -177,18 +189,21 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getConnectionState(BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
- final IBluetoothAvrcpController service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothAvrcpController service = getService();
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -201,17 +216,22 @@
public BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) {
if (DBG) Log.d(TAG, "getPlayerSettings");
BluetoothAvrcpPlayerSettings settings = null;
- final IBluetoothAvrcpController service =
- getService();
- if (service != null && isEnabled()) {
+ final IBluetoothAvrcpController service = getService();
+ final BluetoothAvrcpPlayerSettings defaultValue = null;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- settings = service.getPlayerSettings(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Error talking to BT service in getMetadata() " + e);
- return null;
+ final SynchronousResultReceiver<BluetoothAvrcpPlayerSettings> recv =
+ new SynchronousResultReceiver();
+ service.getPlayerSettings(device, mAttributionSource, recv);
+ settings = recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- return settings;
+ return defaultValue;
}
/**
@@ -222,18 +242,21 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) {
if (DBG) Log.d(TAG, "setPlayerApplicationSetting");
- final IBluetoothAvrcpController service =
- getService();
- if (service != null && isEnabled()) {
+ final IBluetoothAvrcpController service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- return service.setPlayerApplicationSetting(plAppSetting, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Error talking to BT service in setPlayerApplicationSetting() " + e);
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setPlayerApplicationSetting(plAppSetting, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -245,18 +268,20 @@
public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) {
Log.d(TAG, "sendGroupNavigationCmd dev = " + device + " key " + keyCode + " State = "
+ keyState);
- final IBluetoothAvrcpController service =
- getService();
- if (service != null && isEnabled()) {
+ final IBluetoothAvrcpController service = getService();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- service.sendGroupNavigationCmd(device, keyCode, keyState, mAttributionSource);
+ final SynchronousResultReceiver recv = new SynchronousResultReceiver();
+ service.sendGroupNavigationCmd(device, keyCode, keyState, mAttributionSource, recv);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
return;
- } catch (RemoteException e) {
- Log.e(TAG, "Error talking to BT service in sendGroupNavigationCmd()", e);
- return;
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
}
private boolean isEnabled() {
diff --git a/core/java/android/bluetooth/BluetoothCsipSetCoordinator.java b/core/java/android/bluetooth/BluetoothCsipSetCoordinator.java
index f0a8df0..ba57ec4 100644
--- a/core/java/android/bluetooth/BluetoothCsipSetCoordinator.java
+++ b/core/java/android/bluetooth/BluetoothCsipSetCoordinator.java
@@ -17,6 +17,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
@@ -27,13 +29,14 @@
import android.annotation.SystemApi;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.IBinder;
import android.os.ParcelUuid;
import android.os.RemoteException;
import android.util.CloseGuard;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -41,6 +44,7 @@
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Executor;
+import java.util.concurrent.TimeoutException;
/**
* This class provides the public APIs to control the Bluetooth CSIP set coordinator.
@@ -229,8 +233,7 @@
IBluetoothCsipSetCoordinator.class.getName()) {
@Override
public IBluetoothCsipSetCoordinator getServiceInterface(IBinder service) {
- return IBluetoothCsipSetCoordinator.Stub.asInterface(
- Binder.allowBlocking(service));
+ return IBluetoothCsipSetCoordinator.Stub.asInterface(service);
}
};
@@ -283,26 +286,27 @@
public
@Nullable UUID groupLock(int groupId, @Nullable @CallbackExecutor Executor executor,
@Nullable ClientLockCallback cb) {
- if (VDBG) {
- log("groupLockSet()");
- }
+ if (VDBG) log("groupLockSet()");
final IBluetoothCsipSetCoordinator service = getService();
- try {
- if (service != null && isEnabled()) {
- IBluetoothCsipSetCoordinatorLockCallback delegate = null;
- if ((executor != null) && (cb != null)) {
- delegate = new BluetoothCsipSetCoordinatorLockCallbackDelegate(executor, cb);
- }
- return service.groupLock(groupId, delegate, mAttributionSource).getUuid();
+ final UUID defaultValue = null;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ IBluetoothCsipSetCoordinatorLockCallback delegate = null;
+ if ((executor != null) && (cb != null)) {
+ delegate = new BluetoothCsipSetCoordinatorLockCallbackDelegate(executor, cb);
}
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
+ try {
+ final SynchronousResultReceiver<ParcelUuid> recv = new SynchronousResultReceiver();
+ service.groupLock(groupId, delegate, mAttributionSource, recv);
+ final ParcelUuid ret = recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
+ return ret == null ? defaultValue : ret.getUuid();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- return null;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return null;
}
+ return defaultValue;
}
/**
@@ -315,27 +319,26 @@
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
public boolean groupUnlock(@NonNull UUID lockUuid) {
- if (VDBG) {
- log("groupLockSet()");
- }
+ if (VDBG) log("groupLockSet()");
if (lockUuid == null) {
return false;
}
-
final IBluetoothCsipSetCoordinator service = getService();
- try {
- if (service != null && isEnabled()) {
- service.groupUnlock(new ParcelUuid(lockUuid), mAttributionSource);
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver recv = new SynchronousResultReceiver();
+ service.groupUnlock(new ParcelUuid(lockUuid), mAttributionSource, recv);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
return true;
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- }
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -348,22 +351,22 @@
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
public @NonNull Map getGroupUuidMapByDevice(@Nullable BluetoothDevice device) {
- if (VDBG) {
- log("getGroupUuidMapByDevice()");
- }
+ if (VDBG) log("getGroupUuidMapByDevice()");
final IBluetoothCsipSetCoordinator service = getService();
- try {
- if (service != null && isEnabled()) {
- return service.getGroupUuidMapByDevice(device, mAttributionSource);
+ final Map defaultValue = new HashMap<>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Map> recv = new SynchronousResultReceiver();
+ service.getGroupUuidMapByDevice(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- }
- return new HashMap<>();
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new HashMap<>();
}
+ return defaultValue;
}
/**
@@ -376,22 +379,23 @@
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
public @NonNull List<Integer> getAllGroupIds(@Nullable ParcelUuid uuid) {
- if (VDBG) {
- log("getAllGroupIds()");
- }
+ if (VDBG) log("getAllGroupIds()");
final IBluetoothCsipSetCoordinator service = getService();
- try {
- if (service != null && isEnabled()) {
- return service.getAllGroupIds(uuid, mAttributionSource);
+ final List<Integer> defaultValue = new ArrayList<>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<List<Integer>> recv =
+ new SynchronousResultReceiver();
+ service.getAllGroupIds(uuid, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- }
- return new ArrayList<Integer>();
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<Integer>();
}
+ return defaultValue;
}
/**
@@ -399,22 +403,23 @@
*/
@Override
public @NonNull List<BluetoothDevice> getConnectedDevices() {
- if (VDBG) {
- log("getConnectedDevices()");
- }
+ if (VDBG) log("getConnectedDevices()");
final IBluetoothCsipSetCoordinator service = getService();
- if (service != null && isEnabled()) {
- try {
- return service.getConnectedDevices(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
- }
- }
+ final List<BluetoothDevice> defaultValue = new ArrayList<>();
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -422,24 +427,24 @@
*/
@Override
public
- @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(
- @NonNull int[] states) {
- if (VDBG) {
- log("getDevicesMatchingStates(states=" + Arrays.toString(states) + ")");
- }
+ @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[] states) {
+ if (VDBG) log("getDevicesMatchingStates(states=" + Arrays.toString(states) + ")");
final IBluetoothCsipSetCoordinator service = getService();
- if (service != null && isEnabled()) {
- try {
- return service.getDevicesMatchingConnectionStates(states, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
- }
- }
+ final List<BluetoothDevice> defaultValue = new ArrayList<>();
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -447,24 +452,23 @@
*/
@Override
public
- @BluetoothProfile.BtProfileState int getConnectionState(
- @Nullable BluetoothDevice device) {
- if (VDBG) {
- log("getState(" + device + ")");
- }
+ @BluetoothProfile.BtProfileState int getConnectionState(@Nullable BluetoothDevice device) {
+ if (VDBG) log("getState(" + device + ")");
final IBluetoothCsipSetCoordinator service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- try {
- return service.getConnectionState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
- }
- }
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return BluetoothProfile.STATE_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -484,26 +488,24 @@
@RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
public boolean setConnectionPolicy(
@Nullable BluetoothDevice device, @ConnectionPolicy int connectionPolicy) {
- if (DBG) {
- log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
- }
+ if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
final IBluetoothCsipSetCoordinator service = getService();
- try {
- if (service != null && isEnabled() && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- }
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -521,22 +523,22 @@
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) {
- if (VDBG) {
- log("getConnectionPolicy(" + device + ")");
- }
+ if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothCsipSetCoordinator service = getService();
- try {
- if (service != null && isEnabled() && isValidDevice(device)) {
- return service.getConnectionPolicy(device, mAttributionSource);
+ final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- }
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
}
+ return defaultValue;
}
private boolean isEnabled() {
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 9ff4dc3..93f0268 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1177,7 +1177,7 @@
mAddress = address;
mAddressType = ADDRESS_TYPE_PUBLIC;
- mAttributionSource = BluetoothManager.resolveAttributionSource(null);
+ mAttributionSource = AttributionSource.myAttributionSource();
}
/** {@hide} */
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 17c02cd..f2a6276 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -31,7 +33,6 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
-import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
@@ -41,8 +42,11 @@
import android.util.CloseGuard;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
* Public API for controlling the Bluetooth Headset Service. This includes both
@@ -479,16 +483,20 @@
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.connect(device);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connectWithAttribution(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -520,16 +528,20 @@
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.disconnect(device);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnectWithAttribution(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -541,18 +553,23 @@
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevicesWithAttribution(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevicesWithAttribution(mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -564,18 +581,23 @@
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -587,16 +609,20 @@
public int getConnectionState(BluetoothDevice device) {
if (VDBG) log("getConnectionState(" + device + ")");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionStateWithAttribution(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -622,20 +648,22 @@
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
try {
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -655,18 +683,7 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
- final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
- try {
- return BluetoothAdapter.connectionPolicyToPriority(
- service.getPriority(device, mAttributionSource));
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.PRIORITY_OFF;
- }
- }
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.PRIORITY_OFF;
+ return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
}
/**
@@ -689,16 +706,20 @@
public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionPolicy(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return defaultValue;
}
/**
@@ -713,15 +734,20 @@
public boolean isNoiseReductionSupported(@NonNull BluetoothDevice device) {
if (DBG) log("isNoiseReductionSupported()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.isNoiseReductionSupported(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.isNoiseReductionSupported(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -736,15 +762,20 @@
public boolean isVoiceRecognitionSupported(@NonNull BluetoothDevice device) {
if (DBG) log("isVoiceRecognitionSupported()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.isVoiceRecognitionSupported(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.isVoiceRecognitionSupported(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -775,15 +806,20 @@
public boolean startVoiceRecognition(BluetoothDevice device) {
if (DBG) log("startVoiceRecognition()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.startVoiceRecognition(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.startVoiceRecognition(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -804,15 +840,20 @@
public boolean stopVoiceRecognition(BluetoothDevice device) {
if (DBG) log("stopVoiceRecognition()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.stopVoiceRecognition(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.stopVoiceRecognition(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -827,15 +868,20 @@
public boolean isAudioConnected(BluetoothDevice device) {
if (VDBG) log("isAudioConnected()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.isAudioConnected(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.isAudioConnected(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -861,17 +907,20 @@
public int getAudioState(BluetoothDevice device) {
if (VDBG) log("getAudioState");
final IBluetoothHeadset service = mService;
- if (service != null && !isDisabled()) {
- try {
- return service.getAudioState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final int defaultValue = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (!isDisabled()) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getAudioState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -889,15 +938,17 @@
public void setAudioRouteAllowed(boolean allowed) {
if (VDBG) log("setAudioRouteAllowed");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
- try {
- service.setAudioRouteAllowed(allowed, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver recv = new SynchronousResultReceiver();
+ service.setAudioRouteAllowed(allowed, mAttributionSource, recv);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
}
@@ -912,17 +963,20 @@
public boolean getAudioRouteAllowed() {
if (VDBG) log("getAudioRouteAllowed");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
- try {
- return service.getAudioRouteAllowed(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final boolean defaultValue = false;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.getAudioRouteAllowed(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return false;
+ return defaultValue;
}
/**
@@ -937,15 +991,17 @@
public void setForceScoAudio(boolean forced) {
if (VDBG) log("setForceScoAudio " + String.valueOf(forced));
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
- try {
- service.setForceScoAudio(forced, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver recv = new SynchronousResultReceiver();
+ service.setForceScoAudio(forced, mAttributionSource, recv);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
}
@@ -962,16 +1018,20 @@
public boolean isAudioOn() {
if (VDBG) log("isAudioOn()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- return service.isAudioOn(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.isAudioOn(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
-
+ return defaultValue;
}
/**
@@ -996,18 +1056,22 @@
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean connectAudio() {
+ if (VDBG) log("connectAudio()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
- try {
- return service.connectAudio(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final boolean defaultValue = false;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connectAudio(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return false;
+ return defaultValue;
}
/**
@@ -1025,18 +1089,22 @@
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnectAudio() {
+ if (VDBG) log("disconnectAudio()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
- try {
- return service.disconnectAudio(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final boolean defaultValue = false;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnectAudio(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return false;
+ return defaultValue;
}
/**
@@ -1070,17 +1138,20 @@
public boolean startScoUsingVirtualVoiceCall() {
if (DBG) log("startScoUsingVirtualVoiceCall()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
- try {
- return service.startScoUsingVirtualVoiceCall(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final boolean defaultValue = false;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.startScoUsingVirtualVoiceCall(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return false;
+ return defaultValue;
}
/**
@@ -1105,17 +1176,20 @@
public boolean stopScoUsingVirtualVoiceCall() {
if (DBG) log("stopScoUsingVirtualVoiceCall()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
- try {
- return service.stopScoUsingVirtualVoiceCall(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final boolean defaultValue = false;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.stopScoUsingVirtualVoiceCall(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return false;
+ return defaultValue;
}
/**
@@ -1135,16 +1209,16 @@
public void phoneStateChanged(int numActive, int numHeld, int callState, String number,
int type, String name) {
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
service.phoneStateChanged(numActive, numHeld, callState, number, type, name,
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
}
}
@@ -1161,16 +1235,18 @@
public void clccResponse(int index, int direction, int status, int mode, boolean mpty,
String number, int type) {
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
- try {
- service.clccResponse(index, direction, status, mode, mpty, number, type,
- mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver recv = new SynchronousResultReceiver();
+ service.clccResponse(index, direction, status, mode, mpty, number, type,
+ mAttributionSource, recv);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
}
@@ -1202,18 +1278,21 @@
throw new IllegalArgumentException("command is null");
}
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
- try {
- return service.sendVendorSpecificResultCode(device, command, arg,
- mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- }
- }
+ final boolean defaultValue = false;
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.sendVendorSpecificResultCode(device, command, arg,
+ mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return false;
+ return defaultValue;
}
/**
@@ -1247,17 +1326,20 @@
Log.d(TAG, "setActiveDevice: " + device);
}
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && (device == null || isValidDevice(device))) {
- try {
- return service.setActiveDevice(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- }
- }
+ final boolean defaultValue = false;
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && (device == null || isValidDevice(device))) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setActiveDevice(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return false;
+ return defaultValue;
}
/**
@@ -1273,22 +1355,25 @@
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothDevice getActiveDevice() {
- if (VDBG) {
- Log.d(TAG, "getActiveDevice");
- }
+ if (VDBG) Log.d(TAG, "getActiveDevice");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
- try {
- return Attributable.setAttributionSource(
- service.getActiveDevice(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- }
- }
+ final BluetoothDevice defaultValue = null;
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<BluetoothDevice> recv =
+ new SynchronousResultReceiver();
+ service.getActiveDevice(mAttributionSource, recv);
+ return Attributable.setAttributionSource(
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return null;
+ return defaultValue;
}
/**
@@ -1303,21 +1388,22 @@
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
public boolean isInbandRingingEnabled() {
- if (DBG) {
- log("isInbandRingingEnabled()");
- }
+ if (DBG) log("isInbandRingingEnabled()");
final IBluetoothHeadset service = mService;
- if (service != null && isEnabled()) {
- try {
- return service.isInbandRingingEnabled(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- }
- }
+ final boolean defaultValue = false;
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.isInbandRingingEnabled(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return false;
+ return defaultValue;
}
/**
@@ -1337,7 +1423,7 @@
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
if (DBG) Log.d(TAG, "Proxy object connected");
- mService = IBluetoothHeadset.Stub.asInterface(Binder.allowBlocking(service));
+ mService = IBluetoothHeadset.Stub.asInterface(service);
mHandler.sendMessage(mHandler.obtainMessage(
MESSAGE_HEADSET_SERVICE_CONNECTED));
}
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java
index 2ef3710..7d7a7f7 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClient.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
@@ -25,15 +27,17 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
* Public API to control Hands Free Profile (HFP role only).
@@ -432,7 +436,7 @@
"BluetoothHeadsetClient", IBluetoothHeadsetClient.class.getName()) {
@Override
public IBluetoothHeadsetClient getServiceInterface(IBinder service) {
- return IBluetoothHeadsetClient.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothHeadsetClient.Stub.asInterface(service);
}
};
@@ -479,18 +483,21 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.connect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -507,18 +514,21 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.disconnect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -531,19 +541,24 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled()) {
+ final IBluetoothHeadsetClient service = getService();
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -558,20 +573,24 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled()) {
+ final IBluetoothHeadsetClient service = getService();
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -585,18 +604,21 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getConnectionState(BluetoothDevice device) {
if (VDBG) log("getConnectionState(" + device + ")");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -634,22 +656,23 @@
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
try {
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -686,18 +709,21 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final @ConnectionPolicy int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionPolicy(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return defaultValue;
}
/**
@@ -715,17 +741,21 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean startVoiceRecognition(BluetoothDevice device) {
if (DBG) log("startVoiceRecognition()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.startVoiceRecognition(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.startVoiceRecognition(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -739,20 +769,23 @@
*/
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
- public boolean sendVendorAtCommand(BluetoothDevice device, int vendorId,
- String atCommand) {
+ public boolean sendVendorAtCommand(BluetoothDevice device, int vendorId, String atCommand) {
if (DBG) log("sendVendorSpecificCommand()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.sendVendorAtCommand(device, vendorId, atCommand, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.sendVendorAtCommand(device, vendorId, atCommand, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -770,17 +803,21 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean stopVoiceRecognition(BluetoothDevice device) {
if (DBG) log("stopVoiceRecognition()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.stopVoiceRecognition(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.stopVoiceRecognition(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -793,18 +830,24 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) {
if (DBG) log("getCurrentCalls()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final List<BluetoothHeadsetClientCall> defaultValue = null;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
+ final SynchronousResultReceiver<List<BluetoothHeadsetClientCall>> recv =
+ new SynchronousResultReceiver();
+ service.getCurrentCalls(device, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getCurrentCalls(device, mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return null;
+ return defaultValue;
}
/**
@@ -817,17 +860,21 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public Bundle getCurrentAgEvents(BluetoothDevice device) {
if (DBG) log("getCurrentAgEvents()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final Bundle defaultValue = null;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getCurrentAgEvents(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Bundle> recv = new SynchronousResultReceiver();
+ service.getCurrentAgEvents(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return null;
+ return defaultValue;
}
/**
@@ -844,17 +891,21 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean acceptCall(BluetoothDevice device, int flag) {
if (DBG) log("acceptCall()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.acceptCall(device, flag, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.acceptCall(device, flag, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -868,17 +919,21 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean holdCall(BluetoothDevice device) {
if (DBG) log("holdCall()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.holdCall(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.holdCall(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -897,17 +952,21 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean rejectCall(BluetoothDevice device) {
if (DBG) log("rejectCall()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.rejectCall(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.rejectCall(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -930,17 +989,21 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call) {
if (DBG) log("terminateCall()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.terminateCall(device, call, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.terminateCall(device, call, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -961,17 +1024,21 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean enterPrivateMode(BluetoothDevice device, int index) {
if (DBG) log("enterPrivateMode()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.enterPrivateMode(device, index, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.enterPrivateMode(device, index, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -991,17 +1058,21 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean explicitCallTransfer(BluetoothDevice device) {
if (DBG) log("explicitCallTransfer()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.explicitCallTransfer(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.explicitCallTransfer(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -1017,18 +1088,24 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) {
if (DBG) log("dial()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final BluetoothHeadsetClientCall defaultValue = null;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
+ final SynchronousResultReceiver<BluetoothHeadsetClientCall> recv =
+ new SynchronousResultReceiver();
+ service.dial(device, number, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.dial(device, number, mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return null;
+ return defaultValue;
}
/**
@@ -1045,17 +1122,21 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean sendDTMF(BluetoothDevice device, byte code) {
if (DBG) log("sendDTMF()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.sendDTMF(device, code, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.sendDTMF(device, code, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -1074,17 +1155,21 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean getLastVoiceTagNumber(BluetoothDevice device) {
if (DBG) log("getLastVoiceTagNumber()");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getLastVoiceTagNumber(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.getLastVoiceTagNumber(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -1097,17 +1182,21 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getAudioState(BluetoothDevice device) {
if (VDBG) log("getAudioState");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled()) {
+ final IBluetoothHeadsetClient service = getService();
+ final int defaultValue = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- return service.getAudioState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getAudioState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
} else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ return defaultValue;
}
return BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
}
@@ -1123,17 +1212,18 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed) {
if (VDBG) log("setAudioRouteAllowed");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled()) {
- try {
- service.setAudioRouteAllowed(device, allowed, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final IBluetoothHeadsetClient service = getService();
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver recv = new SynchronousResultReceiver();
+ service.setAudioRouteAllowed(device, allowed, mAttributionSource, recv);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
}
@@ -1148,19 +1238,21 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean getAudioRouteAllowed(BluetoothDevice device) {
if (VDBG) log("getAudioRouteAllowed");
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled()) {
- try {
- return service.getAudioRouteAllowed(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.getAudioRouteAllowed(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return false;
+ return defaultValue;
}
/**
@@ -1175,19 +1267,22 @@
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean connectAudio(BluetoothDevice device) {
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled()) {
- try {
- return service.connectAudio(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ if (VDBG) log("connectAudio");
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connectAudio(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return false;
+ return defaultValue;
}
/**
@@ -1202,19 +1297,22 @@
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnectAudio(BluetoothDevice device) {
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled()) {
- try {
- return service.disconnectAudio(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ if (VDBG) log("disconnectAudio");
+ final IBluetoothHeadsetClient service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnectAudio(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return false;
+ return defaultValue;
}
/**
@@ -1226,19 +1324,22 @@
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public Bundle getCurrentAgFeatures(BluetoothDevice device) {
- final IBluetoothHeadsetClient service =
- getService();
- if (service != null && isEnabled()) {
- try {
- return service.getCurrentAgFeatures(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ if (VDBG) log("getCurrentAgFeatures");
+ final IBluetoothHeadsetClient service = getService();
+ final Bundle defaultValue = null;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Bundle> recv = new SynchronousResultReceiver();
+ service.getCurrentAgFeatures(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return null;
+ return defaultValue;
}
private boolean isEnabled() {
diff --git a/core/java/android/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java
index a00b20d..339a75f 100644
--- a/core/java/android/bluetooth/BluetoothHearingAid.java
+++ b/core/java/android/bluetooth/BluetoothHearingAid.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -28,14 +30,16 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
* This class provides the public APIs to control the Hearing Aid profile.
@@ -136,7 +140,7 @@
"BluetoothHearingAid", IBluetoothHearingAid.class.getName()) {
@Override
public IBluetoothHearingAid getServiceInterface(IBinder service) {
- return IBluetoothHearingAid.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothHearingAid.Stub.asInterface(service);
}
};
@@ -181,16 +185,20 @@
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
final IBluetoothHearingAid service = getService();
- try {
- if (service != null && isEnabled() && isValidDevice(device)) {
- return service.connect(device, mAttributionSource);
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -223,16 +231,20 @@
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
final IBluetoothHearingAid service = getService();
- try {
- if (service != null && isEnabled() && isValidDevice(device)) {
- return service.disconnect(device, mAttributionSource);
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -244,17 +256,23 @@
public @NonNull List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
final IBluetoothHearingAid service = getService();
- try {
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
}
+ return defaultValue;
}
/**
@@ -267,18 +285,23 @@
@NonNull int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
final IBluetoothHearingAid service = getService();
- try {
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
}
+ return defaultValue;
}
/**
@@ -291,17 +314,20 @@
@NonNull BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
final IBluetoothHearingAid service = getService();
- try {
- if (service != null && isEnabled()
- && isValidDevice(device)) {
- return service.getConnectionState(device, mAttributionSource);
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
}
+ return defaultValue;
}
/**
@@ -330,18 +356,20 @@
public boolean setActiveDevice(@Nullable BluetoothDevice device) {
if (DBG) log("setActiveDevice(" + device + ")");
final IBluetoothHearingAid service = getService();
- try {
- if (service != null && isEnabled()
- && ((device == null) || isValidDevice(device))) {
- service.setActiveDevice(device, mAttributionSource);
- return true;
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && ((device == null) || isValidDevice(device))) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setActiveDevice(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -359,17 +387,23 @@
public @NonNull List<BluetoothDevice> getActiveDevices() {
if (VDBG) log("getActiveDevices()");
final IBluetoothHearingAid service = getService();
- try {
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getActiveDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getActiveDevices(mAttributionSource), mAttributionSource);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<>();
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<>();
}
+ return defaultValue;
}
/**
@@ -416,21 +450,22 @@
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
verifyDeviceNotNull(device, "setConnectionPolicy");
final IBluetoothHearingAid service = getService();
- try {
- if (service != null && isEnabled()
- && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -474,17 +509,20 @@
if (VDBG) log("getConnectionPolicy(" + device + ")");
verifyDeviceNotNull(device, "getConnectionPolicy");
final IBluetoothHearingAid service = getService();
- try {
- if (service != null && isEnabled()
- && isValidDevice(device)) {
- return service.getConnectionPolicy(device, mAttributionSource);
+ final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
}
+ return defaultValue;
}
/**
@@ -519,19 +557,18 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void setVolume(int volume) {
if (DBG) Log.d(TAG, "setVolume(" + volume + ")");
-
final IBluetoothHearingAid service = getService();
- try {
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- return;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver recv = new SynchronousResultReceiver();
+ service.setVolume(volume, mAttributionSource, recv);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
-
- if (!isEnabled()) return;
-
- service.setVolume(volume, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
}
}
@@ -552,24 +589,23 @@
android.Manifest.permission.BLUETOOTH_PRIVILEGED,
})
public long getHiSyncId(@NonNull BluetoothDevice device) {
- if (VDBG) {
- log("getHiSyncId(" + device + ")");
- }
+ if (VDBG) log("getHiSyncId(" + device + ")");
verifyDeviceNotNull(device, "getConnectionPolicy");
final IBluetoothHearingAid service = getService();
- try {
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- return HI_SYNC_ID_INVALID;
+ final long defaultValue = HI_SYNC_ID_INVALID;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Long> recv = new SynchronousResultReceiver();
+ service.getHiSyncId(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
-
- if (!isEnabled() || !isValidDevice(device)) return HI_SYNC_ID_INVALID;
-
- return service.getHiSyncId(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return HI_SYNC_ID_INVALID;
}
+ return defaultValue;
}
/**
@@ -583,21 +619,22 @@
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getDeviceSide(BluetoothDevice device) {
- if (VDBG) {
- log("getDeviceSide(" + device + ")");
- }
+ if (VDBG) log("getDeviceSide(" + device + ")");
final IBluetoothHearingAid service = getService();
- try {
- if (service != null && isEnabled()
- && isValidDevice(device)) {
- return service.getDeviceSide(device, mAttributionSource);
+ final int defaultValue = SIDE_LEFT;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getDeviceSide(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return SIDE_LEFT;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return SIDE_LEFT;
}
+ return defaultValue;
}
/**
@@ -611,21 +648,22 @@
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getDeviceMode(BluetoothDevice device) {
- if (VDBG) {
- log("getDeviceMode(" + device + ")");
- }
+ if (VDBG) log("getDeviceMode(" + device + ")");
final IBluetoothHearingAid service = getService();
- try {
- if (service != null && isEnabled()
- && isValidDevice(device)) {
- return service.getDeviceMode(device, mAttributionSource);
+ final int defaultValue = MODE_MONAURAL;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getDeviceMode(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return MODE_MONAURAL;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return MODE_MONAURAL;
}
+ return defaultValue;
}
private boolean isEnabled() {
diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java
index f5b444f..44a355b 100644
--- a/core/java/android/bluetooth/BluetoothHidDevice.java
+++ b/core/java/android/bluetooth/BluetoothHidDevice.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
@@ -26,14 +28,16 @@
import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
+import java.util.concurrent.TimeoutException;
/**
* Provides the public APIs to control the Bluetooth HID Device profile.
@@ -431,7 +435,7 @@
"BluetoothHidDevice", IBluetoothHidDevice.class.getName()) {
@Override
public IBluetoothHidDevice getServiceInterface(IBinder service) {
- return IBluetoothHidDevice.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothHidDevice.Stub.asInterface(service);
}
};
@@ -455,18 +459,23 @@
@RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getConnectedDevices() {
final IBluetoothHidDevice service = getService();
- if (service != null) {
- try {
- return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final List<BluetoothDevice> defaultValue = new ArrayList<>();
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
+ return Attributable.setAttributionSource(
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
-
- return new ArrayList<>();
+ return defaultValue;
}
/** {@inheritDoc} */
@@ -475,19 +484,23 @@
@RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
final IBluetoothHidDevice service = getService();
- if (service != null) {
- try {
- return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
- mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final List<BluetoothDevice> defaultValue = new ArrayList<>();
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
+ return Attributable.setAttributionSource(
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
-
- return new ArrayList<>();
+ return defaultValue;
}
/** {@inheritDoc} */
@@ -496,17 +509,20 @@
@RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
public int getConnectionState(BluetoothDevice device) {
final IBluetoothHidDevice service = getService();
- if (service != null) {
- try {
- return service.getConnectionState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final int defaultValue = STATE_DISCONNECTED;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
-
- return STATE_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -555,18 +571,21 @@
}
final IBluetoothHidDevice service = getService();
- if (service != null) {
- try {
- CallbackWrapper cbw = new CallbackWrapper(executor, callback, mAttributionSource);
- result = service.registerApp(sdp, inQos, outQos, cbw, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final boolean defaultValue = result;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ CallbackWrapper cbw = new CallbackWrapper(executor, callback, mAttributionSource);
+ service.registerApp(sdp, inQos, outQos, cbw, mAttributionSource, recv);
+ result = recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
-
- return result;
+ return defaultValue;
}
/**
@@ -582,20 +601,21 @@
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean unregisterApp() {
- boolean result = false;
-
final IBluetoothHidDevice service = getService();
- if (service != null) {
- try {
- result = service.unregisterApp(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final boolean defaultValue = false;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.unregisterApp(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
-
- return result;
+ return defaultValue;
}
/**
@@ -609,20 +629,21 @@
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean sendReport(BluetoothDevice device, int id, byte[] data) {
- boolean result = false;
-
final IBluetoothHidDevice service = getService();
- if (service != null) {
- try {
- result = service.sendReport(device, id, data, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final boolean defaultValue = false;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.sendReport(device, id, data, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
-
- return result;
+ return defaultValue;
}
/**
@@ -637,20 +658,21 @@
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) {
- boolean result = false;
-
final IBluetoothHidDevice service = getService();
- if (service != null) {
- try {
- result = service.replyReport(device, type, id, data, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final boolean defaultValue = false;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.replyReport(device, type, id, data, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
-
- return result;
+ return defaultValue;
}
/**
@@ -663,20 +685,21 @@
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean reportError(BluetoothDevice device, byte error) {
- boolean result = false;
-
final IBluetoothHidDevice service = getService();
- if (service != null) {
- try {
- result = service.reportError(device, error, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final boolean defaultValue = false;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.reportError(device, error, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
-
- return result;
+ return defaultValue;
}
/**
@@ -689,18 +712,20 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public String getUserAppName() {
final IBluetoothHidDevice service = getService();
-
- if (service != null) {
- try {
- return service.getUserAppName(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final String defaultValue = "";
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<String> recv = new SynchronousResultReceiver();
+ service.getUserAppName(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
-
- return "";
+ return defaultValue;
}
/**
@@ -714,20 +739,21 @@
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean connect(BluetoothDevice device) {
- boolean result = false;
-
final IBluetoothHidDevice service = getService();
- if (service != null) {
- try {
- result = service.connect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final boolean defaultValue = false;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
-
- return result;
+ return defaultValue;
}
/**
@@ -740,20 +766,21 @@
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnect(BluetoothDevice device) {
- boolean result = false;
-
final IBluetoothHidDevice service = getService();
- if (service != null) {
- try {
- result = service.disconnect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final boolean defaultValue = false;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
-
- return result;
+ return defaultValue;
}
/**
@@ -781,23 +808,24 @@
})
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
- log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
- try {
- final IBluetoothHidDevice service = getService();
- if (service != null && isEnabled()
- && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
+ if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
+ final IBluetoothHidDevice service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
private boolean isEnabled() {
diff --git a/core/java/android/bluetooth/BluetoothHidHost.java b/core/java/android/bluetooth/BluetoothHidHost.java
index 121aa16..ecbeddf 100644
--- a/core/java/android/bluetooth/BluetoothHidHost.java
+++ b/core/java/android/bluetooth/BluetoothHidHost.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
@@ -28,13 +30,15 @@
import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
@@ -244,7 +248,7 @@
"BluetoothHidHost", IBluetoothHidHost.class.getName()) {
@Override
public IBluetoothHidHost getServiceInterface(IBinder service) {
- return IBluetoothHidHost.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothHidHost.Stub.asInterface(service);
}
};
@@ -292,16 +296,20 @@
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.connect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -334,16 +342,20 @@
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.disconnect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -358,17 +370,23 @@
public @NonNull List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -382,18 +400,23 @@
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -411,16 +434,20 @@
throw new IllegalArgumentException("device must not be null");
}
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -469,20 +496,22 @@
throw new IllegalArgumentException("device must not be null");
}
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
try {
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -528,16 +557,20 @@
throw new IllegalArgumentException("device must not be null");
}
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionPolicy(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return defaultValue;
}
private boolean isEnabled() {
@@ -561,18 +594,20 @@
public boolean virtualUnplug(BluetoothDevice device) {
if (DBG) log("virtualUnplug(" + device + ")");
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.virtualUnplug(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.virtualUnplug(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
-
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
-
+ return defaultValue;
}
/**
@@ -588,16 +623,20 @@
public boolean getProtocolMode(BluetoothDevice device) {
if (VDBG) log("getProtocolMode(" + device + ")");
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getProtocolMode(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.getProtocolMode(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -613,16 +652,20 @@
public boolean setProtocolMode(BluetoothDevice device, int protocolMode) {
if (DBG) log("setProtocolMode(" + device + ")");
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.setProtocolMode(device, protocolMode, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setProtocolMode(device, protocolMode, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -645,17 +688,21 @@
+ "bufferSize=" + bufferSize);
}
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getReport(device, reportType, reportId, bufferSize,
- mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.getReport(device, reportType, reportId, bufferSize, mAttributionSource,
+ recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -673,16 +720,20 @@
public boolean setReport(BluetoothDevice device, byte reportType, String report) {
if (VDBG) log("setReport(" + device + "), reportType=" + reportType + " report=" + report);
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.setReport(device, reportType, report, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setReport(device, reportType, report, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -699,16 +750,20 @@
public boolean sendData(BluetoothDevice device, String report) {
if (DBG) log("sendData(" + device + "), report=" + report);
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.sendData(device, report, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.sendData(device, report, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -724,16 +779,20 @@
public boolean getIdleTime(BluetoothDevice device) {
if (DBG) log("getIdletime(" + device + ")");
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getIdleTime(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.getIdleTime(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -750,16 +809,20 @@
public boolean setIdleTime(BluetoothDevice device, byte idleTime) {
if (DBG) log("setIdletime(" + device + "), idleTime=" + idleTime);
final IBluetoothHidHost service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.setIdleTime(device, idleTime, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setIdleTime(device, idleTime, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
private static void log(String msg) {
diff --git a/core/java/android/bluetooth/BluetoothLeAudio.java b/core/java/android/bluetooth/BluetoothLeAudio.java
index 34398eb..15db686 100644
--- a/core/java/android/bluetooth/BluetoothLeAudio.java
+++ b/core/java/android/bluetooth/BluetoothLeAudio.java
@@ -17,6 +17,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -27,14 +29,16 @@
import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.CloseGuard;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
* This class provides the public APIs to control the LeAudio profile.
@@ -331,7 +335,7 @@
IBluetoothLeAudio.class.getName()) {
@Override
public IBluetoothLeAudio getServiceInterface(IBinder service) {
- return IBluetoothLeAudio.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothLeAudio.Stub.asInterface(service);
}
};
@@ -385,17 +389,21 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean connect(@Nullable BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
- try {
- final IBluetoothLeAudio service = getService();
- if (service != null && mAdapter.isEnabled() && isValidDevice(device)) {
- return service.connect(device, mAttributionSource);
+ final IBluetoothLeAudio service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -425,17 +433,21 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disconnect(@Nullable BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
- try {
- final IBluetoothLeAudio service = getService();
- if (service != null && mAdapter.isEnabled() && isValidDevice(device)) {
- return service.disconnect(device, mAttributionSource);
+ final IBluetoothLeAudio service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -446,18 +458,24 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @NonNull List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
- try {
- final IBluetoothLeAudio service = getService();
- if (service != null && mAdapter.isEnabled()) {
+ final IBluetoothLeAudio service = getService();
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled()) {
+ try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
}
+ return defaultValue;
}
/**
@@ -469,19 +487,24 @@
public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(
@NonNull int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
- try {
- final IBluetoothLeAudio service = getService();
- if (service != null && mAdapter.isEnabled()) {
+ final IBluetoothLeAudio service = getService();
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled()) {
+ try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
}
+ return defaultValue;
}
/**
@@ -493,18 +516,21 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @BtProfileState int getConnectionState(@NonNull BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
- try {
- final IBluetoothLeAudio service = getService();
- if (service != null && mAdapter.isEnabled()
- && isValidDevice(device)) {
- return service.getConnectionState(device, mAttributionSource);
+ final IBluetoothLeAudio service = getService();
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
}
+ return defaultValue;
}
/**
@@ -531,19 +557,21 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean setActiveDevice(@Nullable BluetoothDevice device) {
if (DBG) log("setActiveDevice(" + device + ")");
- try {
- final IBluetoothLeAudio service = getService();
- if (service != null && mAdapter.isEnabled()
- && ((device == null) || isValidDevice(device))) {
- service.setActiveDevice(device, mAttributionSource);
- return true;
+ final IBluetoothLeAudio service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled() && ((device == null) || isValidDevice(device))) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setActiveDevice(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -557,19 +585,25 @@
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getActiveDevices() {
- if (VDBG) log("getActiveDevices()");
- try {
- final IBluetoothLeAudio service = getService();
- if (service != null && mAdapter.isEnabled()) {
+ if (VDBG) log("getActiveDevice()");
+ final IBluetoothLeAudio service = getService();
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled()) {
+ try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getActiveDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getActiveDevices(mAttributionSource), mAttributionSource);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<>();
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<>();
}
+ return defaultValue;
}
/**
@@ -583,17 +617,21 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public int getGroupId(@NonNull BluetoothDevice device) {
if (VDBG) log("getGroupId()");
- try {
- final IBluetoothLeAudio service = getService();
- if (service != null && mAdapter.isEnabled()) {
- return service.getGroupId(device, mAttributionSource);
+ final IBluetoothLeAudio service = getService();
+ final int defaultValue = GROUP_ID_INVALID;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getGroupId(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return GROUP_ID_INVALID;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return GROUP_ID_INVALID;
}
+ return defaultValue;
}
/**
@@ -606,17 +644,18 @@
@RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED})
public void setVolume(int volume) {
if (VDBG) log("setVolume(vol: " + volume + " )");
- try {
- final IBluetoothLeAudio service = getService();
- if (service != null && mAdapter.isEnabled()) {
- service.setVolume(volume, mAttributionSource);
- return;
+ final IBluetoothLeAudio service = getService();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled()) {
+ try {
+ final SynchronousResultReceiver recv = new SynchronousResultReceiver();
+ service.setVolume(volume, mAttributionSource, recv);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return;
}
}
@@ -635,16 +674,20 @@
public boolean groupAddNode(int group_id, @NonNull BluetoothDevice device) {
if (VDBG) log("groupAddNode()");
final IBluetoothLeAudio service = getService();
- try {
- if (service != null && mAdapter.isEnabled()) {
- return service.groupAddNode(group_id, device, mAttributionSource);
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.groupAddNode(group_id, device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -663,16 +706,20 @@
public boolean groupRemoveNode(int group_id, @NonNull BluetoothDevice device) {
if (VDBG) log("groupRemoveNode()");
final IBluetoothLeAudio service = getService();
- try {
- if (service != null && mAdapter.isEnabled()) {
- return service.groupRemoveNode(group_id, device, mAttributionSource);
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.groupRemoveNode(group_id, device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -695,22 +742,23 @@
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
- try {
- final IBluetoothLeAudio service = getService();
- if (service != null && mAdapter.isEnabled()
- && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
+ final IBluetoothLeAudio service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -728,18 +776,21 @@
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
- try {
- final IBluetoothLeAudio service = getService();
- if (service != null && mAdapter.isEnabled()
- && isValidDevice(device)) {
- return service.getConnectionPolicy(device, mAttributionSource);
+ final IBluetoothLeAudio service = getService();
+ final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (mAdapter.isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
}
+ return defaultValue;
}
diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java
index c93de41..fef6f22 100644
--- a/core/java/android/bluetooth/BluetoothManager.java
+++ b/core/java/android/bluetooth/BluetoothManager.java
@@ -16,14 +16,10 @@
package android.bluetooth;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.RequiresFeature;
import android.annotation.RequiresNoPermission;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
-import android.app.ActivityThread;
-import android.app.AppGlobals;
import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.bluetooth.annotations.RequiresLegacyBluetoothPermission;
import android.content.AttributionSource;
@@ -68,37 +64,11 @@
* @hide
*/
public BluetoothManager(Context context) {
- mAttributionSource = resolveAttributionSource(context);
+ mAttributionSource = (context != null) ? context.getAttributionSource() :
+ AttributionSource.myAttributionSource();
mAdapter = BluetoothAdapter.createAdapter(mAttributionSource);
}
- /** {@hide} */
- public static @NonNull AttributionSource resolveAttributionSource(@Nullable Context context) {
- AttributionSource res = null;
- if (context != null) {
- res = context.getAttributionSource();
- }
- if (res == null) {
- res = ActivityThread.currentAttributionSource();
- }
- if (res == null) {
- int uid = android.os.Process.myUid();
- if (uid == android.os.Process.ROOT_UID) {
- uid = android.os.Process.SYSTEM_UID;
- }
- try {
- res = new AttributionSource.Builder(uid)
- .setPackageName(AppGlobals.getPackageManager().getPackagesForUid(uid)[0])
- .build();
- } catch (RemoteException ignored) {
- }
- }
- if (res == null) {
- throw new IllegalStateException("Failed to resolve AttributionSource");
- }
- return res;
- }
-
/**
* Get the BLUETOOTH Adapter for this device.
*
diff --git a/core/java/android/bluetooth/BluetoothMap.java b/core/java/android/bluetooth/BluetoothMap.java
index 474e41f..56e4972 100644
--- a/core/java/android/bluetooth/BluetoothMap.java
+++ b/core/java/android/bluetooth/BluetoothMap.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.RequiresNoPermission;
@@ -28,15 +30,17 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.CloseGuard;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
* This class provides the APIs to control the Bluetooth MAP
@@ -87,7 +91,7 @@
"BluetoothMap", IBluetoothMap.class.getName()) {
@Override
public IBluetoothMap getServiceInterface(IBinder service) {
- return IBluetoothMap.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothMap.Stub.asInterface(service);
}
};
@@ -142,17 +146,20 @@
public int getState() {
if (VDBG) log("getState()");
final IBluetoothMap service = getService();
- if (service != null) {
- try {
- return service.getState(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final int defaultValue = BluetoothMap.STATE_ERROR;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getState(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return BluetoothMap.STATE_ERROR;
+ return defaultValue;
}
/**
@@ -168,18 +175,23 @@
public BluetoothDevice getClient() {
if (VDBG) log("getClient()");
final IBluetoothMap service = getService();
- if (service != null) {
- try {
- return Attributable.setAttributionSource(
- service.getClient(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final BluetoothDevice defaultValue = null;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<BluetoothDevice> recv =
+ new SynchronousResultReceiver();
+ service.getClient(mAttributionSource, recv);
+ return Attributable.setAttributionSource(
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return null;
+ return defaultValue;
}
/**
@@ -194,17 +206,20 @@
public boolean isConnected(BluetoothDevice device) {
if (VDBG) log("isConnected(" + device + ")");
final IBluetoothMap service = getService();
- if (service != null) {
- try {
- return service.isConnected(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final boolean defaultValue = false;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.isConnected(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return false;
+ return defaultValue;
}
/**
@@ -233,16 +248,20 @@
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
final IBluetoothMap service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.disconnect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -284,17 +303,23 @@
public @NonNull List<BluetoothDevice> getConnectedDevices() {
if (DBG) log("getConnectedDevices()");
final IBluetoothMap service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -309,18 +334,23 @@
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (DBG) log("getDevicesMatchingStates()");
final IBluetoothMap service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -335,16 +365,21 @@
public int getConnectionState(BluetoothDevice device) {
if (DBG) log("getConnectionState(" + device + ")");
final IBluetoothMap service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
+ final SynchronousResultReceiver<Integer> recv =
+ new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -390,20 +425,22 @@
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
final IBluetoothMap service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
try {
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -446,16 +483,20 @@
public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothMap service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionPolicy(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return defaultValue;
}
private static void log(String msg) {
diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java
index 8a3f801..03536f9a 100644
--- a/core/java/android/bluetooth/BluetoothMapClient.java
+++ b/core/java/android/bluetooth/BluetoothMapClient.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -29,15 +31,17 @@
import android.content.AttributionSource;
import android.content.Context;
import android.net.Uri;
-import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
* This class provides the APIs to control the Bluetooth MAP MCE Profile.
@@ -180,7 +184,7 @@
"BluetoothMapClient", IBluetoothMapClient.class.getName()) {
@Override
public IBluetoothMapClient getServiceInterface(IBinder service) {
- return IBluetoothMapClient.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothMapClient.Stub.asInterface(service);
}
};
@@ -221,17 +225,20 @@
public boolean isConnected(BluetoothDevice device) {
if (VDBG) Log.d(TAG, "isConnected(" + device + ")");
final IBluetoothMapClient service = getService();
- if (service != null) {
- try {
- return service.isConnected(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final boolean defaultValue = false;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.isConnected(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return false;
+ return defaultValue;
}
/**
@@ -248,17 +255,20 @@
public boolean connect(BluetoothDevice device) {
if (DBG) Log.d(TAG, "connect(" + device + ")" + "for MAPS MCE");
final IBluetoothMapClient service = getService();
- if (service != null) {
- try {
- return service.connect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final boolean defaultValue = false;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return false;
+ return defaultValue;
}
/**
@@ -277,15 +287,20 @@
public boolean disconnect(BluetoothDevice device) {
if (DBG) Log.d(TAG, "disconnect(" + device + ")");
final IBluetoothMapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.disconnect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -300,17 +315,23 @@
public List<BluetoothDevice> getConnectedDevices() {
if (DBG) Log.d(TAG, "getConnectedDevices()");
final IBluetoothMapClient service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<>();
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<>();
+ return defaultValue;
}
/**
@@ -325,18 +346,23 @@
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (DBG) Log.d(TAG, "getDevicesMatchingStates()");
final IBluetoothMapClient service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<>();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<>();
+ return defaultValue;
}
/**
@@ -351,16 +377,20 @@
public int getConnectionState(BluetoothDevice device) {
if (DBG) Log.d(TAG, "getConnectionState(" + device + ")");
final IBluetoothMapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver<>();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -405,20 +435,22 @@
@ConnectionPolicy int connectionPolicy) {
if (DBG) Log.d(TAG, "setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
final IBluetoothMapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
try {
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -460,16 +492,20 @@
public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
if (VDBG) Log.d(TAG, "getConnectionPolicy(" + device + ")");
final IBluetoothMapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionPolicy(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return defaultValue;
}
/**
@@ -494,18 +530,8 @@
public boolean sendMessage(@NonNull BluetoothDevice device, @NonNull Collection<Uri> contacts,
@NonNull String message, @Nullable PendingIntent sentIntent,
@Nullable PendingIntent deliveredIntent) {
- if (DBG) Log.d(TAG, "sendMessage(" + device + ", " + contacts + ", " + message);
- final IBluetoothMapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- try {
- return service.sendMessage(device, contacts.toArray(new Uri[contacts.size()]),
- message, sentIntent, deliveredIntent, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
- }
- }
- return false;
+ return sendMessage(device, contacts.toArray(new Uri[contacts.size()]), message, sentIntent,
+ deliveredIntent);
}
/**
@@ -531,16 +557,21 @@
PendingIntent sentIntent, PendingIntent deliveredIntent) {
if (DBG) Log.d(TAG, "sendMessage(" + device + ", " + contacts + ", " + message);
final IBluetoothMapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.sendMessage(device, contacts, message, sentIntent, deliveredIntent,
- mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.sendMessage(device, contacts, message, sentIntent, deliveredIntent,
+ mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- return false;
+ return defaultValue;
}
/**
@@ -558,15 +589,20 @@
public boolean getUnreadMessages(BluetoothDevice device) {
if (DBG) Log.d(TAG, "getUnreadMessages(" + device + ")");
final IBluetoothMapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getUnreadMessages(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.getUnreadMessages(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- return false;
+ return defaultValue;
}
/**
@@ -580,13 +616,21 @@
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean isUploadingSupported(BluetoothDevice device) {
+ if (DBG) Log.d(TAG, "isUploadingSupported(" + device + ")");
final IBluetoothMapClient service = getService();
- try {
- return (service != null && isEnabled() && isValidDevice(device))
- && ((service.getSupportedFeatures(device, mAttributionSource)
- & UPLOADING_FEATURE_BITMASK) > 0);
- } catch (RemoteException e) {
- Log.e(TAG, e.getMessage());
+ final int defaultValue = 0;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getSupportedFeatures(device, mAttributionSource, recv);
+ return (recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue)
+ & UPLOADING_FEATURE_BITMASK) > 0;
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
return false;
}
@@ -615,16 +659,21 @@
public boolean setMessageStatus(BluetoothDevice device, String handle, int status) {
if (DBG) Log.d(TAG, "setMessageStatus(" + device + ", " + handle + ", " + status + ")");
final IBluetoothMapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device) && handle != null &&
- (status == READ || status == UNREAD || status == UNDELETED || status == DELETED)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device) && handle != null && (status == READ
+ || status == UNREAD || status == UNDELETED || status == DELETED)) {
try {
- return service.setMessageStatus(device, handle, status, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setMessageStatus(device, handle, status, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- return false;
+ return defaultValue;
}
private boolean isEnabled() {
diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java
index ac7a52d..d4ad4ef4 100644
--- a/core/java/android/bluetooth/BluetoothPan.java
+++ b/core/java/android/bluetooth/BluetoothPan.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
@@ -28,16 +30,18 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
* This class provides the APIs to control the Bluetooth Pan
@@ -188,7 +192,7 @@
"BluetoothPan", IBluetoothPan.class.getName()) {
@Override
public IBluetoothPan getServiceInterface(IBinder service) {
- return IBluetoothPan.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothPan.Stub.asInterface(service);
}
};
@@ -249,16 +253,20 @@
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
final IBluetoothPan service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.connect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -289,16 +297,20 @@
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
final IBluetoothPan service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.disconnect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -322,22 +334,23 @@
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
- try {
- final IBluetoothPan service = getService();
- if (service != null && isEnabled()
- && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
+ final IBluetoothPan service = getService();
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
}
+ return defaultValue;
}
/**
@@ -354,17 +367,23 @@
public @NonNull List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
final IBluetoothPan service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -381,18 +400,23 @@
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
final IBluetoothPan service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -409,16 +433,20 @@
public int getConnectionState(@NonNull BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
final IBluetoothPan service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -438,11 +466,16 @@
String pkgName = mContext.getOpPackageName();
if (DBG) log("setBluetoothTethering(" + value + "), calling package:" + pkgName);
final IBluetoothPan service = getService();
- if (service != null && isEnabled()) {
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- service.setBluetoothTethering(value, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver recv = new SynchronousResultReceiver();
+ service.setBluetoothTethering(value, mAttributionSource, recv);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
}
@@ -459,14 +492,20 @@
public boolean isTetheringOn() {
if (VDBG) log("isTetheringOn()");
final IBluetoothPan service = getService();
- if (service != null && isEnabled()) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- return service.isTetheringOn(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.isTetheringOn(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- return false;
+ return defaultValue;
}
@UnsupportedAppUsage
diff --git a/core/java/android/bluetooth/BluetoothPbapClient.java b/core/java/android/bluetooth/BluetoothPbapClient.java
index cc91ad2..e096de8 100644
--- a/core/java/android/bluetooth/BluetoothPbapClient.java
+++ b/core/java/android/bluetooth/BluetoothPbapClient.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
@@ -24,13 +26,15 @@
import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
* This class provides the APIs to control the Bluetooth PBAP Client Profile.
@@ -64,7 +68,7 @@
"BluetoothPbapClient", IBluetoothPbapClient.class.getName()) {
@Override
public IBluetoothPbapClient getServiceInterface(IBinder service) {
- return IBluetoothPbapClient.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothPbapClient.Stub.asInterface(service);
}
};
@@ -123,18 +127,20 @@
log("connect(" + device + ") for PBAP Client.");
}
final IBluetoothPbapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- try {
- return service.connect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
- }
- }
+ final boolean defaultValue = false;
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.connect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return false;
+ return defaultValue;
}
/**
@@ -155,19 +161,21 @@
log("disconnect(" + device + ")" + new Exception());
}
final IBluetoothPbapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- try {
- service.disconnect(device, mAttributionSource);
- return true;
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
- }
- }
+ final boolean defaultValue = true;
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnect(device, mAttributionSource, recv);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ return true;
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return false;
+ return defaultValue;
}
/**
@@ -184,19 +192,23 @@
log("getConnectedDevices()");
}
final IBluetoothPbapClient service = getService();
- if (service != null && isEnabled()) {
- try {
- return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
- }
- }
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
+ return Attributable.setAttributionSource(
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -212,20 +224,23 @@
log("getDevicesMatchingStates()");
}
final IBluetoothPbapClient service = getService();
- if (service != null && isEnabled()) {
- try {
- return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
- mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
- }
- }
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
+ return Attributable.setAttributionSource(
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -241,18 +256,20 @@
log("getConnectionState(" + device + ")");
}
final IBluetoothPbapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- try {
- return service.getConnectionState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
- }
- }
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return BluetoothProfile.STATE_DISCONNECTED;
+ return defaultValue;
}
private static void log(String msg) {
@@ -311,22 +328,22 @@
log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
}
final IBluetoothPbapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
- try {
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
- }
- }
+ final boolean defaultValue = false;
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return false;
+ return defaultValue;
}
/**
@@ -370,17 +387,19 @@
log("getConnectionPolicy(" + device + ")");
}
final IBluetoothPbapClient service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- try {
- return service.getConnectionPolicy(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
- }
- }
+ final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
if (service == null) {
Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return defaultValue;
}
}
diff --git a/core/java/android/bluetooth/BluetoothSap.java b/core/java/android/bluetooth/BluetoothSap.java
index ab2b8ea..808fa39 100644
--- a/core/java/android/bluetooth/BluetoothSap.java
+++ b/core/java/android/bluetooth/BluetoothSap.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.Manifest;
import android.annotation.RequiresNoPermission;
import android.annotation.RequiresPermission;
@@ -26,14 +28,16 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
* This class provides the APIs to control the Bluetooth SIM
@@ -104,7 +108,7 @@
"BluetoothSap", IBluetoothSap.class.getName()) {
@Override
public IBluetoothSap getServiceInterface(IBinder service) {
- return IBluetoothSap.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothSap.Stub.asInterface(service);
}
};
@@ -155,17 +159,20 @@
public int getState() {
if (VDBG) log("getState()");
final IBluetoothSap service = getService();
- if (service != null) {
- try {
- return service.getState(mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final int defaultValue = BluetoothSap.STATE_ERROR;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getState(mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return BluetoothSap.STATE_ERROR;
+ return defaultValue;
}
/**
@@ -180,18 +187,23 @@
public BluetoothDevice getClient() {
if (VDBG) log("getClient()");
final IBluetoothSap service = getService();
- if (service != null) {
- try {
- return Attributable.setAttributionSource(
- service.getClient(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final BluetoothDevice defaultValue = null;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<BluetoothDevice> recv =
+ new SynchronousResultReceiver();
+ service.getClient(mAttributionSource, recv);
+ return Attributable.setAttributionSource(
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return null;
+ return defaultValue;
}
/**
@@ -206,17 +218,20 @@
public boolean isConnected(BluetoothDevice device) {
if (VDBG) log("isConnected(" + device + ")");
final IBluetoothSap service = getService();
- if (service != null) {
- try {
- return service.isConnected(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
+ final boolean defaultValue = false;
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.isConnected(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
- return false;
+ return defaultValue;
}
/**
@@ -244,16 +259,20 @@
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
final IBluetoothSap service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.disconnect(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.disconnect(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -267,17 +286,23 @@
public List<BluetoothDevice> getConnectedDevices() {
if (DBG) log("getConnectedDevices()");
final IBluetoothSap service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -291,18 +316,23 @@
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (DBG) log("getDevicesMatchingStates()");
final IBluetoothSap service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -316,16 +346,20 @@
public int getConnectionState(BluetoothDevice device) {
if (DBG) log("getConnectionState(" + device + ")");
final IBluetoothSap service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -370,20 +404,22 @@
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
final IBluetoothSap service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
try {
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -425,16 +461,20 @@
public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothSap service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionPolicy(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return defaultValue;
}
private static void log(String msg) {
diff --git a/core/java/android/bluetooth/BluetoothUtils.java b/core/java/android/bluetooth/BluetoothUtils.java
new file mode 100644
index 0000000..8674692
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothUtils.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import java.time.Duration;
+
+/**
+ * {@hide}
+ */
+public final class BluetoothUtils {
+ /**
+ * This utility class cannot be instantiated
+ */
+ private BluetoothUtils() {}
+
+ /**
+ * Timeout value for synchronous binder call
+ */
+ private static final Duration SYNC_CALLS_TIMEOUT = Duration.ofSeconds(5);
+
+ /**
+ * @return timeout value for synchronous binder call
+ */
+ static Duration getSyncTimeout() {
+ return SYNC_CALLS_TIMEOUT;
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothVolumeControl.java b/core/java/android/bluetooth/BluetoothVolumeControl.java
index ba83eca..27532aa 100644
--- a/core/java/android/bluetooth/BluetoothVolumeControl.java
+++ b/core/java/android/bluetooth/BluetoothVolumeControl.java
@@ -17,6 +17,8 @@
package android.bluetooth;
+import static android.bluetooth.BluetoothUtils.getSyncTimeout;
+
import android.Manifest;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -29,14 +31,16 @@
import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
import android.content.AttributionSource;
import android.content.Context;
-import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.CloseGuard;
import android.util.Log;
+import com.android.modules.utils.SynchronousResultReceiver;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeoutException;
/**
* This class provides the public APIs to control the Bluetooth Volume Control service.
@@ -86,7 +90,7 @@
IBluetoothVolumeControl.class.getName()) {
@Override
public IBluetoothVolumeControl getServiceInterface(IBinder service) {
- return IBluetoothVolumeControl.Stub.asInterface(Binder.allowBlocking(service));
+ return IBluetoothVolumeControl.Stub.asInterface(service);
}
};
@@ -134,17 +138,23 @@
public @NonNull List<BluetoothDevice> getConnectedDevices() {
if (DBG) log("getConnectedDevices()");
final IBluetoothVolumeControl service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevices(mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getConnectedDevices(mAttributionSource), mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -159,18 +169,23 @@
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (DBG) log("getDevicesMatchingStates()");
final IBluetoothVolumeControl service = getService();
- if (service != null && isEnabled()) {
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getDevicesMatchingConnectionStates(states, mAttributionSource, recv);
return Attributable.setAttributionSource(
- service.getDevicesMatchingConnectionStates(states, mAttributionSource),
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ return defaultValue;
}
/**
@@ -185,16 +200,20 @@
public int getConnectionState(BluetoothDevice device) {
if (DBG) log("getConnectionState(" + device + ")");
final IBluetoothVolumeControl service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionState(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.STATE_DISCONNECTED;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionState(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.STATE_DISCONNECTED;
+ return defaultValue;
}
/**
@@ -212,18 +231,19 @@
})
public void setVolume(@Nullable BluetoothDevice device,
@IntRange(from = 0, to = 255) int volume) {
- if (DBG)
- log("setVolume(" + volume + ")");
+ if (DBG) log("setVolume(" + volume + ")");
final IBluetoothVolumeControl service = getService();
- try {
- if (service != null && isEnabled()) {
- service.setVolume(device, volume, mAttributionSource);
- return;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
+ try {
+ final SynchronousResultReceiver recv = new SynchronousResultReceiver();
+ service.setVolume(device, volume, mAttributionSource, recv);
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
- if (service == null)
- Log.w(TAG, "Proxy not attached to service");
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
}
}
@@ -249,20 +269,22 @@
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
final IBluetoothVolumeControl service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
- if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
- && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
- return false;
- }
+ final boolean defaultValue = false;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)
+ && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+ || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
try {
- return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
+ return defaultValue;
}
/**
@@ -285,16 +307,20 @@
public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothVolumeControl service = getService();
- if (service != null && isEnabled() && isValidDevice(device)) {
+ final int defaultValue = BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
try {
- return service.getConnectionPolicy(device, mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver();
+ service.getConnectionPolicy(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
}
}
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+ return defaultValue;
}
private boolean isEnabled() {
diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index 6ae2bb5..157e709 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -22,6 +22,7 @@
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.ActivityThread;
+import android.app.AppGlobals;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
@@ -191,10 +192,42 @@
return new ScopedParcelState(this);
}
- /** @hide */
- public static AttributionSource myAttributionSource() {
- return new AttributionSource(Process.myUid(), ActivityThread.currentOpPackageName(),
- /*attributionTag*/ null, (String[]) /*renouncedPermissions*/ null, /*next*/ null);
+ /**
+ * Returns a generic {@link AttributionSource} that represents the entire
+ * calling process.
+ *
+ * <p>Callers are <em>strongly</em> encouraged to use a more specific
+ * attribution source whenever possible, such as from
+ * {@link Context#getAttributionSource()}, since that enables developers to
+ * have more detailed and scoped control over attribution within
+ * sub-components of their app.
+ *
+ * @see Context#createAttributionContext(String)
+ * @see Context#getAttributionTag()
+ * @return a generic {@link AttributionSource} representing the entire
+ * calling process
+ * @throws IllegalStateException when no accurate {@link AttributionSource}
+ * can be determined
+ */
+ public static @NonNull AttributionSource myAttributionSource() {
+
+ final AttributionSource globalSource = ActivityThread.currentAttributionSource();
+ if (globalSource != null) {
+ return globalSource;
+ }
+
+ int uid = Process.myUid();
+ if (uid == Process.ROOT_UID) {
+ uid = Process.SYSTEM_UID;
+ }
+ try {
+ return new AttributionSource.Builder(uid)
+ .setPackageName(AppGlobals.getPackageManager().getPackagesForUid(uid)[0])
+ .build();
+ } catch (Exception ignored) {
+ }
+
+ throw new IllegalStateException("Failed to resolve AttributionSource");
}
/**
@@ -247,7 +280,7 @@
* whether the attribution source is one for the calling app to prevent the caller
* to pass you a source from another app without including themselves in the
* attribution chain.
- *f
+ *
* @return if the attribution source cannot be trusted to be from the caller.
*/
public boolean checkCallingUid() {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 983d0cc..1e6029f 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -5915,6 +5915,14 @@
public static final String EXTRA_UID = "android.intent.extra.UID";
/**
+ * Used as an optional int extra field in {@link android.content.Intent#ACTION_PACKAGE_ADDED}
+ * intents to supply the previous uid the package had been assigned.
+ * This would only be set when a package is leaving sharedUserId in an upgrade, or when a
+ * system app upgrade that had left sharedUserId is getting uninstalled.
+ */
+ public static final String EXTRA_PREVIOUS_UID = "android.intent.extra.PREVIOUS_UID";
+
+ /**
* @hide String array of package names.
*/
@SystemApi
@@ -5946,6 +5954,16 @@
public static final String EXTRA_REPLACING = "android.intent.extra.REPLACING";
/**
+ * Used as a boolean extra field in {@link android.content.Intent#ACTION_PACKAGE_REMOVED},
+ * {@link android.content.Intent#ACTION_UID_REMOVED}, and
+ * {@link android.content.Intent#ACTION_PACKAGE_ADDED}
+ * intents to indicate that this package is changing its UID.
+ * This would only be set when a package is leaving sharedUserId in an upgrade, or when a
+ * system app upgrade that had left sharedUserId is getting uninstalled.
+ */
+ public static final String EXTRA_UID_CHANGING = "android.intent.extra.UID_CHANGING";
+
+ /**
* Used as an int extra field in {@link android.app.AlarmManager} pending intents
* to tell the application being invoked how many pending alarms are being
* delivered with the intent. For one-shot alarms this will always be 1.
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 8ebb8ec..01bf49e 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -2432,27 +2432,10 @@
break;
}
- switch (config.uiMode & Configuration.UI_MODE_TYPE_MASK) {
- case Configuration.UI_MODE_TYPE_APPLIANCE:
- parts.add("appliance");
- break;
- case Configuration.UI_MODE_TYPE_DESK:
- parts.add("desk");
- break;
- case Configuration.UI_MODE_TYPE_TELEVISION:
- parts.add("television");
- break;
- case Configuration.UI_MODE_TYPE_CAR:
- parts.add("car");
- break;
- case Configuration.UI_MODE_TYPE_WATCH:
- parts.add("watch");
- break;
- case Configuration.UI_MODE_TYPE_VR_HEADSET:
- parts.add("vrheadset");
- break;
- default:
- break;
+ final String uiModeTypeString =
+ getUiModeTypeString(config.uiMode & Configuration.UI_MODE_TYPE_MASK);
+ if (uiModeTypeString != null) {
+ parts.add(uiModeTypeString);
}
switch (config.uiMode & Configuration.UI_MODE_NIGHT_MASK) {
@@ -2587,6 +2570,28 @@
}
/**
+ * @hide
+ */
+ public static String getUiModeTypeString(int uiModeType) {
+ switch (uiModeType) {
+ case Configuration.UI_MODE_TYPE_APPLIANCE:
+ return "appliance";
+ case Configuration.UI_MODE_TYPE_DESK:
+ return "desk";
+ case Configuration.UI_MODE_TYPE_TELEVISION:
+ return "television";
+ case Configuration.UI_MODE_TYPE_CAR:
+ return "car";
+ case Configuration.UI_MODE_TYPE_WATCH:
+ return "watch";
+ case Configuration.UI_MODE_TYPE_VR_HEADSET:
+ return "vrheadset";
+ default:
+ return null;
+ }
+ }
+
+ /**
* Generate a delta Configuration between <code>base</code> and <code>change</code>. The
* resulting delta can be used with {@link #updateFrom(Configuration)}.
* <p />
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
index ba9332d..c607195 100644
--- a/core/java/android/os/BatteryConsumer.java
+++ b/core/java/android/os/BatteryConsumer.java
@@ -161,6 +161,7 @@
*/
@IntDef(prefix = {"PROCESS_STATE_"}, value = {
PROCESS_STATE_ANY,
+ PROCESS_STATE_UNSPECIFIED,
PROCESS_STATE_FOREGROUND,
PROCESS_STATE_BACKGROUND,
PROCESS_STATE_FOREGROUND_SERVICE,
@@ -169,7 +170,8 @@
public @interface ProcessState {
}
- public static final int PROCESS_STATE_ANY = 0;
+ public static final int PROCESS_STATE_UNSPECIFIED = 0;
+ public static final int PROCESS_STATE_ANY = PROCESS_STATE_UNSPECIFIED;
public static final int PROCESS_STATE_FOREGROUND = 1;
public static final int PROCESS_STATE_BACKGROUND = 2;
public static final int PROCESS_STATE_FOREGROUND_SERVICE = 3;
@@ -180,7 +182,7 @@
static {
// Assign individually to avoid future mismatch
- sProcessStateNames[PROCESS_STATE_ANY] = "any";
+ sProcessStateNames[PROCESS_STATE_UNSPECIFIED] = "unspecified";
sProcessStateNames[PROCESS_STATE_FOREGROUND] = "fg";
sProcessStateNames[PROCESS_STATE_BACKGROUND] = "bg";
sProcessStateNames[PROCESS_STATE_FOREGROUND_SERVICE] = "fgs";
@@ -188,6 +190,7 @@
private static final int[] SUPPORTED_POWER_COMPONENTS_PER_PROCESS_STATE = {
POWER_COMPONENT_CPU,
+ POWER_COMPONENT_MOBILE_RADIO,
};
static final int COLUMN_INDEX_BATTERY_CONSUMER_TYPE = 0;
@@ -213,7 +216,7 @@
sb.append("powerComponent=").append(sPowerComponentNames[powerComponent]);
dimensionSpecified = true;
}
- if (processState != PROCESS_STATE_ANY) {
+ if (processState != PROCESS_STATE_UNSPECIFIED) {
if (dimensionSpecified) {
sb.append(", ");
}
@@ -283,7 +286,7 @@
if (mShortString == null) {
StringBuilder sb = new StringBuilder();
sb.append(powerComponentIdToString(powerComponent));
- if (processState != PROCESS_STATE_ANY) {
+ if (processState != PROCESS_STATE_UNSPECIFIED) {
sb.append(':');
sb.append(processStateToString(processState));
}
@@ -333,7 +336,7 @@
* for all values of other dimensions such as process state.
*/
public Key getKey(@PowerComponent int componentId) {
- return mData.getKey(componentId, PROCESS_STATE_ANY);
+ return mData.getKey(componentId, PROCESS_STATE_UNSPECIFIED);
}
/**
@@ -352,7 +355,7 @@
*/
public double getConsumedPower(@PowerComponent int componentId) {
return mPowerComponents.getConsumedPower(
- mData.getKeyOrThrow(componentId, PROCESS_STATE_ANY));
+ mData.getKeyOrThrow(componentId, PROCESS_STATE_UNSPECIFIED));
}
/**
@@ -374,7 +377,7 @@
*/
public @PowerModel int getPowerModel(@BatteryConsumer.PowerComponent int componentId) {
return mPowerComponents.getPowerModel(
- mData.getKeyOrThrow(componentId, PROCESS_STATE_ANY));
+ mData.getKeyOrThrow(componentId, PROCESS_STATE_UNSPECIFIED));
}
/**
@@ -706,7 +709,7 @@
if (isSupported) {
for (int processState = 0; processState < PROCESS_STATE_COUNT;
processState++) {
- if (processState == PROCESS_STATE_ANY) {
+ if (processState == PROCESS_STATE_UNSPECIFIED) {
continue;
}
@@ -789,7 +792,7 @@
@NonNull
public T setConsumedPower(@PowerComponent int componentId, double componentPower,
@PowerModel int powerModel) {
- mPowerComponentsBuilder.setConsumedPower(getKey(componentId, PROCESS_STATE_ANY),
+ mPowerComponentsBuilder.setConsumedPower(getKey(componentId, PROCESS_STATE_UNSPECIFIED),
componentPower, powerModel);
return (T) this;
}
@@ -825,8 +828,9 @@
@NonNull
public T setUsageDurationMillis(@UidBatteryConsumer.PowerComponent int componentId,
long componentUsageTimeMillis) {
- mPowerComponentsBuilder.setUsageDurationMillis(getKey(componentId, PROCESS_STATE_ANY),
- componentUsageTimeMillis);
+ mPowerComponentsBuilder
+ .setUsageDurationMillis(getKey(componentId, PROCESS_STATE_UNSPECIFIED),
+ componentUsageTimeMillis);
return (T) this;
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 584f3c4..fa209cc 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -669,7 +669,7 @@
case BatteryStats.Uid.PROCESS_STATE_FOREGROUND_SERVICE:
return BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
default:
- return BatteryConsumer.PROCESS_STATE_ANY;
+ return BatteryConsumer.PROCESS_STATE_UNSPECIFIED;
}
}
@@ -963,6 +963,13 @@
public abstract long getNetworkActivityPackets(int type, int which);
@UnsupportedAppUsage
public abstract long getMobileRadioActiveTime(int which);
+
+ /**
+ * Returns the amount of time (in microseconds) this UID was in the specified processState.
+ */
+ public abstract long getMobileRadioActiveTimeInProcessState(
+ @BatteryConsumer.ProcessState int processState);
+
public abstract int getMobileRadioActiveCount(int which);
/**
@@ -1061,6 +1068,16 @@
public abstract long getMobileRadioMeasuredBatteryConsumptionUC();
/**
+ * Returns the battery consumption (in microcoulombs) of the uid's radio usage when in the
+ * specified process state.
+ * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+ *
+ * {@hide}
+ */
+ public abstract long getMobileRadioMeasuredBatteryConsumptionUC(
+ @BatteryConsumer.ProcessState int processState);
+
+ /**
* Returns the battery consumption (in microcoulombs) of the screen while on and uid active,
* derived from on device power measurement data.
* Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index ed44fb6..a23dae8 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -556,7 +556,7 @@
}
String label = BatteryConsumer.powerComponentIdToString(componentId);
- if (key.processState != BatteryConsumer.PROCESS_STATE_ANY) {
+ if (key.processState != BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
label = label
+ "(" + BatteryConsumer.processStateToString(key.processState) + ")";
}
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index e2d7847..429450c 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -1308,10 +1308,11 @@
data.readCallingWorkSourceUid());
observer.callEnded(callSession, data.dataSize(), reply.dataSize(), workSourceUid);
}
+
+ checkParcel(this, code, reply, "Unreasonably large binder reply buffer");
+ reply.recycle();
+ data.recycle();
}
- checkParcel(this, code, reply, "Unreasonably large binder reply buffer");
- reply.recycle();
- data.recycle();
// Just in case -- we are done with the IPC, so there should be no more strict
// mode violations that have gathered for this thread. Either they have been
diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java
index e863111..590494c 100644
--- a/core/java/android/os/PowerComponents.java
+++ b/core/java/android/os/PowerComponents.java
@@ -18,6 +18,7 @@
import static android.os.BatteryConsumer.POWER_COMPONENT_ANY;
import static android.os.BatteryConsumer.POWER_COMPONENT_COUNT;
import static android.os.BatteryConsumer.PROCESS_STATE_ANY;
+import static android.os.BatteryConsumer.PROCESS_STATE_UNSPECIFIED;
import static android.os.BatteryConsumer.convertMahToDeciCoulombs;
import android.annotation.NonNull;
@@ -339,7 +340,7 @@
serializer.startTag(null, BatteryUsageStats.XML_TAG_COMPONENT);
serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_ID, componentId);
- if (key.processState != PROCESS_STATE_ANY) {
+ if (key.processState != PROCESS_STATE_UNSPECIFIED) {
serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_PROCESS_STATE,
key.processState);
}
@@ -398,7 +399,7 @@
switch (parser.getName()) {
case BatteryUsageStats.XML_TAG_COMPONENT: {
int componentId = -1;
- int processState = PROCESS_STATE_ANY;
+ int processState = PROCESS_STATE_UNSPECIFIED;
double powerMah = 0;
long durationMs = 0;
int model = BatteryConsumer.POWER_MODEL_UNDEFINED;
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 563d6cc3..b3639e4 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1546,6 +1546,17 @@
private static final String ACTION_CREATE_USER = "android.os.action.CREATE_USER";
/**
+ * Action to start an activity to create a supervised user.
+ * Only devices with non-empty config_supervisedUserCreationPackage support this.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MANAGE_USERS)
+ public static final String ACTION_CREATE_SUPERVISED_USER =
+ "android.os.action.CREATE_SUPERVISED_USER";
+
+ /**
* Extra containing a name for the user being created. Optional parameter passed to
* ACTION_CREATE_USER activity.
* @hide
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index a00dd51..e3c3969 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -5453,5 +5453,12 @@
*/
public static final String COLUMN_PHONE_NUMBER_SOURCE_IMS =
"phone_number_source_ims";
+
+ /**
+ * TelephonyProvider column name for the device's preferred usage setting.
+ *
+ * @hide
+ */
+ public static final String COLUMN_USAGE_SETTING = "usage_setting";
}
}
diff --git a/core/java/android/service/cloudsearch/OWNERS b/core/java/android/service/cloudsearch/OWNERS
new file mode 100644
index 0000000..aa4da3b
--- /dev/null
+++ b/core/java/android/service/cloudsearch/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 758286
+
+huiwu@google.com
+srazdan@google.com
diff --git a/core/java/android/service/selectiontoolbar/ISelectionToolbarRenderService.aidl b/core/java/android/service/selectiontoolbar/ISelectionToolbarRenderService.aidl
new file mode 100644
index 0000000..2bd99ac
--- /dev/null
+++ b/core/java/android/service/selectiontoolbar/ISelectionToolbarRenderService.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.selectiontoolbar;
+
+import android.view.selectiontoolbar.ISelectionToolbarCallback;
+import android.view.selectiontoolbar.ShowInfo;
+
+/**
+ * The service to render the selection toolbar menus.
+ *
+ * @hide
+ */
+oneway interface ISelectionToolbarRenderService {
+ void onShow(in ShowInfo showInfo, in ISelectionToolbarCallback callback);
+ void onHide(long widgetToken);
+ void onDismiss(long widgetToken);
+}
diff --git a/core/java/android/service/selectiontoolbar/OWNERS b/core/java/android/service/selectiontoolbar/OWNERS
new file mode 100644
index 0000000..5500b92
--- /dev/null
+++ b/core/java/android/service/selectiontoolbar/OWNERS
@@ -0,0 +1,10 @@
+# Bug component: 709498
+
+augale@google.com
+joannechung@google.com
+licha@google.com
+lpeter@google.com
+svetoslavganov@google.com
+toki@google.com
+tonymak@google.com
+tymtsai@google.com
\ No newline at end of file
diff --git a/core/java/android/service/selectiontoolbar/SelectionToolbarRenderCallback.java b/core/java/android/service/selectiontoolbar/SelectionToolbarRenderCallback.java
new file mode 100644
index 0000000..6468183
--- /dev/null
+++ b/core/java/android/service/selectiontoolbar/SelectionToolbarRenderCallback.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.selectiontoolbar;
+
+import android.view.selectiontoolbar.ToolbarMenuItem;
+import android.view.selectiontoolbar.WidgetInfo;
+
+/**
+ * The callback that the render service uses to communicate with the host of the selection toolbar
+ * container.
+ *
+ * @hide
+ */
+public interface SelectionToolbarRenderCallback {
+ /**
+ * The selection toolbar is shown.
+ */
+ void onShown(WidgetInfo widgetInfo);
+ /**
+ * The selection toolbar is hidden.
+ */
+ void onHidden(long widgetToken);
+ /**
+ * The selection toolbar is dismissed.
+ */
+ void onDismissed(long widgetToken);
+ /**
+ * The selection toolbar has changed.
+ */
+ void onWidgetUpdated(WidgetInfo info);
+ /**
+ * The menu item on the selection toolbar has been clicked.
+ */
+ void onMenuItemClicked(ToolbarMenuItem item);
+ /**
+ * The error occurred when operating on the selection toolbar.
+ */
+ void onError(int errorCode);
+}
diff --git a/core/java/android/service/selectiontoolbar/SelectionToolbarRenderService.java b/core/java/android/service/selectiontoolbar/SelectionToolbarRenderService.java
new file mode 100644
index 0000000..6f66c9f
--- /dev/null
+++ b/core/java/android/service/selectiontoolbar/SelectionToolbarRenderService.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.selectiontoolbar;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.selectiontoolbar.ISelectionToolbarCallback;
+import android.view.selectiontoolbar.ShowInfo;
+import android.view.selectiontoolbar.ToolbarMenuItem;
+import android.view.selectiontoolbar.WidgetInfo;
+
+/**
+ * Service for rendering selection toolbar.
+ *
+ * @hide
+ */
+public abstract class SelectionToolbarRenderService extends Service {
+
+ private static final String TAG = "SelectionToolbarRenderService";
+
+ /**
+ * The {@link Intent} that must be declared as handled by the service.
+ *
+ * <p>To be supported, the service must also require the
+ * {@link android.Manifest.permission#BIND_SELECTION_TOOLBAR_RENDER_SERVICE} permission so
+ * that other applications can not abuse it.
+ */
+ public static final String SERVICE_INTERFACE =
+ "android.service.selectiontoolbar.SelectionToolbarRenderService";
+
+ private Handler mHandler;
+
+ /**
+ * Binder to receive calls from system server.
+ */
+ private final ISelectionToolbarRenderService mInterface =
+ new ISelectionToolbarRenderService.Stub() {
+
+ @Override
+ public void onShow(ShowInfo showInfo, ISelectionToolbarCallback callback) {
+ mHandler.sendMessage(obtainMessage(SelectionToolbarRenderService::onShow,
+ SelectionToolbarRenderService.this, showInfo,
+ new RemoteCallbackWrapper(callback)));
+ }
+
+ @Override
+ public void onHide(long widgetToken) {
+ mHandler.sendMessage(obtainMessage(SelectionToolbarRenderService::onHide,
+ SelectionToolbarRenderService.this, widgetToken));
+ }
+
+ @Override
+ public void onDismiss(long widgetToken) {
+ mHandler.sendMessage(obtainMessage(SelectionToolbarRenderService::onDismiss,
+ SelectionToolbarRenderService.this, widgetToken));
+ }
+ };
+
+ @CallSuper
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mHandler = new Handler(Looper.getMainLooper(), null, true);
+ }
+
+ @Override
+ @Nullable
+ public final IBinder onBind(@NonNull Intent intent) {
+ if (SERVICE_INTERFACE.equals(intent.getAction())) {
+ return mInterface.asBinder();
+ }
+ Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent);
+ return null;
+ }
+
+
+ /**
+ * Called when showing the selection toolbar.
+ */
+ public abstract void onShow(ShowInfo showInfo, RemoteCallbackWrapper callbackWrapper);
+
+ /**
+ * Called when hiding the selection toolbar.
+ */
+ public abstract void onHide(long widgetToken);
+
+
+ /**
+ * Called when dismissing the selection toolbar.
+ */
+ public abstract void onDismiss(long widgetToken);
+
+ /**
+ * Add avadoc.
+ */
+ public static final class RemoteCallbackWrapper implements SelectionToolbarRenderCallback {
+
+ private final ISelectionToolbarCallback mRemoteCallback;
+
+ RemoteCallbackWrapper(ISelectionToolbarCallback remoteCallback) {
+ mRemoteCallback = remoteCallback;
+ }
+
+ @Override
+ public void onShown(WidgetInfo widgetInfo) {
+ try {
+ mRemoteCallback.onShown(widgetInfo);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void onHidden(long widgetToken) {
+ try {
+ mRemoteCallback.onHidden(widgetToken);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void onDismissed(long widgetToken) {
+ try {
+ mRemoteCallback.onDismissed(widgetToken);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void onWidgetUpdated(WidgetInfo widgetInfo) {
+ try {
+ mRemoteCallback.onWidgetUpdated(widgetInfo);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void onMenuItemClicked(ToolbarMenuItem item) {
+ try {
+ mRemoteCallback.onMenuItemClicked(item);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void onError(int errorCode) {
+ try {
+ mRemoteCallback.onError(errorCode);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+ }
+}
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index e7f8920..9eaaa91 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -36,18 +36,24 @@
import android.telephony.Annotation.RadioPowerState;
import android.telephony.Annotation.SimActivationState;
import android.telephony.Annotation.SrvccState;
+import android.telephony.TelephonyManager.CarrierPrivilegesListener;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.ImsReasonInfo;
import android.util.ArraySet;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.listeners.ListenerExecutor;
+import com.android.internal.telephony.ICarrierPrivilegesListener;
import com.android.internal.telephony.IOnSubscriptionsChangedListener;
import com.android.internal.telephony.ITelephonyRegistry;
+import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.WeakHashMap;
import java.util.concurrent.Executor;
/**
@@ -1214,4 +1220,117 @@
listenFromCallback(false, false, subId,
pkgName, attributionTag, callback, new int[0], notifyNow);
}
+
+ private static class CarrierPrivilegesListenerWrapper extends ICarrierPrivilegesListener.Stub
+ implements ListenerExecutor {
+ private final WeakReference<CarrierPrivilegesListener> mListener;
+ private final Executor mExecutor;
+
+ CarrierPrivilegesListenerWrapper(CarrierPrivilegesListener listener, Executor executor) {
+ mListener = new WeakReference<>(listener);
+ mExecutor = executor;
+ }
+
+ @Override
+ public void onCarrierPrivilegesChanged(
+ List<String> privilegedPackageNames, int[] privilegedUids) {
+ Binder.withCleanCallingIdentity(
+ () ->
+ executeSafely(
+ mExecutor,
+ mListener::get,
+ cpl ->
+ cpl.onCarrierPrivilegesChanged(
+ privilegedPackageNames, privilegedUids)));
+ }
+ }
+
+ @GuardedBy("sCarrierPrivilegeListeners")
+ private static final WeakHashMap<
+ CarrierPrivilegesListener, WeakReference<CarrierPrivilegesListenerWrapper>>
+ sCarrierPrivilegeListeners = new WeakHashMap<>();
+
+ /**
+ * Registers a {@link CarrierPrivilegesListener} on the given {@code logicalSlotIndex} to
+ * receive callbacks when the set of packages with carrier privileges changes. The callback will
+ * immediately be called with the latest state.
+ *
+ * @param logicalSlotIndex The SIM slot to listen on
+ * @param executor The executor where {@code listener} will be invoked
+ * @param listener The callback to register
+ */
+ public void addCarrierPrivilegesListener(
+ int logicalSlotIndex,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull CarrierPrivilegesListener listener) {
+ if (listener == null || executor == null) {
+ throw new IllegalArgumentException("listener and executor must be non-null");
+ }
+ synchronized (sCarrierPrivilegeListeners) {
+ WeakReference<CarrierPrivilegesListenerWrapper> existing =
+ sCarrierPrivilegeListeners.get(listener);
+ if (existing != null && existing.get() != null) {
+ Log.d(TAG, "addCarrierPrivilegesListener: listener already registered");
+ return;
+ }
+ CarrierPrivilegesListenerWrapper wrapper =
+ new CarrierPrivilegesListenerWrapper(listener, executor);
+ sCarrierPrivilegeListeners.put(listener, new WeakReference<>(wrapper));
+ try {
+ sRegistry.addCarrierPrivilegesListener(
+ logicalSlotIndex,
+ wrapper,
+ mContext.getOpPackageName(),
+ mContext.getAttributionTag());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Unregisters a {@link CarrierPrivilegesListener}.
+ *
+ * @param listener The callback to unregister
+ */
+ public void removeCarrierPrivilegesListener(@NonNull CarrierPrivilegesListener listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException("listener must be non-null");
+ }
+ synchronized (sCarrierPrivilegeListeners) {
+ WeakReference<CarrierPrivilegesListenerWrapper> ref =
+ sCarrierPrivilegeListeners.remove(listener);
+ if (ref == null) return;
+ CarrierPrivilegesListenerWrapper wrapper = ref.get();
+ if (wrapper == null) return;
+ try {
+ sRegistry.removeCarrierPrivilegesListener(wrapper, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Notify listeners that the set of packages with carrier privileges has changed.
+ *
+ * @param logicalSlotIndex The SIM slot the change occurred on
+ * @param privilegedPackageNames The updated set of packages names with carrier privileges
+ * @param privilegedUids The updated set of UIDs with carrier privileges
+ */
+ public void notifyCarrierPrivilegesChanged(
+ int logicalSlotIndex,
+ @NonNull List<String> privilegedPackageNames,
+ @NonNull int[] privilegedUids) {
+ if (privilegedPackageNames == null || privilegedUids == null) {
+ throw new IllegalArgumentException(
+ "privilegedPackageNames and privilegedUids must be non-null");
+ }
+ try {
+ sRegistry.notifyCarrierPrivilegesChanged(
+ logicalSlotIndex, privilegedPackageNames, privilegedUids);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index adb8b86..8db62f6 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -1872,7 +1872,7 @@
float x, float y, float pressure, float size, int metaState,
float xPrecision, float yPrecision, int deviceId, int edgeFlags) {
return obtain(downTime, eventTime, action, x, y, pressure, size, metaState,
- xPrecision, yPrecision, deviceId, edgeFlags, InputDevice.SOURCE_UNKNOWN,
+ xPrecision, yPrecision, deviceId, edgeFlags, InputDevice.SOURCE_CLASS_POINTER,
DEFAULT_DISPLAY);
}
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 07d5fc5..25e0eca 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -1896,7 +1896,7 @@
private Canvas mCanvas;
private Bitmap mBitmap;
- private boolean mEnabledHwBitmapsInSwMode;
+ private boolean mEnabledHwFeaturesInSwMode;
@Override
public Canvas getCanvas(View view, int width, int height) {
@@ -1913,7 +1913,7 @@
if (mCanvas == null) {
mCanvas = new Canvas();
}
- mEnabledHwBitmapsInSwMode = mCanvas.isHwBitmapsInSwModeEnabled();
+ mEnabledHwFeaturesInSwMode = mCanvas.isHwFeaturesInSwModeEnabled();
mCanvas.setBitmap(mBitmap);
return mCanvas;
}
@@ -1921,7 +1921,7 @@
@Override
public Bitmap createBitmap() {
mCanvas.setBitmap(null);
- mCanvas.setHwBitmapsInSwModeEnabled(mEnabledHwBitmapsInSwMode);
+ mCanvas.setHwFeaturesInSwModeEnabled(mEnabledHwFeaturesInSwMode);
return mBitmap;
}
}
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 96198c6..7e6e6fd 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -141,6 +141,12 @@
private final int mHandledConfigChanges;
/**
+ * The flag whether this IME supports Handwriting using stylus input.
+ */
+ private final boolean mSupportsStylusHandwriting;
+
+
+ /**
* @param service the {@link ResolveInfo} corresponds in which the IME is implemented.
* @return a unique ID to be returned by {@link #getId()}. We have used
* {@link ComponentName#flattenToShortString()} for this purpose (and it is already
@@ -234,6 +240,8 @@
com.android.internal.R.styleable.InputMethod_showInInputMethodPicker, true);
mHandledConfigChanges = sa.getInt(
com.android.internal.R.styleable.InputMethod_configChanges, 0);
+ mSupportsStylusHandwriting = sa.getBoolean(
+ com.android.internal.R.styleable.InputMethod_supportsStylusHandwriting, false);
sa.recycle();
final int depth = parser.getDepth();
@@ -323,6 +331,7 @@
mService = ResolveInfo.CREATOR.createFromParcel(source);
mSubtypes = new InputMethodSubtypeArray(source);
mHandledConfigChanges = source.readInt();
+ mSupportsStylusHandwriting = source.readBoolean();
mForceDefault = false;
}
@@ -335,7 +344,7 @@
settingsActivity, null /* subtypes */, 0 /* isDefaultResId */,
false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */,
false /* inlineSuggestionsEnabled */, false /* isVrOnly */,
- 0 /* handledConfigChanges */);
+ 0 /* handledConfigChanges */, false /* supportsStylusHandwriting */);
}
/**
@@ -349,7 +358,8 @@
this(buildFakeResolveInfo(packageName, className, label), false /* isAuxIme */,
settingsActivity, null /* subtypes */, 0 /* isDefaultResId */,
false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */,
- false /* inlineSuggestionsEnabled */, false /* isVrOnly */, handledConfigChanges);
+ false /* inlineSuggestionsEnabled */, false /* isVrOnly */, handledConfigChanges,
+ false /* supportsStylusHandwriting */);
}
/**
@@ -361,7 +371,8 @@
boolean forceDefault) {
this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault,
true /* supportsSwitchingToNextInputMethod */, false /* inlineSuggestionsEnabled */,
- false /* isVrOnly */, 0 /* handledconfigChanges */);
+ false /* isVrOnly */, 0 /* handledconfigChanges */,
+ false /* supportsStylusHandwriting */);
}
/**
@@ -373,7 +384,7 @@
boolean supportsSwitchingToNextInputMethod, boolean isVrOnly) {
this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault,
supportsSwitchingToNextInputMethod, false /* inlineSuggestionsEnabled */, isVrOnly,
- 0 /* handledConfigChanges */);
+ 0 /* handledConfigChanges */, false /* supportsStylusHandwriting */);
}
/**
@@ -383,7 +394,7 @@
public InputMethodInfo(ResolveInfo ri, boolean isAuxIme, String settingsActivity,
List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault,
boolean supportsSwitchingToNextInputMethod, boolean inlineSuggestionsEnabled,
- boolean isVrOnly, int handledConfigChanges) {
+ boolean isVrOnly, int handledConfigChanges, boolean supportsStylusHandwriting) {
final ServiceInfo si = ri.serviceInfo;
mService = ri;
mId = new ComponentName(si.packageName, si.name).flattenToShortString();
@@ -398,6 +409,7 @@
mShowInInputMethodPicker = true;
mIsVrOnly = isVrOnly;
mHandledConfigChanges = handledConfigChanges;
+ mSupportsStylusHandwriting = supportsStylusHandwriting;
}
private static ResolveInfo buildFakeResolveInfo(String packageName, String className,
@@ -556,6 +568,14 @@
return mHandledConfigChanges;
}
+ /**
+ * Returns if IME supports handwriting using stylus input.
+ * @attr ref android.R.styleable#InputMethod_supportsStylusHandwriting
+ */
+ public boolean supportsStylusHandwriting() {
+ return mSupportsStylusHandwriting;
+ }
+
public void dump(Printer pw, String prefix) {
pw.println(prefix + "mId=" + mId
+ " mSettingsActivityName=" + mSettingsActivityName
@@ -563,7 +583,8 @@
+ " mSupportsSwitchingToNextInputMethod=" + mSupportsSwitchingToNextInputMethod
+ " mInlineSuggestionsEnabled=" + mInlineSuggestionsEnabled
+ " mSuppressesSpellChecker=" + mSuppressesSpellChecker
- + " mShowInInputMethodPicker=" + mShowInInputMethodPicker);
+ + " mShowInInputMethodPicker=" + mShowInInputMethodPicker
+ + " mSupportsStylusHandwriting=" + mSupportsStylusHandwriting);
pw.println(prefix + "mIsDefaultResId=0x"
+ Integer.toHexString(mIsDefaultResId));
pw.println(prefix + "Service:");
@@ -667,6 +688,7 @@
mService.writeToParcel(dest, flags);
mSubtypes.writeToParcel(dest);
dest.writeInt(mHandledConfigChanges);
+ dest.writeBoolean(mSupportsStylusHandwriting);
}
/**
diff --git a/core/java/android/view/selectiontoolbar/ISelectionToolbarCallback.aidl b/core/java/android/view/selectiontoolbar/ISelectionToolbarCallback.aidl
index 0e8e57b..48af7b9 100644
--- a/core/java/android/view/selectiontoolbar/ISelectionToolbarCallback.aidl
+++ b/core/java/android/view/selectiontoolbar/ISelectionToolbarCallback.aidl
@@ -25,8 +25,8 @@
*/
oneway interface ISelectionToolbarCallback {
void onShown(in WidgetInfo info);
- void onHidden();
- void onDismissed();
+ void onHidden(long widgetToken);
+ void onDismissed(long widgetToken);
void onWidgetUpdated(in WidgetInfo info);
void onMenuItemClicked(in ToolbarMenuItem item);
void onError(int errorCode);
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index c2224b4..209c64a 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -161,7 +161,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- static final int VERSION = 204;
+ static final int VERSION = 205;
// The maximum number of names wakelocks we will keep track of
// per uid; once the limit is reached, we batch the remaining wakelocks
@@ -240,6 +240,7 @@
private static final int[] SUPPORTED_PER_PROCESS_STATE_STANDARD_ENERGY_BUCKETS = {
MeasuredEnergyStats.POWER_BUCKET_CPU,
+ MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO,
};
// TimeInState counters need NUM_PROCESS_STATE states in order to accommodate
@@ -582,6 +583,8 @@
int UPDATE_ALL =
UPDATE_CPU | UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT | UPDATE_RPM | UPDATE_DISPLAY;
+ int UPDATE_ON_PROC_STATE_CHANGE = UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT;
+
@IntDef(flag = true, prefix = "UPDATE_", value = {
UPDATE_CPU,
UPDATE_WIFI,
@@ -608,6 +611,8 @@
Future<?> scheduleSyncDueToBatteryLevelChange(long delayMillis);
/** Schedule removal of UIDs corresponding to a removed user */
Future<?> scheduleCleanupDueToRemovedUser(int userId);
+ /** Schedule a sync because of a process state change */
+ Future<?> scheduleSyncDueToProcessStateChange(long delayMillis);
}
public Handler mHandler;
@@ -1703,7 +1708,6 @@
}
}
-
private static class TimeMultiStateCounter implements TimeBaseObs {
private final TimeBase mTimeBase;
private final LongMultiStateCounter mCounter;
@@ -1736,10 +1740,6 @@
mCounter.setEnabled(false, elapsedRealtimeUs / 1000);
}
- public LongMultiStateCounter getCounter() {
- return mCounter;
- }
-
public int getStateCount() {
return mCounter.getStateCount();
}
@@ -1753,8 +1753,8 @@
return mCounter.updateValue(value, timestampMs);
}
- public void addCount(long delta) {
- mCounter.addCount(delta);
+ private void increment(long increment, long timestampMs) {
+ mCounter.incrementValue(increment, timestampMs);
}
/**
@@ -1764,6 +1764,10 @@
return mCounter.getCount(procState);
}
+ public long getTotalCountLocked() {
+ return mCounter.getTotalCount();
+ }
+
public void logState(Printer pw, String prefix) {
pw.println(prefix + "mCounter=" + mCounter);
}
@@ -7985,7 +7989,7 @@
LongSamplingCounter[] mNetworkByteActivityCounters;
LongSamplingCounter[] mNetworkPacketActivityCounters;
- LongSamplingCounter mMobileRadioActiveTime;
+ TimeMultiStateCounter mMobileRadioActiveTime;
LongSamplingCounter mMobileRadioActiveCount;
/**
@@ -8195,6 +8199,7 @@
final int batteryConsumerProcessState =
mapUidProcessStateToBatteryConsumerProcessState(procState);
getCpuActiveTimeCounter().setState(batteryConsumerProcessState, elapsedTimeMs);
+ getMobileRadioActiveTimeCounter().setState(batteryConsumerProcessState, elapsedTimeMs);
final MeasuredEnergyStats energyStats =
getOrCreateMeasuredEnergyStatsIfSupportedLocked();
if (energyStats != null) {
@@ -8594,11 +8599,10 @@
/** Adds the given charge to the given standard power bucket for this uid. */
@GuardedBy("mBsi")
private void addChargeToStandardBucketLocked(long chargeDeltaUC,
- @StandardPowerBucket int powerBucket) {
+ @StandardPowerBucket int powerBucket, long timestampMs) {
final MeasuredEnergyStats measuredEnergyStats =
getOrCreateMeasuredEnergyStatsLocked();
- measuredEnergyStats.updateStandardBucket(powerBucket, chargeDeltaUC,
- mBsi.mClock.elapsedRealtime());
+ measuredEnergyStats.updateStandardBucket(powerBucket, chargeDeltaUC, timestampMs);
}
/** Adds the given charge to the given custom power bucket for this uid. */
@@ -8690,6 +8694,13 @@
@GuardedBy("mBsi")
@Override
+ public long getMobileRadioMeasuredBatteryConsumptionUC(int processState) {
+ return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO,
+ processState);
+ }
+
+ @GuardedBy("mBsi")
+ @Override
public long getScreenOnMeasuredBatteryConsumptionUC() {
return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON);
}
@@ -9228,9 +9239,7 @@
}
void noteNetworkActivityLocked(int type, long deltaBytes, long deltaPackets) {
- if (mNetworkByteActivityCounters == null) {
- initNetworkActivityLocked();
- }
+ ensureNetworkActivityLocked();
if (type >= 0 && type < NUM_NETWORK_ACTIVITY_TYPES) {
mNetworkByteActivityCounters[type].addCountLocked(deltaBytes);
mNetworkPacketActivityCounters[type].addCountLocked(deltaPackets);
@@ -9240,14 +9249,25 @@
}
}
- void noteMobileRadioActiveTimeLocked(long batteryUptime) {
- if (mNetworkByteActivityCounters == null) {
- initNetworkActivityLocked();
- }
- mMobileRadioActiveTime.addCountLocked(batteryUptime);
+ void noteMobileRadioActiveTimeLocked(long batteryUptimeDeltaUs, long elapsedTimeMs) {
+ ensureNetworkActivityLocked();
+ getMobileRadioActiveTimeCounter().increment(batteryUptimeDeltaUs, elapsedTimeMs);
mMobileRadioActiveCount.addCountLocked(1);
}
+ private TimeMultiStateCounter getMobileRadioActiveTimeCounter() {
+ if (mMobileRadioActiveTime == null) {
+ final long timestampMs = mBsi.mClock.elapsedRealtime();
+ mMobileRadioActiveTime = new TimeMultiStateCounter(
+ mBsi.mOnBatteryTimeBase, BatteryConsumer.PROCESS_STATE_COUNT, timestampMs);
+ mMobileRadioActiveTime.setState(
+ mapUidProcessStateToBatteryConsumerProcessState(mProcessState),
+ timestampMs);
+ mMobileRadioActiveTime.update(0, timestampMs);
+ }
+ return mMobileRadioActiveTime;
+ }
+
@Override
public boolean hasNetworkActivity() {
return mNetworkByteActivityCounters != null;
@@ -9275,8 +9295,20 @@
@Override
public long getMobileRadioActiveTime(int which) {
- return mMobileRadioActiveTime != null
- ? mMobileRadioActiveTime.getCountLocked(which) : 0;
+ return getMobileRadioActiveTimeInProcessState(BatteryConsumer.PROCESS_STATE_ANY);
+ }
+
+ @Override
+ public long getMobileRadioActiveTimeInProcessState(
+ @BatteryConsumer.ProcessState int processState) {
+ if (mMobileRadioActiveTime == null) {
+ return 0;
+ }
+ if (processState == BatteryConsumer.PROCESS_STATE_ANY) {
+ return mMobileRadioActiveTime.getTotalCountLocked();
+ } else {
+ return mMobileRadioActiveTime.getCountLocked(processState);
+ }
}
@Override
@@ -9388,18 +9420,17 @@
}
}
- void initNetworkActivityLocked() {
- detachIfNotNull(mNetworkByteActivityCounters);
+ void ensureNetworkActivityLocked() {
+ if (mNetworkByteActivityCounters != null) {
+ return;
+ }
+
mNetworkByteActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
- detachIfNotNull(mNetworkPacketActivityCounters);
mNetworkPacketActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
mNetworkByteActivityCounters[i] = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
mNetworkPacketActivityCounters[i] = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
}
- detachIfNotNull(mMobileRadioActiveTime);
- mMobileRadioActiveTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
- detachIfNotNull(mMobileRadioActiveCount);
mMobileRadioActiveCount = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
}
@@ -9896,7 +9927,12 @@
mNetworkByteActivityCounters[i].writeToParcel(out);
mNetworkPacketActivityCounters[i].writeToParcel(out);
}
- mMobileRadioActiveTime.writeToParcel(out);
+ if (mMobileRadioActiveTime != null) {
+ out.writeBoolean(true);
+ mMobileRadioActiveTime.writeToParcel(out);
+ } else {
+ out.writeBoolean(false);
+ }
mMobileRadioActiveCount.writeToParcel(out);
} else {
out.writeInt(0);
@@ -9996,6 +10032,7 @@
@GuardedBy("mBsi")
void readFromParcelLocked(TimeBase timeBase, TimeBase screenOffTimeBase, Parcel in) {
+ final long timestampMs = mBsi.mClock.elapsedRealtime();
mOnBatteryBackgroundTimeBase.readFromParcel(in);
mOnBatteryScreenOffBackgroundTimeBase.readFromParcel(in);
@@ -10205,7 +10242,14 @@
mNetworkPacketActivityCounters[i]
= new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in);
}
- mMobileRadioActiveTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in);
+ if (in.readBoolean()) {
+ final TimeMultiStateCounter counter =
+ new TimeMultiStateCounter(mBsi.mOnBatteryTimeBase, in, timestampMs);
+ if (counter.getStateCount() == BatteryConsumer.PROCESS_STATE_COUNT) {
+ mMobileRadioActiveTime = counter;
+ }
+ }
+
mMobileRadioActiveCount = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in);
} else {
mNetworkByteActivityCounters = null;
@@ -10247,7 +10291,6 @@
mScreenOffCpuFreqTimeMs = LongSamplingCounterArray.readFromParcel(
in, mBsi.mOnBatteryScreenOffTimeBase);
- final long timestampMs = mBsi.mClock.elapsedRealtime();
int stateCount = in.readInt();
if (stateCount != 0) {
final TimeMultiStateCounter counter = new TimeMultiStateCounter(
@@ -11182,6 +11225,9 @@
onBatteryScreenOffCounter.setState(uidRunningState, elapsedRealtimeMs);
}
+ final int prevBatteryConsumerProcessState =
+ mapUidProcessStateToBatteryConsumerProcessState(mProcessState);
+
mProcessState = uidRunningState;
updateOnBatteryBgTimeBase(uptimeMs * 1000, elapsedRealtimeMs * 1000);
@@ -11191,11 +11237,15 @@
mapUidProcessStateToBatteryConsumerProcessState(uidRunningState);
getCpuActiveTimeCounter().setState(batteryConsumerProcessState, elapsedRealtimeMs);
+ getMobileRadioActiveTimeCounter()
+ .setState(batteryConsumerProcessState, elapsedRealtimeMs);
final MeasuredEnergyStats energyStats =
getOrCreateMeasuredEnergyStatsIfSupportedLocked();
if (energyStats != null) {
energyStats.setState(batteryConsumerProcessState, elapsedRealtimeMs);
}
+ maybeScheduleExternalStatsSync(prevBatteryConsumerProcessState,
+ batteryConsumerProcessState);
}
if (userAwareService != mInForegroundService) {
@@ -11208,6 +11258,27 @@
}
}
+ @GuardedBy("mBsi")
+ private void maybeScheduleExternalStatsSync(
+ @BatteryConsumer.ProcessState int oldProcessState,
+ @BatteryConsumer.ProcessState int newProcessState) {
+ if (oldProcessState == newProcessState) {
+ return;
+ }
+ // Transitions between BACKGROUND and such non-foreground states like cached
+ // or nonexistent do not warrant doing a sync. If some of the stats for those
+ // proc states bleed into the PROCESS_STATE_BACKGROUND, that's ok.
+ if ((oldProcessState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED
+ && newProcessState == BatteryConsumer.PROCESS_STATE_BACKGROUND)
+ || (oldProcessState == BatteryConsumer.PROCESS_STATE_BACKGROUND
+ && newProcessState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED)) {
+ return;
+ }
+
+ mBsi.mExternalSync.scheduleSyncDueToProcessStateChange(
+ mBsi.mConstants.PROC_STATE_CHANGE_COLLECTION_DELAY_MS);
+ }
+
/** Whether to consider Uid to be in the background for background timebase purposes. */
public boolean isInBackground() {
// Note that PROCESS_STATE_CACHED and Uid.PROCESS_STATE_NONEXISTENT is
@@ -12805,7 +12876,8 @@
.calcGlobalPowerWithoutControllerDataMah(globalTimeMs);
}
distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_WIFI,
- consumedChargeUC, uidEstimatedConsumptionMah, totalEstimatedConsumptionMah);
+ consumedChargeUC, uidEstimatedConsumptionMah, totalEstimatedConsumptionMah,
+ elapsedRealtimeMs);
}
}
}
@@ -12963,7 +13035,7 @@
final long appPackets = entry.rxPackets + entry.txPackets;
final long appRadioTimeUs =
(totalAppRadioTimeUs * appPackets) / totalPackets;
- u.noteMobileRadioActiveTimeLocked(appRadioTimeUs);
+ u.noteMobileRadioActiveTimeLocked(appRadioTimeUs, elapsedRealtimeMs);
// Distribute measured mobile radio charge consumption based on app radio
// active time
@@ -13042,7 +13114,7 @@
distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO,
consumedChargeUC, uidEstimatedConsumptionMah,
- totalEstimatedConsumptionMah);
+ totalEstimatedConsumptionMah, elapsedRealtimeMs);
}
mNetworkStatsPool.release(delta);
@@ -13330,7 +13402,8 @@
= mBluetoothPowerCalculator.calculatePowerMah(rxTimeMs, txTimeMs, idleTimeMs);
totalEstimatedMah = Math.max(totalEstimatedMah, controllerMaMs / MILLISECONDS_IN_HOUR);
distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH,
- consumedChargeUC, uidEstimatedConsumptionMah, totalEstimatedMah);
+ consumedChargeUC, uidEstimatedConsumptionMah, totalEstimatedMah,
+ elapsedRealtimeMs);
}
mLastBluetoothActivityInfo.set(info);
@@ -13433,8 +13506,10 @@
}
if (totalCpuChargeUC <= 0) return;
+ final long timestampMs = mClock.elapsedRealtime();
+
mGlobalMeasuredEnergyStats.updateStandardBucket(MeasuredEnergyStats.POWER_BUCKET_CPU,
- totalCpuChargeUC, mClock.elapsedRealtime());
+ totalCpuChargeUC, timestampMs);
// Calculate the measured microcoulombs/calculated milliamp-hour charge ratio for each
// cluster to normalize each uid's estimated power usage against actual power usage for
@@ -13483,7 +13558,7 @@
}
uid.addChargeToStandardBucketLocked(uidCpuChargeUC,
- MeasuredEnergyStats.POWER_BUCKET_CPU);
+ MeasuredEnergyStats.POWER_BUCKET_CPU, timestampMs);
}
}
@@ -13583,7 +13658,7 @@
fgTimeUsArray.put(uid.getUid(), (double) fgTimeUs);
}
distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON,
- totalScreenOnChargeUC, fgTimeUsArray, 0);
+ totalScreenOnChargeUC, fgTimeUsArray, 0, elapsedRealtimeMs);
}
/**
@@ -13627,7 +13702,7 @@
gnssTimeUsArray.put(uid.getUid(), (double) gnssTimeUs);
}
distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_GNSS, chargeUC,
- gnssTimeUsArray, 0);
+ gnssTimeUsArray, 0, elapsedRealtimeMs);
}
/**
@@ -13699,7 +13774,7 @@
@SuppressWarnings("GuardedBy") // errorprone false positive on u.addChargeToStandardBucketLocked
private void distributeEnergyToUidsLocked(@StandardPowerBucket int bucket,
long totalConsumedChargeUC, SparseDoubleArray ratioNumerators,
- double minRatioDenominator) {
+ double minRatioDenominator, long timestampMs) {
// If the sum of all app usage was greater than the total, use that instead:
double sumRatioNumerators = 0;
@@ -13714,7 +13789,7 @@
final double ratioNumerator = ratioNumerators.valueAt(i);
final long uidActualUC
= (long) (totalConsumedChargeUC * ratioNumerator / ratioDenominator + 0.5);
- uid.addChargeToStandardBucketLocked(uidActualUC, bucket);
+ uid.addChargeToStandardBucketLocked(uidActualUC, bucket, timestampMs);
}
}
@@ -14433,7 +14508,7 @@
if (childUid != null) {
final long delta =
childUid.cpuActiveCounter.update(cpuActiveTimesMs, elapsedRealtimeMs);
- u.getCpuActiveTimeCounter().addCount(delta);
+ u.getCpuActiveTimeCounter().increment(delta, elapsedRealtimeMs);
}
}
});
@@ -15627,7 +15702,6 @@
for (int procState = 0; procState < BatteryConsumer.PROCESS_STATE_COUNT; procState++) {
procStateNames[procState] = BatteryConsumer.processStateToString(procState);
}
- procStateNames[BatteryConsumer.PROCESS_STATE_ANY] = "untracked";
return procStateNames;
}
@@ -15651,6 +15725,8 @@
= "external_stats_collection_rate_limit_ms";
public static final String KEY_BATTERY_LEVEL_COLLECTION_DELAY_MS
= "battery_level_collection_delay_ms";
+ public static final String KEY_PROC_STATE_CHANGE_COLLECTION_DELAY_MS =
+ "procstate_change_collection_delay_ms";
public static final String KEY_MAX_HISTORY_FILES = "max_history_files";
public static final String KEY_MAX_HISTORY_BUFFER_KB = "max_history_buffer_kb";
public static final String KEY_BATTERY_CHARGED_DELAY_MS =
@@ -15661,6 +15737,7 @@
private static final long DEFAULT_UID_REMOVE_DELAY_MS = 5L * 60L * 1000L;
private static final long DEFAULT_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS = 600_000;
private static final long DEFAULT_BATTERY_LEVEL_COLLECTION_DELAY_MS = 300_000;
+ private static final long DEFAULT_PROC_STATE_CHANGE_COLLECTION_DELAY_MS = 60_000;
private static final int DEFAULT_MAX_HISTORY_FILES = 32;
private static final int DEFAULT_MAX_HISTORY_BUFFER_KB = 128; /*Kilo Bytes*/
private static final int DEFAULT_MAX_HISTORY_FILES_LOW_RAM_DEVICE = 64;
@@ -15676,6 +15753,8 @@
= DEFAULT_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS;
public long BATTERY_LEVEL_COLLECTION_DELAY_MS
= DEFAULT_BATTERY_LEVEL_COLLECTION_DELAY_MS;
+ public long PROC_STATE_CHANGE_COLLECTION_DELAY_MS =
+ DEFAULT_PROC_STATE_CHANGE_COLLECTION_DELAY_MS;
public int MAX_HISTORY_FILES;
public int MAX_HISTORY_BUFFER; /*Bytes*/
public int BATTERY_CHARGED_DELAY_MS = DEFAULT_BATTERY_CHARGED_DELAY_MS;
@@ -15742,6 +15821,9 @@
BATTERY_LEVEL_COLLECTION_DELAY_MS = mParser.getLong(
KEY_BATTERY_LEVEL_COLLECTION_DELAY_MS,
DEFAULT_BATTERY_LEVEL_COLLECTION_DELAY_MS);
+ PROC_STATE_CHANGE_COLLECTION_DELAY_MS = mParser.getLong(
+ KEY_PROC_STATE_CHANGE_COLLECTION_DELAY_MS,
+ DEFAULT_PROC_STATE_CHANGE_COLLECTION_DELAY_MS);
MAX_HISTORY_FILES = mParser.getInt(KEY_MAX_HISTORY_FILES,
ActivityManager.isLowRamDeviceStatic() ?
@@ -15793,6 +15875,8 @@
pw.println(EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS);
pw.print(KEY_BATTERY_LEVEL_COLLECTION_DELAY_MS); pw.print("=");
pw.println(BATTERY_LEVEL_COLLECTION_DELAY_MS);
+ pw.print(KEY_PROC_STATE_CHANGE_COLLECTION_DELAY_MS); pw.print("=");
+ pw.println(PROC_STATE_CHANGE_COLLECTION_DELAY_MS);
pw.print(KEY_MAX_HISTORY_FILES); pw.print("=");
pw.println(MAX_HISTORY_FILES);
pw.print(KEY_MAX_HISTORY_BUFFER_KB); pw.print("=");
@@ -16476,14 +16560,18 @@
}
if (in.readInt() != 0) {
- if (u.mNetworkByteActivityCounters == null) {
- u.initNetworkActivityLocked();
- }
+ u.ensureNetworkActivityLocked();
for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
u.mNetworkByteActivityCounters[i].readSummaryFromParcelLocked(in);
u.mNetworkPacketActivityCounters[i].readSummaryFromParcelLocked(in);
}
- u.mMobileRadioActiveTime.readSummaryFromParcelLocked(in);
+ if (in.readBoolean()) {
+ TimeMultiStateCounter counter = new TimeMultiStateCounter(
+ mOnBatteryTimeBase, in, elapsedRealtimeMs);
+ if (counter.getStateCount() == BatteryConsumer.PROCESS_STATE_COUNT) {
+ u.mMobileRadioActiveTime = counter;
+ }
+ }
u.mMobileRadioActiveCount.readSummaryFromParcelLocked(in);
}
@@ -17033,7 +17121,12 @@
u.mNetworkByteActivityCounters[i].writeSummaryFromParcelLocked(out);
u.mNetworkPacketActivityCounters[i].writeSummaryFromParcelLocked(out);
}
- u.mMobileRadioActiveTime.writeSummaryFromParcelLocked(out);
+ if (u.mMobileRadioActiveTime != null) {
+ out.writeBoolean(true);
+ u.mMobileRadioActiveTime.writeToParcel(out);
+ } else {
+ out.writeBoolean(false);
+ }
u.mMobileRadioActiveCount.writeSummaryFromParcelLocked(out);
}
diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java
index 175f28f..ee614cd 100644
--- a/core/java/com/android/internal/os/CpuPowerCalculator.java
+++ b/core/java/com/android/internal/os/CpuPowerCalculator.java
@@ -156,11 +156,11 @@
private void calculateMeasuredPowerPerProcessState(UidBatteryConsumer.Builder app,
BatteryStats.Uid u, BatteryConsumer.Key[] keys) {
for (BatteryConsumer.Key key : keys) {
- // The key for "PROCESS_STATE_ANY" has already been populated with the
- // full energy across all states. We don't want to override it with
+ // The key for PROCESS_STATE_UNSPECIFIED aka PROCESS_STATE_ANY has already been
+ // populated with the full energy across all states. We don't want to override it with
// the energy for "other" states, which excludes the tracked states like
// foreground, background etc.
- if (key.processState == BatteryConsumer.PROCESS_STATE_ANY) {
+ if (key.processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
continue;
}
@@ -184,7 +184,7 @@
uidProcState++) {
@BatteryConsumer.ProcessState int procState =
BatteryStats.mapUidProcessStateToBatteryConsumerProcessState(uidProcState);
- if (procState == BatteryConsumer.PROCESS_STATE_ANY) {
+ if (procState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
continue;
}
@@ -199,7 +199,7 @@
}
for (BatteryConsumer.Key key : keys) {
- if (key.processState == BatteryConsumer.PROCESS_STATE_ANY) {
+ if (key.processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
continue;
}
diff --git a/core/java/com/android/internal/os/LongMultiStateCounter.java b/core/java/com/android/internal/os/LongMultiStateCounter.java
index ad8e0ed..33a9d54 100644
--- a/core/java/com/android/internal/os/LongMultiStateCounter.java
+++ b/core/java/com/android/internal/os/LongMultiStateCounter.java
@@ -122,6 +122,13 @@
/**
* Adds the supplied values to the current accumulated values in the counter.
*/
+ public void incrementValue(long count, long timestampMs) {
+ native_incrementValue(mNativeObject, count, timestampMs);
+ }
+
+ /**
+ * Adds the supplied values to the current accumulated values in the counter.
+ */
public void addCount(long count) {
native_addCount(mNativeObject, count);
}
@@ -144,6 +151,17 @@
return native_getCount(mNativeObject, state);
}
+ /**
+ * Returns the total accumulated count across all states.
+ */
+ public long getTotalCount() {
+ long total = 0;
+ for (int state = 0; state < mStateCount; state++) {
+ total += native_getCount(mNativeObject, state);
+ }
+ return total;
+ }
+
@Override
public String toString() {
return native_toString(mNativeObject);
@@ -190,6 +208,10 @@
private static native long native_updateValue(long nativeObject, long value, long timestampMs);
@CriticalNative
+ private static native void native_incrementValue(long nativeObject, long increment,
+ long timestampMs);
+
+ @CriticalNative
private static native void native_addCount(long nativeObject, long count);
@CriticalNative
diff --git a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
index eb5993d..28cc836 100644
--- a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
+++ b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
@@ -34,6 +34,8 @@
private static final int NUM_SIGNAL_STRENGTH_LEVELS =
CellSignalStrength.getNumSignalStrengthLevels();
+ private static final BatteryConsumer.Key[] UNINITIALIZED_KEYS = new BatteryConsumer.Key[0];
+
private final UsageBasedPowerEstimator mActivePowerEstimator;
private final UsageBasedPowerEstimator[] mIdlePowerEstimators =
new UsageBasedPowerEstimator[NUM_SIGNAL_STRENGTH_LEVELS];
@@ -89,14 +91,22 @@
PowerAndDuration total = new PowerAndDuration();
- final double powerPerPacketMah = getMobilePowerPerPacket(batteryStats, rawRealtimeUs,
- BatteryStats.STATS_SINCE_CHARGED);
final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
builder.getUidBatteryConsumerBuilders();
+ BatteryConsumer.Key[] keys = UNINITIALIZED_KEYS;
+
for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
final BatteryStats.Uid uid = app.getBatteryStatsUid();
- calculateApp(app, uid, powerPerPacketMah, total, query);
+ if (keys == UNINITIALIZED_KEYS) {
+ if (query.isProcessStateDataNeeded()) {
+ keys = app.getKeys(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO);
+ } else {
+ keys = null;
+ }
+ }
+
+ calculateApp(app, uid, total, query, keys);
}
final long consumptionUC = batteryStats.getMobileRadioMeasuredBatteryConsumptionUC();
@@ -121,34 +131,49 @@
}
private void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
- double powerPerPacketMah, PowerAndDuration total,
- BatteryUsageStatsQuery query) {
+ PowerAndDuration total,
+ BatteryUsageStatsQuery query, BatteryConsumer.Key[] keys) {
final long radioActiveDurationMs = calculateDuration(u, BatteryStats.STATS_SINCE_CHARGED);
total.totalAppDurationMs += radioActiveDurationMs;
final long consumptionUC = u.getMobileRadioMeasuredBatteryConsumptionUC();
final int powerModel = getPowerModel(consumptionUC, query);
- final double powerMah = calculatePower(u, powerModel, powerPerPacketMah,
- radioActiveDurationMs, consumptionUC);
+ final double powerMah = calculatePower(u, powerModel, radioActiveDurationMs, consumptionUC);
total.totalAppPowerMah += powerMah;
app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
radioActiveDurationMs)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, powerMah,
powerModel);
+
+ if (query.isProcessStateDataNeeded() && keys != null) {
+ for (BatteryConsumer.Key key: keys) {
+ final int processState = key.processState;
+ if (processState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
+ // Already populated with the total across all process states
+ continue;
+ }
+
+ final long durationInStateMs =
+ u.getMobileRadioActiveTimeInProcessState(processState) / 1000;
+ final long consumptionInStateUc =
+ u.getMobileRadioMeasuredBatteryConsumptionUC(processState);
+ final double powerInStateMah = calculatePower(u, powerModel, durationInStateMs,
+ consumptionInStateUc);
+ app.setConsumedPower(key, powerInStateMah, powerModel);
+ }
+ }
}
@Override
public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
- final double mobilePowerPerPacket = getMobilePowerPerPacket(batteryStats, rawRealtimeUs,
- statsType);
PowerAndDuration total = new PowerAndDuration();
for (int i = sippers.size() - 1; i >= 0; i--) {
final BatterySipper app = sippers.get(i);
if (app.drainType == BatterySipper.DrainType.APP) {
final BatteryStats.Uid u = app.uidObj;
- calculateApp(app, u, statsType, mobilePowerPerPacket, total);
+ calculateApp(app, u, statsType, total);
}
}
@@ -172,13 +197,12 @@
}
private void calculateApp(BatterySipper app, BatteryStats.Uid u, int statsType,
- double powerPerPacketMah, PowerAndDuration total) {
+ PowerAndDuration total) {
app.mobileActive = calculateDuration(u, statsType);
final long consumptionUC = u.getMobileRadioMeasuredBatteryConsumptionUC();
final int powerModel = getPowerModel(consumptionUC);
- app.mobileRadioPowerMah = calculatePower(u, powerModel, powerPerPacketMah, app.mobileActive,
- consumptionUC);
+ app.mobileRadioPowerMah = calculatePower(u, powerModel, app.mobileActive, consumptionUC);
total.totalAppDurationMs += app.mobileActive;
// Add cost of mobile traffic.
@@ -205,26 +229,15 @@
}
private double calculatePower(BatteryStats.Uid u, @BatteryConsumer.PowerModel int powerModel,
- double powerPerPacketMah, long radioActiveDurationMs, long measuredChargeUC) {
+ long radioActiveDurationMs, long measuredChargeUC) {
if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) {
return uCtoMah(measuredChargeUC);
}
if (radioActiveDurationMs > 0) {
- // We are tracking when the radio is up, so can use the active time to
- // determine power use.
return calcPowerFromRadioActiveDurationMah(radioActiveDurationMs);
- } else {
- // We are not tracking when the radio is up, so must approximate power use
- // based on the number of packets.
- final long mobileRxPackets = u.getNetworkActivityPackets(
- BatteryStats.NETWORK_MOBILE_RX_DATA,
- BatteryStats.STATS_SINCE_CHARGED);
- final long mobileTxPackets = u.getNetworkActivityPackets(
- BatteryStats.NETWORK_MOBILE_TX_DATA,
- BatteryStats.STATS_SINCE_CHARGED);
- return (mobileRxPackets + mobileTxPackets) * powerPerPacketMah;
}
+ return 0;
}
private void calculateRemaining(PowerAndDuration total,
@@ -299,21 +312,4 @@
public double calcScanTimePowerMah(long scanningTimeMs) {
return mScanPowerEstimator.calculatePower(scanningTimeMs);
}
-
- /**
- * Return estimated power (in mAh) of sending or receiving a packet with the mobile radio.
- */
- private double getMobilePowerPerPacket(BatteryStats stats, long rawRealtimeUs, int statsType) {
- final long radioDataUptimeMs =
- stats.getMobileRadioActiveTime(rawRealtimeUs, statsType) / 1000;
- final double mobilePower = calcPowerFromRadioActiveDurationMah(radioDataUptimeMs);
-
- final long mobileRx = stats.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_RX_DATA,
- statsType);
- final long mobileTx = stats.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_TX_DATA,
- statsType);
- final long mobilePackets = mobileRx + mobileTx;
-
- return mobilePackets != 0 ? mobilePower / mobilePackets : 0;
- }
}
diff --git a/core/java/com/android/internal/telephony/ICarrierPrivilegesListener.aidl b/core/java/com/android/internal/telephony/ICarrierPrivilegesListener.aidl
new file mode 100644
index 0000000..6ca8cec
--- /dev/null
+++ b/core/java/com/android/internal/telephony/ICarrierPrivilegesListener.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+oneway interface ICarrierPrivilegesListener {
+ void onCarrierPrivilegesChanged(
+ in List<String> privilegedPackageNames, in int[] privilegedUids);
+}
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 15d4246..9712d7e 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -32,6 +32,7 @@
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.emergency.EmergencyNumber;
+import com.android.internal.telephony.ICarrierPrivilegesListener;
import com.android.internal.telephony.IPhoneStateListener;
import com.android.internal.telephony.IOnSubscriptionsChangedListener;
@@ -100,4 +101,10 @@
void notifyAllowedNetworkTypesChanged(in int phoneId, in int subId, in int reason, in long allowedNetworkType);
void notifyLinkCapacityEstimateChanged(in int phoneId, in int subId,
in List<LinkCapacityEstimate> linkCapacityEstimateList);
+
+ void addCarrierPrivilegesListener(
+ int phoneId, ICarrierPrivilegesListener callback, String pkg, String featureId);
+ void removeCarrierPrivilegesListener(ICarrierPrivilegesListener callback, String pkg);
+ void notifyCarrierPrivilegesChanged(
+ int phoneId, in List<String> privilegedPackageNames, in int[] privilegedUids);
}
diff --git a/core/jni/com_android_internal_os_LongMultiStateCounter.cpp b/core/jni/com_android_internal_os_LongMultiStateCounter.cpp
index 45652a7..69c4f3d 100644
--- a/core/jni/com_android_internal_os_LongMultiStateCounter.cpp
+++ b/core/jni/com_android_internal_os_LongMultiStateCounter.cpp
@@ -84,6 +84,10 @@
return (jlong)asLongMultiStateCounter(nativePtr)->updateValue((int64_t)value, timestamp);
}
+static void native_incrementValue(jlong nativePtr, jlong count, jlong timestamp) {
+ asLongMultiStateCounter(nativePtr)->incrementValue(count, timestamp);
+}
+
static void native_addCount(jlong nativePtr, jlong count) {
asLongMultiStateCounter(nativePtr)->addValue(count);
}
@@ -172,6 +176,8 @@
// @CriticalNative
{"native_updateValue", "(JJJ)J", (void *)native_updateValue},
// @CriticalNative
+ {"native_incrementValue", "(JJJ)V", (void *)native_incrementValue},
+ // @CriticalNative
{"native_addCount", "(JJ)V", (void *)native_addCount},
// @CriticalNative
{"native_reset", "(J)V", (void *)native_reset},
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4f35f2c..e2a2ac6 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -6065,6 +6065,12 @@
<permission android:name="android.permission.LAUNCH_DEVICE_MANAGER_SETUP"
android:protectionLevel="signature|role" />
+ <!-- @SystemApi Allows an application to update certain device management related system
+ resources.
+ @hide -->
+ <permission android:name="android.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES"
+ android:protectionLevel="signature|role" />
+
<!-- Attribution for Geofencing service. -->
<attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
<!-- Attribution for Country Detector. -->
@@ -6566,6 +6572,16 @@
</intent-filter>
</service>
+ <!-- TODO: Move to ExtServices or relevant component. -->
+ <service android:name="com.android.server.selectiontoolbar.DefaultSelectionToolbarRenderService"
+ android:permission="android.permission.BIND_SELECTION_TOOLBAR_RENDER_SERVICE"
+ android:process=":ui"
+ android:exported="false">
+ <intent-filter>
+ <action android:name="android.service.selectiontoolbar.SelectionToolbarRenderService"/>
+ </intent-filter>
+ </service>
+
<provider
android:name="com.android.server.textclassifier.IconsContentProvider"
android:authorities="com.android.textclassifier.icons"
diff --git a/core/res/res/drawable/ic_add_supervised_user.xml b/core/res/res/drawable/ic_add_supervised_user.xml
new file mode 100644
index 0000000..a493775
--- /dev/null
+++ b/core/res/res/drawable/ic_add_supervised_user.xml
@@ -0,0 +1,34 @@
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="40dp"
+ android:height="40dp"
+ android:viewportWidth="20"
+ android:viewportHeight="20"
+ android:tint="?android:attr/colorControlNormal">
+
+ <group
+ android:scaleX="0.5"
+ android:scaleY="0.5">
+
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M15.625,22.5q-2.375,0 -4.063,-1.688 -1.687,-1.687 -1.687,-4.104 0,-2.375 1.688,-4.062 1.687,-1.688 4.062,-1.688 2.417,0 4.105,1.688 1.687,1.687 1.687,4.062 0,2.417 -1.688,4.105 -1.687,1.687 -4.104,1.687zM15.625,19.708q1.292,0 2.146,-0.875 0.854,-0.875 0.854,-2.125t-0.854,-2.125q-0.854,-0.875 -2.146,-0.875 -1.208,0 -2.104,0.875 -0.896,0.875 -0.896,2.125t0.896,2.125q0.896,0.875 2.104,0.875zM27.875,24.333q-1.792,0 -3.063,-1.27 -1.27,-1.271 -1.27,-3.063 0,-1.792 1.27,-3.063 1.271,-1.27 3.063,-1.27 1.833,0 3.083,1.27 1.25,1.271 1.25,3.063 0,1.792 -1.25,3.063 -1.25,1.27 -3.083,1.27zM17.458,33.667q1.959,-3.75 5.063,-5.063 3.104,-1.312 5.354,-1.312 0.958,0 1.813,0.145 0.854,0.146 1.729,0.396 1.041,-1.541 1.75,-3.583 0.708,-2.042 0.708,-4.25 0,-5.792 -4.041,-9.834Q25.791,6.125 20,6.125t-9.834,4.041Q6.125,14.208 6.125,20q0,2.042 0.563,3.938 0.562,1.895 1.604,3.437 1.625,-0.833 3.52,-1.333 1.896,-0.5 3.813,-0.5 1.208,0 2.333,0.188 1.125,0.187 2.125,0.52 -0.833,0.458 -1.562,1 -0.729,0.542 -1.354,1.125 -0.459,-0.042 -0.813,-0.042h-0.729q-1.375,0 -2.916,0.355 -1.542,0.354 -2.751,0.937 1.5,1.583 3.438,2.645 1.937,1.063 4.062,1.397zM20,36.667q-3.417,0 -6.459,-1.313 -3.041,-1.312 -5.312,-3.583 -2.271,-2.271 -3.583,-5.313Q3.333,23.418 3.333,20q0,-3.458 1.313,-6.479Q5.958,10.5 8.229,8.229t5.313,-3.583Q16.582,3.333 20,3.333q3.458,0 6.479,1.313 3.021,1.312 5.292,3.583t3.584,5.292q1.312,3.021 1.312,6.479 0,3.417 -1.313,6.459 -1.312,3.041 -3.583,5.312 -2.271,2.271 -5.292,3.584 -3.021,1.312 -6.479,1.312z"/>
+
+ </group>
+
+</vector>
+
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 6076645..8aa9201 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3638,6 +3638,9 @@
to re-retrieve all resources (including view layouts, drawables, etc)
to correctly handle any configuration change.-->
<attr name="configChanges" />
+ <!-- Specifies whether the IME supports Handwriting using stylus. Defaults to false. -->
+ <attr name="supportsStylusHandwriting" format="boolean" />
+
</declare-styleable>
<!-- This is the subtype of InputMethod. Subtype can describe locales (for example, en_US and
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 4feee41..050d20e 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3333,6 +3333,20 @@
and one pSIM) -->
<integer name="config_num_physical_slots">1</integer>
+ <!--The default "usage setting" indicating that the device is either a voice-centric
+ device (1) or a data-centric device (2). A voice-centric device will require that any cellular
+ service that it uses provides access to voice capability, and a data-centric device will
+ likewise require that the network provides access to data services. These settings are
+ sent to the cellular modem and control the behavior in accordance with 3gpp TS 24.301 sec 4.3
+ (and equivalent functionality in other generations of cellular).-->
+ <integer name="config_default_cellular_usage_setting">1</integer>
+
+ <!--The list of supported cellular usage settings for this device.-->
+ <integer-array translatable="false" name="config_supported_cellular_usage_settings">
+ <item>1</item> <!-- USAGE_SETTING_VOICE_CENTRIC -->
+ <item>2</item> <!-- USAGE_SETTING_DATA_CENTRIC -->
+ </integer-array>
+
<!-- When a radio power off request is received, we will delay completing the request until
either IMS moves to the deregistered state or the timeout defined by this configuration
elapses. If 0, this feature is disabled and we do not delay radio power off requests.-->
@@ -5523,4 +5537,13 @@
</string-array>
<integer name="config_chooser_max_targets_per_row">4</integer>
+
+ <!-- Package that provides the supervised user creation flow. This package must include an
+ activity with an intent filter for {@link UserManager.ACTION_CREATE_SUPERVISED_USER}.
+ When this resource is defined, an extra button in user settings screen will be shown
+ with a title defined in @*android:string/supervised_user_creation_label
+ and an icon defined in @*android:drawable/ic_add_supervised_user.
+ That button will fire an intent targeted for this package with the mentioned action.
+ When this resource is empty, that button will not be shown. -->
+ <string name="config_supervisedUserCreationPackage" translatable="false"></string>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index b9c7564..3d3c860 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3249,6 +3249,7 @@
<public name="canDisplayOnRemoteDevices" />
<public name="supportedTypes" />
<public name="resetEnabledSettingsOnAppDataCleared" />
+ <public name="supportsStylusHandwriting" />
</staging-public-group>
<staging-public-group type="id" first-id="0x01de0000">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 769e667..b16e462 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5374,6 +5374,8 @@
<string name="user_creation_account_exists">Allow <xliff:g id="app" example="Gmail">%1$s</xliff:g> to create a new User with <xliff:g id="account" example="foobar@gmail.com">%2$s</xliff:g> (a User with this account already exists) ?</string>
<!-- Message to user that app is trying to create user for a specified account. [CHAR LIMIT=none] -->
<string name="user_creation_adding">Allow <xliff:g id="app" example="Gmail">%1$s</xliff:g> to create a new User with <xliff:g id="account" example="foobar@gmail.com">%2$s</xliff:g> ?</string>
+ <!-- String label displayed on buttons that trigger the flow for creating supervised users. [CHAR LIMIT=35] -->
+ <string name="supervised_user_creation_label">Add supervised user</string>
<!-- Locale picker strings -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 55bf24b..504ad23 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -492,6 +492,8 @@
<java-symbol type="string" name="config_deviceSpecificDevicePolicyManagerService" />
<java-symbol type="string" name="config_deviceSpecificAudioService" />
<java-symbol type="integer" name="config_num_physical_slots" />
+ <java-symbol type="integer" name="config_default_cellular_usage_setting" />
+ <java-symbol type="array" name="config_supported_cellular_usage_settings" />
<java-symbol type="integer" name="config_delay_for_ims_dereg_millis" />
<java-symbol type="array" name="config_integrityRuleProviderPackages" />
<java-symbol type="bool" name="config_useAssistantVolume" />
@@ -4629,4 +4631,6 @@
<java-symbol type="integer" name="config_mashPressVibrateTimeOnPowerButton" />
<java-symbol type="string" name="config_systemGameService" />
+
+ <java-symbol type="string" name="config_supervisedUserCreationPackage"/>
</resources>
diff --git a/core/tests/coretests/src/android/view/MotionEventTest.java b/core/tests/coretests/src/android/view/MotionEventTest.java
index 78a8f7b..c4c983d 100644
--- a/core/tests/coretests/src/android/view/MotionEventTest.java
+++ b/core/tests/coretests/src/android/view/MotionEventTest.java
@@ -16,6 +16,7 @@
package android.view;
+import static android.view.InputDevice.SOURCE_CLASS_POINTER;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_POINTER_DOWN;
import static android.view.MotionEvent.TOOL_TYPE_FINGER;
@@ -214,4 +215,27 @@
rotInvalid.transform(mat);
assertEquals(-1, rotInvalid.getSurfaceRotation());
}
+
+ @Test
+ public void testUsesPointerSourceByDefault() {
+ final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */,
+ ACTION_DOWN, 0 /* x */, 0 /* y */, 0 /* metaState */);
+ assertTrue(event.isFromSource(SOURCE_CLASS_POINTER));
+ }
+
+ @Test
+ public void testLocationOffsetOnlyAppliedToNonPointerSources() {
+ final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */,
+ ACTION_DOWN, 10 /* x */, 20 /* y */, 0 /* metaState */);
+ event.offsetLocation(40, 50);
+
+ // The offset should be applied since a pointer source is used by default.
+ assertEquals(50, (int) event.getX());
+ assertEquals(70, (int) event.getY());
+
+ // The offset should not be applied if the source is changed to a non-pointer source.
+ event.setSource(InputDevice.SOURCE_JOYSTICK);
+ assertEquals(10, (int) event.getX());
+ assertEquals(20, (int) event.getY());
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
index 4733f86..69e617a 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
@@ -83,7 +83,7 @@
final Parcel parcel = Parcel.obtain();
parcel.writeParcelable(outBatteryUsageStats, 0);
- assertThat(parcel.dataSize()).isLessThan(5000);
+ assertThat(parcel.dataSize()).isLessThan(5500);
parcel.setDataPosition(0);
diff --git a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
index 48a1da1..8d9d79d 100644
--- a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
@@ -26,6 +26,8 @@
import android.net.NetworkCapabilities;
import android.net.NetworkStats;
import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.BatteryUsageStatsQuery;
import android.os.Process;
import android.os.UidBatteryConsumer;
import android.telephony.DataConnectionRealTimeInfo;
@@ -37,12 +39,15 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.google.common.collect.Range;
+
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
+@SuppressWarnings("GuardedBy")
public class MobileRadioPowerCalculatorTest {
private static final double PRECISION = 0.00001;
private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
@@ -55,7 +60,7 @@
.setAveragePower(PowerProfile.POWER_RADIO_SCANNING, 720.0)
.setAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX, 1440.0)
.setAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX,
- new double[] {720.0, 1080.0, 1440.0, 1800.0, 2160.0})
+ new double[]{720.0, 1080.0, 1440.0, 1800.0, 2160.0})
.initMeasuredEnergyStatsLocked();
@Test
@@ -81,7 +86,7 @@
// Note established network
stats.noteNetworkInterfaceForTransports("cellular",
- new int[] { NetworkCapabilities.TRANSPORT_CELLULAR });
+ new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
// Note application network activity
NetworkStats networkStats = new NetworkStats(10000, 1)
@@ -89,7 +94,7 @@
mStatsRule.setNetworkStats(networkStats);
ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
- new int[] {100, 200, 300, 400, 500}, 600);
+ new int[]{100, 200, 300, 400, 500}, 600);
stats.noteModemControllerActivity(mai, POWER_DATA_UNAVAILABLE, 10000, 10000);
mStatsRule.setTime(12_000_000, 12_000_000);
@@ -119,6 +124,90 @@
}
@Test
+ public void testTimerBasedModel_byProcessState() {
+ BatteryStatsImpl stats = mStatsRule.getBatteryStats();
+ BatteryStatsImpl.Uid uid = stats.getUidStatsLocked(APP_UID);
+
+ mStatsRule.setTime(1000, 1000);
+ uid.setProcessStateForTest(
+ BatteryStats.Uid.PROCESS_STATE_FOREGROUND, 1000);
+
+ // Scan for a cell
+ stats.notePhoneStateLocked(ServiceState.STATE_OUT_OF_SERVICE,
+ TelephonyManager.SIM_STATE_READY,
+ 2000, 2000);
+
+ // Found a cell
+ stats.notePhoneStateLocked(ServiceState.STATE_IN_SERVICE, TelephonyManager.SIM_STATE_READY,
+ 5000, 5000);
+
+ // Note cell signal strength
+ SignalStrength signalStrength = mock(SignalStrength.class);
+ when(signalStrength.getLevel()).thenReturn(SignalStrength.SIGNAL_STRENGTH_MODERATE);
+ stats.notePhoneSignalStrengthLocked(signalStrength, 5000, 5000);
+
+ stats.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH,
+ 8_000_000_000L, APP_UID, 8000, 8000);
+
+ // Note established network
+ stats.noteNetworkInterfaceForTransports("cellular",
+ new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
+
+ // Note application network activity
+ mStatsRule.setNetworkStats(new NetworkStats(10000, 1)
+ .insertEntry("cellular", APP_UID, 0, 0, 1000, 100, 2000, 20, 100));
+
+ stats.noteModemControllerActivity(null, POWER_DATA_UNAVAILABLE, 10000, 10000);
+
+ uid.setProcessStateForTest(
+ BatteryStats.Uid.PROCESS_STATE_BACKGROUND, 11000);
+
+ mStatsRule.setNetworkStats(new NetworkStats(12000, 1)
+ .insertEntry("cellular", APP_UID, 0, 0, 1000, 250, 2000, 80, 200));
+
+ stats.noteModemControllerActivity(null, POWER_DATA_UNAVAILABLE, 12000, 12000);
+
+ assertThat(uid.getMobileRadioMeasuredBatteryConsumptionUC()).isAtMost(0);
+ // 12000-8000 = 4000 ms == 4_000_000 us
+ assertThat(uid.getMobileRadioActiveTimeInProcessState(BatteryConsumer.PROCESS_STATE_ANY))
+ .isEqualTo(4_000_000);
+ assertThat(uid.getMobileRadioActiveTimeInProcessState(
+ BatteryConsumer.PROCESS_STATE_FOREGROUND))
+ .isEqualTo(3_000_000);
+ assertThat(uid.getMobileRadioActiveTimeInProcessState(
+ BatteryConsumer.PROCESS_STATE_BACKGROUND))
+ .isEqualTo(1_000_000);
+ assertThat(uid.getMobileRadioActiveTimeInProcessState(
+ BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE))
+ .isEqualTo(0);
+
+ MobileRadioPowerCalculator calculator =
+ new MobileRadioPowerCalculator(mStatsRule.getPowerProfile());
+
+ mStatsRule.apply(new BatteryUsageStatsQuery.Builder()
+ .powerProfileModeledOnly()
+ .includePowerModels()
+ .includeProcessStateData()
+ .build(), calculator);
+
+ UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
+
+ final BatteryConsumer.Key foreground = uidConsumer.getKey(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND);
+ final BatteryConsumer.Key background = uidConsumer.getKey(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
+ BatteryConsumer.PROCESS_STATE_BACKGROUND);
+ final BatteryConsumer.Key fgs = uidConsumer.getKey(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE);
+
+ assertThat(uidConsumer.getConsumedPower(foreground)).isWithin(PRECISION).of(1.2);
+ assertThat(uidConsumer.getConsumedPower(background)).isWithin(PRECISION).of(0.4);
+ assertThat(uidConsumer.getConsumedPower(fgs)).isWithin(PRECISION).of(0);
+ }
+
+ @Test
public void testMeasuredEnergyBasedModel() {
BatteryStatsImpl stats = mStatsRule.getBatteryStats();
@@ -141,7 +230,7 @@
// Note established network
stats.noteNetworkInterfaceForTransports("cellular",
- new int[] { NetworkCapabilities.TRANSPORT_CELLULAR });
+ new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
// Note application network activity
NetworkStats networkStats = new NetworkStats(10000, 1)
@@ -149,7 +238,7 @@
mStatsRule.setNetworkStats(networkStats);
ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
- new int[] {100, 200, 300, 400, 500}, 600);
+ new int[]{100, 200, 300, 400, 500}, 600);
stats.noteModemControllerActivity(mai, 10_000_000, 10000, 10000);
mStatsRule.setTime(12_000_000, 12_000_000);
@@ -177,4 +266,87 @@
assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
.isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
}
+
+ @Test
+ public void testMeasuredEnergyBasedModel_byProcessState() {
+ BatteryStatsImpl stats = mStatsRule.getBatteryStats();
+ BatteryStatsImpl.Uid uid = stats.getUidStatsLocked(APP_UID);
+
+ mStatsRule.setTime(1000, 1000);
+ uid.setProcessStateForTest(
+ BatteryStats.Uid.PROCESS_STATE_FOREGROUND, 1000);
+
+ // Scan for a cell
+ stats.notePhoneStateLocked(ServiceState.STATE_OUT_OF_SERVICE,
+ TelephonyManager.SIM_STATE_READY,
+ 2000, 2000);
+
+ // Found a cell
+ stats.notePhoneStateLocked(ServiceState.STATE_IN_SERVICE, TelephonyManager.SIM_STATE_READY,
+ 5000, 5000);
+
+ // Note cell signal strength
+ SignalStrength signalStrength = mock(SignalStrength.class);
+ when(signalStrength.getLevel()).thenReturn(SignalStrength.SIGNAL_STRENGTH_MODERATE);
+ stats.notePhoneSignalStrengthLocked(signalStrength, 5000, 5000);
+
+ stats.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH,
+ 8_000_000_000L, APP_UID, 8000, 8000);
+
+ // Note established network
+ stats.noteNetworkInterfaceForTransports("cellular",
+ new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
+
+ // Note application network activity
+ mStatsRule.setNetworkStats(new NetworkStats(10000, 1)
+ .insertEntry("cellular", APP_UID, 0, 0, 1000, 100, 2000, 20, 100));
+
+ stats.noteModemControllerActivity(null, 10_000_000, 10000, 10000);
+
+ uid.setProcessStateForTest(
+ BatteryStats.Uid.PROCESS_STATE_BACKGROUND, 11000);
+
+ mStatsRule.setNetworkStats(new NetworkStats(12000, 1)
+ .insertEntry("cellular", APP_UID, 0, 0, 1000, 250, 2000, 80, 200));
+
+ stats.noteModemControllerActivity(null, 15_000_000, 12000, 12000);
+
+ mStatsRule.setTime(20000, 20000);
+
+ assertThat(uid.getMobileRadioMeasuredBatteryConsumptionUC())
+ .isIn(Range.open(20_000_000L, 21_000_000L));
+ assertThat(uid.getMobileRadioMeasuredBatteryConsumptionUC(
+ BatteryConsumer.PROCESS_STATE_FOREGROUND))
+ .isIn(Range.open(13_000_000L, 14_000_000L));
+ assertThat(uid.getMobileRadioMeasuredBatteryConsumptionUC(
+ BatteryConsumer.PROCESS_STATE_BACKGROUND))
+ .isIn(Range.open(7_000_000L, 8_000_000L));
+ assertThat(uid.getMobileRadioMeasuredBatteryConsumptionUC(
+ BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE))
+ .isEqualTo(0);
+
+ MobileRadioPowerCalculator calculator =
+ new MobileRadioPowerCalculator(mStatsRule.getPowerProfile());
+
+ mStatsRule.apply(new BatteryUsageStatsQuery.Builder()
+ .includePowerModels()
+ .includeProcessStateData()
+ .build(), calculator);
+
+ UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
+
+ final BatteryConsumer.Key foreground = uidConsumer.getKey(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND);
+ final BatteryConsumer.Key background = uidConsumer.getKey(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
+ BatteryConsumer.PROCESS_STATE_BACKGROUND);
+ final BatteryConsumer.Key fgs = uidConsumer.getKey(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
+ BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE);
+
+ assertThat(uidConsumer.getConsumedPower(foreground)).isWithin(PRECISION).of(3.62064);
+ assertThat(uidConsumer.getConsumedPower(background)).isWithin(PRECISION).of(2.08130);
+ assertThat(uidConsumer.getConsumedPower(fgs)).isWithin(PRECISION).of(0);
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index e4c83f1..4faf349 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -233,6 +233,11 @@
public Future<?> scheduleSyncDueToBatteryLevelChange(long delayMillis) {
return null;
}
+
+ @Override
+ public Future<?> scheduleSyncDueToProcessStateChange(long delayMillis) {
+ return null;
+ }
}
}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 624940b..e61a665 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -427,7 +427,6 @@
<permission name="android.permission.COMPANION_APPROVE_WIFI_CONNECTIONS" />
<permission name="android.permission.MANAGE_COMPANION_DEVICES" />
<permission name="android.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING" />
- <permission name="android.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION" />
<permission name="android.permission.REQUEST_COMPANION_PROFILE_WATCH" />
<permission name="android.permission.REQUEST_COMPANION_SELF_MANAGED" />
<!-- Permission required for testing registering pull atom callbacks. -->
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index a612265..425a378 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -67,7 +67,7 @@
* @hide
*/
protected int mDensity = Bitmap.DENSITY_NONE;
- private boolean mAllowHwBitmapsInSwMode = false;
+ private boolean mAllowHwFeaturesInSwMode = false;
protected void throwIfCannotDraw(Bitmap bitmap) {
if (bitmap.isRecycled()) {
@@ -101,14 +101,14 @@
public void drawArc(float left, float top, float right, float bottom, float startAngle,
float sweepAngle, boolean useCenter, @NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawArc(mNativeCanvasWrapper, left, top, right, bottom, startAngle, sweepAngle,
useCenter, paint.getNativeInstance());
}
public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter,
@NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
drawArc(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle, useCenter,
paint);
}
@@ -119,14 +119,14 @@
public void drawBitmap(@NonNull Bitmap bitmap, float left, float top, @Nullable Paint paint) {
throwIfCannotDraw(bitmap);
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawBitmap(mNativeCanvasWrapper, bitmap.getNativeInstance(), left, top,
paint != null ? paint.getNativeInstance() : 0, mDensity, mScreenDensity,
bitmap.mDensity);
}
public void drawBitmap(@NonNull Bitmap bitmap, @NonNull Matrix matrix, @Nullable Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawBitmapMatrix(mNativeCanvasWrapper, bitmap.getNativeInstance(), matrix.ni(),
paint != null ? paint.getNativeInstance() : 0);
}
@@ -137,7 +137,7 @@
throw new NullPointerException();
}
throwIfCannotDraw(bitmap);
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
final long nativePaint = paint == null ? 0 : paint.getNativeInstance();
int left, top, right, bottom;
@@ -163,7 +163,7 @@
throw new NullPointerException();
}
throwIfCannotDraw(bitmap);
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
final long nativePaint = paint == null ? 0 : paint.getNativeInstance();
float left, top, right, bottom;
@@ -202,7 +202,7 @@
|| (lastScanline + width > length)) {
throw new ArrayIndexOutOfBoundsException();
}
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
// quick escape if there's nothing to draw
if (width == 0 || height == 0) {
return;
@@ -226,7 +226,7 @@
if ((meshWidth | meshHeight | vertOffset | colorOffset) < 0) {
throw new ArrayIndexOutOfBoundsException();
}
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
if (meshWidth == 0 || meshHeight == 0) {
return;
}
@@ -243,7 +243,7 @@
}
public void drawCircle(float cx, float cy, float radius, @NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawCircle(mNativeCanvasWrapper, cx, cy, radius, paint.getNativeInstance());
}
@@ -275,23 +275,23 @@
public void drawLine(float startX, float startY, float stopX, float stopY,
@NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawLine(mNativeCanvasWrapper, startX, startY, stopX, stopY, paint.getNativeInstance());
}
public void drawLines(@Size(multiple = 4) @NonNull float[] pts, int offset, int count,
@NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawLines(mNativeCanvasWrapper, pts, offset, count, paint.getNativeInstance());
}
public void drawLines(@Size(multiple = 4) @NonNull float[] pts, @NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
drawLines(pts, 0, pts.length, paint);
}
public void drawOval(float left, float top, float right, float bottom, @NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawOval(mNativeCanvasWrapper, left, top, right, bottom, paint.getNativeInstance());
}
@@ -299,18 +299,19 @@
if (oval == null) {
throw new NullPointerException();
}
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
drawOval(oval.left, oval.top, oval.right, oval.bottom, paint);
}
public void drawPaint(@NonNull Paint paint) {
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawPaint(mNativeCanvasWrapper, paint.getNativeInstance());
}
public void drawPatch(@NonNull NinePatch patch, @NonNull Rect dst, @Nullable Paint paint) {
Bitmap bitmap = patch.getBitmap();
throwIfCannotDraw(bitmap);
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
final long nativePaint = paint == null ? 0 : paint.getNativeInstance();
nDrawNinePatch(mNativeCanvasWrapper, bitmap.getNativeInstance(), patch.mNativeChunk,
dst.left, dst.top, dst.right, dst.bottom, nativePaint,
@@ -320,7 +321,7 @@
public void drawPatch(@NonNull NinePatch patch, @NonNull RectF dst, @Nullable Paint paint) {
Bitmap bitmap = patch.getBitmap();
throwIfCannotDraw(bitmap);
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
final long nativePaint = paint == null ? 0 : paint.getNativeInstance();
nDrawNinePatch(mNativeCanvasWrapper, bitmap.getNativeInstance(), patch.mNativeChunk,
dst.left, dst.top, dst.right, dst.bottom, nativePaint,
@@ -328,7 +329,7 @@
}
public void drawPath(@NonNull Path path, @NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
if (path.isSimplePath && path.rects != null) {
nDrawRegion(mNativeCanvasWrapper, path.rects.mNativeRegion, paint.getNativeInstance());
} else {
@@ -337,18 +338,18 @@
}
public void drawPoint(float x, float y, @NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawPoint(mNativeCanvasWrapper, x, y, paint.getNativeInstance());
}
public void drawPoints(@Size(multiple = 2) float[] pts, int offset, int count,
@NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawPoints(mNativeCanvasWrapper, pts, offset, count, paint.getNativeInstance());
}
public void drawPoints(@Size(multiple = 2) @NonNull float[] pts, @NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
drawPoints(pts, 0, pts.length, paint);
}
@@ -359,7 +360,7 @@
if (index < 0 || index + count > text.length || count * 2 > pos.length) {
throw new IndexOutOfBoundsException();
}
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
for (int i = 0; i < count; i++) {
drawText(text, index + i, 1, pos[i * 2], pos[i * 2 + 1], paint);
}
@@ -368,22 +369,22 @@
@Deprecated
public void drawPosText(@NonNull String text, @NonNull @Size(multiple = 2) float[] pos,
@NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
drawPosText(text.toCharArray(), 0, text.length(), pos, paint);
}
public void drawRect(float left, float top, float right, float bottom, @NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawRect(mNativeCanvasWrapper, left, top, right, bottom, paint.getNativeInstance());
}
public void drawRect(@NonNull Rect r, @NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
drawRect(r.left, r.top, r.right, r.bottom, paint);
}
public void drawRect(@NonNull RectF rect, @NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawRect(mNativeCanvasWrapper,
rect.left, rect.top, rect.right, rect.bottom, paint.getNativeInstance());
}
@@ -394,13 +395,13 @@
public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
@NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawRoundRect(mNativeCanvasWrapper, left, top, right, bottom, rx, ry,
paint.getNativeInstance());
}
public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
drawRoundRect(rect.left, rect.top, rect.right, rect.bottom, rx, ry, paint);
}
@@ -410,7 +411,7 @@
*/
public void drawDoubleRoundRect(@NonNull RectF outer, float outerRx, float outerRy,
@NonNull RectF inner, float innerRx, float innerRy, @NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
float outerLeft = outer.left;
float outerTop = outer.top;
float outerRight = outer.right;
@@ -431,7 +432,7 @@
*/
public void drawDoubleRoundRect(@NonNull RectF outer, @NonNull float[] outerRadii,
@NonNull RectF inner, @NonNull float[] innerRadii, @NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
if (innerRadii == null || outerRadii == null
|| innerRadii.length != 8 || outerRadii.length != 8) {
throw new IllegalArgumentException("Both inner and outer radii arrays must contain "
@@ -509,7 +510,7 @@
(text.length - index - count)) < 0) {
throw new IndexOutOfBoundsException();
}
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawText(mNativeCanvasWrapper, text, index, count, x, y, paint.mBidiFlags,
paint.getNativeInstance());
}
@@ -519,7 +520,7 @@
if ((start | end | (end - start) | (text.length() - end)) < 0) {
throw new IndexOutOfBoundsException();
}
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
if (text instanceof String || text instanceof SpannedString ||
text instanceof SpannableString) {
nDrawText(mNativeCanvasWrapper, text.toString(), start, end, x, y,
@@ -537,7 +538,7 @@
}
public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawText(mNativeCanvasWrapper, text, 0, text.length(), x, y, paint.mBidiFlags,
paint.getNativeInstance());
}
@@ -547,7 +548,7 @@
if ((start | end | (end - start) | (text.length() - end)) < 0) {
throw new IndexOutOfBoundsException();
}
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawText(mNativeCanvasWrapper, text, start, end, x, y, paint.mBidiFlags,
paint.getNativeInstance());
}
@@ -557,7 +558,7 @@
if (index < 0 || index + count > text.length) {
throw new ArrayIndexOutOfBoundsException();
}
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawTextOnPath(mNativeCanvasWrapper, text, index, count,
path.readOnlyNI(), hOffset, vOffset,
paint.mBidiFlags, paint.getNativeInstance());
@@ -566,7 +567,7 @@
public void drawTextOnPath(@NonNull String text, @NonNull Path path, float hOffset,
float vOffset, @NonNull Paint paint) {
if (text.length() > 0) {
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawTextOnPath(mNativeCanvasWrapper, text, path.readOnlyNI(), hOffset, vOffset,
paint.mBidiFlags, paint.getNativeInstance());
}
@@ -587,7 +588,7 @@
throw new IndexOutOfBoundsException();
}
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawTextRun(mNativeCanvasWrapper, text, index, count, contextIndex, contextCount,
x, y, isRtl, paint.getNativeInstance(), 0 /* measured text */);
}
@@ -606,7 +607,7 @@
throw new IndexOutOfBoundsException();
}
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
if (text instanceof String || text instanceof SpannedString ||
text instanceof SpannableString) {
nDrawTextRun(mNativeCanvasWrapper, text.toString(), start, end, contextStart,
@@ -664,7 +665,7 @@
if (indices != null) {
checkRange(indices.length, indexOffset, indexCount);
}
- throwIfHasHwBitmapInSwMode(paint);
+ throwIfHasHwFeaturesInSwMode(paint);
nDrawVertices(mNativeCanvasWrapper, mode.nativeInt, vertexCount, verts,
vertOffset, texs, texOffset, colors, colorOffset,
indices, indexOffset, indexCount, paint.getNativeInstance());
@@ -680,50 +681,52 @@
/**
* @hide
*/
- public void setHwBitmapsInSwModeEnabled(boolean enabled) {
- mAllowHwBitmapsInSwMode = enabled;
+ public void setHwFeaturesInSwModeEnabled(boolean enabled) {
+ mAllowHwFeaturesInSwMode = enabled;
}
/**
* @hide
*/
- public boolean isHwBitmapsInSwModeEnabled() {
- return mAllowHwBitmapsInSwMode;
+ public boolean isHwFeaturesInSwModeEnabled() {
+ return mAllowHwFeaturesInSwMode;
}
/**
+ * If true throw an exception
* @hide
*/
- protected void onHwBitmapInSwMode() {
- if (!mAllowHwBitmapsInSwMode) {
+ protected boolean onHwFeatureInSwMode() {
+ return !mAllowHwFeaturesInSwMode;
+ }
+
+ private void throwIfHwBitmapInSwMode(Bitmap bitmap) {
+ if (!isHardwareAccelerated() && bitmap.getConfig() == Bitmap.Config.HARDWARE
+ && onHwFeatureInSwMode()) {
throw new IllegalArgumentException(
"Software rendering doesn't support hardware bitmaps");
}
}
- private void throwIfHwBitmapInSwMode(Bitmap bitmap) {
- if (!isHardwareAccelerated() && bitmap.getConfig() == Bitmap.Config.HARDWARE) {
- onHwBitmapInSwMode();
- }
- }
-
- private void throwIfHasHwBitmapInSwMode(Paint p) {
+ private void throwIfHasHwFeaturesInSwMode(Paint p) {
if (isHardwareAccelerated() || p == null) {
return;
}
- throwIfHasHwBitmapInSwMode(p.getShader());
+ throwIfHasHwFeaturesInSwMode(p.getShader());
}
- private void throwIfHasHwBitmapInSwMode(Shader shader) {
+ private void throwIfHasHwFeaturesInSwMode(Shader shader) {
if (shader == null) {
return;
}
if (shader instanceof BitmapShader) {
throwIfHwBitmapInSwMode(((BitmapShader) shader).mBitmap);
- }
- if (shader instanceof ComposeShader) {
- throwIfHasHwBitmapInSwMode(((ComposeShader) shader).mShaderA);
- throwIfHasHwBitmapInSwMode(((ComposeShader) shader).mShaderB);
+ } else if (shader instanceof RuntimeShader && onHwFeatureInSwMode()) {
+ throw new IllegalArgumentException(
+ "Software rendering doesn't support RuntimeShader");
+ } else if (shader instanceof ComposeShader) {
+ throwIfHasHwFeaturesInSwMode(((ComposeShader) shader).mShaderA);
+ throwIfHasHwFeaturesInSwMode(((ComposeShader) shader).mShaderB);
}
}
diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java
index 390d3d4..ee4165b 100644
--- a/graphics/java/android/graphics/Picture.java
+++ b/graphics/java/android/graphics/Picture.java
@@ -124,7 +124,7 @@
public void endRecording() {
verifyValid();
if (mRecordingCanvas != null) {
- mRequiresHwAcceleration = mRecordingCanvas.mHoldsHwBitmap;
+ mRequiresHwAcceleration = mRecordingCanvas.mUsesHwFeature;
mRecordingCanvas = null;
nativeEndRecording(mNativePicture);
}
@@ -182,8 +182,10 @@
if (mRecordingCanvas != null) {
endRecording();
}
- if (mRequiresHwAcceleration && !canvas.isHardwareAccelerated()) {
- canvas.onHwBitmapInSwMode();
+ if (mRequiresHwAcceleration && !canvas.isHardwareAccelerated()
+ && canvas.onHwFeatureInSwMode()) {
+ throw new IllegalArgumentException("Software rendering not supported for Pictures that"
+ + " require hardware acceleration");
}
nativeDraw(canvas.getNativeCanvasWrapper(), mNativePicture);
}
@@ -242,7 +244,7 @@
private static class PictureCanvas extends Canvas {
private final Picture mPicture;
- boolean mHoldsHwBitmap;
+ boolean mUsesHwFeature;
public PictureCanvas(Picture pict, long nativeCanvas) {
super(nativeCanvas);
@@ -265,8 +267,9 @@
}
@Override
- protected void onHwBitmapInSwMode() {
- mHoldsHwBitmap = true;
+ protected boolean onHwFeatureInSwMode() {
+ mUsesHwFeature = true;
+ return false;
}
}
}
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index a0f6fb9..9147c12 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -2720,6 +2720,42 @@
*/
public static final String COLUMN_GLOBAL_CONTENT_ID = "global_content_id";
+ /**
+ * The flag indicating whether this TV program is scrambled or not.
+ *
+ * <p>Use the same coding for scrambled in the underlying broadcast standard
+ * if {@code free_ca_mode} in EIT is defined there (e.g. ETSI EN 300 468).
+ *
+ * <p>Type: INTEGER (boolean)
+ */
+ public static final String COLUMN_SCRAMBLED = "scrambled";
+
+ /**
+ * The comma-separated series IDs of this TV program for episodic TV shows.
+ *
+ * <p>This is used to indicate the series IDs.
+ * Programs in the same series share a series ID.
+ * Use this instead of {@link #COLUMN_SERIES_ID} if more than one series IDs
+ * are assigned to the TV program.
+ *
+ * <p>Can be empty.
+ *
+ * <p>Type: TEXT
+ */
+ public static final String COLUMN_MULTI_SERIES_ID = "multi_series_id";
+
+ /**
+ * The internal ID used by individual TV input services.
+ *
+ * <p>This is internal to the provider that inserted it, and should not be decoded by other
+ * apps.
+ *
+ * <p>Can be empty.
+ *
+ * <p>Type: TEXT
+ */
+ public static final String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
+
private Programs() {}
/** Canonical genres for TV programs. */
@@ -3052,6 +3088,32 @@
public static final String COLUMN_RECORDING_EXPIRE_TIME_UTC_MILLIS =
"recording_expire_time_utc_millis";
+ /**
+ * The comma-separated series IDs of this TV program for episodic TV shows.
+ *
+ * <p>This is used to indicate the series IDs.
+ * Programs in the same series share a series ID.
+ * Use this instead of {@link #COLUMN_SERIES_ID} if more than one series IDs
+ * are assigned to the TV program.
+ *
+ * <p>Can be empty.
+ *
+ * <p>Type: TEXT
+ */
+ public static final String COLUMN_MULTI_SERIES_ID = "multi_series_id";
+
+ /**
+ * The internal ID used by individual TV input services.
+ *
+ * <p>This is internal to the provider that inserted it, and should not be decoded by other
+ * apps.
+ *
+ * <p>Can be empty.
+ *
+ * <p>Type: TEXT
+ */
+ public static final String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
+
private RecordedPrograms() {}
}
diff --git a/media/java/android/media/tv/tuner/filter/SectionSettings.java b/media/java/android/media/tv/tuner/filter/SectionSettings.java
index 58e22c9..e7fc129 100644
--- a/media/java/android/media/tv/tuner/filter/SectionSettings.java
+++ b/media/java/android/media/tv/tuner/filter/SectionSettings.java
@@ -45,8 +45,19 @@
public boolean isCrcEnabled() {
return mCrcEnabled;
}
+
/**
- * Returns whether the filter repeats the data with the same version.
+ * Returns whether the filter repeats the data.
+ *
+ * If {@code false}, for {@link SectionSettingsWithTableInfo}, HAL filters out all sections
+ * based on {@link SectionSettingsWithTableInfo} TableId and Version, and stops filtering data.
+ * For {@linkSectionSettingsWithSectionBits}, HAL filters out the first section which matches
+ * the {@linkSectionSettingsWithSectionBits} configuration, and stops filtering data.
+ *
+ * If {@code true}, for {@link SectionSettingsWithTableInfo}, HAL filters out all sections based
+ * on {@link SectionSettingsWithTableInfo} TableId and Version, and repeats. For
+ * {@linkSectionSettingsWithSectionBits}, HAL filters out sections which match the
+ * {@linkSectionSettingsWithSectionBits} configuration, and repeats.
*/
public boolean isRepeat() {
return mIsRepeat;
@@ -83,8 +94,20 @@
mCrcEnabled = crcEnabled;
return self();
}
+
/**
- * Sets whether the filter repeats the data with the same version.
+ * Sets whether the filter repeats the data.
+ *
+ * If {@code false}, for {@link SectionSettingsWithTableInfo}, HAL filters out all sections
+ * based on {@link SectionSettingsWithTableInfo} TableId and Version, and stops filtering
+ * data. For {@linkSectionSettingsWithSectionBits}, HAL filters out the first section which
+ * matches the {@linkSectionSettingsWithSectionBits} configuration, and stops filtering
+ * data.
+ *
+ * If {@code true}, for {@link SectionSettingsWithTableInfo}, HAL filters out all sections
+ * based on {@link SectionSettingsWithTableInfo} TableId and Version, and repeats. For
+ * {@linkSectionSettingsWithSectionBits}, HAL filters out sections which match the
+ * {@linkSectionSettingsWithSectionBits} configuration, and repeats.
*/
@NonNull
public T setRepeat(boolean isRepeat) {
diff --git a/packages/ConnectivityT/framework-t/src/android/net/IpSecAlgorithm.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecAlgorithm.java
index 5e647fe..a84e7a9 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/IpSecAlgorithm.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/IpSecAlgorithm.java
@@ -23,7 +23,6 @@
import android.os.Parcelable;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.HexDump;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index b25e9a1..a3f07d8 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -207,6 +207,7 @@
<uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
<uses-permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS" />
<uses-permission android:name="android.permission.QUERY_ADMIN_POLICY" />
+ <uses-permission android:name="android.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES" />
<uses-permission android:name="android.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS" />
<uses-permission android:name="android.permission.CLEAR_FREEZE_PERIOD" />
<uses-permission android:name="android.permission.MODIFY_QUIET_MODE" />
diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
index a16f5cd..da9a92a 100644
--- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
+++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
@@ -20,9 +20,11 @@
import android.app.smartspace.SmartspaceAction;
import android.app.smartspace.SmartspaceTarget;
import android.app.smartspace.SmartspaceTargetEvent;
+import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Parcelable;
+import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -39,6 +41,7 @@
public interface BcSmartspaceDataPlugin extends Plugin {
String ACTION = "com.android.systemui.action.PLUGIN_BC_SMARTSPACE_DATA";
int VERSION = 1;
+ String TAG = "BcSmartspaceDataPlugin";
/** Register a listener to get Smartspace data. */
void registerListener(SmartspaceTargetListener listener);
@@ -124,10 +127,14 @@
/** Interface for launching Intents, which can differ on the lockscreen */
interface IntentStarter {
default void startFromAction(SmartspaceAction action, View v, boolean showOnLockscreen) {
- if (action.getIntent() != null) {
- startIntent(v, action.getIntent(), showOnLockscreen);
- } else if (action.getPendingIntent() != null) {
- startPendingIntent(action.getPendingIntent(), showOnLockscreen);
+ try {
+ if (action.getIntent() != null) {
+ startIntent(v, action.getIntent(), showOnLockscreen);
+ } else if (action.getPendingIntent() != null) {
+ startPendingIntent(action.getPendingIntent(), showOnLockscreen);
+ }
+ } catch (ActivityNotFoundException e) {
+ Log.w(TAG, "Could not launch intent for action: " + action, e);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index e35b558..1496f17 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -636,7 +636,9 @@
mIndicatorView.setText(message);
mIndicatorView.setTextColor(mTextColorError);
mIndicatorView.setVisibility(View.VISIBLE);
- mIndicatorView.setSelected(true);
+ // select to enable marquee unless a screen reader is enabled
+ mIndicatorView.setSelected(!mAccessibilityManager.isEnabled()
+ || !mAccessibilityManager.isTouchExplorationEnabled());
mHandler.postDelayed(resetMessageRunnable, mInjector.getDelayAfterError());
Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
index 07aec69..73e3aec 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
@@ -75,7 +75,7 @@
@Override
protected void onViewDetached() {
mPanelExpansionStateManager.removeExpansionListener(mPanelExpansionListener);
- mDialogManager.registerListener(mDialogListener);
+ mDialogManager.unregisterListener(mDialogListener);
mDumpManger.unregisterDumpable(getDumpTag());
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index fb601e3..c8cd432 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -21,6 +21,7 @@
import android.animation.ValueAnimator
import android.annotation.IntDef
import android.content.Context
+import android.content.res.Configuration
import android.graphics.Rect
import android.util.MathUtils
import android.view.View
@@ -41,6 +42,7 @@
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.Utils
import com.android.systemui.util.animation.UniqueObjectHostView
import javax.inject.Inject
@@ -186,6 +188,8 @@
@MediaLocation
private var currentAttachmentLocation = -1
+ private var inSplitShade = false
+
/**
* Is there any active media in the carousel?
*/
@@ -390,8 +394,9 @@
init {
updateConfiguration()
configurationController.addCallback(object : ConfigurationController.ConfigurationListener {
- override fun onDensityOrFontScaleChanged() {
+ override fun onConfigChanged(newConfig: Configuration?) {
updateConfiguration()
+ updateDesiredLocation(forceNoAnimation = true, forceStateUpdate = true)
}
})
statusBarStateController.addCallback(object : StatusBarStateController.StateListener {
@@ -467,6 +472,7 @@
private fun updateConfiguration() {
distanceForFullShadeTransition = context.resources.getDimensionPixelSize(
R.dimen.lockscreen_shade_media_transition_distance)
+ inSplitShade = Utils.shouldUseSplitNotificationShade(context.resources)
}
/**
@@ -803,7 +809,7 @@
private fun getQSTransformationProgress(): Float {
val currentHost = getHost(desiredLocation)
val previousHost = getHost(previousLocation)
- if (hasActiveMedia && currentHost?.location == LOCATION_QS) {
+ if (hasActiveMedia && (currentHost?.location == LOCATION_QS && !inSplitShade)) {
if (previousHost?.location == LOCATION_QQS) {
if (previousHost.visible || statusbarState != StatusBarState.KEYGUARD) {
return qsExpansion
@@ -934,7 +940,7 @@
statusbarState == StatusBarState.FULLSCREEN_USER_SWITCHER))
val allowedOnLockscreen = notifLockscreenUserManager.shouldShowLockscreenNotifications()
val location = when {
- qsExpansion > 0.0f && !onLockscreen -> LOCATION_QS
+ (qsExpansion > 0.0f || inSplitShade) && !onLockscreen -> LOCATION_QS
qsExpansion > 0.4f && onLockscreen -> LOCATION_QS
!hasActiveMedia -> LOCATION_QS
onLockscreen && isTransformingToFullShadeAndInQQS() -> LOCATION_QQS
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index a44de2c..a4e2d5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -241,6 +241,10 @@
configurationController.addCallback(configChangeListener)
statusBarStateController.addCallback(statusBarStateListener)
+ plugin.registerSmartspaceEventNotifier {
+ e -> session?.notifySmartspaceEvent(e)
+ }
+
reloadSmartspace()
}
@@ -266,6 +270,7 @@
statusBarStateController.removeCallback(statusBarStateListener)
session = null
+ plugin?.registerSmartspaceEventNotifier(null)
plugin?.onTargetsAvailable(emptyList())
Log.d(TAG, "Ending smartspace session for lockscreen")
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
index bfe352d..aa511c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
@@ -146,6 +146,7 @@
protected void onAttachedToWindow() {
super.onAttachedToWindow();
logOptionSelection(MetricsEvent.NOTIFICATION_SNOOZE_CLICKED, mDefaultOption);
+ dispatchConfigurationChanged(getResources().getConfiguration());
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 518788b..79b05c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -412,7 +412,7 @@
private NotificationShelf mShelf;
private int mMaxDisplayedNotifications = -1;
private float mKeyguardBottomPadding = -1;
- private int mStatusBarHeight;
+ @VisibleForTesting int mStatusBarHeight;
private int mMinInteractionHeight;
private final Rect mClipRect = new Rect();
private boolean mIsClipped;
@@ -4854,8 +4854,12 @@
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
public int getMinExpansionHeight() {
+ // shelf height is defined in dp but status bar height can be defined in px, that makes
+ // relation between them variable - sometimes one might be bigger than the other when
+ // changing density. That’s why we need to ensure we’re not subtracting negative value below
return mShelf.getIntrinsicHeight()
- - (mShelf.getIntrinsicHeight() - mStatusBarHeight + mWaterfallTopInset) / 2
+ - Math.max(0,
+ (mShelf.getIntrinsicHeight() - mStatusBarHeight + mWaterfallTopInset) / 2)
+ mWaterfallTopInset;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
index de627de..1961ab2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
@@ -214,6 +214,8 @@
// THEN the session is created
verify(smartspaceManager).createSmartspaceSession(any())
+ // THEN an event notifier is registered
+ verify(plugin).registerSmartspaceEventNotifier(any())
}
@Test
@@ -241,7 +243,7 @@
}
@Test
- fun testEmptyListIsEmittedAfterDisconnect() {
+ fun testEmptyListIsEmittedAndNotifierRemovedAfterDisconnect() {
// GIVEN a registered listener on an active session
connectSession()
clearInvocations(plugin)
@@ -250,8 +252,9 @@
controller.stateChangeListener.onViewDetachedFromWindow(smartspaceView as View)
controller.disconnect()
- // THEN the listener receives an empty list of targets
+ // THEN the listener receives an empty list of targets and unregisters the notifier
verify(plugin).onTargetsAvailable(emptyList())
+ verify(plugin).registerSmartspaceEventNotifier(null)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 9be2837..eda0e83 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -22,6 +22,7 @@
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
+import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static junit.framework.Assert.assertEquals;
@@ -102,6 +103,7 @@
@Mock private NotificationSwipeHelper mNotificationSwipeHelper;
@Mock private NotificationStackScrollLayoutController mStackScrollLayoutController;
@Mock private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
+ @Mock private NotificationShelf mNotificationShelf;
@Before
@UiThreadTest
@@ -123,13 +125,13 @@
mDependency.injectTestDependency(GroupMembershipManager.class, mGroupMembershipManger);
mDependency.injectTestDependency(GroupExpansionManager.class, mGroupExpansionManager);
mDependency.injectTestDependency(AmbientState.class, mAmbientState);
+ mDependency.injectTestDependency(NotificationShelf.class, mNotificationShelf);
mDependency.injectTestDependency(
UnlockedScreenOffAnimationController.class, mUnlockedScreenOffAnimationController);
NotificationShelfController notificationShelfController =
mock(NotificationShelfController.class);
- NotificationShelf notificationShelf = mock(NotificationShelf.class);
- when(notificationShelfController.getView()).thenReturn(notificationShelf);
+ when(notificationShelfController.getView()).thenReturn(mNotificationShelf);
when(mNotificationSectionsManager.createSectionsForBuckets()).thenReturn(
new NotificationSection[]{
mNotificationSection
@@ -159,7 +161,7 @@
anyBoolean());
doNothing().when(mGroupExpansionManager).collapseGroups();
doNothing().when(mExpandHelper).cancelImmediately();
- doNothing().when(notificationShelf).setAnimationsEnabled(anyBoolean());
+ doNothing().when(mNotificationShelf).setAnimationsEnabled(anyBoolean());
}
@Test
@@ -202,21 +204,43 @@
@Test
@UiThreadTest
- public void testSetExpandedHeight_blockingHelperManagerReceivedCallbacks() {
- final float[] expectedHeight = {0f};
- final float[] expectedAppear = {0f};
+ public void testSetExpandedHeight_listenerReceivedCallbacks() {
+ final float expectedHeight = 0f;
mStackScroller.addOnExpandedHeightChangedListener((height, appear) -> {
- Assert.assertEquals(expectedHeight[0], height, 0);
- Assert.assertEquals(expectedAppear[0], appear, .1);
+ Assert.assertEquals(expectedHeight, height, 0);
});
- expectedHeight[0] = 1f;
- expectedAppear[0] = 1f;
- mStackScroller.setExpandedHeight(expectedHeight[0]);
+ mStackScroller.setExpandedHeight(expectedHeight);
+ }
- expectedHeight[0] = 100f;
- expectedAppear[0] = 0f;
- mStackScroller.setExpandedHeight(expectedHeight[0]);
+ @Test
+ public void testAppearFractionCalculation() {
+ // appear start position
+ when(mNotificationShelf.getIntrinsicHeight()).thenReturn(100);
+ // because it's the same as shelf height, appear start position equals shelf height
+ mStackScroller.mStatusBarHeight = 100;
+ // appear end position
+ when(mEmptyShadeView.getHeight()).thenReturn(200);
+
+ assertEquals(0f, mStackScroller.calculateAppearFraction(100));
+ assertEquals(1f, mStackScroller.calculateAppearFraction(200));
+ assertEquals(0.5f, mStackScroller.calculateAppearFraction(150));
+ }
+
+ @Test
+ public void testAppearFractionCalculationIsNotNegativeWhenShelfBecomesSmaller() {
+ // this situation might occur if status bar height is defined in pixels while shelf height
+ // in dp and screen density changes - appear start position
+ // (calculated in NSSL#getMinExpansionHeight) that is adjusting for status bar might
+ // increase and become bigger that end position, which should be prevented
+
+ // appear start position
+ when(mNotificationShelf.getIntrinsicHeight()).thenReturn(80);
+ mStackScroller.mStatusBarHeight = 100;
+ // appear end position
+ when(mEmptyShadeView.getHeight()).thenReturn(90);
+
+ assertThat(mStackScroller.calculateAppearFraction(100)).isAtLeast(0);
}
@Test
diff --git a/services/cloudsearch/OWNERS b/services/cloudsearch/OWNERS
new file mode 100644
index 0000000..aa4da3b
--- /dev/null
+++ b/services/cloudsearch/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 758286
+
+huiwu@google.com
+srazdan@google.com
diff --git a/services/companion/java/com/android/server/companion/PermissionsUtils.java b/services/companion/java/com/android/server/companion/PermissionsUtils.java
index 3a8ee73..b981ff1 100644
--- a/services/companion/java/com/android/server/companion/PermissionsUtils.java
+++ b/services/companion/java/com/android/server/companion/PermissionsUtils.java
@@ -84,18 +84,6 @@
throw new IllegalArgumentException("Unsupported device profile: " + deviceProfile);
}
- if (DEVICE_PROFILE_APP_STREAMING.equals(deviceProfile)) {
- // TODO: remove, when properly supporting this profile.
- throw new UnsupportedOperationException(
- "DEVICE_PROFILE_APP_STREAMING is not fully supported yet.");
- }
-
- if (DEVICE_PROFILE_AUTOMOTIVE_PROJECTION.equals(deviceProfile)) {
- // TODO: remove, when properly supporting this profile.
- throw new UnsupportedOperationException(
- "DEVICE_PROFILE_AUTOMOTIVE_PROJECTION is not fully supported yet.");
- }
-
final String permission = DEVICE_PROFILE_TO_PERMISSION.get(deviceProfile);
if (context.checkPermission(permission, getCallingPid(), packageUid)
!= PERMISSION_GRANTED) {
diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
index a6a8793..e98b63e 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -29,6 +29,7 @@
import android.os.UserHandle;
import android.window.DisplayWindowPolicyController;
+import java.util.HashSet;
import java.util.List;
@@ -45,6 +46,8 @@
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
public static final long ALLOW_SECURE_ACTIVITY_DISPLAY_ON_REMOTE_DEVICE = 201712607L;
+ @NonNull final HashSet<Integer> mRunningUids = new HashSet<>();
+
GenericWindowPolicyController(int windowFlags, int systemWindowFlags) {
setInterestedWindowFlags(windowFlags, systemWindowFlags);
}
@@ -89,6 +92,17 @@
@Override
public void onRunningAppsChanged(int[] runningUids) {
+ mRunningUids.clear();
+ for (int i = 0; i < runningUids.length; i++) {
+ mRunningUids.add(runningUids[i]);
+ }
+ }
+ /**
+ * Returns true if an app with the given UID has an activity running on the virtual display for
+ * this controller.
+ */
+ boolean containsUid(int uid) {
+ return mRunningUids.contains(uid);
}
}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 022da43..36edf4f 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -32,6 +32,7 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.util.SparseArray;
import android.window.DisplayWindowPolicyController;
import com.android.internal.annotations.VisibleForTesting;
@@ -50,12 +51,18 @@
private final Context mContext;
private final AssociationInfo mAssociationInfo;
private final int mOwnerUid;
- private final GenericWindowPolicyController mGenericWindowPolicyController;
private final InputController mInputController;
@VisibleForTesting
final List<Integer> mVirtualDisplayIds = new ArrayList<>();
private final OnDeviceCloseListener mListener;
+ /**
+ * A mapping from the virtual display ID to its corresponding
+ * {@link GenericWindowPolicyController}.
+ */
+ private final SparseArray<GenericWindowPolicyController> mWindowPolicyControllers =
+ new SparseArray<>();
+
VirtualDeviceImpl(Context context, AssociationInfo associationInfo,
IBinder token, int ownerUid, OnDeviceCloseListener listener) {
this(context, associationInfo, token, ownerUid, /* inputController= */ null, listener);
@@ -66,8 +73,6 @@
int ownerUid, InputController inputController, OnDeviceCloseListener listener) {
mContext = context;
mAssociationInfo = associationInfo;
- mGenericWindowPolicyController = new GenericWindowPolicyController(FLAG_SECURE,
- SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
mOwnerUid = ownerUid;
if (inputController == null) {
mInputController = new InputController(mVirtualDeviceLock);
@@ -257,7 +262,11 @@
"Virtual device already have a virtual display with ID " + displayId);
}
mVirtualDisplayIds.add(displayId);
- return mGenericWindowPolicyController;
+ final GenericWindowPolicyController dwpc =
+ new GenericWindowPolicyController(FLAG_SECURE,
+ SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+ mWindowPolicyControllers.put(displayId, dwpc);
+ return dwpc;
}
void onVirtualDisplayRemovedLocked(int displayId) {
@@ -266,12 +275,27 @@
"Virtual device doesn't have a virtual display with ID " + displayId);
}
mVirtualDisplayIds.remove(displayId);
+ mWindowPolicyControllers.remove(displayId);
}
int getOwnerUid() {
return mOwnerUid;
}
+ /**
+ * Returns true if an app with the given {@code uid} is currently running on this virtual
+ * device.
+ */
+ boolean isAppRunningOnVirtualDevice(int uid) {
+ final int size = mWindowPolicyControllers.size();
+ for (int i = 0; i < size; i++) {
+ if (mWindowPolicyControllers.valueAt(i).containsUid(uid)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
interface OnDeviceCloseListener {
void onClose(int associationId);
}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 46e75f7..0db670e 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -252,7 +252,14 @@
@Override
public boolean isAppRunningOnAnyVirtualDevice(int uid) {
- // TODO(yukl): Implement this using DWPC.onRunningAppsChanged
+ synchronized (mVirtualDeviceManagerLock) {
+ int size = mVirtualDevices.size();
+ for (int i = 0; i < size; i++) {
+ if (mVirtualDevices.valueAt(i).isAppRunningOnVirtualDevice(uid)) {
+ return true;
+ }
+ }
+ }
return false;
}
}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index d7c1cfb..811f2f5 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -91,6 +91,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
+import com.android.internal.telephony.ICarrierPrivilegesListener;
import com.android.internal.telephony.IOnSubscriptionsChangedListener;
import com.android.internal.telephony.IPhoneStateListener;
import com.android.internal.telephony.ITelephonyRegistry;
@@ -106,6 +107,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -149,6 +151,7 @@
IPhoneStateListener callback;
IOnSubscriptionsChangedListener onSubscriptionsChangedListenerCallback;
IOnSubscriptionsChangedListener onOpportunisticSubscriptionsChangedListenerCallback;
+ ICarrierPrivilegesListener carrierPrivilegesListener;
int callerUid;
int callerPid;
@@ -173,6 +176,10 @@
return (onOpportunisticSubscriptionsChangedListenerCallback != null);
}
+ boolean matchCarrierPrivilegesListener() {
+ return carrierPrivilegesListener != null;
+ }
+
boolean canReadCallLog() {
try {
return TelephonyPermissions.checkReadCallLog(
@@ -189,8 +196,9 @@
+ " onSubscriptionsChangedListenererCallback="
+ onSubscriptionsChangedListenerCallback
+ " onOpportunisticSubscriptionsChangedListenererCallback="
- + onOpportunisticSubscriptionsChangedListenerCallback + " subId=" + subId
- + " phoneId=" + phoneId + " events=" + eventList + "}";
+ + onOpportunisticSubscriptionsChangedListenerCallback
+ + " carrierPrivilegesListener=" + carrierPrivilegesListener
+ + " subId=" + subId + " phoneId=" + phoneId + " events=" + eventList + "}";
}
}
@@ -402,6 +410,10 @@
*/
private List<Map<Pair<Integer, ApnSetting>, PreciseDataConnectionState>>
mPreciseDataConnectionStates;
+
+ /** Per-phoneId snapshot of privileged packages (names + UIDs). */
+ private List<Pair<List<String>, int[]>> mCarrierPrivilegeStates;
+
/**
* Support backward compatibility for {@link android.telephony.TelephonyDisplayInfo}.
*/
@@ -689,6 +701,7 @@
cutListToSize(mBarringInfo, mNumPhones);
cutListToSize(mPhysicalChannelConfigs, mNumPhones);
cutListToSize(mLinkCapacityEstimateLists, mNumPhones);
+ cutListToSize(mCarrierPrivilegeStates, mNumPhones);
return;
}
@@ -729,6 +742,7 @@
mAllowedNetworkTypeReason[i] = -1;
mAllowedNetworkTypeValue[i] = -1;
mLinkCapacityEstimateLists.add(i, INVALID_LCE_LIST);
+ mCarrierPrivilegeStates.add(i, new Pair<>(Collections.emptyList(), new int[0]));
}
}
@@ -794,6 +808,7 @@
mIsDataEnabled = new boolean[numPhones];
mDataEnabledReason = new int[numPhones];
mLinkCapacityEstimateLists = new ArrayList<>();
+ mCarrierPrivilegeStates = new ArrayList<>();
for (int i = 0; i < numPhones; i++) {
mCallState[i] = TelephonyManager.CALL_STATE_IDLE;
@@ -831,6 +846,7 @@
mAllowedNetworkTypeReason[i] = -1;
mAllowedNetworkTypeValue[i] = -1;
mLinkCapacityEstimateLists.add(i, INVALID_LCE_LIST);
+ mCarrierPrivilegeStates.add(i, new Pair<>(Collections.emptyList(), new int[0]));
}
mAppOps = mContext.getSystemService(AppOpsManager.class);
@@ -2766,6 +2782,104 @@
}
@Override
+ public void addCarrierPrivilegesListener(
+ int phoneId,
+ ICarrierPrivilegesListener callback,
+ String callingPackage,
+ String callingFeatureId) {
+ int callerUserId = UserHandle.getCallingUserId();
+ mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ "addCarrierPrivilegesListener");
+ if (VDBG) {
+ log(
+ "listen carrier privs: E pkg=" + pii(callingPackage) + " phoneId=" + phoneId
+ + " uid=" + Binder.getCallingUid()
+ + " myUserId=" + UserHandle.myUserId() + " callerUserId=" + callerUserId
+ + " callback=" + callback
+ + " callback.asBinder=" + callback.asBinder());
+ }
+ if (!validatePhoneId(phoneId)) {
+ throw new IllegalArgumentException("Invalid slot index: " + phoneId);
+ }
+
+ synchronized (mRecords) {
+ Record r = add(
+ callback.asBinder(), Binder.getCallingUid(), Binder.getCallingPid(), false);
+
+ if (r == null) return;
+
+ r.context = mContext;
+ r.carrierPrivilegesListener = callback;
+ r.callingPackage = callingPackage;
+ r.callingFeatureId = callingFeatureId;
+ r.callerUid = Binder.getCallingUid();
+ r.callerPid = Binder.getCallingPid();
+ r.phoneId = phoneId;
+ r.eventList = new ArraySet<>();
+ if (DBG) {
+ log("listen carrier privs: Register r=" + r);
+ }
+
+ Pair<List<String>, int[]> state = mCarrierPrivilegeStates.get(phoneId);
+ try {
+ r.carrierPrivilegesListener.onCarrierPrivilegesChanged(
+ Collections.unmodifiableList(state.first),
+ Arrays.copyOf(state.second, state.second.length));
+ } catch (RemoteException ex) {
+ remove(r.binder);
+ }
+ }
+ }
+
+ @Override
+ public void removeCarrierPrivilegesListener(
+ ICarrierPrivilegesListener callback, String callingPackage) {
+ mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ "removeCarrierPrivilegesListener");
+ remove(callback.asBinder());
+ }
+
+ @Override
+ public void notifyCarrierPrivilegesChanged(
+ int phoneId, List<String> privilegedPackageNames, int[] privilegedUids) {
+ if (!checkNotifyPermission("notifyCarrierPrivilegesChanged")) {
+ return;
+ }
+ if (!validatePhoneId(phoneId)) return;
+ if (VDBG) {
+ log(
+ "notifyCarrierPrivilegesChanged: phoneId=" + phoneId
+ + ", <packages=" + pii(privilegedPackageNames)
+ + ", uids=" + Arrays.toString(privilegedUids) + ">");
+ }
+ synchronized (mRecords) {
+ mCarrierPrivilegeStates.set(
+ phoneId, new Pair<>(privilegedPackageNames, privilegedUids));
+ for (Record r : mRecords) {
+ // Listeners are per-slot, not per-subscription. This is to provide a stable
+ // view across SIM profile switches.
+ if (!r.matchCarrierPrivilegesListener()
+ || !idMatch(r, SubscriptionManager.INVALID_SUBSCRIPTION_ID, phoneId)) {
+ continue;
+ }
+ try {
+ // Make sure even in-process listeners can't modify the values.
+ r.carrierPrivilegesListener.onCarrierPrivilegesChanged(
+ Collections.unmodifiableList(privilegedPackageNames),
+ Arrays.copyOf(privilegedUids, privilegedUids.length));
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+ handleRemoveListLocked();
+ }
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
@@ -2814,6 +2928,11 @@
pw.println("mAllowedNetworkTypeValue=" + mAllowedNetworkTypeValue[i]);
pw.println("mPhysicalChannelConfigs=" + mPhysicalChannelConfigs.get(i));
pw.println("mLinkCapacityEstimateList=" + mLinkCapacityEstimateLists.get(i));
+ // We need to obfuscate package names, and primitive arrays' native toString is ugly
+ Pair<List<String>, int[]> carrierPrivilegeState = mCarrierPrivilegeStates.get(i);
+ pw.println(
+ "mCarrierPrivilegeState=<packages=" + pii(carrierPrivilegeState.first)
+ + ", uids=" + Arrays.toString(carrierPrivilegeState.second) + ">");
pw.decreaseIndent();
}
@@ -3540,4 +3659,10 @@
private static String pii(String packageName) {
return Build.IS_DEBUGGABLE ? packageName : "***";
}
+
+ /** Redacts an entire list of package names if necessary. */
+ private static String pii(List<String> packageNames) {
+ if (packageNames.isEmpty() || Build.IS_DEBUGGABLE) return packageNames.toString();
+ return "[***, size=" + packageNames.size() + "]";
+ }
}
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 9180ef8..336572f 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -130,6 +130,9 @@
@GuardedBy("this")
private Future<?> mBatteryLevelSync;
+ @GuardedBy("this")
+ private Future<?> mProcessStateSync;
+
// If both mStats and mWorkerLock need to be synchronized, mWorkerLock must be acquired first.
private final Object mWorkerLock = new Object();
@@ -316,6 +319,25 @@
}
@Override
+ public Future<?> scheduleSyncDueToProcessStateChange(long delayMillis) {
+ synchronized (BatteryExternalStatsWorker.this) {
+ mProcessStateSync = scheduleDelayedSyncLocked(mProcessStateSync,
+ () -> scheduleSync("procstate-change", UPDATE_ON_PROC_STATE_CHANGE),
+ delayMillis);
+ return mProcessStateSync;
+ }
+ }
+
+ public void cancelSyncDueToProcessStateChange() {
+ synchronized (BatteryExternalStatsWorker.this) {
+ if (mProcessStateSync != null) {
+ mProcessStateSync.cancel(false);
+ mProcessStateSync = null;
+ }
+ }
+ }
+
+ @Override
public Future<?> scheduleCleanupDueToRemovedUser(int userId) {
synchronized (BatteryExternalStatsWorker.this) {
return mExecutorService.schedule(() -> {
@@ -434,6 +456,9 @@
if ((updateFlags & UPDATE_CPU) != 0) {
cancelCpuSyncDueToWakelockChange();
}
+ if ((updateFlags & UPDATE_ON_PROC_STATE_CHANGE) == UPDATE_ON_PROC_STATE_CHANGE) {
+ cancelSyncDueToProcessStateChange();
+ }
}
try {
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 093ecd5..e120343 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -50,6 +50,8 @@
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.IUserManager;
+import android.os.Looper;
+import android.os.Message;
import android.os.Parcel;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
@@ -74,6 +76,8 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.UiThread;
@@ -99,6 +103,22 @@
private static final boolean IS_EMULATOR =
SystemProperties.getBoolean("ro.boot.qemu", false);
+ @VisibleForTesting
+ public static final long DEFAULT_CLIPBOARD_TIMEOUT_MILLIS = 3600000;
+
+ /**
+ * Device config property for whether clipboard auto clear is enabled on the device
+ **/
+ public static final String PROPERTY_AUTO_CLEAR_ENABLED =
+ "auto_clear_enabled";
+
+ /**
+ * Device config property for time period in milliseconds after which clipboard is auto
+ * cleared
+ **/
+ public static final String PROPERTY_AUTO_CLEAR_TIMEOUT =
+ "auto_clear_timeout";
+
// DeviceConfig properties
private static final String PROPERTY_MAX_CLASSIFICATION_LENGTH = "max_classification_length";
private static final int DEFAULT_MAX_CLASSIFICATION_LENGTH = 400;
@@ -312,6 +332,10 @@
* 'intendingUserId' and the uid is called 'intendingUid'.
*/
private class ClipboardImpl extends IClipboard.Stub {
+
+ private final Handler mClipboardClearHandler = new ClipboardClearHandler(
+ mWorkerHandler.getLooper());
+
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
@@ -352,10 +376,34 @@
}
checkDataOwner(clip, intendingUid);
synchronized (mLock) {
+ scheduleAutoClear(userId);
setPrimaryClipInternalLocked(clip, intendingUid, sourcePackage);
}
}
+ private void scheduleAutoClear(@UserIdInt int userId) {
+ final long oldIdentity = Binder.clearCallingIdentity();
+ try {
+ if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_CLIPBOARD,
+ PROPERTY_AUTO_CLEAR_ENABLED, false)) {
+ mClipboardClearHandler.removeEqualMessages(ClipboardClearHandler.MSG_CLEAR,
+ userId);
+ Message clearMessage = Message.obtain(mClipboardClearHandler,
+ ClipboardClearHandler.MSG_CLEAR, userId, 0, userId);
+ mClipboardClearHandler.sendMessageDelayed(clearMessage,
+ getTimeoutForAutoClear());
+ }
+ } finally {
+ Binder.restoreCallingIdentity(oldIdentity);
+ }
+ }
+
+ private long getTimeoutForAutoClear() {
+ return DeviceConfig.getLong(DeviceConfig.NAMESPACE_CLIPBOARD,
+ PROPERTY_AUTO_CLEAR_TIMEOUT,
+ DEFAULT_CLIPBOARD_TIMEOUT_MILLIS);
+ }
+
@Override
public void clearPrimaryClip(String callingPackage, @UserIdInt int userId) {
final int intendingUid = getIntendingUid(callingPackage, userId);
@@ -365,6 +413,8 @@
return;
}
synchronized (mLock) {
+ mClipboardClearHandler.removeEqualMessages(ClipboardClearHandler.MSG_CLEAR,
+ userId);
setPrimaryClipInternalLocked(null, intendingUid, callingPackage);
}
}
@@ -391,6 +441,9 @@
PerUserClipboard clipboard = getClipboardLocked(intendingUserId);
showAccessNotificationLocked(pkg, intendingUid, intendingUserId, clipboard);
notifyTextClassifierLocked(clipboard, pkg, intendingUid);
+ if (clipboard.primaryClip != null) {
+ scheduleAutoClear(userId);
+ }
return clipboard.primaryClip;
}
}
@@ -484,6 +537,32 @@
return null;
}
}
+
+ private class ClipboardClearHandler extends Handler {
+
+ public static final int MSG_CLEAR = 101;
+
+ ClipboardClearHandler(Looper looper) {
+ super(looper);
+ }
+
+ public void handleMessage(@NonNull Message msg) {
+ switch (msg.what) {
+ case MSG_CLEAR:
+ final int userId = msg.arg1;
+ synchronized (mLock) {
+ if (getClipboardLocked(userId).primaryClip != null) {
+ FrameworkStatsLog.write(FrameworkStatsLog.CLIPBOARD_CLEARED,
+ FrameworkStatsLog.CLIPBOARD_CLEARED__SOURCE__AUTO_CLEAR);
+ setPrimaryClipInternalLocked(null, Binder.getCallingUid(), null);
+ }
+ }
+ break;
+ default:
+ Slog.wtf(TAG, "ClipboardClearHandler received unknown message " + msg.what);
+ }
+ }
+ }
};
@GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/display/DensityMap.java b/services/core/java/com/android/server/display/DensityMap.java
new file mode 100644
index 0000000..4aafd14
--- /dev/null
+++ b/services/core/java/com/android/server/display/DensityMap.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+/**
+ * Class which can compute the logical density for a display resolution. It holds a collection
+ * of pre-configured densities, which are used for look-up and interpolation.
+ */
+public class DensityMap {
+
+ // Instead of resolutions we store the squared diagonal size. Diagonals make the map
+ // keys invariant to rotations and are useful for interpolation because they're scalars.
+ // Squared diagonals have the same properties as diagonals (the square function is monotonic)
+ // but also allow us to use integer types and avoid floating point arithmetics.
+ private final Entry[] mSortedDensityMapEntries;
+
+ /**
+ * Creates a density map. The newly created object takes ownership of the passed array.
+ */
+ static DensityMap createByOwning(Entry[] densityMapEntries) {
+ return new DensityMap(densityMapEntries);
+ }
+
+ private DensityMap(Entry[] densityMapEntries) {
+ Arrays.sort(densityMapEntries, Comparator.comparingInt(entry -> entry.squaredDiagonal));
+ mSortedDensityMapEntries = densityMapEntries;
+ verifyDensityMap(mSortedDensityMapEntries);
+ }
+
+ /**
+ * Returns the logical density for the given resolution.
+ *
+ * If the resolution matches one of the entries in the map, the corresponding density is
+ * returned. Otherwise the return value is interpolated using the closest entries in the map.
+ */
+ public int getDensityForResolution(int width, int height) {
+ int squaredDiagonal = width * width + height * height;
+
+ // Search for two pre-configured entries "left" and "right" with the following criteria
+ // * left <= squaredDiagonal
+ // * squaredDiagonal - left is minimal
+ // * right > squaredDiagonal
+ // * right - squaredDiagonal is minimal
+ Entry left = Entry.ZEROES;
+ Entry right = null;
+
+ for (Entry entry : mSortedDensityMapEntries) {
+ if (entry.squaredDiagonal <= squaredDiagonal) {
+ left = entry;
+ } else {
+ right = entry;
+ break;
+ }
+ }
+
+ // Check if we found an exact match.
+ if (left.squaredDiagonal == squaredDiagonal) {
+ return left.density;
+ }
+
+ // If no configured resolution is higher than the specified resolution, interpolate
+ // between (0,0) and (maxConfiguredDiagonal, maxConfiguredDensity).
+ if (right == null) {
+ right = left; // largest entry in the sorted array
+ left = Entry.ZEROES;
+ }
+
+ double leftDiagonal = Math.sqrt(left.squaredDiagonal);
+ double rightDiagonal = Math.sqrt(right.squaredDiagonal);
+ double diagonal = Math.sqrt(squaredDiagonal);
+
+ return (int) Math.round((diagonal - leftDiagonal) * (right.density - left.density)
+ / (rightDiagonal - leftDiagonal) + left.density);
+ }
+
+ private static void verifyDensityMap(Entry[] sortedEntries) {
+ for (int i = 1; i < sortedEntries.length; i++) {
+ Entry prev = sortedEntries[i - 1];
+ Entry curr = sortedEntries[i];
+
+ if (prev.squaredDiagonal == curr.squaredDiagonal) {
+ // This will most often happen because there are two entries with the same
+ // resolution (AxB and AxB) or rotated resolution (AxB and BxA), but it can also
+ // happen in the very rare cases when two different resolutions happen to have
+ // the same diagonal (e.g. 100x700 and 500x500).
+ throw new IllegalStateException("Found two entries in the density map with"
+ + " the same diagonal: " + prev + ", " + curr);
+ } else if (prev.density > curr.density) {
+ throw new IllegalStateException("Found two entries in the density map with"
+ + " increasing diagonal but decreasing density: " + prev + ", " + curr);
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "DensityMap{"
+ + "mDensityMapEntries=" + Arrays.toString(mSortedDensityMapEntries)
+ + '}';
+ }
+
+ static class Entry {
+ public static final Entry ZEROES = new Entry(0, 0, 0);
+
+ public final int squaredDiagonal;
+ public final int density;
+
+ Entry(int width, int height, int density) {
+ this.squaredDiagonal = width * width + height * height;
+ this.density = density;
+ }
+
+ @Override
+ public String toString() {
+ return "DensityMapEntry{"
+ + "squaredDiagonal=" + squaredDiagonal
+ + ", density=" + density + '}';
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 2ae5cbb..a9e1647 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation;
@@ -31,6 +32,7 @@
import com.android.internal.R;
import com.android.internal.display.BrightnessSynchronizer;
+import com.android.server.display.config.Density;
import com.android.server.display.config.DisplayConfiguration;
import com.android.server.display.config.DisplayQuirks;
import com.android.server.display.config.HbmTiming;
@@ -52,6 +54,7 @@
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.List;
import javax.xml.datatype.DatatypeConfigurationException;
@@ -70,6 +73,8 @@
private static final String ETC_DIR = "etc";
private static final String DISPLAY_CONFIG_DIR = "displayconfig";
private static final String CONFIG_FILE_FORMAT = "display_%s.xml";
+ private static final String DEFAULT_CONFIG_FILE = "default.xml";
+ private static final String DEFAULT_CONFIG_FILE_WITH_UIMODE_FORMAT = "default_%s.xml";
private static final String PORT_SUFFIX_FORMAT = "port_%d";
private static final String STABLE_ID_SUFFIX_FORMAT = "id_%d";
private static final String NO_SUFFIX_FORMAT = "%d";
@@ -121,6 +126,7 @@
private List<String> mQuirks;
private boolean mIsHighBrightnessModeEnabled = false;
private HighBrightnessModeData mHbmData;
+ private DensityMap mDensityMap;
private String mLoadedFrom = null;
private DisplayDeviceConfig(Context context) {
@@ -141,6 +147,33 @@
*/
public static DisplayDeviceConfig create(Context context, long physicalDisplayId,
boolean isDefaultDisplay) {
+ final DisplayDeviceConfig config = createWithoutDefaultValues(context, physicalDisplayId,
+ isDefaultDisplay);
+
+ config.copyUninitializedValuesFromSecondaryConfig(loadDefaultConfigurationXml(context));
+ return config;
+ }
+
+ /**
+ * Creates an instance using global values since no display device config xml exists.
+ * Uses values from config or PowerManager.
+ *
+ * @param context
+ * @param useConfigXml
+ * @return A configuration instance.
+ */
+ public static DisplayDeviceConfig create(Context context, boolean useConfigXml) {
+ final DisplayDeviceConfig config;
+ if (useConfigXml) {
+ config = getConfigFromGlobalXml(context);
+ } else {
+ config = getConfigFromPmValues(context);
+ }
+ return config;
+ }
+
+ private static DisplayDeviceConfig createWithoutDefaultValues(Context context,
+ long physicalDisplayId, boolean isDefaultDisplay) {
DisplayDeviceConfig config;
config = loadConfigFromDirectory(context, Environment.getProductDirectory(),
@@ -161,22 +194,53 @@
return create(context, isDefaultDisplay);
}
- /**
- * Creates an instance using global values since no display device config xml exists.
- * Uses values from config or PowerManager.
- *
- * @param context
- * @param useConfigXml
- * @return A configuration instance.
- */
- public static DisplayDeviceConfig create(Context context, boolean useConfigXml) {
- DisplayDeviceConfig config;
- if (useConfigXml) {
- config = getConfigFromGlobalXml(context);
- } else {
- config = getConfigFromPmValues(context);
+ private static DisplayConfiguration loadDefaultConfigurationXml(Context context) {
+ List<File> defaultXmlLocations = new ArrayList<>();
+ defaultXmlLocations.add(Environment.buildPath(Environment.getProductDirectory(),
+ ETC_DIR, DISPLAY_CONFIG_DIR, DEFAULT_CONFIG_FILE));
+ defaultXmlLocations.add(Environment.buildPath(Environment.getVendorDirectory(),
+ ETC_DIR, DISPLAY_CONFIG_DIR, DEFAULT_CONFIG_FILE));
+
+ // Read config_defaultUiModeType directly because UiModeManager hasn't started yet.
+ final int uiModeType = context.getResources()
+ .getInteger(com.android.internal.R.integer.config_defaultUiModeType);
+ final String uiModeTypeStr = Configuration.getUiModeTypeString(uiModeType);
+ if (uiModeTypeStr != null) {
+ defaultXmlLocations.add(Environment.buildPath(Environment.getRootDirectory(),
+ ETC_DIR, DISPLAY_CONFIG_DIR,
+ String.format(DEFAULT_CONFIG_FILE_WITH_UIMODE_FORMAT, uiModeTypeStr)));
}
- return config;
+ defaultXmlLocations.add(Environment.buildPath(Environment.getRootDirectory(),
+ ETC_DIR, DISPLAY_CONFIG_DIR, DEFAULT_CONFIG_FILE));
+
+ final File configFile = getFirstExistingFile(defaultXmlLocations);
+ if (configFile == null) {
+ // Display configuration files aren't required to exist.
+ return null;
+ }
+
+ DisplayConfiguration defaultConfig = null;
+
+ try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) {
+ defaultConfig = XmlParser.read(in);
+ if (defaultConfig == null) {
+ Slog.i(TAG, "Default DisplayDeviceConfig file is null");
+ }
+ } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
+ Slog.e(TAG, "Encountered an error while reading/parsing display config file: "
+ + configFile, e);
+ }
+
+ return defaultConfig;
+ }
+
+ private static File getFirstExistingFile(Collection<File> files) {
+ for (File file : files) {
+ if (file.exists() && file.isFile()) {
+ return file;
+ }
+ }
+ return null;
}
private static DisplayDeviceConfig loadConfigFromDirectory(Context context,
@@ -316,9 +380,13 @@
return mRefreshRateLimitations;
}
+ public DensityMap getDensityMap() {
+ return mDensityMap;
+ }
+
@Override
public String toString() {
- String str = "DisplayDeviceConfig{"
+ return "DisplayDeviceConfig{"
+ "mLoadedFrom=" + mLoadedFrom
+ ", mBacklight=" + Arrays.toString(mBacklight)
+ ", mNits=" + Arrays.toString(mNits)
@@ -340,8 +408,8 @@
+ ", mAmbientLightSensor=" + mAmbientLightSensor
+ ", mProximitySensor=" + mProximitySensor
+ ", mRefreshRateLimitations= " + Arrays.toString(mRefreshRateLimitations.toArray())
+ + ", mDensityMap= " + mDensityMap
+ "}";
- return str;
}
private static DisplayDeviceConfig getConfigFromSuffix(Context context, File baseDirectory,
@@ -384,6 +452,7 @@
try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) {
final DisplayConfiguration config = XmlParser.read(in);
if (config != null) {
+ loadDensityMap(config);
loadBrightnessDefaultFromDdcXml(config);
loadBrightnessConstraintsFromConfigXml();
loadBrightnessMap(config);
@@ -429,6 +498,35 @@
setProxSensorUnspecified();
}
+ private void copyUninitializedValuesFromSecondaryConfig(DisplayConfiguration defaultConfig) {
+ if (defaultConfig == null) {
+ return;
+ }
+
+ if (mDensityMap == null) {
+ loadDensityMap(defaultConfig);
+ }
+ }
+
+ private void loadDensityMap(DisplayConfiguration config) {
+ if (config.getDensityMap() == null) {
+ return;
+ }
+
+ final List<Density> entriesFromXml = config.getDensityMap().getDensity();
+
+ final DensityMap.Entry[] entries =
+ new DensityMap.Entry[entriesFromXml.size()];
+ for (int i = 0; i < entriesFromXml.size(); i++) {
+ final Density density = entriesFromXml.get(i);
+ entries[i] = new DensityMap.Entry(
+ density.getWidth().intValue(),
+ density.getHeight().intValue(),
+ density.getDensity().intValue());
+ }
+ mDensityMap = DensityMap.createByOwning(entries);
+ }
+
private void loadBrightnessDefaultFromDdcXml(DisplayConfiguration config) {
// Default brightness values are stored in the displayDeviceConfig file,
// Or we fallback standard values if not.
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index b6d13e0..300f59e 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -426,6 +426,15 @@
: mDefaultModeId;
}
+ private int getLogicalDensity() {
+ DensityMap densityMap = getDisplayDeviceConfig().getDensityMap();
+ if (densityMap == null) {
+ return (int) (mStaticDisplayInfo.density * 160 + 0.5);
+ }
+
+ return densityMap.getDensityForResolution(mInfo.width, mInfo.height);
+ }
+
private void loadDisplayDeviceConfig() {
// Load display device config
final Context context = getOverlayContext();
@@ -591,7 +600,7 @@
final DisplayAddress.Physical physicalAddress =
DisplayAddress.fromPhysicalDisplayId(mPhysicalDisplayId);
mInfo.address = physicalAddress;
- mInfo.densityDpi = (int) (mStaticDisplayInfo.density * 160 + 0.5f);
+ mInfo.densityDpi = getLogicalDensity();
mInfo.xDpi = mActiveSfDisplayMode.xDpi;
mInfo.yDpi = mActiveSfDisplayMode.yDpi;
mInfo.deviceProductInfo = mStaticDisplayInfo.deviceProductInfo;
@@ -1029,7 +1038,7 @@
for (int i = 0; i < mSupportedModes.size(); i++) {
pw.println(" " + mSupportedModes.valueAt(i));
}
- pw.println("mSupportedColorModes=" + mSupportedColorModes.toString());
+ pw.println("mSupportedColorModes=" + mSupportedColorModes);
pw.println("mDisplayDeviceConfig=" + mDisplayDeviceConfig);
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index 3d91fee..c5dc23e 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -82,6 +82,7 @@
private IBinder mCurToken;
private int mCurSeq;
private boolean mVisibleBound;
+ private boolean mSupportsStylusHw;
/**
* Binding flags for establishing connection to the {@link InputMethodService}.
@@ -295,6 +296,10 @@
mService.scheduleNotifyImeUidToAudioService(mCurMethodUid);
mService.reRequestCurrentClientSessionLocked();
}
+ mSupportsStylusHw = mMethodMap.get(mSelectedMethodId).supportsStylusHandwriting();
+ if (mSupportsStylusHw) {
+ // TODO init Handwriting spy.
+ }
}
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 7d4877c..258ae8c 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -2151,6 +2151,10 @@
final PackagePreferences r = mPackagePreferences.valueAt(i);
event.writeInt(r.uid);
event.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
+
+ // collect whether this package's importance info was user-set for later, if needed
+ // before the migration is enabled, this will simply default to false in all cases.
+ boolean importanceIsUserSet = false;
if (mPermissionHelper.isMigrationEnabled()) {
// Even if this package's data is not present, we need to write something;
// so default to IMPORTANCE_NONE, since if PM doesn't know about the package
@@ -2158,8 +2162,12 @@
int importance = IMPORTANCE_NONE;
Pair<Integer, String> key = new Pair<>(r.uid, r.pkg);
if (pkgPermissions != null && pkgsWithPermissionsToHandle.contains(key)) {
- importance = pkgPermissions.get(key).first
+ Pair<Boolean, Boolean> permissionPair = pkgPermissions.get(key);
+ importance = permissionPair.first
? IMPORTANCE_DEFAULT : IMPORTANCE_NONE;
+ // cache the second value for writing later
+ importanceIsUserSet = permissionPair.second;
+
pkgsWithPermissionsToHandle.remove(key);
}
event.writeInt(importance);
@@ -2168,6 +2176,7 @@
}
event.writeInt(r.visibility);
event.writeInt(r.lockedAppFields);
+ event.writeBoolean(importanceIsUserSet); // optional bool user_set_importance = 5;
events.add(event.build());
}
}
@@ -2189,6 +2198,7 @@
// builder
event.writeInt(DEFAULT_VISIBILITY);
event.writeInt(DEFAULT_LOCKED_APP_FIELDS);
+ event.writeBoolean(pkgPermissions.get(p).second); // user_set_importance field
events.add(event.build());
}
}
diff --git a/services/core/java/com/android/server/pm/AppDataHelper.java b/services/core/java/com/android/server/pm/AppDataHelper.java
index e99512d..bedb8b9 100644
--- a/services/core/java/com/android/server/pm/AppDataHelper.java
+++ b/services/core/java/com/android/server/pm/AppDataHelper.java
@@ -74,13 +74,6 @@
mArtManagerService = mInjector.getArtManagerService();
}
- AppDataHelper(PackageManagerService pm, PackageManagerServiceInjector injector) {
- mPm = pm;
- mInjector = injector;
- mInstaller = injector.getInstaller();
- mArtManagerService = injector.getArtManagerService();
- }
-
/**
* Prepare app data for the given app just after it was installed or
* upgraded. This method carefully only touches users that it's installed
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 8fda109..e84d990 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -236,7 +236,9 @@
if (res) {
final boolean killApp = (deleteFlags & PackageManager.DELETE_DONT_KILL_APP) == 0;
info.sendPackageRemovedBroadcasts(killApp, removedBySystem);
- info.sendSystemPackageUpdatedBroadcasts();
+ if (disabledSystemPs != null) {
+ info.sendSystemPackageUpdatedBroadcasts(disabledSystemPs.getAppId());
+ }
}
// Force a gc here.
Runtime.getRuntime().gc();
@@ -591,6 +593,7 @@
if (outInfo != null) {
// Delete the updated package
outInfo.mIsRemovedPackageSystemUpdate = true;
+ outInfo.mAppIdChanging = disabledPs.getAppId() != deletedPs.getAppId();
}
if (disabledPs.getVersionCode() < deletedPs.getVersionCode()
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index c8594eb..8e6746d 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -32,8 +32,6 @@
import static android.content.pm.PackageManager.INSTALL_FAILED_TEST_ONLY;
import static android.content.pm.PackageManager.INSTALL_FAILED_UID_CHANGED;
import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
-import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_RESTORE;
import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_SETUP;
import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
@@ -47,7 +45,6 @@
import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
-import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
@@ -56,6 +53,8 @@
import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING;
import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE;
+import static com.android.server.pm.PackageManagerService.DEBUG_UPGRADE;
+import static com.android.server.pm.PackageManagerService.DEBUG_VERIFY;
import static com.android.server.pm.PackageManagerService.EMPTY_INT_ARRAY;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import static com.android.server.pm.PackageManagerService.POST_INSTALL;
@@ -73,13 +72,16 @@
import static com.android.server.pm.PackageManagerService.SCAN_AS_VIRTUAL_PRELOAD;
import static com.android.server.pm.PackageManagerService.SCAN_BOOTING;
import static com.android.server.pm.PackageManagerService.SCAN_DONT_KILL_APP;
+import static com.android.server.pm.PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE;
import static com.android.server.pm.PackageManagerService.SCAN_IGNORE_FROZEN;
import static com.android.server.pm.PackageManagerService.SCAN_INITIAL;
import static com.android.server.pm.PackageManagerService.SCAN_MOVE;
import static com.android.server.pm.PackageManagerService.SCAN_NEW_INSTALL;
import static com.android.server.pm.PackageManagerService.SCAN_NO_DEX;
+import static com.android.server.pm.PackageManagerService.SCAN_REQUIRE_KNOWN;
import static com.android.server.pm.PackageManagerService.SCAN_UPDATE_SIGNATURE;
import static com.android.server.pm.PackageManagerService.TAG;
+import static com.android.server.pm.PackageManagerServiceUtils.comparePackageSignatures;
import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
import static com.android.server.pm.PackageManagerServiceUtils.compressedFileExists;
import static com.android.server.pm.PackageManagerServiceUtils.deriveAbiOverride;
@@ -89,6 +91,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.AppOpsManager;
import android.app.ApplicationPackageManager;
import android.app.backup.IBackupManager;
import android.content.ContentResolver;
@@ -125,7 +128,6 @@
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
@@ -145,14 +147,15 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.F2fsUtils;
-import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.content.PackageHelper;
import com.android.internal.security.VerityUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.EventLogTags;
-import com.android.server.Watchdog;
+import com.android.server.pm.dex.ArtManagerService;
+import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DexoptOptions;
+import com.android.server.pm.dex.ViewCompiler;
import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
@@ -190,271 +193,45 @@
final class InstallPackageHelper {
private final PackageManagerService mPm;
private final AppDataHelper mAppDataHelper;
- private final PackageManagerServiceInjector mInjector;
private final BroadcastHelper mBroadcastHelper;
private final RemovePackageHelper mRemovePackageHelper;
- private final ScanPackageHelper mScanPackageHelper;
+ private final StorageManager mStorageManager;
+ private final RollbackManagerInternal mRollbackManager;
+ private final IncrementalManager mIncrementalManager;
+ private final ApexManager mApexManager;
+ private final DexManager mDexManager;
+ private final ArtManagerService mArtManagerService;
+ private final AppOpsManager mAppOpsManager;
+ private final Context mContext;
+ private final PackageDexOptimizer mPackageDexOptimizer;
+ private final PackageAbiHelper mPackageAbiHelper;
+ private final ViewCompiler mViewCompiler;
+ private final IBackupManager mIBackupManager;
// TODO(b/198166813): remove PMS dependency
InstallPackageHelper(PackageManagerService pm, AppDataHelper appDataHelper) {
mPm = pm;
- mInjector = pm.mInjector;
mAppDataHelper = appDataHelper;
- mBroadcastHelper = new BroadcastHelper(mInjector);
+ mBroadcastHelper = new BroadcastHelper(pm.mInjector);
mRemovePackageHelper = new RemovePackageHelper(pm);
- mScanPackageHelper = new ScanPackageHelper(pm);
+ mStorageManager = pm.mInjector.getSystemService(StorageManager.class);
+ mRollbackManager = pm.mInjector.getLocalService(RollbackManagerInternal.class);
+ mIncrementalManager = pm.mInjector.getIncrementalManager();
+ mApexManager = pm.mInjector.getApexManager();
+ mDexManager = pm.mInjector.getDexManager();
+ mArtManagerService = pm.mInjector.getArtManagerService();
+ mAppOpsManager = pm.mInjector.getSystemService(AppOpsManager.class);
+ mContext = pm.mInjector.getContext();
+ mPackageDexOptimizer = pm.mInjector.getPackageDexOptimizer();
+ mPackageAbiHelper = pm.mInjector.getAbiHelper();
+ mViewCompiler = pm.mInjector.getViewCompiler();
+ mIBackupManager = pm.mInjector.getIBackupManager();
}
InstallPackageHelper(PackageManagerService pm) {
this(pm, new AppDataHelper(pm));
}
- InstallPackageHelper(PackageManagerService pm, PackageManagerServiceInjector injector) {
- mPm = pm;
- mInjector = injector;
- mAppDataHelper = new AppDataHelper(pm, mInjector);
- mBroadcastHelper = new BroadcastHelper(injector);
- mRemovePackageHelper = new RemovePackageHelper(pm);
- mScanPackageHelper = new ScanPackageHelper(pm);
- }
-
- @GuardedBy("mPm.mLock")
- public Map<String, ReconciledPackage> reconcilePackagesLocked(
- final ReconcileRequest request, KeySetManagerService ksms,
- PackageManagerServiceInjector injector)
- throws ReconcileFailure {
- final Map<String, ScanResult> scannedPackages = request.mScannedPackages;
-
- final Map<String, ReconciledPackage> result = new ArrayMap<>(scannedPackages.size());
-
- // make a copy of the existing set of packages so we can combine them with incoming packages
- final ArrayMap<String, AndroidPackage> combinedPackages =
- new ArrayMap<>(request.mAllPackages.size() + scannedPackages.size());
-
- combinedPackages.putAll(request.mAllPackages);
-
- final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> incomingSharedLibraries =
- new ArrayMap<>();
-
- for (String installPackageName : scannedPackages.keySet()) {
- final ScanResult scanResult = scannedPackages.get(installPackageName);
-
- // add / replace existing with incoming packages
- combinedPackages.put(scanResult.mPkgSetting.getPackageName(),
- scanResult.mRequest.mParsedPackage);
-
- // in the first pass, we'll build up the set of incoming shared libraries
- final List<SharedLibraryInfo> allowedSharedLibInfos =
- SharedLibraryHelper.getAllowedSharedLibInfos(scanResult,
- request.mSharedLibrarySource);
- if (allowedSharedLibInfos != null) {
- for (SharedLibraryInfo info : allowedSharedLibInfos) {
- if (!SharedLibraryHelper.addSharedLibraryToPackageVersionMap(
- incomingSharedLibraries, info)) {
- throw new ReconcileFailure("Shared Library " + info.getName()
- + " is being installed twice in this set!");
- }
- }
- }
-
- // the following may be null if we're just reconciling on boot (and not during install)
- final InstallArgs installArgs = request.mInstallArgs.get(installPackageName);
- final PackageInstalledInfo res = request.mInstallResults.get(installPackageName);
- final PrepareResult prepareResult = request.mPreparedPackages.get(installPackageName);
- final boolean isInstall = installArgs != null;
- if (isInstall && (res == null || prepareResult == null)) {
- throw new ReconcileFailure("Reconcile arguments are not balanced for "
- + installPackageName + "!");
- }
-
- final DeletePackageAction deletePackageAction;
- // we only want to try to delete for non system apps
- if (isInstall && prepareResult.mReplace && !prepareResult.mSystem) {
- final boolean killApp = (scanResult.mRequest.mScanFlags & SCAN_DONT_KILL_APP) == 0;
- final int deleteFlags = PackageManager.DELETE_KEEP_DATA
- | (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP);
- deletePackageAction = DeletePackageHelper.mayDeletePackageLocked(res.mRemovedInfo,
- prepareResult.mOriginalPs, prepareResult.mDisabledPs,
- deleteFlags, null /* all users */);
- if (deletePackageAction == null) {
- throw new ReconcileFailure(
- PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE,
- "May not delete " + installPackageName + " to replace");
- }
- } else {
- deletePackageAction = null;
- }
-
- final int scanFlags = scanResult.mRequest.mScanFlags;
- final int parseFlags = scanResult.mRequest.mParseFlags;
- final ParsedPackage parsedPackage = scanResult.mRequest.mParsedPackage;
-
- final PackageSetting disabledPkgSetting = scanResult.mRequest.mDisabledPkgSetting;
- final PackageSetting lastStaticSharedLibSetting =
- request.mLastStaticSharedLibSettings.get(installPackageName);
- final PackageSetting signatureCheckPs =
- (prepareResult != null && lastStaticSharedLibSetting != null)
- ? lastStaticSharedLibSetting
- : scanResult.mPkgSetting;
- boolean removeAppKeySetData = false;
- boolean sharedUserSignaturesChanged = false;
- SigningDetails signingDetails = null;
- if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
- if (ksms.checkUpgradeKeySetLocked(signatureCheckPs, parsedPackage)) {
- // We just determined the app is signed correctly, so bring
- // over the latest parsed certs.
- } else {
- if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
- throw new ReconcileFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
- "Package " + parsedPackage.getPackageName()
- + " upgrade keys do not match the previously installed"
- + " version");
- } else {
- String msg = "System package " + parsedPackage.getPackageName()
- + " signature changed; retaining data.";
- PackageManagerService.reportSettingsProblem(Log.WARN, msg);
- }
- }
- signingDetails = parsedPackage.getSigningDetails();
- } else {
- try {
- final Settings.VersionInfo versionInfo =
- request.mVersionInfos.get(installPackageName);
- final boolean compareCompat = isCompatSignatureUpdateNeeded(versionInfo);
- final boolean compareRecover = isRecoverSignatureUpdateNeeded(versionInfo);
- final boolean isRollback = installArgs != null
- && installArgs.mInstallReason == PackageManager.INSTALL_REASON_ROLLBACK;
- final boolean compatMatch = verifySignatures(signatureCheckPs,
- disabledPkgSetting, parsedPackage.getSigningDetails(), compareCompat,
- compareRecover, isRollback);
- // The new KeySets will be re-added later in the scanning process.
- if (compatMatch) {
- removeAppKeySetData = true;
- }
- // We just determined the app is signed correctly, so bring
- // over the latest parsed certs.
- signingDetails = parsedPackage.getSigningDetails();
-
- // if this is is a sharedUser, check to see if the new package is signed by a
- // newer
- // signing certificate than the existing one, and if so, copy over the new
- // details
- if (signatureCheckPs.getSharedUser() != null) {
- // Attempt to merge the existing lineage for the shared SigningDetails with
- // the lineage of the new package; if the shared SigningDetails are not
- // returned this indicates the new package added new signers to the lineage
- // and/or changed the capabilities of existing signers in the lineage.
- SigningDetails sharedSigningDetails =
- signatureCheckPs.getSharedUser().signatures.mSigningDetails;
- SigningDetails mergedDetails = sharedSigningDetails.mergeLineageWith(
- signingDetails);
- if (mergedDetails != sharedSigningDetails) {
- signatureCheckPs.getSharedUser().signatures.mSigningDetails =
- mergedDetails;
- }
- if (signatureCheckPs.getSharedUser().signaturesChanged == null) {
- signatureCheckPs.getSharedUser().signaturesChanged = Boolean.FALSE;
- }
- }
- } catch (PackageManagerException e) {
- if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
- throw new ReconcileFailure(e);
- }
- signingDetails = parsedPackage.getSigningDetails();
-
- // If the system app is part of a shared user we allow that shared user to
- // change
- // signatures as well as part of an OTA. We still need to verify that the
- // signatures
- // are consistent within the shared user for a given boot, so only allow
- // updating
- // the signatures on the first package scanned for the shared user (i.e. if the
- // signaturesChanged state hasn't been initialized yet in SharedUserSetting).
- if (signatureCheckPs.getSharedUser() != null) {
- final Signature[] sharedUserSignatures = signatureCheckPs.getSharedUser()
- .signatures.mSigningDetails.getSignatures();
- if (signatureCheckPs.getSharedUser().signaturesChanged != null
- && compareSignatures(sharedUserSignatures,
- parsedPackage.getSigningDetails().getSignatures())
- != PackageManager.SIGNATURE_MATCH) {
- if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 29) {
- // Mismatched signatures is an error and silently skipping system
- // packages will likely break the device in unforeseen ways.
- // However, we allow the device to boot anyway because, prior to Q,
- // vendors were not expecting the platform to crash in this
- // situation.
- // This WILL be a hard failure on any new API levels after Q.
- throw new ReconcileFailure(
- INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
- "Signature mismatch for shared user: "
- + scanResult.mPkgSetting.getSharedUser());
- } else {
- // Treat mismatched signatures on system packages using a shared
- // UID as
- // fatal for the system overall, rather than just failing to install
- // whichever package happened to be scanned later.
- throw new IllegalStateException(
- "Signature mismatch on system package "
- + parsedPackage.getPackageName()
- + " for shared user "
- + scanResult.mPkgSetting.getSharedUser());
- }
- }
-
- sharedUserSignaturesChanged = true;
- signatureCheckPs.getSharedUser().signatures.mSigningDetails =
- parsedPackage.getSigningDetails();
- signatureCheckPs.getSharedUser().signaturesChanged = Boolean.TRUE;
- }
- // File a report about this.
- String msg = "System package " + parsedPackage.getPackageName()
- + " signature changed; retaining data.";
- PackageManagerService.reportSettingsProblem(Log.WARN, msg);
- } catch (IllegalArgumentException e) {
- // should never happen: certs matched when checking, but not when comparing
- // old to new for sharedUser
- throw new RuntimeException(
- "Signing certificates comparison made on incomparable signing details"
- + " but somehow passed verifySignatures!", e);
- }
- }
-
- result.put(installPackageName,
- new ReconciledPackage(request, installArgs, scanResult.mPkgSetting,
- res, request.mPreparedPackages.get(installPackageName), scanResult,
- deletePackageAction, allowedSharedLibInfos, signingDetails,
- sharedUserSignaturesChanged, removeAppKeySetData));
- }
-
- for (String installPackageName : scannedPackages.keySet()) {
- // Check all shared libraries and map to their actual file path.
- // We only do this here for apps not on a system dir, because those
- // are the only ones that can fail an install due to this. We
- // will take care of the system apps by updating all of their
- // library paths after the scan is done. Also during the initial
- // scan don't update any libs as we do this wholesale after all
- // apps are scanned to avoid dependency based scanning.
- final ScanResult scanResult = scannedPackages.get(installPackageName);
- if ((scanResult.mRequest.mScanFlags & SCAN_BOOTING) != 0
- || (scanResult.mRequest.mParseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR)
- != 0) {
- continue;
- }
- try {
- result.get(installPackageName).mCollectedSharedLibraryInfos =
- SharedLibraryHelper.collectSharedLibraryInfos(
- scanResult.mRequest.mParsedPackage,
- combinedPackages, request.mSharedLibrarySource,
- incomingSharedLibraries, injector.getCompatibility());
-
- } catch (PackageManagerException e) {
- throw new ReconcileFailure(e.error, e.getMessage());
- }
- }
-
- return result;
- }
-
/**
* Commits the package scan and modifies system state.
* <p><em>WARNING:</em> The method may throw an exception in the middle
@@ -671,7 +448,7 @@
// Add the new setting to mPackages
mPm.mPackages.put(pkg.getPackageName(), pkg);
if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0) {
- mPm.mApexManager.registerApkInApex(pkg);
+ mApexManager.registerApkInApex(pkg);
}
// Add the package's KeySets to the global KeySetManagerService
@@ -722,27 +499,6 @@
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
- /**
- * If the database version for this type of package (internal storage or
- * external storage) is less than the version where package signatures
- * were updated, return true.
- */
- public boolean isCompatSignatureUpdateNeeded(AndroidPackage pkg) {
- return isCompatSignatureUpdateNeeded(mPm.getSettingsVersionForPackage(pkg));
- }
-
- public static boolean isCompatSignatureUpdateNeeded(Settings.VersionInfo ver) {
- return ver.databaseVersion < Settings.DatabaseVersion.SIGNATURE_END_ENTITY;
- }
-
- public boolean isRecoverSignatureUpdateNeeded(AndroidPackage pkg) {
- return isRecoverSignatureUpdateNeeded(mPm.getSettingsVersionForPackage(pkg));
- }
-
- public static boolean isRecoverSignatureUpdateNeeded(Settings.VersionInfo ver) {
- return ver.databaseVersion < Settings.DatabaseVersion.SIGNATURE_MALFORMED_RECOVER;
- }
-
public int installExistingPackageAsUser(@Nullable String packageName, @UserIdInt int userId,
@PackageManager.InstallFlags int installFlags,
@PackageManager.InstallReason int installReason,
@@ -755,9 +511,9 @@
}
final int callingUid = Binder.getCallingUid();
- if (mPm.mContext.checkCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES)
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES)
!= PackageManager.PERMISSION_GRANTED
- && mPm.mContext.checkCallingOrSelfPermission(
+ && mContext.checkCallingOrSelfPermission(
android.Manifest.permission.INSTALL_EXISTING_PACKAGES)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Neither user " + callingUid + " nor current process has "
@@ -810,7 +566,8 @@
// upgrade app from instant to full; we don't allow app downgrade
installed = true;
}
- mPm.setInstantAppForUser(mInjector, pkgSetting, userId, instantApp, fullApp);
+ ScanPackageUtils.setInstantAppForUser(mPm.mInjector, pkgSetting, userId, instantApp,
+ fullApp);
}
if (installed) {
@@ -847,7 +604,7 @@
mPm.restorePermissionsAndUpdateRolesForNewUserInstall(packageName,
userId);
if (intentSender != null) {
- onRestoreComplete(res.mReturnCode, mPm.mContext, intentSender);
+ onRestoreComplete(res.mReturnCode, mContext, intentSender);
}
});
restoreAndPostInstall(userId, res, postInstallData);
@@ -933,9 +690,7 @@
* Returns whether the restore successfully completed.
*/
private boolean performBackupManagerRestore(int userId, int token, PackageInstalledInfo res) {
- IBackupManager bm = IBackupManager.Stub.asInterface(
- ServiceManager.getService(Context.BACKUP_SERVICE));
- if (bm != null) {
+ if (mIBackupManager != null) {
// For backwards compatibility as USER_ALL previously routed directly to USER_SYSTEM
// in the BackupManager. USER_ALL is used in compatibility tests.
if (userId == UserHandle.USER_ALL) {
@@ -946,8 +701,8 @@
}
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "restore", token);
try {
- if (bm.isUserReadyForBackup(userId)) {
- bm.restoreAtInstallForUser(
+ if (mIBackupManager.isUserReadyForBackup(userId)) {
+ mIBackupManager.restoreAtInstallForUser(
userId, res.mPkg.getPackageName(), token);
} else {
Slog.w(TAG, "User " + userId + " is not ready. Restore at install "
@@ -973,8 +728,6 @@
*/
private boolean performRollbackManagerRestore(int userId, int token, PackageInstalledInfo res,
PostInstallData data) {
- RollbackManagerInternal rm = mInjector.getLocalService(RollbackManagerInternal.class);
-
final String packageName = res.mPkg.getPackageName();
final int[] allUsers = mPm.mUserManager.getUserIds();
final int[] installedUsers;
@@ -1000,8 +753,8 @@
if (ps != null && doSnapshotOrRestore) {
final String seInfo = AndroidPackageUtils.getSeInfo(res.mPkg, ps);
- rm.snapshotAndRestoreUserData(packageName, UserHandle.toUserHandles(installedUsers),
- appId, ceDataInode, seInfo, token);
+ mRollbackManager.snapshotAndRestoreUserData(packageName,
+ UserHandle.toUserHandles(installedUsers), appId, ceDataInode, seInfo, token);
return true;
}
return false;
@@ -1093,7 +846,7 @@
+ " got: " + apexes.length);
}
try (PackageParser2 packageParser = mPm.mInjector.getScanningPackageParser()) {
- mPm.mApexManager.installPackage(apexes[0], packageParser);
+ mApexManager.installPackage(apexes[0], packageParser);
}
} catch (PackageManagerException e) {
request.mInstallResult.setError("APEX installation failed", e);
@@ -1170,7 +923,7 @@
installResults.put(packageName, request.mInstallResult);
installArgs.put(packageName, request.mArgs);
try {
- final ScanResult result = mScanPackageHelper.scanPackageTracedLI(
+ final ScanResult result = scanPackageTracedLI(
prepareResult.mPackageToScan, prepareResult.mParseFlags,
prepareResult.mScanFlags, System.currentTimeMillis(),
request.mArgs.mUser, request.mArgs.mAbiOverride);
@@ -1183,6 +936,9 @@
+ " in multi-package install request.");
return;
}
+ if (result.needsNewAppId()) {
+ request.mInstallResult.mRemovedInfo.mAppIdChanging = true;
+ }
createdAppId.put(packageName, optimisticallyRegisterAppId(result));
versionInfos.put(result.mPkgSetting.getPkg().getPackageName(),
mPm.getSettingsVersionForPackage(result.mPkgSetting.getPkg()));
@@ -1213,7 +969,7 @@
Map<String, ReconciledPackage> reconciledPackages;
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages");
- reconciledPackages = reconcilePackagesLocked(
+ reconciledPackages = ReconcilePackageUtils.reconcilePackages(
reconcileRequest, mPm.mSettings.getKeySetManagerService(),
mPm.mInjector);
} catch (ReconcileFailure e) {
@@ -1256,7 +1012,7 @@
.buildVerificationRootHashString(baseCodePath, splitCodePaths);
VerificationUtils.broadcastPackageVerified(verificationId, originUri,
PackageManager.VERIFICATION_ALLOW, rootHashString,
- args.mDataLoaderType, args.getUser(), mPm.mContext);
+ args.mDataLoaderType, args.getUser(), mContext);
}
} else {
for (ScanResult result : preparedScans.values()) {
@@ -1472,9 +1228,11 @@
} else {
try {
final boolean compareCompat =
- isCompatSignatureUpdateNeeded(parsedPackage);
+ ReconcilePackageUtils.isCompatSignatureUpdateNeeded(
+ mPm.getSettingsVersionForPackage(parsedPackage));
final boolean compareRecover =
- isRecoverSignatureUpdateNeeded(parsedPackage);
+ ReconcilePackageUtils.isRecoverSignatureUpdateNeeded(
+ mPm.getSettingsVersionForPackage(parsedPackage));
// We don't care about disabledPkgSetting on install for now.
final boolean compatMatch = verifySignatures(signatureCheckPs, null,
parsedPackage.getSigningDetails(), compareCompat, compareRecover,
@@ -1676,9 +1434,9 @@
final String abiOverride = deriveAbiOverride(args.mAbiOverride);
boolean isUpdatedSystemAppInferred = oldPackage != null && oldPackage.isSystem();
final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths>
- derivedAbi = mPm.mInjector.getAbiHelper().derivePackageAbi(parsedPackage,
+ derivedAbi = mPackageAbiHelper.derivePackageAbi(parsedPackage,
isUpdatedSystemAppFromExistingSetting || isUpdatedSystemAppInferred,
- abiOverride, mPm.mAppLib32InstallDir);
+ abiOverride, ScanPackageUtils.getAppLib32InstallDir());
derivedAbi.first.applyTo(parsedPackage);
derivedAbi.second.applyTo(parsedPackage);
} catch (PackageManagerException pme) {
@@ -2376,8 +2134,8 @@
// that can be used for all the packages.
final String codePath = ps.getPathString();
if (IncrementalManager.isIncrementalPath(codePath)
- && mPm.mIncrementalManager != null) {
- mPm.mIncrementalManager.registerLoadingProgressCallback(codePath,
+ && mIncrementalManager != null) {
+ mIncrementalManager.registerLoadingProgressCallback(codePath,
new IncrementalProgressListener(ps.getPackageName(), mPm));
}
@@ -2438,17 +2196,16 @@
*/
private void executePostCommitSteps(CommitRequest commitRequest) {
final ArraySet<IncrementalStorage> incrementalStorages = new ArraySet<>();
- final AppDataHelper appDataHelper = new AppDataHelper(mPm);
for (ReconciledPackage reconciledPkg : commitRequest.mReconciledPackages.values()) {
final boolean instantApp = ((reconciledPkg.mScanResult.mRequest.mScanFlags
& SCAN_AS_INSTANT_APP) != 0);
final AndroidPackage pkg = reconciledPkg.mPkgSetting.getPkg();
final String packageName = pkg.getPackageName();
final String codePath = pkg.getPath();
- final boolean onIncremental = mPm.mIncrementalManager != null
+ final boolean onIncremental = mIncrementalManager != null
&& isIncrementalPath(codePath);
if (onIncremental) {
- IncrementalStorage storage = mPm.mIncrementalManager.openStorage(codePath);
+ IncrementalStorage storage = mIncrementalManager.openStorage(codePath);
if (storage == null) {
throw new IllegalArgumentException(
"Install: null storage for incremental package " + packageName);
@@ -2460,28 +2217,28 @@
// Only set previousAppId if the app is migrating out of shared UID
previousAppId = reconciledPkg.mScanResult.mPreviousAppId;
}
- appDataHelper.prepareAppDataPostCommitLIF(pkg, previousAppId);
+ mAppDataHelper.prepareAppDataPostCommitLIF(pkg, previousAppId);
if (reconciledPkg.mPrepareResult.mClearCodeCache) {
- appDataHelper.clearAppDataLIF(pkg, UserHandle.USER_ALL,
+ mAppDataHelper.clearAppDataLIF(pkg, UserHandle.USER_ALL,
FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL
| Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
}
if (reconciledPkg.mPrepareResult.mReplace) {
- mPm.getDexManager().notifyPackageUpdated(pkg.getPackageName(),
+ mDexManager.notifyPackageUpdated(pkg.getPackageName(),
pkg.getBaseApkPath(), pkg.getSplitCodePaths());
}
// Prepare the application profiles for the new code paths.
// This needs to be done before invoking dexopt so that any install-time profile
// can be used for optimizations.
- mPm.mArtManagerService.prepareAppProfiles(
+ mArtManagerService.prepareAppProfiles(
pkg,
mPm.resolveUserIds(reconciledPkg.mInstallArgs.mUser.getIdentifier()),
/* updateReferenceProfileContent= */ true);
// Compute the compilation reason from the installation scenario.
final int compilationReason =
- mPm.getDexManager().getCompilationReasonForInstallScenario(
+ mDexManager.getCompilationReasonForInstallScenario(
reconciledPkg.mInstallArgs.mInstallScenario);
// Construct the DexoptOptions early to see if we should skip running dexopt.
@@ -2529,7 +2286,7 @@
// path moved to SCENARIO_FAST.
final boolean performDexopt =
(!instantApp || android.provider.Settings.Global.getInt(
- mPm.mContext.getContentResolver(),
+ mContext.getContentResolver(),
android.provider.Settings.Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
&& !pkg.isDebuggable()
&& (!onIncremental)
@@ -2539,7 +2296,7 @@
// Compile the layout resources.
if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "compileLayouts");
- mPm.mViewCompiler.compileLayouts(pkg);
+ mViewCompiler.compileLayouts(pkg);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -2564,10 +2321,10 @@
realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp);
- mPm.mPackageDexOptimizer.performDexOpt(pkg, realPkgSetting,
+ mPackageDexOptimizer.performDexOpt(pkg, realPkgSetting,
null /* instructionSets */,
mPm.getOrCreateCompilerPackageStats(pkg),
- mPm.getDexManager().getPackageUseInfoOrDefault(packageName),
+ mDexManager.getPackageUseInfoOrDefault(packageName),
dexoptOptions);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -2580,7 +2337,8 @@
notifyPackageChangeObserversOnUpdate(reconciledPkg);
}
- waitForNativeBinariesExtraction(incrementalStorages);
+ PackageManagerServiceUtils.waitForNativeBinariesExtractionForIncremental(
+ incrementalStorages);
}
private void notifyPackageChangeObserversOnUpdate(ReconciledPackage reconciledPkg) {
@@ -2599,27 +2357,6 @@
mPm.notifyPackageChangeObservers(pkgChangeEvent);
}
- private static void waitForNativeBinariesExtraction(
- ArraySet<IncrementalStorage> incrementalStorages) {
- if (incrementalStorages.isEmpty()) {
- return;
- }
- try {
- // Native library extraction may take very long time: each page could potentially
- // wait for either 10s or 100ms (adb vs non-adb data loader), and that easily adds
- // up to a full watchdog timeout of 1 min, killing the system after that. It doesn't
- // make much sense as blocking here doesn't lock up the framework, but only blocks
- // the installation session and the following ones.
- Watchdog.getInstance().pauseWatchingCurrentThread("native_lib_extract");
- for (int i = 0; i < incrementalStorages.size(); ++i) {
- IncrementalStorage storage = incrementalStorages.valueAtUnchecked(i);
- storage.waitForNativeBinariesExtraction();
- }
- } finally {
- Watchdog.getInstance().resumeWatchingCurrentThread("native_lib_extract");
- }
- }
-
public int installLocationPolicy(PackageInfoLite pkgLite, int installFlags) {
String packageName = pkgLite.packageName;
int installLocation = pkgLite.installLocation;
@@ -2705,7 +2442,7 @@
if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags,
dataOwnerPkg.isDebuggable())) {
try {
- checkDowngrade(dataOwnerPkg, pkgLite);
+ PackageManagerServiceUtils.checkDowngrade(dataOwnerPkg, pkgLite);
} catch (PackageManagerException e) {
String errorMsg = "Downgrade detected: " + e.getMessage();
Slog.w(TAG, errorMsg);
@@ -2722,7 +2459,7 @@
long requiredInstalledVersionCode, int installFlags) {
String packageName = pkgLite.packageName;
- final PackageInfo activePackage = mPm.mApexManager.getPackageInfo(packageName,
+ final PackageInfo activePackage = mApexManager.getPackageInfo(packageName,
ApexManager.MATCH_ACTIVE_PACKAGE);
if (activePackage == null) {
String errorMsg = "Attempting to install new APEX package " + packageName;
@@ -2755,41 +2492,6 @@
return Pair.create(PackageManager.INSTALL_SUCCEEDED, null);
}
- /**
- * Check and throw if the given before/after packages would be considered a
- * downgrade.
- */
- private static void checkDowngrade(AndroidPackage before, PackageInfoLite after)
- throws PackageManagerException {
- if (after.getLongVersionCode() < before.getLongVersionCode()) {
- throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
- "Update version code " + after.versionCode + " is older than current "
- + before.getLongVersionCode());
- } else if (after.getLongVersionCode() == before.getLongVersionCode()) {
- if (after.baseRevisionCode < before.getBaseRevisionCode()) {
- throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
- "Update base revision code " + after.baseRevisionCode
- + " is older than current " + before.getBaseRevisionCode());
- }
-
- if (!ArrayUtils.isEmpty(after.splitNames)) {
- for (int i = 0; i < after.splitNames.length; i++) {
- final String splitName = after.splitNames[i];
- final int j = ArrayUtils.indexOf(before.getSplitNames(), splitName);
- if (j != -1) {
- if (after.splitRevisionCodes[i] < before.getSplitRevisionCodes()[j]) {
- throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
- "Update split " + splitName + " revision code "
- + after.splitRevisionCodes[i]
- + " is older than current "
- + before.getSplitRevisionCodes()[j]);
- }
- }
- }
- }
- }
- }
-
int getUidForVerifier(VerifierInfo verifierInfo) {
synchronized (mPm.mLock) {
final AndroidPackage pkg = mPm.mPackages.get(verifierInfo.packageName);
@@ -2883,6 +2585,8 @@
final int dataLoaderType = installArgs.mDataLoaderType;
final boolean succeeded = res.mReturnCode == PackageManager.INSTALL_SUCCEEDED;
final boolean update = res.mRemovedInfo != null && res.mRemovedInfo.mRemovedPackage != null;
+ final int previousAppId = (res.mRemovedInfo != null && res.mRemovedInfo.mAppIdChanging)
+ ? res.mRemovedInfo.mUid : Process.INVALID_UID;
final String packageName = res.mName;
final PackageStateInternal pkgSetting =
succeeded ? mPm.getPackageStateInternal(packageName) : null;
@@ -2979,9 +2683,12 @@
dataLoaderType);
// Send added for users that don't see the package for the first time
- Bundle extras = new Bundle(1);
+ Bundle extras = new Bundle();
extras.putInt(Intent.EXTRA_UID, res.mUid);
- if (update) {
+ if (previousAppId != Process.INVALID_UID) {
+ extras.putBoolean(Intent.EXTRA_UID_CHANGING, true);
+ extras.putInt(Intent.EXTRA_PREVIOUS_UID, previousAppId);
+ } else if (update) {
extras.putBoolean(Intent.EXTRA_REPLACING, true);
}
extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);
@@ -3024,22 +2731,27 @@
// Send replaced for users that don't see the package for the first time
if (update) {
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
- packageName, extras, 0 /*flags*/,
- null /*targetPackage*/, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, res.mRemovedInfo.mBroadcastAllowList,
- null);
- if (installerPackageName != null) {
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
- extras, 0 /*flags*/,
- installerPackageName, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, null /*broadcastAllowList*/, null);
- }
- if (notifyVerifier) {
- mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
- extras, 0 /*flags*/,
- mPm.mRequiredVerifierPackage, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, null /*broadcastAllowList*/, null);
+ // Only send PACKAGE_REPLACED if appId has not changed
+ if (previousAppId == Process.INVALID_UID) {
+ mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
+ packageName, extras, 0 /*flags*/,
+ null /*targetPackage*/, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds, res.mRemovedInfo.mBroadcastAllowList,
+ null);
+ if (installerPackageName != null) {
+ mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
+ extras, 0 /*flags*/,
+ installerPackageName, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds, null /*broadcastAllowList*/,
+ null);
+ }
+ if (notifyVerifier) {
+ mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
+ extras, 0 /*flags*/,
+ mPm.mRequiredVerifierPackage, null /*finishedReceiver*/,
+ updateUserIds, instantUserIds, null /*broadcastAllowList*/,
+ null);
+ }
}
mPm.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
null /*package*/, null /*extras*/, 0 /*flags*/,
@@ -3062,10 +2774,8 @@
// Send broadcast package appeared if external for all users
if (res.mPkg.isExternalStorage()) {
if (!update) {
- final StorageManager storage = mPm.mInjector.getSystemService(
- StorageManager.class);
VolumeInfo volume =
- storage.findVolumeByUuid(
+ mStorageManager.findVolumeByUuid(
StorageManager.convert(
res.mPkg.getVolumeUuid()).toString());
int packageExternalStorageType =
@@ -3147,7 +2857,7 @@
// There's a race currently where some install events may interleave with an
// uninstall. This can lead to package info being null (b/36642664).
if (info != null) {
- mPm.getDexManager().notifyPackageInstalled(info, userId);
+ mDexManager.notifyPackageInstalled(info, userId);
}
}
}
@@ -3175,7 +2885,7 @@
* @return the current "allow unknown sources" setting
*/
private int getUnknownSourcesSettings() {
- return android.provider.Settings.Secure.getIntForUser(mPm.mContext.getContentResolver(),
+ return android.provider.Settings.Secure.getIntForUser(mContext.getContentResolver(),
android.provider.Settings.Secure.INSTALL_NON_MARKET_APPS,
-1, UserHandle.USER_SYSTEM);
}
@@ -3304,7 +3014,7 @@
mAppDataHelper.clearAppDataLIF(pkg, UserHandle.USER_ALL,
FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL
| Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
- mPm.getDexManager().notifyPackageUpdated(pkg.getPackageName(),
+ mDexManager.notifyPackageUpdated(pkg.getPackageName(),
pkg.getBaseApkPath(), pkg.getSplitCodePaths());
}
return true;
@@ -3358,7 +3068,7 @@
packageName);
int ret = PackageManagerServiceUtils.decompressFiles(codePath, dstCodePath, packageName);
if (ret == PackageManager.INSTALL_SUCCEEDED) {
- ret = extractNativeBinaries(dstCodePath, packageName);
+ ret = PackageManagerServiceUtils.extractNativeBinaries(dstCodePath, packageName);
}
if (ret == PackageManager.INSTALL_SUCCEEDED) {
// NOTE: During boot, we have to delay releasing cblocks for no other reason than
@@ -3371,7 +3081,7 @@
}
mPm.mReleaseOnSystemReady.add(dstCodePath);
} else {
- final ContentResolver resolver = mPm.mContext.getContentResolver();
+ final ContentResolver resolver = mContext.getContentResolver();
F2fsUtils.releaseCompressedBlocks(resolver, dstCodePath);
}
} else {
@@ -3385,22 +3095,6 @@
return dstCodePath;
}
- private static int extractNativeBinaries(File dstCodePath, String packageName) {
- final File libraryRoot = new File(dstCodePath, LIB_DIR_NAME);
- NativeLibraryHelper.Handle handle = null;
- try {
- handle = NativeLibraryHelper.Handle.create(dstCodePath);
- return NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
- null /*abiOverride*/, false /*isIncremental*/);
- } catch (IOException e) {
- logCriticalInfo(Log.ERROR, "Failed to extract native libraries"
- + "; pkg: " + packageName);
- return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
- } finally {
- IoUtils.closeQuietly(handle);
- }
- }
-
/**
* Tries to restore the disabled system package after an update has been deleted.
*/
@@ -3418,7 +3112,7 @@
// Reinstate the old system package
mPm.mSettings.enableSystemPackageLPw(disabledPs.getPkg().getPackageName());
// Remove any native libraries from the upgraded package.
- removeNativeBinariesLI(deletedPs);
+ PackageManagerServiceUtils.removeNativeBinariesLI(deletedPs);
// Install the system package
if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs);
@@ -3461,12 +3155,6 @@
}
}
- private static void removeNativeBinariesLI(PackageSetting ps) {
- if (ps != null) {
- NativeLibraryHelper.removeNativeBinariesLI(ps.getLegacyNativeLibraryPath());
- }
- }
-
/**
* Installs a package that's already on the system partition.
*/
@@ -3737,7 +3425,7 @@
}
if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0 && errorCode != INSTALL_SUCCEEDED) {
- mPm.mApexManager.reportErrorWithApkInApex(scanDir.getAbsolutePath(), errorMsg);
+ mApexManager.reportErrorWithApkInApex(scanDir.getAbsolutePath(), errorMsg);
}
// Delete invalid userdata apps
@@ -3820,7 +3508,7 @@
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
final ParsedPackage parsedPackage;
- try (PackageParser2 pp = mInjector.getScanningPackageParser()) {
+ try (PackageParser2 pp = mPm.mInjector.getScanningPackageParser()) {
parsedPackage = pp.parsePackage(scanFile, parseFlags, false);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -3853,7 +3541,7 @@
@PackageManagerService.ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user) throws PackageManagerException {
- final Pair<ScanResult, Boolean> scanResultPair = mScanPackageHelper.scanSystemPackageLI(
+ final Pair<ScanResult, Boolean> scanResultPair = scanSystemPackageLI(
parsedPackage, parseFlags, scanFlags, currentTime, user);
final ScanResult scanResult = scanResultPair.first;
boolean shouldHideSystemApp = scanResultPair.second;
@@ -3863,7 +3551,7 @@
try {
final String pkgName = scanResult.mPkgSetting.getPackageName();
final Map<String, ReconciledPackage> reconcileResult =
- reconcilePackagesLocked(
+ ReconcilePackageUtils.reconcilePackages(
new ReconcileRequest(
Collections.singletonMap(pkgName, scanResult),
mPm.mSharedLibraries,
@@ -3875,7 +3563,7 @@
Collections.singletonMap(pkgName,
mPm.getSharedLibLatestVersionSetting(
scanResult))),
- mPm.mSettings.getKeySetManagerService(), mInjector);
+ mPm.mSettings.getKeySetManagerService(), mPm.mInjector);
appIdCreated = optimisticallyRegisterAppId(scanResult);
commitReconciledScanResultLocked(
reconcileResult.get(pkgName), mPm.mUserManager.getUserIds());
@@ -3893,10 +3581,10 @@
mPm.mSettings.disableSystemPackageLPw(parsedPackage.getPackageName(), true);
}
}
- if (mPm.mIncrementalManager != null && isIncrementalPath(parsedPackage.getPath())) {
+ if (mIncrementalManager != null && isIncrementalPath(parsedPackage.getPath())) {
if (scanResult.mPkgSetting != null && scanResult.mPkgSetting.isLoading()) {
// Continue monitoring loading progress of active incremental packages
- mPm.mIncrementalManager.registerLoadingProgressCallback(parsedPackage.getPath(),
+ mIncrementalManager.registerLoadingProgressCallback(parsedPackage.getPath(),
new IncrementalProgressListener(parsedPackage.getPackageName(), mPm));
}
}
@@ -3934,5 +3622,743 @@
}
}
+ @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+ private ScanResult scanPackageTracedLI(ParsedPackage parsedPackage,
+ final @ParsingPackageUtils.ParseFlags int parseFlags,
+ @PackageManagerService.ScanFlags int scanFlags, long currentTime,
+ @Nullable UserHandle user, String cpuAbiOverride) throws PackageManagerException {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage");
+ try {
+ return scanPackageNewLI(parsedPackage, parseFlags, scanFlags, currentTime, user,
+ cpuAbiOverride);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }
+ private ScanRequest prepareInitialScanRequest(@NonNull ParsedPackage parsedPackage,
+ @ParsingPackageUtils.ParseFlags int parseFlags,
+ @PackageManagerService.ScanFlags int scanFlags,
+ @Nullable UserHandle user, String cpuAbiOverride)
+ throws PackageManagerException {
+ final AndroidPackage platformPackage;
+ final String realPkgName;
+ final PackageSetting disabledPkgSetting;
+ final PackageSetting installedPkgSetting;
+ final PackageSetting originalPkgSetting;
+ final SharedUserSetting sharedUserSetting;
+
+ synchronized (mPm.mLock) {
+ platformPackage = mPm.getPlatformPackage();
+ final String renamedPkgName = mPm.mSettings.getRenamedPackageLPr(
+ AndroidPackageUtils.getRealPackageOrNull(parsedPackage));
+ realPkgName = ScanPackageUtils.getRealPackageName(parsedPackage, renamedPkgName);
+ if (realPkgName != null) {
+ ScanPackageUtils.ensurePackageRenamed(parsedPackage, renamedPkgName);
+ }
+ originalPkgSetting = getOriginalPackageLocked(parsedPackage, renamedPkgName);
+ installedPkgSetting = mPm.mSettings.getPackageLPr(parsedPackage.getPackageName());
+ if (mPm.mTransferredPackages.contains(parsedPackage.getPackageName())) {
+ Slog.w(TAG, "Package " + parsedPackage.getPackageName()
+ + " was transferred to another, but its .apk remains");
+ }
+ disabledPkgSetting = mPm.mSettings.getDisabledSystemPkgLPr(
+ parsedPackage.getPackageName());
+ sharedUserSetting = (parsedPackage.getSharedUserId() != null)
+ ? mPm.mSettings.getSharedUserLPw(parsedPackage.getSharedUserId(),
+ 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true)
+ : null;
+ if (DEBUG_PACKAGE_SCANNING
+ && (parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0
+ && sharedUserSetting != null) {
+ Log.d(TAG, "Shared UserID " + parsedPackage.getSharedUserId()
+ + " (uid=" + sharedUserSetting.userId + "):"
+ + " packages=" + sharedUserSetting.packages);
+ }
+ }
+
+ final boolean isPlatformPackage = platformPackage != null
+ && platformPackage.getPackageName().equals(parsedPackage.getPackageName());
+
+ return new ScanRequest(parsedPackage, sharedUserSetting,
+ installedPkgSetting == null ? null : installedPkgSetting.getPkg() /* oldPkg */,
+ installedPkgSetting /* packageSetting */,
+ disabledPkgSetting /* disabledPackageSetting */,
+ originalPkgSetting /* originalPkgSetting */,
+ realPkgName, parseFlags, scanFlags, isPlatformPackage, user, cpuAbiOverride);
+ }
+
+ @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
+ private ScanResult scanPackageNewLI(@NonNull ParsedPackage parsedPackage,
+ final @ParsingPackageUtils.ParseFlags int parseFlags,
+ @PackageManagerService.ScanFlags int scanFlags, long currentTime,
+ @Nullable UserHandle user, String cpuAbiOverride) throws PackageManagerException {
+
+ final ScanRequest initialScanRequest = prepareInitialScanRequest(parsedPackage, parseFlags,
+ scanFlags, user, cpuAbiOverride);
+ final PackageSetting installedPkgSetting = initialScanRequest.mPkgSetting;
+ final PackageSetting disabledPkgSetting = initialScanRequest.mDisabledPkgSetting;
+
+ boolean isUpdatedSystemApp;
+ if (installedPkgSetting != null) {
+ isUpdatedSystemApp = installedPkgSetting.getPkgState().isUpdatedSystemApp();
+ } else {
+ isUpdatedSystemApp = disabledPkgSetting != null;
+ }
+
+ final int newScanFlags = adjustScanFlags(scanFlags, installedPkgSetting, disabledPkgSetting,
+ user, parsedPackage);
+ ScanPackageUtils.applyPolicy(parsedPackage, newScanFlags,
+ mPm.getPlatformPackage(), isUpdatedSystemApp);
+
+ synchronized (mPm.mLock) {
+ assertPackageIsValid(parsedPackage, parseFlags, newScanFlags);
+ final ScanRequest request = new ScanRequest(parsedPackage,
+ initialScanRequest.mSharedUserSetting,
+ initialScanRequest.mOldPkg, installedPkgSetting, disabledPkgSetting,
+ initialScanRequest.mOriginalPkgSetting, initialScanRequest.mRealPkgName,
+ parseFlags, scanFlags, initialScanRequest.mIsPlatformPackage, user,
+ cpuAbiOverride);
+ return ScanPackageUtils.scanPackageOnlyLI(request, mPm.mInjector, mPm.mFactoryTest,
+ currentTime);
+ }
+ }
+
+ private Pair<ScanResult, Boolean> scanSystemPackageLI(ParsedPackage parsedPackage,
+ @ParsingPackageUtils.ParseFlags int parseFlags,
+ @PackageManagerService.ScanFlags int scanFlags, long currentTime,
+ @Nullable UserHandle user) throws PackageManagerException {
+ final boolean scanSystemPartition =
+ (parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0;
+ final ScanRequest initialScanRequest = prepareInitialScanRequest(parsedPackage, parseFlags,
+ scanFlags, user, null);
+ final PackageSetting installedPkgSetting = initialScanRequest.mPkgSetting;
+ final PackageSetting originalPkgSetting = initialScanRequest.mOriginalPkgSetting;
+ final PackageSetting pkgSetting =
+ originalPkgSetting == null ? installedPkgSetting : originalPkgSetting;
+ final boolean pkgAlreadyExists = pkgSetting != null;
+ final String disabledPkgName = pkgAlreadyExists
+ ? pkgSetting.getPackageName() : parsedPackage.getPackageName();
+ final boolean isSystemPkgUpdated;
+ final boolean isUpgrade;
+ synchronized (mPm.mLock) {
+ isUpgrade = mPm.isDeviceUpgrading();
+ if (scanSystemPartition && !pkgAlreadyExists
+ && mPm.mSettings.getDisabledSystemPkgLPr(disabledPkgName) != null) {
+ // The updated-package data for /system apk remains inconsistently
+ // after the package data for /data apk is lost accidentally.
+ // To recover it, enable /system apk and install it as non-updated system app.
+ Slog.w(TAG, "Inconsistent package setting of updated system app for "
+ + disabledPkgName + ". To recover it, enable the system app "
+ + "and install it as non-updated system app.");
+ mPm.mSettings.removeDisabledSystemPackageLPw(disabledPkgName);
+ }
+ final PackageSetting disabledPkgSetting =
+ mPm.mSettings.getDisabledSystemPkgLPr(disabledPkgName);
+ isSystemPkgUpdated = disabledPkgSetting != null;
+
+ if (DEBUG_INSTALL && isSystemPkgUpdated) {
+ Slog.d(TAG, "updatedPkg = " + disabledPkgSetting);
+ }
+
+ if (scanSystemPartition && isSystemPkgUpdated) {
+ // we're updating the disabled package, so, scan it as the package setting
+ final ScanRequest request = new ScanRequest(parsedPackage,
+ initialScanRequest.mSharedUserSetting,
+ null, disabledPkgSetting /* pkgSetting */,
+ null /* disabledPkgSetting */, null /* originalPkgSetting */,
+ null, parseFlags, scanFlags,
+ initialScanRequest.mIsPlatformPackage, user, null);
+ ScanPackageUtils.applyPolicy(parsedPackage, scanFlags,
+ mPm.getPlatformPackage(), true);
+ final ScanResult scanResult =
+ ScanPackageUtils.scanPackageOnlyLI(request, mPm.mInjector,
+ mPm.mFactoryTest, -1L);
+ if (scanResult.mExistingSettingCopied
+ && scanResult.mRequest.mPkgSetting != null) {
+ scanResult.mRequest.mPkgSetting.updateFrom(scanResult.mPkgSetting);
+ }
+ }
+ } // End of mLock
+
+ final boolean newPkgChangedPaths = pkgAlreadyExists
+ && !pkgSetting.getPathString().equals(parsedPackage.getPath());
+ final boolean newPkgVersionGreater = pkgAlreadyExists
+ && parsedPackage.getLongVersionCode() > pkgSetting.getVersionCode();
+ final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated
+ && newPkgChangedPaths && newPkgVersionGreater;
+ if (isSystemPkgBetter) {
+ // The version of the application on /system is greater than the version on
+ // /data. Switch back to the application on /system.
+ // It's safe to assume the application on /system will correctly scan. If not,
+ // there won't be a working copy of the application.
+ synchronized (mPm.mLock) {
+ // just remove the loaded entries from package lists
+ mPm.mPackages.remove(pkgSetting.getPackageName());
+ }
+
+ logCriticalInfo(Log.WARN,
+ "System package updated;"
+ + " name: " + pkgSetting.getPackageName()
+ + "; " + pkgSetting.getVersionCode() + " --> "
+ + parsedPackage.getLongVersionCode()
+ + "; " + pkgSetting.getPathString()
+ + " --> " + parsedPackage.getPath());
+
+ final InstallArgs args = new FileInstallArgs(
+ pkgSetting.getPathString(), getAppDexInstructionSets(
+ pkgSetting.getPrimaryCpuAbi(), pkgSetting.getSecondaryCpuAbi()), mPm);
+ args.cleanUpResourcesLI();
+ synchronized (mPm.mLock) {
+ mPm.mSettings.enableSystemPackageLPw(pkgSetting.getPackageName());
+ }
+ }
+
+ // The version of the application on the /system partition is less than or
+ // equal to the version on the /data partition. Throw an exception and use
+ // the application already installed on the /data partition.
+ if (scanSystemPartition && isSystemPkgUpdated && !isSystemPkgBetter) {
+ // In the case of a skipped package, commitReconciledScanResultLocked is not called to
+ // add the object to the "live" data structures, so this is the final mutation step
+ // for the package. Which means it needs to be finalized here to cache derived fields.
+ // This is relevant for cases where the disabled system package is used for flags or
+ // other metadata.
+ parsedPackage.hideAsFinal();
+ throw new PackageManagerException(Log.WARN, "Package " + parsedPackage.getPackageName()
+ + " at " + parsedPackage.getPath() + " ignored: updated version "
+ + (pkgAlreadyExists ? String.valueOf(pkgSetting.getVersionCode()) : "unknown")
+ + " better than this " + parsedPackage.getLongVersionCode());
+ }
+
+ // Verify certificates against what was last scanned. Force re-collecting certificate in two
+ // special cases:
+ // 1) when scanning system, force re-collect only if system is upgrading.
+ // 2) when scanning /data, force re-collect only if the app is privileged (updated from
+ // preinstall, or treated as privileged, e.g. due to shared user ID).
+ final boolean forceCollect = scanSystemPartition ? isUpgrade
+ : PackageManagerServiceUtils.isApkVerificationForced(pkgSetting);
+ if (DEBUG_VERIFY && forceCollect) {
+ Slog.d(TAG, "Force collect certificate of " + parsedPackage.getPackageName());
+ }
+
+ // Full APK verification can be skipped during certificate collection, only if the file is
+ // in verified partition, or can be verified on access (when apk verity is enabled). In both
+ // cases, only data in Signing Block is verified instead of the whole file.
+ final boolean skipVerify = scanSystemPartition
+ || (forceCollect && canSkipForcedPackageVerification(parsedPackage));
+ ScanPackageUtils.collectCertificatesLI(pkgSetting, parsedPackage,
+ mPm.getSettingsVersionForPackage(parsedPackage), forceCollect, skipVerify,
+ mPm.isPreNMR1Upgrade());
+
+ // Reset profile if the application version is changed
+ maybeClearProfilesForUpgradesLI(pkgSetting, parsedPackage);
+
+ /*
+ * A new system app appeared, but we already had a non-system one of the
+ * same name installed earlier.
+ */
+ boolean shouldHideSystemApp = false;
+ // A new application appeared on /system, but, we already have a copy of
+ // the application installed on /data.
+ if (scanSystemPartition && !isSystemPkgUpdated && pkgAlreadyExists
+ && !pkgSetting.isSystem()) {
+
+ if (!parsedPackage.getSigningDetails()
+ .checkCapability(pkgSetting.getSigningDetails(),
+ SigningDetails.CertCapabilities.INSTALLED_DATA)
+ && !pkgSetting.getSigningDetails().checkCapability(
+ parsedPackage.getSigningDetails(),
+ SigningDetails.CertCapabilities.ROLLBACK)) {
+ logCriticalInfo(Log.WARN,
+ "System package signature mismatch;"
+ + " name: " + pkgSetting.getPackageName());
+ try (@SuppressWarnings("unused") PackageFreezer freezer = mPm.freezePackage(
+ parsedPackage.getPackageName(),
+ "scanPackageInternalLI")) {
+ DeletePackageHelper deletePackageHelper = new DeletePackageHelper(mPm);
+ deletePackageHelper.deletePackageLIF(parsedPackage.getPackageName(), null, true,
+ mPm.mUserManager.getUserIds(), 0, null, false);
+ }
+ } else if (newPkgVersionGreater) {
+ // The application on /system is newer than the application on /data.
+ // Simply remove the application on /data [keeping application data]
+ // and replace it with the version on /system.
+ logCriticalInfo(Log.WARN,
+ "System package enabled;"
+ + " name: " + pkgSetting.getPackageName()
+ + "; " + pkgSetting.getVersionCode() + " --> "
+ + parsedPackage.getLongVersionCode()
+ + "; " + pkgSetting.getPathString() + " --> "
+ + parsedPackage.getPath());
+ InstallArgs args = new FileInstallArgs(
+ pkgSetting.getPathString(), getAppDexInstructionSets(
+ pkgSetting.getPrimaryCpuAbi(), pkgSetting.getSecondaryCpuAbi()),
+ mPm);
+ synchronized (mPm.mInstallLock) {
+ args.cleanUpResourcesLI();
+ }
+ } else {
+ // The application on /system is older than the application on /data. Hide
+ // the application on /system and the version on /data will be scanned later
+ // and re-added like an update.
+ shouldHideSystemApp = true;
+ logCriticalInfo(Log.INFO,
+ "System package disabled;"
+ + " name: " + pkgSetting.getPackageName()
+ + "; old: " + pkgSetting.getPathString() + " @ "
+ + pkgSetting.getVersionCode()
+ + "; new: " + parsedPackage.getPath() + " @ "
+ + parsedPackage.getPath());
+ }
+ }
+
+ final ScanResult scanResult = scanPackageNewLI(parsedPackage, parseFlags,
+ scanFlags | SCAN_UPDATE_SIGNATURE, currentTime, user, null);
+ return new Pair<>(scanResult, shouldHideSystemApp);
+ }
+
+ /**
+ * Returns if forced apk verification can be skipped for the whole package, including splits.
+ */
+ private boolean canSkipForcedPackageVerification(AndroidPackage pkg) {
+ final String packageName = pkg.getPackageName();
+ if (!canSkipForcedApkVerification(packageName, pkg.getBaseApkPath())) {
+ return false;
+ }
+ // TODO: Allow base and splits to be verified individually.
+ String[] splitCodePaths = pkg.getSplitCodePaths();
+ if (!ArrayUtils.isEmpty(splitCodePaths)) {
+ for (int i = 0; i < splitCodePaths.length; i++) {
+ if (!canSkipForcedApkVerification(packageName, splitCodePaths[i])) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns if forced apk verification can be skipped, depending on current FSVerity setup and
+ * whether the apk contains signed root hash. Note that the signer's certificate still needs to
+ * match one in a trusted source, and should be done separately.
+ */
+ private boolean canSkipForcedApkVerification(String packageName, String apkPath) {
+ if (!PackageManagerServiceUtils.isLegacyApkVerityEnabled()) {
+ return VerityUtils.hasFsverity(apkPath);
+ }
+
+ try {
+ final byte[] rootHashObserved = VerityUtils.generateApkVerityRootHash(apkPath);
+ if (rootHashObserved == null) {
+ return false; // APK does not contain Merkle tree root hash.
+ }
+ synchronized (mPm.mInstallLock) {
+ // Returns whether the observed root hash matches what kernel has.
+ mPm.mInstaller.assertFsverityRootHashMatches(packageName, apkPath,
+ rootHashObserved);
+ return true;
+ }
+ } catch (Installer.InstallerException | IOException | DigestException
+ | NoSuchAlgorithmException e) {
+ Slog.w(TAG, "Error in fsverity check. Fallback to full apk verification.", e);
+ }
+ return false;
+ }
+
+ /**
+ * Clear the package profile if this was an upgrade and the package
+ * version was updated.
+ */
+ private void maybeClearProfilesForUpgradesLI(
+ @Nullable PackageSetting originalPkgSetting,
+ @NonNull AndroidPackage pkg) {
+ if (originalPkgSetting == null || !mPm.isDeviceUpgrading()) {
+ return;
+ }
+ if (originalPkgSetting.getVersionCode() == pkg.getLongVersionCode()) {
+ return;
+ }
+
+ mAppDataHelper.clearAppProfilesLIF(pkg);
+ if (DEBUG_INSTALL) {
+ Slog.d(TAG, originalPkgSetting.getPackageName()
+ + " clear profile due to version change "
+ + originalPkgSetting.getVersionCode() + " != "
+ + pkg.getLongVersionCode());
+ }
+ }
+
+ /**
+ * Returns the original package setting.
+ * <p>A package can migrate its name during an update. In this scenario, a package
+ * designates a set of names that it considers as one of its original names.
+ * <p>An original package must be signed identically and it must have the same
+ * shared user [if any].
+ */
+ @GuardedBy("mPm.mLock")
+ @Nullable
+ private PackageSetting getOriginalPackageLocked(@NonNull AndroidPackage pkg,
+ @Nullable String renamedPkgName) {
+ if (ScanPackageUtils.isPackageRenamed(pkg, renamedPkgName)) {
+ return null;
+ }
+ for (int i = ArrayUtils.size(pkg.getOriginalPackages()) - 1; i >= 0; --i) {
+ final PackageSetting originalPs =
+ mPm.mSettings.getPackageLPr(pkg.getOriginalPackages().get(i));
+ if (originalPs != null) {
+ // the package is already installed under its original name...
+ // but, should we use it?
+ if (!verifyPackageUpdateLPr(originalPs, pkg)) {
+ // the new package is incompatible with the original
+ continue;
+ } else if (originalPs.getSharedUser() != null) {
+ if (!originalPs.getSharedUser().name.equals(pkg.getSharedUserId())) {
+ // the shared user id is incompatible with the original
+ Slog.w(TAG, "Unable to migrate data from " + originalPs.getPackageName()
+ + " to " + pkg.getPackageName() + ": old uid "
+ + originalPs.getSharedUser().name
+ + " differs from " + pkg.getSharedUserId());
+ continue;
+ }
+ // TODO: Add case when shared user id is added [b/28144775]
+ } else {
+ if (DEBUG_UPGRADE) {
+ Log.v(TAG, "Renaming new package "
+ + pkg.getPackageName() + " to old name "
+ + originalPs.getPackageName());
+ }
+ }
+ return originalPs;
+ }
+ }
+ return null;
+ }
+
+ @GuardedBy("mPm.mLock")
+ private boolean verifyPackageUpdateLPr(PackageSetting oldPkg, AndroidPackage newPkg) {
+ if ((oldPkg.getFlags() & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ Slog.w(TAG, "Unable to update from " + oldPkg.getPackageName()
+ + " to " + newPkg.getPackageName()
+ + ": old package not in system partition");
+ return false;
+ } else if (mPm.mPackages.get(oldPkg.getPackageName()) != null) {
+ Slog.w(TAG, "Unable to update from " + oldPkg.getPackageName()
+ + " to " + newPkg.getPackageName()
+ + ": old package still exists");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Asserts the parsed package is valid according to the given policy. If the
+ * package is invalid, for whatever reason, throws {@link PackageManagerException}.
+ * <p>
+ * Implementation detail: This method must NOT have any side effects. It would
+ * ideally be static, but, it requires locks to read system state.
+ *
+ * @throws PackageManagerException If the package fails any of the validation checks
+ */
+ private void assertPackageIsValid(AndroidPackage pkg,
+ final @ParsingPackageUtils.ParseFlags int parseFlags,
+ final @PackageManagerService.ScanFlags int scanFlags)
+ throws PackageManagerException {
+ if ((parseFlags & ParsingPackageUtils.PARSE_ENFORCE_CODE) != 0) {
+ ScanPackageUtils.assertCodePolicy(pkg);
+ }
+
+ if (pkg.getPath() == null) {
+ // Bail out. The resource and code paths haven't been set.
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+ "Code and resource paths haven't been set correctly");
+ }
+
+ // Check that there is an APEX package with the same name only during install/first boot
+ // after OTA.
+ final boolean isUserInstall = (scanFlags & SCAN_BOOTING) == 0;
+ final boolean isFirstBootOrUpgrade = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0;
+ if ((isUserInstall || isFirstBootOrUpgrade)
+ && mApexManager.isApexPackage(pkg.getPackageName())) {
+ throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
+ pkg.getPackageName()
+ + " is an APEX package and can't be installed as an APK.");
+ }
+
+ // Make sure we're not adding any bogus keyset info
+ final KeySetManagerService ksms = mPm.mSettings.getKeySetManagerService();
+ ksms.assertScannedPackageValid(pkg);
+
+ synchronized (mPm.mLock) {
+ // The special "android" package can only be defined once
+ if (pkg.getPackageName().equals("android")) {
+ if (mPm.getCoreAndroidApplication() != null) {
+ Slog.w(TAG, "*************************************************");
+ Slog.w(TAG, "Core android package being redefined. Skipping.");
+ Slog.w(TAG, " codePath=" + pkg.getPath());
+ Slog.w(TAG, "*************************************************");
+ throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
+ "Core android package being redefined. Skipping.");
+ }
+ }
+
+ // A package name must be unique; don't allow duplicates
+ if ((scanFlags & SCAN_NEW_INSTALL) == 0
+ && mPm.mPackages.containsKey(pkg.getPackageName())) {
+ throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
+ "Application package " + pkg.getPackageName()
+ + " already installed. Skipping duplicate.");
+ }
+
+ if (pkg.isStaticSharedLibrary()) {
+ // Static libs have a synthetic package name containing the version
+ // but we still want the base name to be unique.
+ if ((scanFlags & SCAN_NEW_INSTALL) == 0
+ && mPm.mPackages.containsKey(pkg.getManifestPackageName())) {
+ throw new PackageManagerException(
+ "Duplicate static shared lib provider package");
+ }
+ ScanPackageUtils.assertStaticSharedLibraryIsValid(pkg, scanFlags);
+ assertStaticSharedLibraryVersionCodeIsValid(pkg);
+ }
+
+ // If we're only installing presumed-existing packages, require that the
+ // scanned APK is both already known and at the path previously established
+ // for it. Previously unknown packages we pick up normally, but if we have an
+ // a priori expectation about this package's install presence, enforce it.
+ // With a singular exception for new system packages. When an OTA contains
+ // a new system package, we allow the codepath to change from a system location
+ // to the user-installed location. If we don't allow this change, any newer,
+ // user-installed version of the application will be ignored.
+ if ((scanFlags & SCAN_REQUIRE_KNOWN) != 0) {
+ if (mPm.isExpectingBetter(pkg.getPackageName())) {
+ Slog.w(TAG, "Relax SCAN_REQUIRE_KNOWN requirement for package "
+ + pkg.getPackageName());
+ } else {
+ PackageSetting known = mPm.mSettings.getPackageLPr(pkg.getPackageName());
+ if (known != null) {
+ if (DEBUG_PACKAGE_SCANNING) {
+ Log.d(TAG, "Examining " + pkg.getPath()
+ + " and requiring known path " + known.getPathString());
+ }
+ if (!pkg.getPath().equals(known.getPathString())) {
+ throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED,
+ "Application package " + pkg.getPackageName()
+ + " found at " + pkg.getPath()
+ + " but expected at " + known.getPathString()
+ + "; ignoring.");
+ }
+ } else {
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
+ "Application package " + pkg.getPackageName()
+ + " not found; ignoring.");
+ }
+ }
+ }
+
+ // Verify that this new package doesn't have any content providers
+ // that conflict with existing packages. Only do this if the
+ // package isn't already installed, since we don't want to break
+ // things that are installed.
+ if ((scanFlags & SCAN_NEW_INSTALL) != 0) {
+ mPm.mComponentResolver.assertProvidersNotDefined(pkg);
+ }
+
+ // If this package has defined explicit processes, then ensure that these are
+ // the only processes used by its components.
+ ScanPackageUtils.assertProcessesAreValid(pkg);
+
+ // Verify that packages sharing a user with a privileged app are marked as privileged.
+ assertPackageWithSharedUserIdIsPrivileged(pkg);
+
+ // Apply policies specific for runtime resource overlays (RROs).
+ if (pkg.getOverlayTarget() != null) {
+ assertOverlayIsValid(pkg, parseFlags, scanFlags);
+ }
+
+ // If the package is not on a system partition ensure it is signed with at least the
+ // minimum signature scheme version required for its target SDK.
+ ScanPackageUtils.assertMinSignatureSchemeIsValid(pkg, parseFlags);
+ }
+ }
+
+ private void assertStaticSharedLibraryVersionCodeIsValid(AndroidPackage pkg)
+ throws PackageManagerException {
+ // The version codes must be ordered as lib versions
+ long minVersionCode = Long.MIN_VALUE;
+ long maxVersionCode = Long.MAX_VALUE;
+
+ WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mPm.mSharedLibraries.get(
+ pkg.getStaticSharedLibName());
+ if (versionedLib != null) {
+ final int versionCount = versionedLib.size();
+ for (int i = 0; i < versionCount; i++) {
+ SharedLibraryInfo libInfo = versionedLib.valueAt(i);
+ final long libVersionCode = libInfo.getDeclaringPackage()
+ .getLongVersionCode();
+ if (libInfo.getLongVersion() < pkg.getStaticSharedLibVersion()) {
+ minVersionCode = Math.max(minVersionCode, libVersionCode + 1);
+ } else if (libInfo.getLongVersion()
+ > pkg.getStaticSharedLibVersion()) {
+ maxVersionCode = Math.min(maxVersionCode, libVersionCode - 1);
+ } else {
+ minVersionCode = maxVersionCode = libVersionCode;
+ break;
+ }
+ }
+ }
+ if (pkg.getLongVersionCode() < minVersionCode
+ || pkg.getLongVersionCode() > maxVersionCode) {
+ throw new PackageManagerException("Static shared"
+ + " lib version codes must be ordered as lib versions");
+ }
+ }
+
+ private void assertOverlayIsValid(AndroidPackage pkg,
+ @ParsingPackageUtils.ParseFlags int parseFlags,
+ @PackageManagerService.ScanFlags int scanFlags) throws PackageManagerException {
+ // System overlays have some restrictions on their use of the 'static' state.
+ if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
+ // We are scanning a system overlay. This can be the first scan of the
+ // system/vendor/oem partition, or an update to the system overlay.
+ if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
+ // This must be an update to a system overlay. Immutable overlays cannot be
+ // upgraded.
+ if (!mPm.isOverlayMutable(pkg.getPackageName())) {
+ throw new PackageManagerException("Overlay "
+ + pkg.getPackageName()
+ + " is static and cannot be upgraded.");
+ }
+ } else {
+ if ((scanFlags & SCAN_AS_VENDOR) != 0) {
+ if (pkg.getTargetSdkVersion() < ScanPackageUtils.getVendorPartitionVersion()) {
+ Slog.w(TAG, "System overlay " + pkg.getPackageName()
+ + " targets an SDK below the required SDK level of vendor"
+ + " overlays ("
+ + ScanPackageUtils.getVendorPartitionVersion()
+ + ")."
+ + " This will become an install error in a future release");
+ }
+ } else if (pkg.getTargetSdkVersion() < Build.VERSION.SDK_INT) {
+ Slog.w(TAG, "System overlay " + pkg.getPackageName()
+ + " targets an SDK below the required SDK level of system"
+ + " overlays (" + Build.VERSION.SDK_INT + ")."
+ + " This will become an install error in a future release");
+ }
+ }
+ } else {
+ // A non-preloaded overlay packages must have targetSdkVersion >= Q, or be
+ // signed with the platform certificate. Check this in increasing order of
+ // computational cost.
+ if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.Q) {
+ final PackageSetting platformPkgSetting =
+ mPm.mSettings.getPackageLPr("android");
+ if (!comparePackageSignatures(platformPkgSetting,
+ pkg.getSigningDetails().getSignatures())) {
+ throw new PackageManagerException("Overlay "
+ + pkg.getPackageName()
+ + " must target Q or later, "
+ + "or be signed with the platform certificate");
+ }
+ }
+
+ // A non-preloaded overlay package, without <overlay android:targetName>, will
+ // only be used if it is signed with the same certificate as its target OR if
+ // it is signed with the same certificate as a reference package declared
+ // in 'overlay-config-signature' tag of SystemConfig.
+ // If the target is already installed or 'overlay-config-signature' tag in
+ // SystemConfig is set, check this here to augment the last line of defense
+ // which is OMS.
+ if (pkg.getOverlayTargetOverlayableName() == null) {
+ final PackageSetting targetPkgSetting =
+ mPm.mSettings.getPackageLPr(pkg.getOverlayTarget());
+ if (targetPkgSetting != null) {
+ if (!comparePackageSignatures(targetPkgSetting,
+ pkg.getSigningDetails().getSignatures())) {
+ // check reference signature
+ if (mPm.mOverlayConfigSignaturePackage == null) {
+ throw new PackageManagerException("Overlay "
+ + pkg.getPackageName() + " and target "
+ + pkg.getOverlayTarget() + " signed with"
+ + " different certificates, and the overlay lacks"
+ + " <overlay android:targetName>");
+ }
+ final PackageSetting refPkgSetting =
+ mPm.mSettings.getPackageLPr(
+ mPm.mOverlayConfigSignaturePackage);
+ if (!comparePackageSignatures(refPkgSetting,
+ pkg.getSigningDetails().getSignatures())) {
+ throw new PackageManagerException("Overlay "
+ + pkg.getPackageName() + " signed with a different "
+ + "certificate than both the reference package and "
+ + "target " + pkg.getOverlayTarget() + ", and the "
+ + "overlay lacks <overlay android:targetName>");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void assertPackageWithSharedUserIdIsPrivileged(AndroidPackage pkg)
+ throws PackageManagerException {
+ if (!pkg.isPrivileged() && (pkg.getSharedUserId() != null)) {
+ SharedUserSetting sharedUserSetting = null;
+ try {
+ sharedUserSetting = mPm.mSettings.getSharedUserLPw(pkg.getSharedUserId(),
+ 0, 0, false);
+ } catch (PackageManagerException ignore) {
+ }
+ if (sharedUserSetting != null && sharedUserSetting.isPrivileged()) {
+ // Exempt SharedUsers signed with the platform key.
+ PackageSetting platformPkgSetting = mPm.mSettings.getPackageLPr("android");
+ if (!comparePackageSignatures(platformPkgSetting,
+ pkg.getSigningDetails().getSignatures())) {
+ throw new PackageManagerException("Apps that share a user with a "
+ + "privileged app must themselves be marked as privileged. "
+ + pkg.getPackageName() + " shares privileged user "
+ + pkg.getSharedUserId() + ".");
+ }
+ }
+ }
+ }
+
+ private @PackageManagerService.ScanFlags int adjustScanFlags(
+ @PackageManagerService.ScanFlags int scanFlags,
+ PackageSetting pkgSetting, PackageSetting disabledPkgSetting, UserHandle user,
+ AndroidPackage pkg) {
+ scanFlags = ScanPackageUtils.adjustScanFlagsWithPackageSetting(scanFlags, pkgSetting,
+ disabledPkgSetting, user);
+
+ // Exception for privileged apps that share a user with a priv-app.
+ final boolean skipVendorPrivilegeScan = ((scanFlags & SCAN_AS_VENDOR) != 0)
+ && ScanPackageUtils.getVendorPartitionVersion() < 28;
+ if (((scanFlags & SCAN_AS_PRIVILEGED) == 0)
+ && !pkg.isPrivileged()
+ && (pkg.getSharedUserId() != null)
+ && !skipVendorPrivilegeScan) {
+ SharedUserSetting sharedUserSetting = null;
+ synchronized (mPm.mLock) {
+ try {
+ sharedUserSetting = mPm.mSettings.getSharedUserLPw(pkg.getSharedUserId(), 0,
+ 0, false);
+ } catch (PackageManagerException ignore) {
+ }
+ if (sharedUserSetting != null && sharedUserSetting.isPrivileged()) {
+ // Exempt SharedUsers signed with the platform key.
+ // TODO(b/72378145) Fix this exemption. Force signature apps
+ // to allowlist their privileged permissions just like other
+ // priv-apps.
+ PackageSetting platformPkgSetting = mPm.mSettings.getPackageLPr("android");
+ if ((compareSignatures(
+ platformPkgSetting.getSigningDetails().getSignatures(),
+ pkg.getSigningDetails().getSignatures())
+ != PackageManager.SIGNATURE_MATCH)) {
+ scanFlags |= SCAN_AS_PRIVILEGED;
+ }
+ }
+ }
+ }
+
+ return scanFlags;
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 8f6ac07..6f8703b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -57,6 +57,7 @@
import android.app.IActivityManager;
import android.app.admin.IDevicePolicyManager;
import android.app.admin.SecurityLog;
+import android.app.backup.IBackupManager;
import android.app.role.RoleManager;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
@@ -582,13 +583,6 @@
/** Directory where installed applications are stored */
private final File mAppInstallDir;
- /** Directory where installed application's 32-bit native libraries are copied. */
- @VisibleForTesting
- final File mAppLib32InstallDir;
-
- static File getAppLib32InstallDir() {
- return new File(Environment.getDataDirectory(), "app-lib");
- }
// ----------------------------------------------------------------
@@ -975,6 +969,7 @@
private final DeletePackageHelper mDeletePackageHelper;
private final InitAndSystemPackageHelper mInitAndSystemPackageHelper;
private final AppDataHelper mAppDataHelper;
+ private final InstallPackageHelper mInstallPackageHelper;
private final PreferredActivityHelper mPreferredActivityHelper;
private final ResolveIntentHelper mResolveIntentHelper;
private final DexOptHelper mDexOptHelper;
@@ -1525,7 +1520,9 @@
new DefaultSystemWrapper(),
LocalServices::getService,
context::getSystemService,
- (i, pm) -> new BackgroundDexOptService(i.getContext(), i.getDexManager(), pm));
+ (i, pm) -> new BackgroundDexOptService(i.getContext(), i.getDexManager(), pm),
+ (i, pm) -> IBackupManager.Stub.asInterface(ServiceManager.getService(
+ Context.BACKUP_SERVICE)));
if (Build.VERSION.SDK_INT <= 0) {
Slog.w(TAG, "**** ro.build.version.sdk not set!");
@@ -1697,7 +1694,6 @@
mEnableFreeCacheV2 = testParams.enableFreeCacheV2;
mSdkVersion = testParams.sdkVersion;
mAppInstallDir = testParams.appInstallDir;
- mAppLib32InstallDir = testParams.appLib32InstallDir;
mIsEngBuild = testParams.isEngBuild;
mIsUserDebugBuild = testParams.isUserDebugBuild;
mIncrementalVersion = testParams.incrementalVersion;
@@ -1705,6 +1701,7 @@
mBroadcastHelper = testParams.broadcastHelper;
mAppDataHelper = testParams.appDataHelper;
+ mInstallPackageHelper = testParams.installPackageHelper;
mRemovePackageHelper = testParams.removePackageHelper;
mInitAndSystemPackageHelper = testParams.initAndSystemPackageHelper;
mDeletePackageHelper = testParams.deletePackageHelper;
@@ -1837,7 +1834,6 @@
mInstantAppRegistry = new InstantAppRegistry(this, mPermissionManager, mPmInternal);
mAppInstallDir = new File(Environment.getDataDirectory(), "app");
- mAppLib32InstallDir = getAppLib32InstallDir();
mDomainVerificationConnection = new DomainVerificationConnection(this);
mDomainVerificationManager = injector.getDomainVerificationManagerInternal();
@@ -1845,6 +1841,7 @@
mBroadcastHelper = new BroadcastHelper(mInjector);
mAppDataHelper = new AppDataHelper(this);
+ mInstallPackageHelper = new InstallPackageHelper(this, mAppDataHelper);
mRemovePackageHelper = new RemovePackageHelper(this, mAppDataHelper);
mInitAndSystemPackageHelper = new InitAndSystemPackageHelper(this);
mDeletePackageHelper = new DeletePackageHelper(this, mRemovePackageHelper,
@@ -2006,7 +2003,7 @@
// the rest of the commands above) because there's precious little we
// can do about it. A settings error is reported, though.
final List<String> changedAbiCodePath =
- ScanPackageHelper.applyAdjustedAbiToSharedUser(
+ ScanPackageUtils.applyAdjustedAbiToSharedUser(
setting, null /*scannedPackage*/,
mInjector.getAbiHelper().getAdjustedAbiForSharedUser(
setting.packages, null /*scannedPackage*/));
@@ -4698,35 +4695,10 @@
@Override
public int installExistingPackageAsUser(String packageName, int userId, int installFlags,
int installReason, List<String> whiteListedPermissions) {
- final InstallPackageHelper installPackageHelper = new InstallPackageHelper(
- this, mAppDataHelper);
- return installPackageHelper.installExistingPackageAsUser(packageName, userId, installFlags,
+ return mInstallPackageHelper.installExistingPackageAsUser(packageName, userId, installFlags,
installReason, whiteListedPermissions, null);
}
- static void setInstantAppForUser(PackageManagerServiceInjector injector,
- PackageSetting pkgSetting, int userId, boolean instantApp, boolean fullApp) {
- // no state specified; do nothing
- if (!instantApp && !fullApp) {
- return;
- }
- if (userId != UserHandle.USER_ALL) {
- if (instantApp && !pkgSetting.getInstantApp(userId)) {
- pkgSetting.setInstantApp(true /*instantApp*/, userId);
- } else if (fullApp && pkgSetting.getInstantApp(userId)) {
- pkgSetting.setInstantApp(false /*instantApp*/, userId);
- }
- } else {
- for (int currentUserId : injector.getUserManagerInternal().getUserIds()) {
- if (instantApp && !pkgSetting.getInstantApp(currentUserId)) {
- pkgSetting.setInstantApp(true /*instantApp*/, currentUserId);
- } else if (fullApp && pkgSetting.getInstantApp(currentUserId)) {
- pkgSetting.setInstantApp(false /*instantApp*/, currentUserId);
- }
- }
- }
- }
-
boolean isUserRestricted(int userId, String restrictionKey) {
Bundle restrictions = mUserManager.getUserRestrictions(userId);
if (restrictions.getBoolean(restrictionKey, false)) {
@@ -6733,8 +6705,7 @@
if (isSystemStub
&& (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
|| newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED)) {
- if (!new InstallPackageHelper(this).enableCompressedPackage(deletedPkg,
- pkgSetting)) {
+ if (!mInstallPackageHelper.enableCompressedPackage(deletedPkg, pkgSetting)) {
Slog.w(TAG, "Failed setApplicationEnabledSetting: failed to enable "
+ "commpressed package " + setting.getPackageName());
updateAllowed[i] = false;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
index 97a09ff..d14cc1f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
@@ -17,6 +17,7 @@
package com.android.server.pm;
import android.app.ActivityManagerInternal;
+import android.app.backup.IBackupManager;
import android.content.ComponentName;
import android.content.Context;
import android.os.Handler;
@@ -135,6 +136,7 @@
mDomainVerificationManagerInternalProducer;
private final Singleton<Handler> mHandlerProducer;
private final Singleton<BackgroundDexOptService> mBackgroundDexOptService;
+ private final Singleton<IBackupManager> mIBackupManager;
PackageManagerServiceInjector(Context context, PackageManagerTracedLock lock,
Installer installer, Object installLock, PackageAbiHelper abiHelper,
@@ -170,7 +172,8 @@
SystemWrapper systemWrapper,
ServiceProducer getLocalServiceProducer,
ServiceProducer getSystemServiceProducer,
- Producer<BackgroundDexOptService> backgroundDexOptService) {
+ Producer<BackgroundDexOptService> backgroundDexOptService,
+ Producer<IBackupManager> iBackupManager) {
mContext = context;
mLock = lock;
mInstaller = installer;
@@ -220,6 +223,7 @@
domainVerificationManagerInternalProducer);
mHandlerProducer = new Singleton<>(handlerProducer);
mBackgroundDexOptService = new Singleton<>(backgroundDexOptService);
+ mIBackupManager = new Singleton<>(iBackupManager);
}
/**
@@ -384,6 +388,10 @@
return mBackgroundDexOptService.get(this, mPackageManager);
}
+ public IBackupManager getIBackupManager() {
+ return mIBackupManager.get(this, mPackageManager);
+ }
+
/** Provides an abstraction to static access to system state. */
public interface SystemWrapper {
void disablePackageCaches();
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
index 9327c5f..a1acc38 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
@@ -104,6 +104,7 @@
public final String incrementalVersion = Build.VERSION.INCREMENTAL;
public BroadcastHelper broadcastHelper;
public AppDataHelper appDataHelper;
+ public InstallPackageHelper installPackageHelper;
public RemovePackageHelper removePackageHelper;
public InitAndSystemPackageHelper initAndSystemPackageHelper;
public DeletePackageHelper deletePackageHelper;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index bcd0708..898f673 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -18,9 +18,11 @@
import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
+import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
import static android.system.OsConstants.O_CREAT;
import static android.system.OsConstants.O_RDWR;
+import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
import static com.android.server.pm.PackageManagerService.COMPRESSED_EXTENSION;
import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION;
import static com.android.server.pm.PackageManagerService.DEBUG_INTENT_MATCHING;
@@ -60,6 +62,7 @@
import android.os.Process;
import android.os.SystemProperties;
import android.os.incremental.IncrementalManager;
+import android.os.incremental.IncrementalStorage;
import android.os.incremental.V4Signature;
import android.os.incremental.V4Signature.HashingInfo;
import android.os.storage.DiskInfo;
@@ -84,6 +87,7 @@
import com.android.internal.util.HexDump;
import com.android.server.EventLogTags;
import com.android.server.IntentResolver;
+import com.android.server.Watchdog;
import com.android.server.compat.PlatformCompat;
import com.android.server.pm.dex.PackageDexUsage;
import com.android.server.pm.parsing.pkg.AndroidPackage;
@@ -649,6 +653,58 @@
}
/**
+ * Extract native libraries to a target path
+ */
+ public static int extractNativeBinaries(File dstCodePath, String packageName) {
+ final File libraryRoot = new File(dstCodePath, LIB_DIR_NAME);
+ NativeLibraryHelper.Handle handle = null;
+ try {
+ handle = NativeLibraryHelper.Handle.create(dstCodePath);
+ return NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
+ null /*abiOverride*/, false /*isIncremental*/);
+ } catch (IOException e) {
+ logCriticalInfo(Log.ERROR, "Failed to extract native libraries"
+ + "; pkg: " + packageName);
+ return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+ } finally {
+ IoUtils.closeQuietly(handle);
+ }
+ }
+
+ /**
+ * Remove native libraries of a given package
+ */
+ public static void removeNativeBinariesLI(PackageSetting ps) {
+ if (ps != null) {
+ NativeLibraryHelper.removeNativeBinariesLI(ps.getLegacyNativeLibraryPath());
+ }
+ }
+
+ /**
+ * Wait for native library extraction to be done in IncrementalService
+ */
+ public static void waitForNativeBinariesExtractionForIncremental(
+ ArraySet<IncrementalStorage> incrementalStorages) {
+ if (incrementalStorages.isEmpty()) {
+ return;
+ }
+ try {
+ // Native library extraction may take very long time: each page could potentially
+ // wait for either 10s or 100ms (adb vs non-adb data loader), and that easily adds
+ // up to a full watchdog timeout of 1 min, killing the system after that. It doesn't
+ // make much sense as blocking here doesn't lock up the framework, but only blocks
+ // the installation session and the following ones.
+ Watchdog.getInstance().pauseWatchingCurrentThread("native_lib_extract");
+ for (int i = 0; i < incrementalStorages.size(); ++i) {
+ IncrementalStorage storage = incrementalStorages.valueAtUnchecked(i);
+ storage.waitForNativeBinariesExtraction();
+ }
+ } finally {
+ Watchdog.getInstance().resumeWatchingCurrentThread("native_lib_extract");
+ }
+ }
+
+ /**
* Decompress files stored in codePath to dstCodePath for a certain package.
*/
public static int decompressFiles(String codePath, File dstCodePath, String packageName) {
@@ -1280,4 +1336,39 @@
return cacheDir;
}
+
+ /**
+ * Check and throw if the given before/after packages would be considered a
+ * downgrade.
+ */
+ public static void checkDowngrade(AndroidPackage before, PackageInfoLite after)
+ throws PackageManagerException {
+ if (after.getLongVersionCode() < before.getLongVersionCode()) {
+ throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
+ "Update version code " + after.versionCode + " is older than current "
+ + before.getLongVersionCode());
+ } else if (after.getLongVersionCode() == before.getLongVersionCode()) {
+ if (after.baseRevisionCode < before.getBaseRevisionCode()) {
+ throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
+ "Update base revision code " + after.baseRevisionCode
+ + " is older than current " + before.getBaseRevisionCode());
+ }
+
+ if (!ArrayUtils.isEmpty(after.splitNames)) {
+ for (int i = 0; i < after.splitNames.length; i++) {
+ final String splitName = after.splitNames[i];
+ final int j = ArrayUtils.indexOf(before.getSplitNames(), splitName);
+ if (j != -1) {
+ if (after.splitRevisionCodes[i] < before.getSplitRevisionCodes()[j]) {
+ throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
+ "Update split " + splitName + " revision code "
+ + after.splitRevisionCodes[i]
+ + " is older than current "
+ + before.getSplitRevisionCodes()[j]);
+ }
+ }
+ }
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageRemovedInfo.java b/services/core/java/com/android/server/pm/PackageRemovedInfo.java
index a60d2c8..48dc3cb 100644
--- a/services/core/java/com/android/server/pm/PackageRemovedInfo.java
+++ b/services/core/java/com/android/server/pm/PackageRemovedInfo.java
@@ -49,6 +49,7 @@
boolean mDataRemoved;
boolean mRemovedForAllUsers;
boolean mIsStaticSharedLib;
+ boolean mAppIdChanging = false;
// a two dimensional array mapping userId to the set of appIds that can receive notice
// of package changes
SparseArray<int[]> mBroadcastAllowList;
@@ -64,33 +65,43 @@
sendPackageRemovedBroadcastInternal(killApp, removedBySystem);
}
- void sendSystemPackageUpdatedBroadcasts() {
+ void sendSystemPackageUpdatedBroadcasts(int newAppId) {
if (mIsRemovedPackageSystemUpdate) {
- sendSystemPackageUpdatedBroadcastsInternal();
+ sendSystemPackageUpdatedBroadcastsInternal(newAppId);
}
}
- private void sendSystemPackageUpdatedBroadcastsInternal() {
+ private void sendSystemPackageUpdatedBroadcastsInternal(int newAppId) {
Bundle extras = new Bundle(2);
- extras.putInt(Intent.EXTRA_UID, mRemovedAppId >= 0 ? mRemovedAppId : mUid);
- extras.putBoolean(Intent.EXTRA_REPLACING, true);
+ extras.putInt(Intent.EXTRA_UID, newAppId);
+ // When appId changes, do not set the replacing extra
+ if (mAppIdChanging) {
+ extras.putBoolean(Intent.EXTRA_UID_CHANGING, true);
+ extras.putInt(Intent.EXTRA_PREVIOUS_UID, mRemovedAppId >= 0 ? mRemovedAppId : mUid);
+ } else {
+ extras.putBoolean(Intent.EXTRA_REPLACING, true);
+ }
mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, mRemovedPackage, extras,
0, null /*targetPackage*/, null, null, null, mBroadcastAllowList, null);
- mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, mRemovedPackage,
- extras, 0, null /*targetPackage*/, null, null, null, mBroadcastAllowList, null);
- mPackageSender.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null, null, 0,
- mRemovedPackage, null, null, null, null /* broadcastAllowList */,
- getTemporaryAppAllowlistBroadcastOptions(REASON_PACKAGE_REPLACED).toBundle());
if (mInstallerPackageName != null) {
mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
mRemovedPackage, extras, 0 /*flags*/,
mInstallerPackageName, null, null, null, null /* broadcastAllowList */,
null);
- mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
- mRemovedPackage, extras, 0 /*flags*/,
- mInstallerPackageName, null, null, null, null /* broadcastAllowList */,
- null);
}
+ if (!mAppIdChanging) {
+ mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, mRemovedPackage,
+ extras, 0, null /*targetPackage*/, null, null, null, mBroadcastAllowList, null);
+ if (mInstallerPackageName != null) {
+ mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
+ mRemovedPackage, extras, 0 /*flags*/,
+ mInstallerPackageName, null, null, null, null /* broadcastAllowList */,
+ null);
+ }
+ }
+ mPackageSender.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null, null, 0,
+ mRemovedPackage, null, null, null, null /* broadcastAllowList */,
+ getTemporaryAppAllowlistBroadcastOptions(REASON_PACKAGE_REPLACED).toBundle());
}
private static @NonNull BroadcastOptions getTemporaryAppAllowlistBroadcastOptions(
@@ -115,15 +126,20 @@
if (mIsStaticSharedLib) {
return;
}
- Bundle extras = new Bundle(2);
+ Bundle extras = new Bundle();
final int removedUid = mRemovedAppId >= 0 ? mRemovedAppId : mUid;
extras.putInt(Intent.EXTRA_UID, removedUid);
extras.putBoolean(Intent.EXTRA_DATA_REMOVED, mDataRemoved);
extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, !killApp);
extras.putBoolean(Intent.EXTRA_USER_INITIATED, !removedBySystem);
- if (mIsUpdate || mIsRemovedPackageSystemUpdate) {
+
+ // When appId changes, do not set the replacing extra
+ if (mAppIdChanging) {
+ extras.putBoolean(Intent.EXTRA_UID_CHANGING, true);
+ } else if (mIsUpdate || mIsRemovedPackageSystemUpdate) {
extras.putBoolean(Intent.EXTRA_REPLACING, true);
}
+
extras.putBoolean(Intent.EXTRA_REMOVED_FOR_ALL_USERS, mRemovedForAllUsers);
if (mRemovedPackage != null) {
mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED,
@@ -146,9 +162,9 @@
}
}
if (mRemovedAppId >= 0) {
- // If a system app's updates are uninstalled the UID is not actually removed. Some
- // services need to know the package name affected.
- if (extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
+ // If the package is not actually removed, some services need to know the
+ // package name affected.
+ if (mAppIdChanging || mIsUpdate || mIsRemovedPackageSystemUpdate) {
extras.putString(Intent.EXTRA_PACKAGE_NAME, mRemovedPackage);
}
diff --git a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
new file mode 100644
index 0000000..5a25004
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
+
+import static com.android.server.pm.PackageManagerService.SCAN_BOOTING;
+import static com.android.server.pm.PackageManagerService.SCAN_DONT_KILL_APP;
+import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
+import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures;
+
+import android.content.pm.PackageManager;
+import android.content.pm.SharedLibraryInfo;
+import android.content.pm.Signature;
+import android.content.pm.SigningDetails;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.os.SystemProperties;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.server.utils.WatchedLongSparseArray;
+
+import java.util.List;
+import java.util.Map;
+
+final class ReconcilePackageUtils {
+ public static Map<String, ReconciledPackage> reconcilePackages(
+ final ReconcileRequest request, KeySetManagerService ksms,
+ PackageManagerServiceInjector injector)
+ throws ReconcileFailure {
+ final Map<String, ScanResult> scannedPackages = request.mScannedPackages;
+
+ final Map<String, ReconciledPackage> result = new ArrayMap<>(scannedPackages.size());
+
+ // make a copy of the existing set of packages so we can combine them with incoming packages
+ final ArrayMap<String, AndroidPackage> combinedPackages =
+ new ArrayMap<>(request.mAllPackages.size() + scannedPackages.size());
+
+ combinedPackages.putAll(request.mAllPackages);
+
+ final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> incomingSharedLibraries =
+ new ArrayMap<>();
+
+ for (String installPackageName : scannedPackages.keySet()) {
+ final ScanResult scanResult = scannedPackages.get(installPackageName);
+
+ // add / replace existing with incoming packages
+ combinedPackages.put(scanResult.mPkgSetting.getPackageName(),
+ scanResult.mRequest.mParsedPackage);
+
+ // in the first pass, we'll build up the set of incoming shared libraries
+ final List<SharedLibraryInfo> allowedSharedLibInfos =
+ SharedLibraryHelper.getAllowedSharedLibInfos(scanResult,
+ request.mSharedLibrarySource);
+ if (allowedSharedLibInfos != null) {
+ for (SharedLibraryInfo info : allowedSharedLibInfos) {
+ if (!SharedLibraryHelper.addSharedLibraryToPackageVersionMap(
+ incomingSharedLibraries, info)) {
+ throw new ReconcileFailure("Shared Library " + info.getName()
+ + " is being installed twice in this set!");
+ }
+ }
+ }
+
+ // the following may be null if we're just reconciling on boot (and not during install)
+ final InstallArgs installArgs = request.mInstallArgs.get(installPackageName);
+ final PackageInstalledInfo res = request.mInstallResults.get(installPackageName);
+ final PrepareResult prepareResult = request.mPreparedPackages.get(installPackageName);
+ final boolean isInstall = installArgs != null;
+ if (isInstall && (res == null || prepareResult == null)) {
+ throw new ReconcileFailure("Reconcile arguments are not balanced for "
+ + installPackageName + "!");
+ }
+
+ final DeletePackageAction deletePackageAction;
+ // we only want to try to delete for non system apps
+ if (isInstall && prepareResult.mReplace && !prepareResult.mSystem) {
+ final boolean killApp = (scanResult.mRequest.mScanFlags & SCAN_DONT_KILL_APP) == 0;
+ final int deleteFlags = PackageManager.DELETE_KEEP_DATA
+ | (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP);
+ deletePackageAction = DeletePackageHelper.mayDeletePackageLocked(res.mRemovedInfo,
+ prepareResult.mOriginalPs, prepareResult.mDisabledPs,
+ deleteFlags, null /* all users */);
+ if (deletePackageAction == null) {
+ throw new ReconcileFailure(
+ PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE,
+ "May not delete " + installPackageName + " to replace");
+ }
+ } else {
+ deletePackageAction = null;
+ }
+
+ final int scanFlags = scanResult.mRequest.mScanFlags;
+ final int parseFlags = scanResult.mRequest.mParseFlags;
+ final ParsedPackage parsedPackage = scanResult.mRequest.mParsedPackage;
+
+ final PackageSetting disabledPkgSetting = scanResult.mRequest.mDisabledPkgSetting;
+ final PackageSetting lastStaticSharedLibSetting =
+ request.mLastStaticSharedLibSettings.get(installPackageName);
+ final PackageSetting signatureCheckPs =
+ (prepareResult != null && lastStaticSharedLibSetting != null)
+ ? lastStaticSharedLibSetting
+ : scanResult.mPkgSetting;
+ boolean removeAppKeySetData = false;
+ boolean sharedUserSignaturesChanged = false;
+ SigningDetails signingDetails = null;
+ if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
+ if (ksms.checkUpgradeKeySetLocked(signatureCheckPs, parsedPackage)) {
+ // We just determined the app is signed correctly, so bring
+ // over the latest parsed certs.
+ } else {
+ if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
+ throw new ReconcileFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
+ "Package " + parsedPackage.getPackageName()
+ + " upgrade keys do not match the previously installed"
+ + " version");
+ } else {
+ String msg = "System package " + parsedPackage.getPackageName()
+ + " signature changed; retaining data.";
+ PackageManagerService.reportSettingsProblem(Log.WARN, msg);
+ }
+ }
+ signingDetails = parsedPackage.getSigningDetails();
+ } else {
+ try {
+ final Settings.VersionInfo versionInfo =
+ request.mVersionInfos.get(installPackageName);
+ final boolean compareCompat = isCompatSignatureUpdateNeeded(versionInfo);
+ final boolean compareRecover = isRecoverSignatureUpdateNeeded(versionInfo);
+ final boolean isRollback = installArgs != null
+ && installArgs.mInstallReason == PackageManager.INSTALL_REASON_ROLLBACK;
+ final boolean compatMatch = verifySignatures(signatureCheckPs,
+ disabledPkgSetting, parsedPackage.getSigningDetails(), compareCompat,
+ compareRecover, isRollback);
+ // The new KeySets will be re-added later in the scanning process.
+ if (compatMatch) {
+ removeAppKeySetData = true;
+ }
+ // We just determined the app is signed correctly, so bring
+ // over the latest parsed certs.
+ signingDetails = parsedPackage.getSigningDetails();
+
+ // if this is is a sharedUser, check to see if the new package is signed by a
+ // newer
+ // signing certificate than the existing one, and if so, copy over the new
+ // details
+ if (signatureCheckPs.getSharedUser() != null) {
+ // Attempt to merge the existing lineage for the shared SigningDetails with
+ // the lineage of the new package; if the shared SigningDetails are not
+ // returned this indicates the new package added new signers to the lineage
+ // and/or changed the capabilities of existing signers in the lineage.
+ SigningDetails sharedSigningDetails =
+ signatureCheckPs.getSharedUser().signatures.mSigningDetails;
+ SigningDetails mergedDetails = sharedSigningDetails.mergeLineageWith(
+ signingDetails);
+ if (mergedDetails != sharedSigningDetails) {
+ signatureCheckPs.getSharedUser().signatures.mSigningDetails =
+ mergedDetails;
+ }
+ if (signatureCheckPs.getSharedUser().signaturesChanged == null) {
+ signatureCheckPs.getSharedUser().signaturesChanged = Boolean.FALSE;
+ }
+ }
+ } catch (PackageManagerException e) {
+ if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
+ throw new ReconcileFailure(e);
+ }
+ signingDetails = parsedPackage.getSigningDetails();
+
+ // If the system app is part of a shared user we allow that shared user to
+ // change
+ // signatures as well as part of an OTA. We still need to verify that the
+ // signatures
+ // are consistent within the shared user for a given boot, so only allow
+ // updating
+ // the signatures on the first package scanned for the shared user (i.e. if the
+ // signaturesChanged state hasn't been initialized yet in SharedUserSetting).
+ if (signatureCheckPs.getSharedUser() != null) {
+ final Signature[] sharedUserSignatures = signatureCheckPs.getSharedUser()
+ .signatures.mSigningDetails.getSignatures();
+ if (signatureCheckPs.getSharedUser().signaturesChanged != null
+ && compareSignatures(sharedUserSignatures,
+ parsedPackage.getSigningDetails().getSignatures())
+ != PackageManager.SIGNATURE_MATCH) {
+ if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 29) {
+ // Mismatched signatures is an error and silently skipping system
+ // packages will likely break the device in unforeseen ways.
+ // However, we allow the device to boot anyway because, prior to Q,
+ // vendors were not expecting the platform to crash in this
+ // situation.
+ // This WILL be a hard failure on any new API levels after Q.
+ throw new ReconcileFailure(
+ INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
+ "Signature mismatch for shared user: "
+ + scanResult.mPkgSetting.getSharedUser());
+ } else {
+ // Treat mismatched signatures on system packages using a shared
+ // UID as
+ // fatal for the system overall, rather than just failing to install
+ // whichever package happened to be scanned later.
+ throw new IllegalStateException(
+ "Signature mismatch on system package "
+ + parsedPackage.getPackageName()
+ + " for shared user "
+ + scanResult.mPkgSetting.getSharedUser());
+ }
+ }
+
+ sharedUserSignaturesChanged = true;
+ signatureCheckPs.getSharedUser().signatures.mSigningDetails =
+ parsedPackage.getSigningDetails();
+ signatureCheckPs.getSharedUser().signaturesChanged = Boolean.TRUE;
+ }
+ // File a report about this.
+ String msg = "System package " + parsedPackage.getPackageName()
+ + " signature changed; retaining data.";
+ PackageManagerService.reportSettingsProblem(Log.WARN, msg);
+ } catch (IllegalArgumentException e) {
+ // should never happen: certs matched when checking, but not when comparing
+ // old to new for sharedUser
+ throw new RuntimeException(
+ "Signing certificates comparison made on incomparable signing details"
+ + " but somehow passed verifySignatures!", e);
+ }
+ }
+
+ result.put(installPackageName,
+ new ReconciledPackage(request, installArgs, scanResult.mPkgSetting,
+ res, request.mPreparedPackages.get(installPackageName), scanResult,
+ deletePackageAction, allowedSharedLibInfos, signingDetails,
+ sharedUserSignaturesChanged, removeAppKeySetData));
+ }
+
+ for (String installPackageName : scannedPackages.keySet()) {
+ // Check all shared libraries and map to their actual file path.
+ // We only do this here for apps not on a system dir, because those
+ // are the only ones that can fail an install due to this. We
+ // will take care of the system apps by updating all of their
+ // library paths after the scan is done. Also during the initial
+ // scan don't update any libs as we do this wholesale after all
+ // apps are scanned to avoid dependency based scanning.
+ final ScanResult scanResult = scannedPackages.get(installPackageName);
+ if ((scanResult.mRequest.mScanFlags & SCAN_BOOTING) != 0
+ || (scanResult.mRequest.mParseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR)
+ != 0) {
+ continue;
+ }
+ try {
+ result.get(installPackageName).mCollectedSharedLibraryInfos =
+ SharedLibraryHelper.collectSharedLibraryInfos(
+ scanResult.mRequest.mParsedPackage,
+ combinedPackages, request.mSharedLibrarySource,
+ incomingSharedLibraries, injector.getCompatibility());
+
+ } catch (PackageManagerException e) {
+ throw new ReconcileFailure(e.error, e.getMessage());
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * If the database version for this type of package (internal storage or
+ * external storage) is less than the version where package signatures
+ * were updated, return true.
+ */
+ public static boolean isCompatSignatureUpdateNeeded(Settings.VersionInfo ver) {
+ return ver.databaseVersion < Settings.DatabaseVersion.SIGNATURE_END_ENTITY;
+ }
+
+ public static boolean isRecoverSignatureUpdateNeeded(Settings.VersionInfo ver) {
+ return ver.databaseVersion < Settings.DatabaseVersion.SIGNATURE_MALFORMED_RECOVER;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/ScanPackageHelper.java b/services/core/java/com/android/server/pm/ScanPackageHelper.java
deleted file mode 100644
index eafe0d98..0000000
--- a/services/core/java/com/android/server/pm/ScanPackageHelper.java
+++ /dev/null
@@ -1,1716 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.pm;
-
-import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
-import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
-import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
-import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
-import static android.content.pm.PackageManager.INSTALL_FAILED_PROCESS_NOT_DEFINED;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
-import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
-
-import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
-import static com.android.server.pm.PackageManagerService.DEBUG_ABI_SELECTION;
-import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
-import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING;
-import static com.android.server.pm.PackageManagerService.DEBUG_UPGRADE;
-import static com.android.server.pm.PackageManagerService.DEBUG_VERIFY;
-import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_FULL_APP;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_INSTANT_APP;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_ODM;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_OEM;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_PRIVILEGED;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_PRODUCT;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM_EXT;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_VENDOR;
-import static com.android.server.pm.PackageManagerService.SCAN_AS_VIRTUAL_PRELOAD;
-import static com.android.server.pm.PackageManagerService.SCAN_BOOTING;
-import static com.android.server.pm.PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE;
-import static com.android.server.pm.PackageManagerService.SCAN_MOVE;
-import static com.android.server.pm.PackageManagerService.SCAN_NEW_INSTALL;
-import static com.android.server.pm.PackageManagerService.SCAN_NO_DEX;
-import static com.android.server.pm.PackageManagerService.SCAN_REQUIRE_KNOWN;
-import static com.android.server.pm.PackageManagerService.SCAN_UPDATE_SIGNATURE;
-import static com.android.server.pm.PackageManagerService.SCAN_UPDATE_TIME;
-import static com.android.server.pm.PackageManagerService.TAG;
-import static com.android.server.pm.PackageManagerServiceUtils.comparePackageSignatures;
-import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
-import static com.android.server.pm.PackageManagerServiceUtils.compressedFileExists;
-import static com.android.server.pm.PackageManagerServiceUtils.deriveAbiOverride;
-import static com.android.server.pm.PackageManagerServiceUtils.getLastModifiedTime;
-import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.SharedLibraryInfo;
-import android.content.pm.SigningDetails;
-import android.content.pm.parsing.ParsingPackageUtils;
-import android.content.pm.parsing.component.ComponentMutateUtils;
-import android.content.pm.parsing.component.ParsedActivity;
-import android.content.pm.parsing.component.ParsedMainComponent;
-import android.content.pm.parsing.component.ParsedProcess;
-import android.content.pm.parsing.component.ParsedProvider;
-import android.content.pm.parsing.component.ParsedService;
-import android.content.pm.parsing.result.ParseResult;
-import android.content.pm.parsing.result.ParseTypeImpl;
-import android.os.Build;
-import android.os.Process;
-import android.os.SystemProperties;
-import android.os.Trace;
-import android.os.UserHandle;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.Log;
-import android.util.Pair;
-import android.util.Slog;
-import android.util.apk.ApkSignatureVerifier;
-import android.util.jar.StrictJarFile;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.security.VerityUtils;
-import com.android.internal.util.ArrayUtils;
-import com.android.server.SystemConfig;
-import com.android.server.pm.parsing.PackageInfoUtils;
-import com.android.server.pm.parsing.library.PackageBackwardCompatibility;
-import com.android.server.pm.parsing.pkg.AndroidPackage;
-import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
-import com.android.server.utils.WatchedLongSparseArray;
-
-import dalvik.system.VMRuntime;
-
-import java.io.File;
-import java.io.IOException;
-import java.security.DigestException;
-import java.security.NoSuchAlgorithmException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.UUID;
-
-/**
- * Helper class that handles package scanning logic
- */
-final class ScanPackageHelper {
- final PackageManagerService mPm;
- final PackageManagerServiceInjector mInjector;
-
- // TODO(b/198166813): remove PMS dependency
- public ScanPackageHelper(PackageManagerService pm) {
- mPm = pm;
- mInjector = pm.mInjector;
- }
-
- ScanPackageHelper(PackageManagerService pm, PackageManagerServiceInjector injector) {
- mPm = pm;
- mInjector = injector;
- }
-
- /**
- * Similar to the other scanPackageTracedLI but accepting a ParsedPackage instead of a File.
- */
- @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
- public ScanResult scanPackageTracedLI(ParsedPackage parsedPackage,
- final @ParsingPackageUtils.ParseFlags int parseFlags,
- @PackageManagerService.ScanFlags int scanFlags, long currentTime,
- @Nullable UserHandle user, String cpuAbiOverride) throws PackageManagerException {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage");
- try {
- return scanPackageNewLI(parsedPackage, parseFlags, scanFlags, currentTime, user,
- cpuAbiOverride);
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
- }
-
- // TODO(b/199937291): scanPackageNewLI() and scanPackageOnlyLI() should be merged.
- // But, first, committing the results / removing app data needs to be moved up a level to the
- // callers of this method. Also, we need to solve the problem of potentially creating a new
- // shared user setting. That can probably be done later and patch things up after the fact.
- @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
- private ScanResult scanPackageNewLI(@NonNull ParsedPackage parsedPackage,
- final @ParsingPackageUtils.ParseFlags int parseFlags,
- @PackageManagerService.ScanFlags int scanFlags, long currentTime,
- @Nullable UserHandle user, String cpuAbiOverride) throws PackageManagerException {
-
- final String renamedPkgName = mPm.mSettings.getRenamedPackageLPr(
- AndroidPackageUtils.getRealPackageOrNull(parsedPackage));
- final String realPkgName = getRealPackageName(parsedPackage, renamedPkgName);
- if (realPkgName != null) {
- ensurePackageRenamed(parsedPackage, renamedPkgName);
- }
- final PackageSetting originalPkgSetting = getOriginalPackageLocked(parsedPackage,
- renamedPkgName);
- final PackageSetting pkgSetting =
- mPm.mSettings.getPackageLPr(parsedPackage.getPackageName());
- final PackageSetting disabledPkgSetting =
- mPm.mSettings.getDisabledSystemPkgLPr(parsedPackage.getPackageName());
-
- if (mPm.mTransferredPackages.contains(parsedPackage.getPackageName())) {
- Slog.w(TAG, "Package " + parsedPackage.getPackageName()
- + " was transferred to another, but its .apk remains");
- }
-
- scanFlags = adjustScanFlags(scanFlags, pkgSetting, disabledPkgSetting, user, parsedPackage);
- synchronized (mPm.mLock) {
- boolean isUpdatedSystemApp;
- if (pkgSetting != null) {
- isUpdatedSystemApp = pkgSetting.getPkgState().isUpdatedSystemApp();
- } else {
- isUpdatedSystemApp = disabledPkgSetting != null;
- }
- applyPolicy(parsedPackage, scanFlags, mPm.getPlatformPackage(), isUpdatedSystemApp);
- assertPackageIsValid(parsedPackage, parseFlags, scanFlags);
-
- SharedUserSetting sharedUserSetting = null;
- if (parsedPackage.getSharedUserId() != null) {
- // SIDE EFFECTS; may potentially allocate a new shared user
- sharedUserSetting = mPm.mSettings.getSharedUserLPw(parsedPackage.getSharedUserId(),
- 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true /*create*/);
- if (DEBUG_PACKAGE_SCANNING) {
- if ((parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0) {
- Log.d(TAG, "Shared UserID " + parsedPackage.getSharedUserId()
- + " (uid=" + sharedUserSetting.userId + "):"
- + " packages=" + sharedUserSetting.packages);
- }
- }
- }
- String platformPackageName = mPm.getPlatformPackage() == null
- ? null : mPm.getPlatformPackage().getPackageName();
- final ScanRequest request = new ScanRequest(parsedPackage, sharedUserSetting,
- pkgSetting == null ? null : pkgSetting.getPkg(), pkgSetting, disabledPkgSetting,
- originalPkgSetting, realPkgName, parseFlags, scanFlags,
- Objects.equals(parsedPackage.getPackageName(), platformPackageName), user,
- cpuAbiOverride);
- return scanPackageOnlyLI(request, mInjector, mPm.mFactoryTest, currentTime);
- }
- }
-
- /**
- * Just scans the package without any side effects.
- * <p>Not entirely true at the moment. There is still one side effect -- this
- * method potentially modifies a live {@link PackageSetting} object representing
- * the package being scanned. This will be resolved in the future.
- *
- * @param injector injector for acquiring dependencies
- * @param request Information about the package to be scanned
- * @param isUnderFactoryTest Whether or not the device is under factory test
- * @param currentTime The current time, in millis
- * @return The results of the scan
- */
- @GuardedBy("mPm.mInstallLock")
- @VisibleForTesting
- @NonNull
- public ScanResult scanPackageOnlyLI(@NonNull ScanRequest request,
- PackageManagerServiceInjector injector,
- boolean isUnderFactoryTest, long currentTime)
- throws PackageManagerException {
- final PackageAbiHelper packageAbiHelper = injector.getAbiHelper();
- ParsedPackage parsedPackage = request.mParsedPackage;
- PackageSetting pkgSetting = request.mPkgSetting;
- final PackageSetting disabledPkgSetting = request.mDisabledPkgSetting;
- final PackageSetting originalPkgSetting = request.mOriginalPkgSetting;
- final @ParsingPackageUtils.ParseFlags int parseFlags = request.mParseFlags;
- final @PackageManagerService.ScanFlags int scanFlags = request.mScanFlags;
- final String realPkgName = request.mRealPkgName;
- final SharedUserSetting sharedUserSetting = request.mSharedUserSetting;
- final UserHandle user = request.mUser;
- final boolean isPlatformPackage = request.mIsPlatformPackage;
-
- List<String> changedAbiCodePath = null;
-
- if (DEBUG_PACKAGE_SCANNING) {
- if ((parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0) {
- Log.d(TAG, "Scanning package " + parsedPackage.getPackageName());
- }
- }
-
- // Initialize package source and resource directories
- final File destCodeFile = new File(parsedPackage.getPath());
-
- // We keep references to the derived CPU Abis from settings in oder to reuse
- // them in the case where we're not upgrading or booting for the first time.
- String primaryCpuAbiFromSettings = null;
- String secondaryCpuAbiFromSettings = null;
- boolean needToDeriveAbi = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0;
- if (!needToDeriveAbi) {
- if (pkgSetting != null) {
- // TODO(b/154610922): if it is not first boot or upgrade, we should directly use
- // API info from existing package setting. However, stub packages currently do not
- // preserve ABI info, thus the special condition check here. Remove the special
- // check after we fix the stub generation.
- if (pkgSetting.getPkg() != null && pkgSetting.getPkg().isStub()) {
- needToDeriveAbi = true;
- } else {
- primaryCpuAbiFromSettings = pkgSetting.getPrimaryCpuAbi();
- secondaryCpuAbiFromSettings = pkgSetting.getSecondaryCpuAbi();
- }
- } else {
- // Re-scanning a system package after uninstalling updates; need to derive ABI
- needToDeriveAbi = true;
- }
- }
-
- int previousAppId = Process.INVALID_UID;
-
- if (pkgSetting != null && pkgSetting.getSharedUser() != sharedUserSetting) {
- if (pkgSetting.getSharedUser() != null && sharedUserSetting == null) {
- previousAppId = pkgSetting.getAppId();
- // Log that something is leaving shareduid and keep going
- Slog.i(TAG,
- "Package " + parsedPackage.getPackageName() + " shared user changed from "
- + pkgSetting.getSharedUser().name + " to " + "<nothing>.");
- } else {
- PackageManagerService.reportSettingsProblem(Log.WARN,
- "Package " + parsedPackage.getPackageName() + " shared user changed from "
- + (pkgSetting.getSharedUser() != null
- ? pkgSetting.getSharedUser().name : "<nothing>")
- + " to "
- + (sharedUserSetting != null ? sharedUserSetting.name : "<nothing>")
- + "; replacing with new");
- pkgSetting = null;
- }
- }
-
- String[] usesSdkLibraries = null;
- if (!parsedPackage.getUsesSdkLibraries().isEmpty()) {
- usesSdkLibraries = new String[parsedPackage.getUsesSdkLibraries().size()];
- parsedPackage.getUsesSdkLibraries().toArray(usesSdkLibraries);
- }
-
- String[] usesStaticLibraries = null;
- if (!parsedPackage.getUsesStaticLibraries().isEmpty()) {
- usesStaticLibraries = new String[parsedPackage.getUsesStaticLibraries().size()];
- parsedPackage.getUsesStaticLibraries().toArray(usesStaticLibraries);
- }
-
- final UUID newDomainSetId = injector.getDomainVerificationManagerInternal().generateNewId();
-
- // TODO(b/135203078): Remove appInfoFlag usage in favor of individually assigned booleans
- // to avoid adding something that's unsupported due to lack of state, since it's called
- // with null.
- final boolean createNewPackage = (pkgSetting == null);
- if (createNewPackage) {
- final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
- final boolean virtualPreload = (scanFlags & SCAN_AS_VIRTUAL_PRELOAD) != 0;
-
- // Flags contain system values stored in the server variant of AndroidPackage,
- // and so the server-side PackageInfoUtils is still called, even without a
- // PackageSetting to pass in.
- int pkgFlags = PackageInfoUtils.appInfoFlags(parsedPackage, null);
- int pkgPrivateFlags = PackageInfoUtils.appInfoPrivateFlags(parsedPackage, null);
-
- // REMOVE SharedUserSetting from method; update in a separate call
- pkgSetting = Settings.createNewSetting(parsedPackage.getPackageName(),
- originalPkgSetting, disabledPkgSetting, realPkgName, sharedUserSetting,
- destCodeFile, parsedPackage.getNativeLibraryRootDir(),
- AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage),
- AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage),
- parsedPackage.getLongVersionCode(), pkgFlags, pkgPrivateFlags, user,
- true /*allowInstall*/, instantApp, virtualPreload,
- UserManagerService.getInstance(), usesSdkLibraries,
- parsedPackage.getUsesSdkLibrariesVersionsMajor(), usesStaticLibraries,
- parsedPackage.getUsesStaticLibrariesVersions(), parsedPackage.getMimeGroups(),
- newDomainSetId);
- } else {
- // make a deep copy to avoid modifying any existing system state.
- pkgSetting = new PackageSetting(pkgSetting);
- pkgSetting.setPkg(parsedPackage);
-
- // REMOVE SharedUserSetting from method; update in a separate call.
- //
- // TODO(narayan): This update is bogus. nativeLibraryDir & primaryCpuAbi,
- // secondaryCpuAbi are not known at this point so we always update them
- // to null here, only to reset them at a later point.
- Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, sharedUserSetting,
- destCodeFile, parsedPackage.getNativeLibraryDir(),
- AndroidPackageUtils.getPrimaryCpuAbi(parsedPackage, pkgSetting),
- AndroidPackageUtils.getSecondaryCpuAbi(parsedPackage, pkgSetting),
- PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting),
- PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting),
- UserManagerService.getInstance(),
- usesSdkLibraries, parsedPackage.getUsesSdkLibrariesVersionsMajor(),
- usesStaticLibraries, parsedPackage.getUsesStaticLibrariesVersions(),
- parsedPackage.getMimeGroups(), newDomainSetId);
- }
- if (createNewPackage && originalPkgSetting != null) {
- // This is the initial transition from the original package, so,
- // fix up the new package's name now. We must do this after looking
- // up the package under its new name, so getPackageLP takes care of
- // fiddling things correctly.
- parsedPackage.setPackageName(originalPkgSetting.getPackageName());
-
- // File a report about this.
- String msg = "New package " + pkgSetting.getRealName()
- + " renamed to replace old package " + pkgSetting.getPackageName();
- PackageManagerService.reportSettingsProblem(Log.WARN, msg);
- }
-
- final int userId = (user == null ? UserHandle.USER_SYSTEM : user.getIdentifier());
- // for existing packages, change the install state; but, only if it's explicitly specified
- if (!createNewPackage) {
- final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
- final boolean fullApp = (scanFlags & SCAN_AS_FULL_APP) != 0;
- PackageManagerService.setInstantAppForUser(
- injector, pkgSetting, userId, instantApp, fullApp);
- }
- // TODO(patb): see if we can do away with disabled check here.
- if (disabledPkgSetting != null
- || (0 != (scanFlags & SCAN_NEW_INSTALL)
- && pkgSetting != null && pkgSetting.isSystem())) {
- pkgSetting.getPkgState().setUpdatedSystemApp(true);
- }
-
- parsedPackage.setSeInfo(SELinuxMMAC.getSeInfo(parsedPackage, sharedUserSetting,
- injector.getCompatibility()));
-
- if (parsedPackage.isSystem()) {
- configurePackageComponents(parsedPackage);
- }
-
- final String cpuAbiOverride = deriveAbiOverride(request.mCpuAbiOverride);
- final boolean isUpdatedSystemApp = pkgSetting.getPkgState().isUpdatedSystemApp();
-
- final File appLib32InstallDir = PackageManagerService.getAppLib32InstallDir();
- if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
- if (needToDeriveAbi) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi");
- final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths> derivedAbi =
- packageAbiHelper.derivePackageAbi(parsedPackage, isUpdatedSystemApp,
- cpuAbiOverride, appLib32InstallDir);
- derivedAbi.first.applyTo(parsedPackage);
- derivedAbi.second.applyTo(parsedPackage);
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-
- // Some system apps still use directory structure for native libraries
- // in which case we might end up not detecting abi solely based on apk
- // structure. Try to detect abi based on directory structure.
-
- String pkgRawPrimaryCpuAbi = AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage);
- if (parsedPackage.isSystem() && !isUpdatedSystemApp
- && pkgRawPrimaryCpuAbi == null) {
- final PackageAbiHelper.Abis abis = packageAbiHelper.getBundledAppAbis(
- parsedPackage);
- abis.applyTo(parsedPackage);
- abis.applyTo(pkgSetting);
- final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
- packageAbiHelper.deriveNativeLibraryPaths(parsedPackage,
- isUpdatedSystemApp, appLib32InstallDir);
- nativeLibraryPaths.applyTo(parsedPackage);
- }
- } else {
- // This is not a first boot or an upgrade, don't bother deriving the
- // ABI during the scan. Instead, trust the value that was stored in the
- // package setting.
- parsedPackage.setPrimaryCpuAbi(primaryCpuAbiFromSettings)
- .setSecondaryCpuAbi(secondaryCpuAbiFromSettings);
-
- final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
- packageAbiHelper.deriveNativeLibraryPaths(parsedPackage,
- isUpdatedSystemApp, appLib32InstallDir);
- nativeLibraryPaths.applyTo(parsedPackage);
-
- if (DEBUG_ABI_SELECTION) {
- Slog.i(TAG, "Using ABIS and native lib paths from settings : "
- + parsedPackage.getPackageName() + " "
- + AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage)
- + ", "
- + AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage));
- }
- }
- } else {
- if ((scanFlags & SCAN_MOVE) != 0) {
- // We haven't run dex-opt for this move (since we've moved the compiled output too)
- // but we already have this packages package info in the PackageSetting. We just
- // use that and derive the native library path based on the new code path.
- parsedPackage.setPrimaryCpuAbi(pkgSetting.getPrimaryCpuAbi())
- .setSecondaryCpuAbi(pkgSetting.getSecondaryCpuAbi());
- }
-
- // Set native library paths again. For moves, the path will be updated based on the
- // ABIs we've determined above. For non-moves, the path will be updated based on the
- // ABIs we determined during compilation, but the path will depend on the final
- // package path (after the rename away from the stage path).
- final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
- packageAbiHelper.deriveNativeLibraryPaths(parsedPackage, isUpdatedSystemApp,
- appLib32InstallDir);
- nativeLibraryPaths.applyTo(parsedPackage);
- }
-
- // This is a special case for the "system" package, where the ABI is
- // dictated by the zygote configuration (and init.rc). We should keep track
- // of this ABI so that we can deal with "normal" applications that run under
- // the same UID correctly.
- if (isPlatformPackage) {
- parsedPackage.setPrimaryCpuAbi(VMRuntime.getRuntime().is64Bit()
- ? Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0]);
- }
-
- // If there's a mismatch between the abi-override in the package setting
- // and the abiOverride specified for the install. Warn about this because we
- // would've already compiled the app without taking the package setting into
- // account.
- if ((scanFlags & SCAN_NO_DEX) == 0 && (scanFlags & SCAN_NEW_INSTALL) != 0) {
- if (cpuAbiOverride == null) {
- Slog.w(TAG, "Ignoring persisted ABI override for package "
- + parsedPackage.getPackageName());
- }
- }
-
- pkgSetting.setPrimaryCpuAbi(AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage))
- .setSecondaryCpuAbi(AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage))
- .setCpuAbiOverride(cpuAbiOverride);
-
- if (DEBUG_ABI_SELECTION) {
- Slog.d(TAG, "Resolved nativeLibraryRoot for " + parsedPackage.getPackageName()
- + " to root=" + parsedPackage.getNativeLibraryRootDir()
- + ", to dir=" + parsedPackage.getNativeLibraryDir()
- + ", isa=" + parsedPackage.isNativeLibraryRootRequiresIsa());
- }
-
- // Push the derived path down into PackageSettings so we know what to
- // clean up at uninstall time.
- pkgSetting.setLegacyNativeLibraryPath(parsedPackage.getNativeLibraryRootDir());
-
- if (DEBUG_ABI_SELECTION) {
- Log.d(TAG, "Abis for package[" + parsedPackage.getPackageName() + "] are"
- + " primary=" + pkgSetting.getPrimaryCpuAbi()
- + " secondary=" + pkgSetting.getSecondaryCpuAbi()
- + " abiOverride=" + pkgSetting.getCpuAbiOverride());
- }
-
- if ((scanFlags & SCAN_BOOTING) == 0 && pkgSetting.getSharedUser() != null) {
- // We don't do this here during boot because we can do it all
- // at once after scanning all existing packages.
- //
- // We also do this *before* we perform dexopt on this package, so that
- // we can avoid redundant dexopts, and also to make sure we've got the
- // code and package path correct.
- changedAbiCodePath = applyAdjustedAbiToSharedUser(pkgSetting.getSharedUser(),
- parsedPackage, packageAbiHelper.getAdjustedAbiForSharedUser(
- pkgSetting.getSharedUser().packages, parsedPackage));
- }
-
- parsedPackage.setFactoryTest(isUnderFactoryTest && parsedPackage.getRequestedPermissions()
- .contains(android.Manifest.permission.FACTORY_TEST));
-
- if (parsedPackage.isSystem()) {
- pkgSetting.setIsOrphaned(true);
- }
-
- // Take care of first install / last update times.
- final long scanFileTime = getLastModifiedTime(parsedPackage);
- if (currentTime != 0) {
- if (pkgSetting.getFirstInstallTime() == 0) {
- pkgSetting.setFirstInstallTime(currentTime)
- .setLastUpdateTime(currentTime);
- } else if ((scanFlags & SCAN_UPDATE_TIME) != 0) {
- pkgSetting.setLastUpdateTime(currentTime);
- }
- } else if (pkgSetting.getFirstInstallTime() == 0) {
- // We need *something*. Take time time stamp of the file.
- pkgSetting.setFirstInstallTime(scanFileTime)
- .setLastUpdateTime(scanFileTime);
- } else if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0) {
- if (scanFileTime != pkgSetting.getLastModifiedTime()) {
- // A package on the system image has changed; consider this
- // to be an update.
- pkgSetting.setLastUpdateTime(scanFileTime);
- }
- }
- pkgSetting.setLastModifiedTime(scanFileTime);
- // TODO(b/135203078): Remove, move to constructor
- pkgSetting.setPkg(parsedPackage)
- .setFlags(PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting))
- .setPrivateFlags(
- PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting));
- if (parsedPackage.getLongVersionCode() != pkgSetting.getVersionCode()) {
- pkgSetting.setLongVersionCode(parsedPackage.getLongVersionCode());
- }
- // Update volume if needed
- final String volumeUuid = parsedPackage.getVolumeUuid();
- if (!Objects.equals(volumeUuid, pkgSetting.getVolumeUuid())) {
- Slog.i(PackageManagerService.TAG,
- "Update" + (pkgSetting.isSystem() ? " system" : "")
- + " package " + parsedPackage.getPackageName()
- + " volume from " + pkgSetting.getVolumeUuid()
- + " to " + volumeUuid);
- pkgSetting.setVolumeUuid(volumeUuid);
- }
-
- SharedLibraryInfo sdkLibraryInfo = null;
- if (!TextUtils.isEmpty(parsedPackage.getSdkLibName())) {
- sdkLibraryInfo = AndroidPackageUtils.createSharedLibraryForSdk(parsedPackage);
- }
- SharedLibraryInfo staticSharedLibraryInfo = null;
- if (!TextUtils.isEmpty(parsedPackage.getStaticSharedLibName())) {
- staticSharedLibraryInfo =
- AndroidPackageUtils.createSharedLibraryForStatic(parsedPackage);
- }
- List<SharedLibraryInfo> dynamicSharedLibraryInfos = null;
- if (!ArrayUtils.isEmpty(parsedPackage.getLibraryNames())) {
- dynamicSharedLibraryInfos = new ArrayList<>(parsedPackage.getLibraryNames().size());
- for (String name : parsedPackage.getLibraryNames()) {
- dynamicSharedLibraryInfos.add(
- AndroidPackageUtils.createSharedLibraryForDynamic(parsedPackage, name));
- }
- }
-
- return new ScanResult(request, true, pkgSetting, changedAbiCodePath,
- !createNewPackage /* existingSettingCopied */,
- previousAppId, sdkLibraryInfo, staticSharedLibraryInfo,
- dynamicSharedLibraryInfos);
- }
-
- /**
- * Returns the actual scan flags depending upon the state of the other settings.
- * <p>Updated system applications will not have the following flags set
- * by default and need to be adjusted after the fact:
- * <ul>
- * <li>{@link PackageManagerService.SCAN_AS_SYSTEM}</li>
- * <li>{@link PackageManagerService.SCAN_AS_PRIVILEGED}</li>
- * <li>{@link PackageManagerService.SCAN_AS_OEM}</li>
- * <li>{@link PackageManagerService.SCAN_AS_VENDOR}</li>
- * <li>{@link PackageManagerService.SCAN_AS_PRODUCT}</li>
- * <li>{@link PackageManagerService.SCAN_AS_SYSTEM_EXT}</li>
- * <li>{@link PackageManagerService.SCAN_AS_INSTANT_APP}</li>
- * <li>{@link PackageManagerService.SCAN_AS_VIRTUAL_PRELOAD}</li>
- * <li>{@link PackageManagerService.SCAN_AS_ODM}</li>
- * </ul>
- */
- private @PackageManagerService.ScanFlags int adjustScanFlags(
- @PackageManagerService.ScanFlags int scanFlags,
- PackageSetting pkgSetting, PackageSetting disabledPkgSetting, UserHandle user,
- AndroidPackage pkg) {
-
- // TODO(patb): Do away entirely with disabledPkgSetting here. PkgSetting will always contain
- // the correct isSystem value now that we don't disable system packages before scan.
- final PackageSetting systemPkgSetting =
- (scanFlags & SCAN_NEW_INSTALL) != 0 && disabledPkgSetting == null
- && pkgSetting != null && pkgSetting.isSystem()
- ? pkgSetting
- : disabledPkgSetting;
- if (systemPkgSetting != null) {
- // updated system application, must at least have SCAN_AS_SYSTEM
- scanFlags |= SCAN_AS_SYSTEM;
- if ((systemPkgSetting.getPrivateFlags()
- & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
- scanFlags |= SCAN_AS_PRIVILEGED;
- }
- if ((systemPkgSetting.getPrivateFlags()
- & ApplicationInfo.PRIVATE_FLAG_OEM) != 0) {
- scanFlags |= SCAN_AS_OEM;
- }
- if ((systemPkgSetting.getPrivateFlags()
- & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0) {
- scanFlags |= SCAN_AS_VENDOR;
- }
- if ((systemPkgSetting.getPrivateFlags()
- & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0) {
- scanFlags |= SCAN_AS_PRODUCT;
- }
- if ((systemPkgSetting.getPrivateFlags()
- & ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT) != 0) {
- scanFlags |= SCAN_AS_SYSTEM_EXT;
- }
- if ((systemPkgSetting.getPrivateFlags()
- & ApplicationInfo.PRIVATE_FLAG_ODM) != 0) {
- scanFlags |= SCAN_AS_ODM;
- }
- }
- if (pkgSetting != null) {
- final int userId = ((user == null) ? 0 : user.getIdentifier());
- if (pkgSetting.getInstantApp(userId)) {
- scanFlags |= SCAN_AS_INSTANT_APP;
- }
- if (pkgSetting.getVirtualPreload(userId)) {
- scanFlags |= SCAN_AS_VIRTUAL_PRELOAD;
- }
- }
-
- // Scan as privileged apps that share a user with a priv-app.
- final boolean skipVendorPrivilegeScan = ((scanFlags & SCAN_AS_VENDOR) != 0)
- && getVendorPartitionVersion() < 28;
- if (((scanFlags & SCAN_AS_PRIVILEGED) == 0)
- && !pkg.isPrivileged()
- && (pkg.getSharedUserId() != null)
- && !skipVendorPrivilegeScan) {
- SharedUserSetting sharedUserSetting = null;
- try {
- sharedUserSetting = mPm.mSettings.getSharedUserLPw(pkg.getSharedUserId(), 0,
- 0, false);
- } catch (PackageManagerException ignore) {
- }
- if (sharedUserSetting != null && sharedUserSetting.isPrivileged()) {
- // Exempt SharedUsers signed with the platform key.
- // TODO(b/72378145) Fix this exemption. Force signature apps
- // to allowlist their privileged permissions just like other
- // priv-apps.
- synchronized (mPm.mLock) {
- PackageSetting platformPkgSetting = mPm.mSettings.getPackageLPr("android");
- if ((compareSignatures(
- platformPkgSetting.getSigningDetails().getSignatures(),
- pkg.getSigningDetails().getSignatures())
- != PackageManager.SIGNATURE_MATCH)) {
- scanFlags |= SCAN_AS_PRIVILEGED;
- }
- }
- }
- }
-
- return scanFlags;
- }
-
- public Pair<ScanResult, Boolean> scanSystemPackageLI(ParsedPackage parsedPackage,
- @ParsingPackageUtils.ParseFlags int parseFlags,
- @PackageManagerService.ScanFlags int scanFlags, long currentTime,
- @Nullable UserHandle user) throws PackageManagerException {
- final boolean scanSystemPartition =
- (parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0;
- final String renamedPkgName;
- final PackageSetting disabledPkgSetting;
- final boolean isSystemPkgUpdated;
- final boolean pkgAlreadyExists;
- PackageSetting pkgSetting;
- AndroidPackage platformPackage;
- final boolean isUpgrade = mPm.isDeviceUpgrading();
-
- synchronized (mPm.mLock) {
- platformPackage = mPm.getPlatformPackage();
- renamedPkgName = mPm.mSettings.getRenamedPackageLPr(
- AndroidPackageUtils.getRealPackageOrNull(parsedPackage));
- final String realPkgName = getRealPackageName(parsedPackage,
- renamedPkgName);
- if (realPkgName != null) {
- ensurePackageRenamed(parsedPackage, renamedPkgName);
- }
- final PackageSetting originalPkgSetting = getOriginalPackageLocked(parsedPackage,
- renamedPkgName);
- final PackageSetting installedPkgSetting = mPm.mSettings.getPackageLPr(
- parsedPackage.getPackageName());
- pkgSetting = originalPkgSetting == null ? installedPkgSetting : originalPkgSetting;
- pkgAlreadyExists = pkgSetting != null;
- final String disabledPkgName = pkgAlreadyExists
- ? pkgSetting.getPackageName() : parsedPackage.getPackageName();
- if (scanSystemPartition && !pkgAlreadyExists
- && mPm.mSettings.getDisabledSystemPkgLPr(disabledPkgName) != null) {
- // The updated-package data for /system apk remains inconsistently
- // after the package data for /data apk is lost accidentally.
- // To recover it, enable /system apk and install it as non-updated system app.
- Slog.w(TAG, "Inconsistent package setting of updated system app for "
- + disabledPkgName + ". To recover it, enable the system app"
- + "and install it as non-updated system app.");
- mPm.mSettings.removeDisabledSystemPackageLPw(disabledPkgName);
- }
- disabledPkgSetting = mPm.mSettings.getDisabledSystemPkgLPr(disabledPkgName);
- isSystemPkgUpdated = disabledPkgSetting != null;
-
- if (DEBUG_INSTALL && isSystemPkgUpdated) {
- Slog.d(TAG, "updatedPkg = " + disabledPkgSetting);
- }
-
- final SharedUserSetting sharedUserSetting = (parsedPackage.getSharedUserId() != null)
- ? mPm.mSettings.getSharedUserLPw(parsedPackage.getSharedUserId(),
- 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true)
- : null;
- if (DEBUG_PACKAGE_SCANNING
- && (parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0
- && sharedUserSetting != null) {
- Log.d(TAG, "Shared UserID " + parsedPackage.getSharedUserId()
- + " (uid=" + sharedUserSetting.userId + "):"
- + " packages=" + sharedUserSetting.packages);
- }
-
- if (scanSystemPartition) {
- if (isSystemPkgUpdated) {
- // we're updating the disabled package, so, scan it as the package setting
- boolean isPlatformPackage = platformPackage != null
- && platformPackage.getPackageName().equals(
- parsedPackage.getPackageName());
- final ScanRequest request = new ScanRequest(parsedPackage, sharedUserSetting,
- null, disabledPkgSetting /* pkgSetting */,
- null /* disabledPkgSetting */, null /* originalPkgSetting */,
- null, parseFlags, scanFlags, isPlatformPackage, user, null);
- applyPolicy(parsedPackage, scanFlags,
- platformPackage, true);
- final ScanResult scanResult =
- scanPackageOnlyLI(request, mInjector,
- mPm.mFactoryTest, -1L);
- if (scanResult.mExistingSettingCopied
- && scanResult.mRequest.mPkgSetting != null) {
- scanResult.mRequest.mPkgSetting.updateFrom(scanResult.mPkgSetting);
- }
- }
- }
- }
-
- final boolean newPkgChangedPaths = pkgAlreadyExists
- && !pkgSetting.getPathString().equals(parsedPackage.getPath());
- final boolean newPkgVersionGreater = pkgAlreadyExists
- && parsedPackage.getLongVersionCode() > pkgSetting.getVersionCode();
- final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated
- && newPkgChangedPaths && newPkgVersionGreater;
- if (isSystemPkgBetter) {
- // The version of the application on /system is greater than the version on
- // /data. Switch back to the application on /system.
- // It's safe to assume the application on /system will correctly scan. If not,
- // there won't be a working copy of the application.
- synchronized (mPm.mLock) {
- // just remove the loaded entries from package lists
- mPm.mPackages.remove(pkgSetting.getPackageName());
- }
-
- logCriticalInfo(Log.WARN,
- "System package updated;"
- + " name: " + pkgSetting.getPackageName()
- + "; " + pkgSetting.getVersionCode() + " --> "
- + parsedPackage.getLongVersionCode()
- + "; " + pkgSetting.getPathString()
- + " --> " + parsedPackage.getPath());
-
- final InstallArgs args = new FileInstallArgs(
- pkgSetting.getPathString(), getAppDexInstructionSets(
- pkgSetting.getPrimaryCpuAbi(), pkgSetting.getSecondaryCpuAbi()), mPm);
- args.cleanUpResourcesLI();
- synchronized (mPm.mLock) {
- mPm.mSettings.enableSystemPackageLPw(pkgSetting.getPackageName());
- }
- }
-
- // The version of the application on the /system partition is less than or
- // equal to the version on the /data partition. Throw an exception and use
- // the application already installed on the /data partition.
- if (scanSystemPartition && isSystemPkgUpdated && !isSystemPkgBetter) {
- // In the case of a skipped package, commitReconciledScanResultLocked is not called to
- // add the object to the "live" data structures, so this is the final mutation step
- // for the package. Which means it needs to be finalized here to cache derived fields.
- // This is relevant for cases where the disabled system package is used for flags or
- // other metadata.
- parsedPackage.hideAsFinal();
- throw new PackageManagerException(Log.WARN, "Package " + parsedPackage.getPackageName()
- + " at " + parsedPackage.getPath() + " ignored: updated version "
- + (pkgAlreadyExists ? String.valueOf(pkgSetting.getVersionCode()) : "unknown")
- + " better than this " + parsedPackage.getLongVersionCode());
- }
-
- // Verify certificates against what was last scanned. Force re-collecting certificate in two
- // special cases:
- // 1) when scanning system, force re-collect only if system is upgrading.
- // 2) when scannning /data, force re-collect only if the app is privileged (updated from
- // preinstall, or treated as privileged, e.g. due to shared user ID).
- final boolean forceCollect = scanSystemPartition ? isUpgrade
- : PackageManagerServiceUtils.isApkVerificationForced(pkgSetting);
- if (DEBUG_VERIFY && forceCollect) {
- Slog.d(TAG, "Force collect certificate of " + parsedPackage.getPackageName());
- }
-
- // Full APK verification can be skipped during certificate collection, only if the file is
- // in verified partition, or can be verified on access (when apk verity is enabled). In both
- // cases, only data in Signing Block is verified instead of the whole file.
- // TODO(b/136132412): skip for Incremental installation
- final boolean skipVerify = scanSystemPartition
- || (forceCollect && canSkipForcedPackageVerification(parsedPackage));
- collectCertificatesLI(pkgSetting, parsedPackage, forceCollect, skipVerify);
-
- // Reset profile if the application version is changed
- maybeClearProfilesForUpgradesLI(pkgSetting, parsedPackage);
-
- /*
- * A new system app appeared, but we already had a non-system one of the
- * same name installed earlier.
- */
- boolean shouldHideSystemApp = false;
- // A new application appeared on /system, but, we already have a copy of
- // the application installed on /data.
- if (scanSystemPartition && !isSystemPkgUpdated && pkgAlreadyExists
- && !pkgSetting.isSystem()) {
-
- if (!parsedPackage.getSigningDetails()
- .checkCapability(pkgSetting.getSigningDetails(),
- SigningDetails.CertCapabilities.INSTALLED_DATA)
- && !pkgSetting.getSigningDetails().checkCapability(
- parsedPackage.getSigningDetails(),
- SigningDetails.CertCapabilities.ROLLBACK)) {
- logCriticalInfo(Log.WARN,
- "System package signature mismatch;"
- + " name: " + pkgSetting.getPackageName());
- try (@SuppressWarnings("unused") PackageFreezer freezer = mPm.freezePackage(
- parsedPackage.getPackageName(),
- "scanPackageInternalLI")) {
- DeletePackageHelper deletePackageHelper = new DeletePackageHelper(mPm);
- deletePackageHelper.deletePackageLIF(parsedPackage.getPackageName(), null, true,
- mPm.mUserManager.getUserIds(), 0, null, false);
- }
- pkgSetting = null;
- } else if (newPkgVersionGreater) {
- // The application on /system is newer than the application on /data.
- // Simply remove the application on /data [keeping application data]
- // and replace it with the version on /system.
- logCriticalInfo(Log.WARN,
- "System package enabled;"
- + " name: " + pkgSetting.getPackageName()
- + "; " + pkgSetting.getVersionCode() + " --> "
- + parsedPackage.getLongVersionCode()
- + "; " + pkgSetting.getPathString() + " --> "
- + parsedPackage.getPath());
- InstallArgs args = new FileInstallArgs(
- pkgSetting.getPathString(), getAppDexInstructionSets(
- pkgSetting.getPrimaryCpuAbi(), pkgSetting.getSecondaryCpuAbi()),
- mPm);
- synchronized (mPm.mInstallLock) {
- args.cleanUpResourcesLI();
- }
- } else {
- // The application on /system is older than the application on /data. Hide
- // the application on /system and the version on /data will be scanned later
- // and re-added like an update.
- shouldHideSystemApp = true;
- logCriticalInfo(Log.INFO,
- "System package disabled;"
- + " name: " + pkgSetting.getPackageName()
- + "; old: " + pkgSetting.getPathString() + " @ "
- + pkgSetting.getVersionCode()
- + "; new: " + parsedPackage.getPath() + " @ "
- + parsedPackage.getPath());
- }
- }
-
- final ScanResult scanResult = scanPackageNewLI(parsedPackage, parseFlags,
- scanFlags | SCAN_UPDATE_SIGNATURE, currentTime, user, null);
- return new Pair<>(scanResult, shouldHideSystemApp);
- }
-
- /**
- * Returns if forced apk verification can be skipped for the whole package, including splits.
- */
- private boolean canSkipForcedPackageVerification(AndroidPackage pkg) {
- final String packageName = pkg.getPackageName();
- if (!canSkipForcedApkVerification(packageName, pkg.getBaseApkPath())) {
- return false;
- }
- // TODO: Allow base and splits to be verified individually.
- String[] splitCodePaths = pkg.getSplitCodePaths();
- if (!ArrayUtils.isEmpty(splitCodePaths)) {
- for (int i = 0; i < splitCodePaths.length; i++) {
- if (!canSkipForcedApkVerification(packageName, splitCodePaths[i])) {
- return false;
- }
- }
- }
- return true;
- }
-
- /**
- * Returns if forced apk verification can be skipped, depending on current FSVerity setup and
- * whether the apk contains signed root hash. Note that the signer's certificate still needs to
- * match one in a trusted source, and should be done separately.
- */
- private boolean canSkipForcedApkVerification(String packageName, String apkPath) {
- if (!PackageManagerServiceUtils.isLegacyApkVerityEnabled()) {
- return VerityUtils.hasFsverity(apkPath);
- }
-
- try {
- final byte[] rootHashObserved = VerityUtils.generateApkVerityRootHash(apkPath);
- if (rootHashObserved == null) {
- return false; // APK does not contain Merkle tree root hash.
- }
- synchronized (mPm.mInstallLock) {
- // Returns whether the observed root hash matches what kernel has.
- mPm.mInstaller.assertFsverityRootHashMatches(packageName, apkPath,
- rootHashObserved);
- return true;
- }
- } catch (Installer.InstallerException | IOException | DigestException
- | NoSuchAlgorithmException e) {
- Slog.w(TAG, "Error in fsverity check. Fallback to full apk verification.", e);
- }
- return false;
- }
-
- private void collectCertificatesLI(PackageSetting ps, ParsedPackage parsedPackage,
- boolean forceCollect, boolean skipVerify) throws PackageManagerException {
- // When upgrading from pre-N MR1, verify the package time stamp using the package
- // directory and not the APK file.
- final long lastModifiedTime = mPm.isPreNMR1Upgrade()
- ? new File(parsedPackage.getPath()).lastModified()
- : getLastModifiedTime(parsedPackage);
- final Settings.VersionInfo settingsVersionForPackage =
- mPm.getSettingsVersionForPackage(parsedPackage);
- if (ps != null && !forceCollect
- && ps.getPathString().equals(parsedPackage.getPath())
- && ps.getLastModifiedTime() == lastModifiedTime
- && !InstallPackageHelper.isCompatSignatureUpdateNeeded(settingsVersionForPackage)
- && !InstallPackageHelper.isRecoverSignatureUpdateNeeded(
- settingsVersionForPackage)) {
- if (ps.getSigningDetails().getSignatures() != null
- && ps.getSigningDetails().getSignatures().length != 0
- && ps.getSigningDetails().getSignatureSchemeVersion()
- != SigningDetails.SignatureSchemeVersion.UNKNOWN) {
- // Optimization: reuse the existing cached signing data
- // if the package appears to be unchanged.
- parsedPackage.setSigningDetails(
- new SigningDetails(ps.getSigningDetails()));
- return;
- }
-
- Slog.w(TAG, "PackageSetting for " + ps.getPackageName()
- + " is missing signatures. Collecting certs again to recover them.");
- } else {
- Slog.i(TAG, parsedPackage.getPath() + " changed; collecting certs"
- + (forceCollect ? " (forced)" : ""));
- }
-
- try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
- final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
- final ParseResult<SigningDetails> result = ParsingPackageUtils.getSigningDetails(
- input, parsedPackage, skipVerify);
- if (result.isError()) {
- throw new PackageManagerException(
- result.getErrorCode(), result.getErrorMessage(), result.getException());
- }
- parsedPackage.setSigningDetails(result.getResult());
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
- }
-
- /**
- * Clear the package profile if this was an upgrade and the package
- * version was updated.
- */
- private void maybeClearProfilesForUpgradesLI(
- @Nullable PackageSetting originalPkgSetting,
- @NonNull AndroidPackage pkg) {
- if (originalPkgSetting == null || !mPm.isDeviceUpgrading()) {
- return;
- }
- if (originalPkgSetting.getVersionCode() == pkg.getLongVersionCode()) {
- return;
- }
-
- final AppDataHelper appDataHelper = new AppDataHelper(mPm);
- appDataHelper.clearAppProfilesLIF(pkg);
- if (DEBUG_INSTALL) {
- Slog.d(TAG, originalPkgSetting.getPackageName()
- + " clear profile due to version change "
- + originalPkgSetting.getVersionCode() + " != "
- + pkg.getLongVersionCode());
- }
- }
-
- /**
- * Returns the original package setting.
- * <p>A package can migrate its name during an update. In this scenario, a package
- * designates a set of names that it considers as one of its original names.
- * <p>An original package must be signed identically and it must have the same
- * shared user [if any].
- */
- @GuardedBy("mPm.mLock")
- @Nullable
- private PackageSetting getOriginalPackageLocked(@NonNull AndroidPackage pkg,
- @Nullable String renamedPkgName) {
- if (isPackageRenamed(pkg, renamedPkgName)) {
- return null;
- }
- for (int i = ArrayUtils.size(pkg.getOriginalPackages()) - 1; i >= 0; --i) {
- final PackageSetting originalPs =
- mPm.mSettings.getPackageLPr(pkg.getOriginalPackages().get(i));
- if (originalPs != null) {
- // the package is already installed under its original name...
- // but, should we use it?
- if (!verifyPackageUpdateLPr(originalPs, pkg)) {
- // the new package is incompatible with the original
- continue;
- } else if (originalPs.getSharedUser() != null) {
- if (!originalPs.getSharedUser().name.equals(pkg.getSharedUserId())) {
- // the shared user id is incompatible with the original
- Slog.w(TAG, "Unable to migrate data from " + originalPs.getPackageName()
- + " to " + pkg.getPackageName() + ": old uid "
- + originalPs.getSharedUser().name
- + " differs from " + pkg.getSharedUserId());
- continue;
- }
- // TODO: Add case when shared user id is added [b/28144775]
- } else {
- if (DEBUG_UPGRADE) {
- Log.v(TAG, "Renaming new package "
- + pkg.getPackageName() + " to old name "
- + originalPs.getPackageName());
- }
- }
- return originalPs;
- }
- }
- return null;
- }
-
- @GuardedBy("mPm.mLock")
- private boolean verifyPackageUpdateLPr(PackageSetting oldPkg, AndroidPackage newPkg) {
- if ((oldPkg.getFlags() & ApplicationInfo.FLAG_SYSTEM) == 0) {
- Slog.w(TAG, "Unable to update from " + oldPkg.getPackageName()
- + " to " + newPkg.getPackageName()
- + ": old package not in system partition");
- return false;
- } else if (mPm.mPackages.get(oldPkg.getPackageName()) != null) {
- Slog.w(TAG, "Unable to update from " + oldPkg.getPackageName()
- + " to " + newPkg.getPackageName()
- + ": old package still exists");
- return false;
- }
- return true;
- }
-
- /**
- * Asserts the parsed package is valid according to the given policy. If the
- * package is invalid, for whatever reason, throws {@link PackageManagerException}.
- * <p>
- * Implementation detail: This method must NOT have any side effects. It would
- * ideally be static, but, it requires locks to read system state.
- *
- * @throws PackageManagerException If the package fails any of the validation checks
- */
- private void assertPackageIsValid(AndroidPackage pkg,
- final @ParsingPackageUtils.ParseFlags int parseFlags,
- final @PackageManagerService.ScanFlags int scanFlags)
- throws PackageManagerException {
- if ((parseFlags & ParsingPackageUtils.PARSE_ENFORCE_CODE) != 0) {
- assertCodePolicy(pkg);
- }
-
- if (pkg.getPath() == null) {
- // Bail out. The resource and code paths haven't been set.
- throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
- "Code and resource paths haven't been set correctly");
- }
-
- // Check that there is an APEX package with the same name only during install/first boot
- // after OTA.
- final boolean isUserInstall = (scanFlags & SCAN_BOOTING) == 0;
- final boolean isFirstBootOrUpgrade = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0;
- if ((isUserInstall || isFirstBootOrUpgrade)
- && mPm.mApexManager.isApexPackage(pkg.getPackageName())) {
- throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
- pkg.getPackageName()
- + " is an APEX package and can't be installed as an APK.");
- }
-
- // Make sure we're not adding any bogus keyset info
- final KeySetManagerService ksms = mPm.mSettings.getKeySetManagerService();
- ksms.assertScannedPackageValid(pkg);
-
- synchronized (mPm.mLock) {
- // The special "android" package can only be defined once
- if (pkg.getPackageName().equals("android")) {
- if (mPm.getCoreAndroidApplication() != null) {
- Slog.w(TAG, "*************************************************");
- Slog.w(TAG, "Core android package being redefined. Skipping.");
- Slog.w(TAG, " codePath=" + pkg.getPath());
- Slog.w(TAG, "*************************************************");
- throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
- "Core android package being redefined. Skipping.");
- }
- }
-
- // A package name must be unique; don't allow duplicates
- if ((scanFlags & SCAN_NEW_INSTALL) == 0
- && mPm.mPackages.containsKey(pkg.getPackageName())) {
- throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
- "Application package " + pkg.getPackageName()
- + " already installed. Skipping duplicate.");
- }
-
- if (pkg.isStaticSharedLibrary()) {
- // Static libs have a synthetic package name containing the version
- // but we still want the base name to be unique.
- if ((scanFlags & SCAN_NEW_INSTALL) == 0
- && mPm.mPackages.containsKey(pkg.getManifestPackageName())) {
- throw new PackageManagerException(
- "Duplicate static shared lib provider package");
- }
-
- // Static shared libraries should have at least O target SDK
- if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.O) {
- throw new PackageManagerException(
- "Packages declaring static-shared libs must target O SDK or higher");
- }
-
- // Package declaring static a shared lib cannot be instant apps
- if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
- throw new PackageManagerException(
- "Packages declaring static-shared libs cannot be instant apps");
- }
-
- // Package declaring static a shared lib cannot be renamed since the package
- // name is synthetic and apps can't code around package manager internals.
- if (!ArrayUtils.isEmpty(pkg.getOriginalPackages())) {
- throw new PackageManagerException(
- "Packages declaring static-shared libs cannot be renamed");
- }
-
- // Package declaring static a shared lib cannot declare dynamic libs
- if (!ArrayUtils.isEmpty(pkg.getLibraryNames())) {
- throw new PackageManagerException(
- "Packages declaring static-shared libs cannot declare dynamic libs");
- }
-
- // Package declaring static a shared lib cannot declare shared users
- if (pkg.getSharedUserId() != null) {
- throw new PackageManagerException(
- "Packages declaring static-shared libs cannot declare shared users");
- }
-
- // Static shared libs cannot declare activities
- if (!pkg.getActivities().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare activities");
- }
-
- // Static shared libs cannot declare services
- if (!pkg.getServices().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare services");
- }
-
- // Static shared libs cannot declare providers
- if (!pkg.getProviders().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare content providers");
- }
-
- // Static shared libs cannot declare receivers
- if (!pkg.getReceivers().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare broadcast receivers");
- }
-
- // Static shared libs cannot declare permission groups
- if (!pkg.getPermissionGroups().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare permission groups");
- }
-
- // Static shared libs cannot declare attributions
- if (!pkg.getAttributions().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare features");
- }
-
- // Static shared libs cannot declare permissions
- if (!pkg.getPermissions().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare permissions");
- }
-
- // Static shared libs cannot declare protected broadcasts
- if (!pkg.getProtectedBroadcasts().isEmpty()) {
- throw new PackageManagerException(
- "Static shared libs cannot declare protected broadcasts");
- }
-
- // Static shared libs cannot be overlay targets
- if (pkg.getOverlayTarget() != null) {
- throw new PackageManagerException(
- "Static shared libs cannot be overlay targets");
- }
-
- // The version codes must be ordered as lib versions
- long minVersionCode = Long.MIN_VALUE;
- long maxVersionCode = Long.MAX_VALUE;
-
- WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mPm.mSharedLibraries.get(
- pkg.getStaticSharedLibName());
- if (versionedLib != null) {
- final int versionCount = versionedLib.size();
- for (int i = 0; i < versionCount; i++) {
- SharedLibraryInfo libInfo = versionedLib.valueAt(i);
- final long libVersionCode = libInfo.getDeclaringPackage()
- .getLongVersionCode();
- if (libInfo.getLongVersion() < pkg.getStaticSharedLibVersion()) {
- minVersionCode = Math.max(minVersionCode, libVersionCode + 1);
- } else if (libInfo.getLongVersion()
- > pkg.getStaticSharedLibVersion()) {
- maxVersionCode = Math.min(maxVersionCode, libVersionCode - 1);
- } else {
- minVersionCode = maxVersionCode = libVersionCode;
- break;
- }
- }
- }
- if (pkg.getLongVersionCode() < minVersionCode
- || pkg.getLongVersionCode() > maxVersionCode) {
- throw new PackageManagerException("Static shared"
- + " lib version codes must be ordered as lib versions");
- }
- }
-
- // If we're only installing presumed-existing packages, require that the
- // scanned APK is both already known and at the path previously established
- // for it. Previously unknown packages we pick up normally, but if we have an
- // a priori expectation about this package's install presence, enforce it.
- // With a singular exception for new system packages. When an OTA contains
- // a new system package, we allow the codepath to change from a system location
- // to the user-installed location. If we don't allow this change, any newer,
- // user-installed version of the application will be ignored.
- if ((scanFlags & SCAN_REQUIRE_KNOWN) != 0) {
- if (mPm.isExpectingBetter(pkg.getPackageName())) {
- Slog.w(TAG, "Relax SCAN_REQUIRE_KNOWN requirement for package "
- + pkg.getPackageName());
- } else {
- PackageSetting known = mPm.mSettings.getPackageLPr(pkg.getPackageName());
- if (known != null) {
- if (DEBUG_PACKAGE_SCANNING) {
- Log.d(TAG, "Examining " + pkg.getPath()
- + " and requiring known path " + known.getPathString());
- }
- if (!pkg.getPath().equals(known.getPathString())) {
- throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED,
- "Application package " + pkg.getPackageName()
- + " found at " + pkg.getPath()
- + " but expected at " + known.getPathString()
- + "; ignoring.");
- }
- } else {
- throw new PackageManagerException(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
- "Application package " + pkg.getPackageName()
- + " not found; ignoring.");
- }
- }
- }
-
- // Verify that this new package doesn't have any content providers
- // that conflict with existing packages. Only do this if the
- // package isn't already installed, since we don't want to break
- // things that are installed.
- if ((scanFlags & SCAN_NEW_INSTALL) != 0) {
- mPm.mComponentResolver.assertProvidersNotDefined(pkg);
- }
-
- // If this package has defined explicit processes, then ensure that these are
- // the only processes used by its components.
- final Map<String, ParsedProcess> procs = pkg.getProcesses();
- if (!procs.isEmpty()) {
- if (!procs.containsKey(pkg.getProcessName())) {
- throw new PackageManagerException(
- INSTALL_FAILED_PROCESS_NOT_DEFINED,
- "Can't install because application tag's process attribute "
- + pkg.getProcessName()
- + " (in package " + pkg.getPackageName()
- + ") is not included in the <processes> list");
- }
- assertPackageProcesses(pkg, pkg.getActivities(), procs, "activity");
- assertPackageProcesses(pkg, pkg.getServices(), procs, "service");
- assertPackageProcesses(pkg, pkg.getReceivers(), procs, "receiver");
- assertPackageProcesses(pkg, pkg.getProviders(), procs, "provider");
- }
-
- // Verify that packages sharing a user with a privileged app are marked as privileged.
- if (!pkg.isPrivileged() && (pkg.getSharedUserId() != null)) {
- SharedUserSetting sharedUserSetting = null;
- try {
- sharedUserSetting = mPm.mSettings.getSharedUserLPw(pkg.getSharedUserId(),
- 0, 0, false);
- } catch (PackageManagerException ignore) {
- }
- if (sharedUserSetting != null && sharedUserSetting.isPrivileged()) {
- // Exempt SharedUsers signed with the platform key.
- PackageSetting platformPkgSetting = mPm.mSettings.getPackageLPr("android");
- if (!comparePackageSignatures(platformPkgSetting,
- pkg.getSigningDetails().getSignatures())) {
- throw new PackageManagerException("Apps that share a user with a "
- + "privileged app must themselves be marked as privileged. "
- + pkg.getPackageName() + " shares privileged user "
- + pkg.getSharedUserId() + ".");
- }
- }
- }
-
- // Apply policies specific for runtime resource overlays (RROs).
- if (pkg.getOverlayTarget() != null) {
- // System overlays have some restrictions on their use of the 'static' state.
- if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
- // We are scanning a system overlay. This can be the first scan of the
- // system/vendor/oem partition, or an update to the system overlay.
- if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
- // This must be an update to a system overlay. Immutable overlays cannot be
- // upgraded.
- if (!mPm.isOverlayMutable(pkg.getPackageName())) {
- throw new PackageManagerException("Overlay "
- + pkg.getPackageName()
- + " is static and cannot be upgraded.");
- }
- } else {
- if ((scanFlags & SCAN_AS_VENDOR) != 0) {
- if (pkg.getTargetSdkVersion() < getVendorPartitionVersion()) {
- Slog.w(TAG, "System overlay " + pkg.getPackageName()
- + " targets an SDK below the required SDK level of vendor"
- + " overlays (" + getVendorPartitionVersion() + ")."
- + " This will become an install error in a future release");
- }
- } else if (pkg.getTargetSdkVersion() < Build.VERSION.SDK_INT) {
- Slog.w(TAG, "System overlay " + pkg.getPackageName()
- + " targets an SDK below the required SDK level of system"
- + " overlays (" + Build.VERSION.SDK_INT + ")."
- + " This will become an install error in a future release");
- }
- }
- } else {
- // A non-preloaded overlay packages must have targetSdkVersion >= Q, or be
- // signed with the platform certificate. Check this in increasing order of
- // computational cost.
- if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.Q) {
- final PackageSetting platformPkgSetting =
- mPm.mSettings.getPackageLPr("android");
- if (!comparePackageSignatures(platformPkgSetting,
- pkg.getSigningDetails().getSignatures())) {
- throw new PackageManagerException("Overlay "
- + pkg.getPackageName()
- + " must target Q or later, "
- + "or be signed with the platform certificate");
- }
- }
-
- // A non-preloaded overlay package, without <overlay android:targetName>, will
- // only be used if it is signed with the same certificate as its target OR if
- // it is signed with the same certificate as a reference package declared
- // in 'overlay-config-signature' tag of SystemConfig.
- // If the target is already installed or 'overlay-config-signature' tag in
- // SystemConfig is set, check this here to augment the last line of defense
- // which is OMS.
- if (pkg.getOverlayTargetOverlayableName() == null) {
- final PackageSetting targetPkgSetting =
- mPm.mSettings.getPackageLPr(pkg.getOverlayTarget());
- if (targetPkgSetting != null) {
- if (!comparePackageSignatures(targetPkgSetting,
- pkg.getSigningDetails().getSignatures())) {
- // check reference signature
- if (mPm.mOverlayConfigSignaturePackage == null) {
- throw new PackageManagerException("Overlay "
- + pkg.getPackageName() + " and target "
- + pkg.getOverlayTarget() + " signed with"
- + " different certificates, and the overlay lacks"
- + " <overlay android:targetName>");
- }
- final PackageSetting refPkgSetting =
- mPm.mSettings.getPackageLPr(
- mPm.mOverlayConfigSignaturePackage);
- if (!comparePackageSignatures(refPkgSetting,
- pkg.getSigningDetails().getSignatures())) {
- throw new PackageManagerException("Overlay "
- + pkg.getPackageName() + " signed with a different "
- + "certificate than both the reference package and "
- + "target " + pkg.getOverlayTarget() + ", and the "
- + "overlay lacks <overlay android:targetName>");
- }
- }
- }
- }
- }
- }
-
- // If the package is not on a system partition ensure it is signed with at least the
- // minimum signature scheme version required for its target SDK.
- if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
- int minSignatureSchemeVersion =
- ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
- pkg.getTargetSdkVersion());
- if (pkg.getSigningDetails().getSignatureSchemeVersion()
- < minSignatureSchemeVersion) {
- throw new PackageManagerException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
- "No signature found in package of version " + minSignatureSchemeVersion
- + " or newer for package " + pkg.getPackageName());
- }
- }
- }
- }
-
- private static <T extends ParsedMainComponent> void assertPackageProcesses(AndroidPackage pkg,
- List<T> components, Map<String, ParsedProcess> procs, String compName)
- throws PackageManagerException {
- if (components == null) {
- return;
- }
- for (int i = components.size() - 1; i >= 0; i--) {
- final ParsedMainComponent component = components.get(i);
- if (!procs.containsKey(component.getProcessName())) {
- throw new PackageManagerException(
- INSTALL_FAILED_PROCESS_NOT_DEFINED,
- "Can't install because " + compName + " " + component.getClassName()
- + "'s process attribute " + component.getProcessName()
- + " (in package " + pkg.getPackageName()
- + ") is not included in the <processes> list");
- }
- }
- }
-
- /**
- * Applies the adjusted ABI calculated by
- * {@link PackageAbiHelper#getAdjustedAbiForSharedUser(Set, AndroidPackage)} to all
- * relevant packages and settings.
- * @param sharedUserSetting The {@code SharedUserSetting} to adjust
- * @param scannedPackage the package being scanned or null
- * @param adjustedAbi the adjusted ABI calculated by {@link PackageAbiHelper}
- * @return the list of code paths that belong to packages that had their ABIs adjusted.
- */
- public static List<String> applyAdjustedAbiToSharedUser(SharedUserSetting sharedUserSetting,
- ParsedPackage scannedPackage, String adjustedAbi) {
- if (scannedPackage != null) {
- scannedPackage.setPrimaryCpuAbi(adjustedAbi);
- }
- List<String> changedAbiCodePath = null;
- for (PackageSetting ps : sharedUserSetting.packages) {
- if (scannedPackage == null
- || !scannedPackage.getPackageName().equals(ps.getPackageName())) {
- if (ps.getPrimaryCpuAbi() != null) {
- continue;
- }
-
- ps.setPrimaryCpuAbi(adjustedAbi);
- if (ps.getPkg() != null) {
- if (!TextUtils.equals(adjustedAbi,
- AndroidPackageUtils.getRawPrimaryCpuAbi(ps.getPkg()))) {
- if (DEBUG_ABI_SELECTION) {
- Slog.i(TAG,
- "Adjusting ABI for " + ps.getPackageName() + " to "
- + adjustedAbi + " (scannedPackage="
- + (scannedPackage != null ? scannedPackage : "null")
- + ")");
- }
- if (changedAbiCodePath == null) {
- changedAbiCodePath = new ArrayList<>();
- }
- changedAbiCodePath.add(ps.getPathString());
- }
- }
- }
- }
- return changedAbiCodePath;
- }
-
- /**
- * Applies policy to the parsed package based upon the given policy flags.
- * Ensures the package is in a good state.
- * <p>
- * Implementation detail: This method must NOT have any side effect. It would
- * ideally be static, but, it requires locks to read system state.
- */
- private static void applyPolicy(ParsedPackage parsedPackage,
- final @PackageManagerService.ScanFlags int scanFlags, AndroidPackage platformPkg,
- boolean isUpdatedSystemApp) {
- if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
- parsedPackage.setSystem(true);
- // TODO(b/135203078): Can this be done in PackageParser? Or just inferred when the flag
- // is set during parse.
- if (parsedPackage.isDirectBootAware()) {
- parsedPackage.setAllComponentsDirectBootAware(true);
- }
- if (compressedFileExists(parsedPackage.getPath())) {
- parsedPackage.setStub(true);
- }
- } else {
- parsedPackage
- // Non system apps cannot mark any broadcast as protected
- .clearProtectedBroadcasts()
- // non system apps can't be flagged as core
- .setCoreApp(false)
- // clear flags not applicable to regular apps
- .setPersistent(false)
- .setDefaultToDeviceProtectedStorage(false)
- .setDirectBootAware(false)
- // non system apps can't have permission priority
- .capPermissionPriorities();
- }
- if ((scanFlags & SCAN_AS_PRIVILEGED) == 0) {
- parsedPackage
- .markNotActivitiesAsNotExportedIfSingleUser();
- }
-
- parsedPackage.setPrivileged((scanFlags & SCAN_AS_PRIVILEGED) != 0)
- .setOem((scanFlags & SCAN_AS_OEM) != 0)
- .setVendor((scanFlags & SCAN_AS_VENDOR) != 0)
- .setProduct((scanFlags & SCAN_AS_PRODUCT) != 0)
- .setSystemExt((scanFlags & SCAN_AS_SYSTEM_EXT) != 0)
- .setOdm((scanFlags & SCAN_AS_ODM) != 0);
-
- // Check if the package is signed with the same key as the platform package.
- parsedPackage.setSignedWithPlatformKey(
- (PLATFORM_PACKAGE_NAME.equals(parsedPackage.getPackageName())
- || (platformPkg != null && compareSignatures(
- platformPkg.getSigningDetails().getSignatures(),
- parsedPackage.getSigningDetails().getSignatures()
- ) == PackageManager.SIGNATURE_MATCH))
- );
-
- if (!parsedPackage.isSystem()) {
- // Only system apps can use these features.
- parsedPackage.clearOriginalPackages()
- .clearAdoptPermissions();
- }
-
- PackageBackwardCompatibility.modifySharedLibraries(parsedPackage, isUpdatedSystemApp);
- }
-
- /**
- * Enforces code policy for the package. This ensures that if an APK has
- * declared hasCode="true" in its manifest that the APK actually contains
- * code.
- *
- * @throws PackageManagerException If bytecode could not be found when it should exist
- */
- private static void assertCodePolicy(AndroidPackage pkg)
- throws PackageManagerException {
- final boolean shouldHaveCode = pkg.isHasCode();
- if (shouldHaveCode && !apkHasCode(pkg.getBaseApkPath())) {
- throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
- "Package " + pkg.getBaseApkPath() + " code is missing");
- }
-
- if (!ArrayUtils.isEmpty(pkg.getSplitCodePaths())) {
- for (int i = 0; i < pkg.getSplitCodePaths().length; i++) {
- final boolean splitShouldHaveCode =
- (pkg.getSplitFlags()[i] & ApplicationInfo.FLAG_HAS_CODE) != 0;
- if (splitShouldHaveCode && !apkHasCode(pkg.getSplitCodePaths()[i])) {
- throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
- "Package " + pkg.getSplitCodePaths()[i] + " code is missing");
- }
- }
- }
- }
-
- /**
- * Returns the "real" name of the package.
- * <p>This may differ from the package's actual name if the application has already
- * been installed under one of this package's original names.
- */
- private static @Nullable String getRealPackageName(@NonNull AndroidPackage pkg,
- @Nullable String renamedPkgName) {
- if (isPackageRenamed(pkg, renamedPkgName)) {
- return AndroidPackageUtils.getRealPackageOrNull(pkg);
- }
- return null;
- }
-
- /** Returns {@code true} if the package has been renamed. Otherwise, {@code false}. */
- private static boolean isPackageRenamed(@NonNull AndroidPackage pkg,
- @Nullable String renamedPkgName) {
- return pkg.getOriginalPackages().contains(renamedPkgName);
- }
-
- /**
- * Renames the package if it was installed under a different name.
- * <p>When we've already installed the package under an original name, update
- * the new package so we can continue to have the old name.
- */
- private static void ensurePackageRenamed(@NonNull ParsedPackage parsedPackage,
- @NonNull String renamedPackageName) {
- if (!parsedPackage.getOriginalPackages().contains(renamedPackageName)
- || parsedPackage.getPackageName().equals(renamedPackageName)) {
- return;
- }
- parsedPackage.setPackageName(renamedPackageName);
- }
-
- /**
- * Returns {@code true} if the given file contains code. Otherwise {@code false}.
- */
- private static boolean apkHasCode(String fileName) {
- StrictJarFile jarFile = null;
- try {
- jarFile = new StrictJarFile(fileName,
- false /*verify*/, false /*signatureSchemeRollbackProtectionsEnforced*/);
- return jarFile.findEntry("classes.dex") != null;
- } catch (IOException ignore) {
- } finally {
- try {
- if (jarFile != null) {
- jarFile.close();
- }
- } catch (IOException ignore) {
- }
- }
- return false;
- }
-
- /**
- * Sets the enabled state of components configured through {@link SystemConfig}.
- * This modifies the {@link PackageSetting} object.
- *
- * TODO(b/135203078): Move this to package parsing
- **/
- private static void configurePackageComponents(AndroidPackage pkg) {
- final ArrayMap<String, Boolean> componentsEnabledStates = SystemConfig.getInstance()
- .getComponentsEnabledStates(pkg.getPackageName());
- if (componentsEnabledStates == null) {
- return;
- }
-
- for (int i = ArrayUtils.size(pkg.getActivities()) - 1; i >= 0; i--) {
- final ParsedActivity component = pkg.getActivities().get(i);
- final Boolean enabled = componentsEnabledStates.get(component.getName());
- if (enabled != null) {
- ComponentMutateUtils.setEnabled(component, enabled);
- }
- }
-
- for (int i = ArrayUtils.size(pkg.getReceivers()) - 1; i >= 0; i--) {
- final ParsedActivity component = pkg.getReceivers().get(i);
- final Boolean enabled = componentsEnabledStates.get(component.getName());
- if (enabled != null) {
- ComponentMutateUtils.setEnabled(component, enabled);
- }
- }
-
- for (int i = ArrayUtils.size(pkg.getProviders()) - 1; i >= 0; i--) {
- final ParsedProvider component = pkg.getProviders().get(i);
- final Boolean enabled = componentsEnabledStates.get(component.getName());
- if (enabled != null) {
- ComponentMutateUtils.setEnabled(component, enabled);
- }
- }
-
- for (int i = ArrayUtils.size(pkg.getServices()) - 1; i >= 0; i--) {
- final ParsedService component = pkg.getServices().get(i);
- final Boolean enabled = componentsEnabledStates.get(component.getName());
- if (enabled != null) {
- ComponentMutateUtils.setEnabled(component, enabled);
- }
- }
- }
-
- private static int getVendorPartitionVersion() {
- final String version = SystemProperties.get("ro.vndk.version");
- if (!version.isEmpty()) {
- try {
- return Integer.parseInt(version);
- } catch (NumberFormatException ignore) {
- if (ArrayUtils.contains(Build.VERSION.ACTIVE_CODENAMES, version)) {
- return Build.VERSION_CODES.CUR_DEVELOPMENT;
- }
- }
- }
- return Build.VERSION_CODES.P;
- }
-}
diff --git a/services/core/java/com/android/server/pm/ScanPackageUtils.java b/services/core/java/com/android/server/pm/ScanPackageUtils.java
new file mode 100644
index 0000000..378c9e0
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ScanPackageUtils.java
@@ -0,0 +1,1006 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
+import static android.content.pm.PackageManager.INSTALL_FAILED_PROCESS_NOT_DEFINED;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
+import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+
+import static com.android.server.pm.PackageManagerService.DEBUG_ABI_SELECTION;
+import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING;
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_FULL_APP;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_INSTANT_APP;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_ODM;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_OEM;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_PRIVILEGED;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_PRODUCT;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_SYSTEM_EXT;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_VENDOR;
+import static com.android.server.pm.PackageManagerService.SCAN_AS_VIRTUAL_PRELOAD;
+import static com.android.server.pm.PackageManagerService.SCAN_BOOTING;
+import static com.android.server.pm.PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE;
+import static com.android.server.pm.PackageManagerService.SCAN_MOVE;
+import static com.android.server.pm.PackageManagerService.SCAN_NEW_INSTALL;
+import static com.android.server.pm.PackageManagerService.SCAN_NO_DEX;
+import static com.android.server.pm.PackageManagerService.SCAN_UPDATE_TIME;
+import static com.android.server.pm.PackageManagerService.TAG;
+import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
+import static com.android.server.pm.PackageManagerServiceUtils.compressedFileExists;
+import static com.android.server.pm.PackageManagerServiceUtils.deriveAbiOverride;
+import static com.android.server.pm.PackageManagerServiceUtils.getLastModifiedTime;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.SharedLibraryInfo;
+import android.content.pm.SigningDetails;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.component.ComponentMutateUtils;
+import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedMainComponent;
+import android.content.pm.parsing.component.ParsedProcess;
+import android.content.pm.parsing.component.ParsedProvider;
+import android.content.pm.parsing.component.ParsedService;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
+import android.os.Build;
+import android.os.Environment;
+import android.os.Process;
+import android.os.SystemProperties;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.apk.ApkSignatureVerifier;
+import android.util.jar.StrictJarFile;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+import com.android.server.SystemConfig;
+import com.android.server.pm.parsing.PackageInfoUtils;
+import com.android.server.pm.parsing.library.PackageBackwardCompatibility;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+
+import dalvik.system.VMRuntime;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Helper class that handles package scanning logic
+ */
+final class ScanPackageUtils {
+ /**
+ * Just scans the package without any side effects.
+ *
+ * @param injector injector for acquiring dependencies
+ * @param request Information about the package to be scanned
+ * @param isUnderFactoryTest Whether or not the device is under factory test
+ * @param currentTime The current time, in millis
+ * @return The results of the scan
+ */
+ @GuardedBy("mPm.mInstallLock")
+ @VisibleForTesting
+ @NonNull
+ public static ScanResult scanPackageOnlyLI(@NonNull ScanRequest request,
+ PackageManagerServiceInjector injector,
+ boolean isUnderFactoryTest, long currentTime)
+ throws PackageManagerException {
+ final PackageAbiHelper packageAbiHelper = injector.getAbiHelper();
+ ParsedPackage parsedPackage = request.mParsedPackage;
+ PackageSetting pkgSetting = request.mPkgSetting;
+ final PackageSetting disabledPkgSetting = request.mDisabledPkgSetting;
+ final PackageSetting originalPkgSetting = request.mOriginalPkgSetting;
+ final @ParsingPackageUtils.ParseFlags int parseFlags = request.mParseFlags;
+ final @PackageManagerService.ScanFlags int scanFlags = request.mScanFlags;
+ final String realPkgName = request.mRealPkgName;
+ final SharedUserSetting sharedUserSetting = request.mSharedUserSetting;
+ final UserHandle user = request.mUser;
+ final boolean isPlatformPackage = request.mIsPlatformPackage;
+
+ List<String> changedAbiCodePath = null;
+
+ if (DEBUG_PACKAGE_SCANNING) {
+ if ((parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0) {
+ Log.d(TAG, "Scanning package " + parsedPackage.getPackageName());
+ }
+ }
+
+ // Initialize package source and resource directories
+ final File destCodeFile = new File(parsedPackage.getPath());
+
+ // We keep references to the derived CPU Abis from settings in oder to reuse
+ // them in the case where we're not upgrading or booting for the first time.
+ String primaryCpuAbiFromSettings = null;
+ String secondaryCpuAbiFromSettings = null;
+ boolean needToDeriveAbi = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0;
+ if (!needToDeriveAbi) {
+ if (pkgSetting != null) {
+ // TODO(b/154610922): if it is not first boot or upgrade, we should directly use
+ // API info from existing package setting. However, stub packages currently do not
+ // preserve ABI info, thus the special condition check here. Remove the special
+ // check after we fix the stub generation.
+ if (pkgSetting.getPkg() != null && pkgSetting.getPkg().isStub()) {
+ needToDeriveAbi = true;
+ } else {
+ primaryCpuAbiFromSettings = pkgSetting.getPrimaryCpuAbi();
+ secondaryCpuAbiFromSettings = pkgSetting.getSecondaryCpuAbi();
+ }
+ } else {
+ // Re-scanning a system package after uninstalling updates; need to derive ABI
+ needToDeriveAbi = true;
+ }
+ }
+
+ int previousAppId = Process.INVALID_UID;
+
+ if (pkgSetting != null && pkgSetting.getSharedUser() != sharedUserSetting) {
+ if (pkgSetting.getSharedUser() != null && sharedUserSetting == null) {
+ previousAppId = pkgSetting.getAppId();
+ // Log that something is leaving shareduid and keep going
+ Slog.i(TAG,
+ "Package " + parsedPackage.getPackageName() + " shared user changed from "
+ + pkgSetting.getSharedUser().name + " to " + "<nothing>.");
+ } else {
+ PackageManagerService.reportSettingsProblem(Log.WARN,
+ "Package " + parsedPackage.getPackageName() + " shared user changed from "
+ + (pkgSetting.getSharedUser() != null
+ ? pkgSetting.getSharedUser().name : "<nothing>")
+ + " to "
+ + (sharedUserSetting != null ? sharedUserSetting.name : "<nothing>")
+ + "; replacing with new");
+ pkgSetting = null;
+ }
+ }
+
+ String[] usesSdkLibraries = null;
+ if (!parsedPackage.getUsesSdkLibraries().isEmpty()) {
+ usesSdkLibraries = new String[parsedPackage.getUsesSdkLibraries().size()];
+ parsedPackage.getUsesSdkLibraries().toArray(usesSdkLibraries);
+ }
+
+ String[] usesStaticLibraries = null;
+ if (!parsedPackage.getUsesStaticLibraries().isEmpty()) {
+ usesStaticLibraries = new String[parsedPackage.getUsesStaticLibraries().size()];
+ parsedPackage.getUsesStaticLibraries().toArray(usesStaticLibraries);
+ }
+
+ final UUID newDomainSetId = injector.getDomainVerificationManagerInternal().generateNewId();
+
+ // TODO(b/135203078): Remove appInfoFlag usage in favor of individually assigned booleans
+ // to avoid adding something that's unsupported due to lack of state, since it's called
+ // with null.
+ final boolean createNewPackage = (pkgSetting == null);
+ if (createNewPackage) {
+ final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
+ final boolean virtualPreload = (scanFlags & SCAN_AS_VIRTUAL_PRELOAD) != 0;
+
+ // Flags contain system values stored in the server variant of AndroidPackage,
+ // and so the server-side PackageInfoUtils is still called, even without a
+ // PackageSetting to pass in.
+ int pkgFlags = PackageInfoUtils.appInfoFlags(parsedPackage, null);
+ int pkgPrivateFlags = PackageInfoUtils.appInfoPrivateFlags(parsedPackage, null);
+
+ // REMOVE SharedUserSetting from method; update in a separate call
+ pkgSetting = Settings.createNewSetting(parsedPackage.getPackageName(),
+ originalPkgSetting, disabledPkgSetting, realPkgName, sharedUserSetting,
+ destCodeFile, parsedPackage.getNativeLibraryRootDir(),
+ AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage),
+ AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage),
+ parsedPackage.getLongVersionCode(), pkgFlags, pkgPrivateFlags, user,
+ true /*allowInstall*/, instantApp, virtualPreload,
+ UserManagerService.getInstance(), usesSdkLibraries,
+ parsedPackage.getUsesSdkLibrariesVersionsMajor(), usesStaticLibraries,
+ parsedPackage.getUsesStaticLibrariesVersions(), parsedPackage.getMimeGroups(),
+ newDomainSetId);
+ } else {
+ // make a deep copy to avoid modifying any existing system state.
+ pkgSetting = new PackageSetting(pkgSetting);
+ pkgSetting.setPkg(parsedPackage);
+
+ // REMOVE SharedUserSetting from method; update in a separate call.
+ //
+ // TODO(narayan): This update is bogus. nativeLibraryDir & primaryCpuAbi,
+ // secondaryCpuAbi are not known at this point so we always update them
+ // to null here, only to reset them at a later point.
+ Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, sharedUserSetting,
+ destCodeFile, parsedPackage.getNativeLibraryDir(),
+ AndroidPackageUtils.getPrimaryCpuAbi(parsedPackage, pkgSetting),
+ AndroidPackageUtils.getSecondaryCpuAbi(parsedPackage, pkgSetting),
+ PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting),
+ PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting),
+ UserManagerService.getInstance(),
+ usesSdkLibraries, parsedPackage.getUsesSdkLibrariesVersionsMajor(),
+ usesStaticLibraries, parsedPackage.getUsesStaticLibrariesVersions(),
+ parsedPackage.getMimeGroups(), newDomainSetId);
+ }
+ if (createNewPackage && originalPkgSetting != null) {
+ // This is the initial transition from the original package, so,
+ // fix up the new package's name now. We must do this after looking
+ // up the package under its new name, so getPackageLP takes care of
+ // fiddling things correctly.
+ parsedPackage.setPackageName(originalPkgSetting.getPackageName());
+
+ // File a report about this.
+ String msg = "New package " + pkgSetting.getRealName()
+ + " renamed to replace old package " + pkgSetting.getPackageName();
+ PackageManagerService.reportSettingsProblem(Log.WARN, msg);
+ }
+
+ final int userId = (user == null ? UserHandle.USER_SYSTEM : user.getIdentifier());
+ // for existing packages, change the install state; but, only if it's explicitly specified
+ if (!createNewPackage) {
+ final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
+ final boolean fullApp = (scanFlags & SCAN_AS_FULL_APP) != 0;
+ setInstantAppForUser(injector, pkgSetting, userId, instantApp, fullApp);
+ }
+ // TODO(patb): see if we can do away with disabled check here.
+ if (disabledPkgSetting != null
+ || (0 != (scanFlags & SCAN_NEW_INSTALL)
+ && pkgSetting != null && pkgSetting.isSystem())) {
+ pkgSetting.getPkgState().setUpdatedSystemApp(true);
+ }
+
+ parsedPackage.setSeInfo(SELinuxMMAC.getSeInfo(parsedPackage, sharedUserSetting,
+ injector.getCompatibility()));
+
+ if (parsedPackage.isSystem()) {
+ configurePackageComponents(parsedPackage);
+ }
+
+ final String cpuAbiOverride = deriveAbiOverride(request.mCpuAbiOverride);
+ final boolean isUpdatedSystemApp = pkgSetting.getPkgState().isUpdatedSystemApp();
+
+ final File appLib32InstallDir = getAppLib32InstallDir();
+ if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
+ if (needToDeriveAbi) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi");
+ final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths> derivedAbi =
+ packageAbiHelper.derivePackageAbi(parsedPackage, isUpdatedSystemApp,
+ cpuAbiOverride, appLib32InstallDir);
+ derivedAbi.first.applyTo(parsedPackage);
+ derivedAbi.second.applyTo(parsedPackage);
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+
+ // Some system apps still use directory structure for native libraries
+ // in which case we might end up not detecting abi solely based on apk
+ // structure. Try to detect abi based on directory structure.
+
+ String pkgRawPrimaryCpuAbi = AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage);
+ if (parsedPackage.isSystem() && !isUpdatedSystemApp
+ && pkgRawPrimaryCpuAbi == null) {
+ final PackageAbiHelper.Abis abis = packageAbiHelper.getBundledAppAbis(
+ parsedPackage);
+ abis.applyTo(parsedPackage);
+ abis.applyTo(pkgSetting);
+ final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
+ packageAbiHelper.deriveNativeLibraryPaths(parsedPackage,
+ isUpdatedSystemApp, appLib32InstallDir);
+ nativeLibraryPaths.applyTo(parsedPackage);
+ }
+ } else {
+ // This is not a first boot or an upgrade, don't bother deriving the
+ // ABI during the scan. Instead, trust the value that was stored in the
+ // package setting.
+ parsedPackage.setPrimaryCpuAbi(primaryCpuAbiFromSettings)
+ .setSecondaryCpuAbi(secondaryCpuAbiFromSettings);
+
+ final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
+ packageAbiHelper.deriveNativeLibraryPaths(parsedPackage,
+ isUpdatedSystemApp, appLib32InstallDir);
+ nativeLibraryPaths.applyTo(parsedPackage);
+
+ if (DEBUG_ABI_SELECTION) {
+ Slog.i(TAG, "Using ABIS and native lib paths from settings : "
+ + parsedPackage.getPackageName() + " "
+ + AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage)
+ + ", "
+ + AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage));
+ }
+ }
+ } else {
+ if ((scanFlags & SCAN_MOVE) != 0) {
+ // We haven't run dex-opt for this move (since we've moved the compiled output too)
+ // but we already have this packages package info in the PackageSetting. We just
+ // use that and derive the native library path based on the new code path.
+ parsedPackage.setPrimaryCpuAbi(pkgSetting.getPrimaryCpuAbi())
+ .setSecondaryCpuAbi(pkgSetting.getSecondaryCpuAbi());
+ }
+
+ // Set native library paths again. For moves, the path will be updated based on the
+ // ABIs we've determined above. For non-moves, the path will be updated based on the
+ // ABIs we determined during compilation, but the path will depend on the final
+ // package path (after the rename away from the stage path).
+ final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
+ packageAbiHelper.deriveNativeLibraryPaths(parsedPackage, isUpdatedSystemApp,
+ appLib32InstallDir);
+ nativeLibraryPaths.applyTo(parsedPackage);
+ }
+
+ // This is a special case for the "system" package, where the ABI is
+ // dictated by the zygote configuration (and init.rc). We should keep track
+ // of this ABI so that we can deal with "normal" applications that run under
+ // the same UID correctly.
+ if (isPlatformPackage) {
+ parsedPackage.setPrimaryCpuAbi(VMRuntime.getRuntime().is64Bit()
+ ? Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0]);
+ }
+
+ // If there's a mismatch between the abi-override in the package setting
+ // and the abiOverride specified for the install. Warn about this because we
+ // would've already compiled the app without taking the package setting into
+ // account.
+ if ((scanFlags & SCAN_NO_DEX) == 0 && (scanFlags & SCAN_NEW_INSTALL) != 0) {
+ if (cpuAbiOverride == null) {
+ Slog.w(TAG, "Ignoring persisted ABI override for package "
+ + parsedPackage.getPackageName());
+ }
+ }
+
+ pkgSetting.setPrimaryCpuAbi(AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage))
+ .setSecondaryCpuAbi(AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage))
+ .setCpuAbiOverride(cpuAbiOverride);
+
+ if (DEBUG_ABI_SELECTION) {
+ Slog.d(TAG, "Resolved nativeLibraryRoot for " + parsedPackage.getPackageName()
+ + " to root=" + parsedPackage.getNativeLibraryRootDir()
+ + ", to dir=" + parsedPackage.getNativeLibraryDir()
+ + ", isa=" + parsedPackage.isNativeLibraryRootRequiresIsa());
+ }
+
+ // Push the derived path down into PackageSettings so we know what to
+ // clean up at uninstall time.
+ pkgSetting.setLegacyNativeLibraryPath(parsedPackage.getNativeLibraryRootDir());
+
+ if (DEBUG_ABI_SELECTION) {
+ Log.d(TAG, "Abis for package[" + parsedPackage.getPackageName() + "] are"
+ + " primary=" + pkgSetting.getPrimaryCpuAbi()
+ + " secondary=" + pkgSetting.getSecondaryCpuAbi()
+ + " abiOverride=" + pkgSetting.getCpuAbiOverride());
+ }
+
+ if ((scanFlags & SCAN_BOOTING) == 0 && pkgSetting.getSharedUser() != null) {
+ // We don't do this here during boot because we can do it all
+ // at once after scanning all existing packages.
+ //
+ // We also do this *before* we perform dexopt on this package, so that
+ // we can avoid redundant dexopts, and also to make sure we've got the
+ // code and package path correct.
+ changedAbiCodePath = applyAdjustedAbiToSharedUser(pkgSetting.getSharedUser(),
+ parsedPackage, packageAbiHelper.getAdjustedAbiForSharedUser(
+ pkgSetting.getSharedUser().packages, parsedPackage));
+ }
+
+ parsedPackage.setFactoryTest(isUnderFactoryTest && parsedPackage.getRequestedPermissions()
+ .contains(android.Manifest.permission.FACTORY_TEST));
+
+ if (parsedPackage.isSystem()) {
+ pkgSetting.setIsOrphaned(true);
+ }
+
+ // Take care of first install / last update times.
+ final long scanFileTime = getLastModifiedTime(parsedPackage);
+ if (currentTime != 0) {
+ if (pkgSetting.getFirstInstallTime() == 0) {
+ pkgSetting.setFirstInstallTime(currentTime)
+ .setLastUpdateTime(currentTime);
+ } else if ((scanFlags & SCAN_UPDATE_TIME) != 0) {
+ pkgSetting.setLastUpdateTime(currentTime);
+ }
+ } else if (pkgSetting.getFirstInstallTime() == 0) {
+ // We need *something*. Take time time stamp of the file.
+ pkgSetting.setFirstInstallTime(scanFileTime)
+ .setLastUpdateTime(scanFileTime);
+ } else if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0) {
+ if (scanFileTime != pkgSetting.getLastModifiedTime()) {
+ // A package on the system image has changed; consider this
+ // to be an update.
+ pkgSetting.setLastUpdateTime(scanFileTime);
+ }
+ }
+ pkgSetting.setLastModifiedTime(scanFileTime);
+ // TODO(b/135203078): Remove, move to constructor
+ pkgSetting.setPkg(parsedPackage)
+ .setFlags(PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting))
+ .setPrivateFlags(
+ PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting));
+ if (parsedPackage.getLongVersionCode() != pkgSetting.getVersionCode()) {
+ pkgSetting.setLongVersionCode(parsedPackage.getLongVersionCode());
+ }
+ // Update volume if needed
+ final String volumeUuid = parsedPackage.getVolumeUuid();
+ if (!Objects.equals(volumeUuid, pkgSetting.getVolumeUuid())) {
+ Slog.i(PackageManagerService.TAG,
+ "Update" + (pkgSetting.isSystem() ? " system" : "")
+ + " package " + parsedPackage.getPackageName()
+ + " volume from " + pkgSetting.getVolumeUuid()
+ + " to " + volumeUuid);
+ pkgSetting.setVolumeUuid(volumeUuid);
+ }
+
+ SharedLibraryInfo sdkLibraryInfo = null;
+ if (!TextUtils.isEmpty(parsedPackage.getSdkLibName())) {
+ sdkLibraryInfo = AndroidPackageUtils.createSharedLibraryForSdk(parsedPackage);
+ }
+ SharedLibraryInfo staticSharedLibraryInfo = null;
+ if (!TextUtils.isEmpty(parsedPackage.getStaticSharedLibName())) {
+ staticSharedLibraryInfo =
+ AndroidPackageUtils.createSharedLibraryForStatic(parsedPackage);
+ }
+ List<SharedLibraryInfo> dynamicSharedLibraryInfos = null;
+ if (!ArrayUtils.isEmpty(parsedPackage.getLibraryNames())) {
+ dynamicSharedLibraryInfos = new ArrayList<>(parsedPackage.getLibraryNames().size());
+ for (String name : parsedPackage.getLibraryNames()) {
+ dynamicSharedLibraryInfos.add(
+ AndroidPackageUtils.createSharedLibraryForDynamic(parsedPackage, name));
+ }
+ }
+
+ return new ScanResult(request, true, pkgSetting, changedAbiCodePath,
+ !createNewPackage /* existingSettingCopied */,
+ previousAppId, sdkLibraryInfo, staticSharedLibraryInfo,
+ dynamicSharedLibraryInfos);
+ }
+
+ /**
+ * Returns the actual scan flags depending upon the state of the other settings.
+ * <p>Updated system applications will not have the following flags set
+ * by default and need to be adjusted after the fact:
+ * <ul>
+ * <li>{@link PackageManagerService.SCAN_AS_SYSTEM}</li>
+ * <li>{@link PackageManagerService.SCAN_AS_PRIVILEGED}</li>
+ * <li>{@link PackageManagerService.SCAN_AS_OEM}</li>
+ * <li>{@link PackageManagerService.SCAN_AS_VENDOR}</li>
+ * <li>{@link PackageManagerService.SCAN_AS_PRODUCT}</li>
+ * <li>{@link PackageManagerService.SCAN_AS_SYSTEM_EXT}</li>
+ * <li>{@link PackageManagerService.SCAN_AS_INSTANT_APP}</li>
+ * <li>{@link PackageManagerService.SCAN_AS_VIRTUAL_PRELOAD}</li>
+ * <li>{@link PackageManagerService.SCAN_AS_ODM}</li>
+ * </ul>
+ */
+ public static @PackageManagerService.ScanFlags int adjustScanFlagsWithPackageSetting(
+ @PackageManagerService.ScanFlags int scanFlags,
+ PackageSetting pkgSetting, PackageSetting disabledPkgSetting, UserHandle user) {
+
+ // TODO(patb): Do away entirely with disabledPkgSetting here. PkgSetting will always contain
+ // the correct isSystem value now that we don't disable system packages before scan.
+ final PackageSetting systemPkgSetting =
+ (scanFlags & SCAN_NEW_INSTALL) != 0 && disabledPkgSetting == null
+ && pkgSetting != null && pkgSetting.isSystem()
+ ? pkgSetting
+ : disabledPkgSetting;
+ if (systemPkgSetting != null) {
+ // updated system application, must at least have SCAN_AS_SYSTEM
+ scanFlags |= SCAN_AS_SYSTEM;
+ if ((systemPkgSetting.getPrivateFlags()
+ & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
+ scanFlags |= SCAN_AS_PRIVILEGED;
+ }
+ if ((systemPkgSetting.getPrivateFlags()
+ & ApplicationInfo.PRIVATE_FLAG_OEM) != 0) {
+ scanFlags |= SCAN_AS_OEM;
+ }
+ if ((systemPkgSetting.getPrivateFlags()
+ & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0) {
+ scanFlags |= SCAN_AS_VENDOR;
+ }
+ if ((systemPkgSetting.getPrivateFlags()
+ & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0) {
+ scanFlags |= SCAN_AS_PRODUCT;
+ }
+ if ((systemPkgSetting.getPrivateFlags()
+ & ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT) != 0) {
+ scanFlags |= SCAN_AS_SYSTEM_EXT;
+ }
+ if ((systemPkgSetting.getPrivateFlags()
+ & ApplicationInfo.PRIVATE_FLAG_ODM) != 0) {
+ scanFlags |= SCAN_AS_ODM;
+ }
+ }
+ if (pkgSetting != null) {
+ final int userId = ((user == null) ? 0 : user.getIdentifier());
+ if (pkgSetting.getInstantApp(userId)) {
+ scanFlags |= SCAN_AS_INSTANT_APP;
+ }
+ if (pkgSetting.getVirtualPreload(userId)) {
+ scanFlags |= SCAN_AS_VIRTUAL_PRELOAD;
+ }
+ }
+
+ return scanFlags;
+ }
+
+ /**
+ * Enforces code policy for the package. This ensures that if an APK has
+ * declared hasCode="true" in its manifest that the APK actually contains
+ * code.
+ *
+ * @throws PackageManagerException If bytecode could not be found when it should exist
+ */
+ public static void assertCodePolicy(AndroidPackage pkg)
+ throws PackageManagerException {
+ final boolean shouldHaveCode = pkg.isHasCode();
+ if (shouldHaveCode && !apkHasCode(pkg.getBaseApkPath())) {
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+ "Package " + pkg.getBaseApkPath() + " code is missing");
+ }
+
+ if (!ArrayUtils.isEmpty(pkg.getSplitCodePaths())) {
+ for (int i = 0; i < pkg.getSplitCodePaths().length; i++) {
+ final boolean splitShouldHaveCode =
+ (pkg.getSplitFlags()[i] & ApplicationInfo.FLAG_HAS_CODE) != 0;
+ if (splitShouldHaveCode && !apkHasCode(pkg.getSplitCodePaths()[i])) {
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+ "Package " + pkg.getSplitCodePaths()[i] + " code is missing");
+ }
+ }
+ }
+ }
+
+ public static void assertStaticSharedLibraryIsValid(AndroidPackage pkg,
+ @PackageManagerService.ScanFlags int scanFlags) throws PackageManagerException {
+ // Static shared libraries should have at least O target SDK
+ if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.O) {
+ throw new PackageManagerException(
+ "Packages declaring static-shared libs must target O SDK or higher");
+ }
+
+ // Package declaring static a shared lib cannot be instant apps
+ if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
+ throw new PackageManagerException(
+ "Packages declaring static-shared libs cannot be instant apps");
+ }
+
+ // Package declaring static a shared lib cannot be renamed since the package
+ // name is synthetic and apps can't code around package manager internals.
+ if (!ArrayUtils.isEmpty(pkg.getOriginalPackages())) {
+ throw new PackageManagerException(
+ "Packages declaring static-shared libs cannot be renamed");
+ }
+
+ // Package declaring static a shared lib cannot declare dynamic libs
+ if (!ArrayUtils.isEmpty(pkg.getLibraryNames())) {
+ throw new PackageManagerException(
+ "Packages declaring static-shared libs cannot declare dynamic libs");
+ }
+
+ // Package declaring static a shared lib cannot declare shared users
+ if (pkg.getSharedUserId() != null) {
+ throw new PackageManagerException(
+ "Packages declaring static-shared libs cannot declare shared users");
+ }
+
+ // Static shared libs cannot declare activities
+ if (!pkg.getActivities().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare activities");
+ }
+
+ // Static shared libs cannot declare services
+ if (!pkg.getServices().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare services");
+ }
+
+ // Static shared libs cannot declare providers
+ if (!pkg.getProviders().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare content providers");
+ }
+
+ // Static shared libs cannot declare receivers
+ if (!pkg.getReceivers().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare broadcast receivers");
+ }
+
+ // Static shared libs cannot declare permission groups
+ if (!pkg.getPermissionGroups().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare permission groups");
+ }
+
+ // Static shared libs cannot declare attributions
+ if (!pkg.getAttributions().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare features");
+ }
+
+ // Static shared libs cannot declare permissions
+ if (!pkg.getPermissions().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare permissions");
+ }
+
+ // Static shared libs cannot declare protected broadcasts
+ if (!pkg.getProtectedBroadcasts().isEmpty()) {
+ throw new PackageManagerException(
+ "Static shared libs cannot declare protected broadcasts");
+ }
+
+ // Static shared libs cannot be overlay targets
+ if (pkg.getOverlayTarget() != null) {
+ throw new PackageManagerException(
+ "Static shared libs cannot be overlay targets");
+ }
+ }
+
+ public static void assertProcessesAreValid(AndroidPackage pkg) throws PackageManagerException {
+ final Map<String, ParsedProcess> procs = pkg.getProcesses();
+ if (!procs.isEmpty()) {
+ if (!procs.containsKey(pkg.getProcessName())) {
+ throw new PackageManagerException(
+ INSTALL_FAILED_PROCESS_NOT_DEFINED,
+ "Can't install because application tag's process attribute "
+ + pkg.getProcessName()
+ + " (in package " + pkg.getPackageName()
+ + ") is not included in the <processes> list");
+ }
+ assertPackageProcesses(pkg, pkg.getActivities(), procs, "activity");
+ assertPackageProcesses(pkg, pkg.getServices(), procs, "service");
+ assertPackageProcesses(pkg, pkg.getReceivers(), procs, "receiver");
+ assertPackageProcesses(pkg, pkg.getProviders(), procs, "provider");
+ }
+ }
+
+ private static <T extends ParsedMainComponent> void assertPackageProcesses(AndroidPackage pkg,
+ List<T> components, Map<String, ParsedProcess> procs, String compName)
+ throws PackageManagerException {
+ if (components == null) {
+ return;
+ }
+ for (int i = components.size() - 1; i >= 0; i--) {
+ final ParsedMainComponent component = components.get(i);
+ if (!procs.containsKey(component.getProcessName())) {
+ throw new PackageManagerException(
+ INSTALL_FAILED_PROCESS_NOT_DEFINED,
+ "Can't install because " + compName + " " + component.getClassName()
+ + "'s process attribute " + component.getProcessName()
+ + " (in package " + pkg.getPackageName()
+ + ") is not included in the <processes> list");
+ }
+ }
+ }
+
+ public static void assertMinSignatureSchemeIsValid(AndroidPackage pkg,
+ @ParsingPackageUtils.ParseFlags int parseFlags) throws PackageManagerException {
+ if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
+ int minSignatureSchemeVersion =
+ ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
+ pkg.getTargetSdkVersion());
+ if (pkg.getSigningDetails().getSignatureSchemeVersion()
+ < minSignatureSchemeVersion) {
+ throw new PackageManagerException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+ "No signature found in package of version " + minSignatureSchemeVersion
+ + " or newer for package " + pkg.getPackageName());
+ }
+ }
+ }
+
+ /**
+ * Returns the "real" name of the package.
+ * <p>This may differ from the package's actual name if the application has already
+ * been installed under one of this package's original names.
+ */
+ public static @Nullable String getRealPackageName(@NonNull AndroidPackage pkg,
+ @Nullable String renamedPkgName) {
+ if (isPackageRenamed(pkg, renamedPkgName)) {
+ return AndroidPackageUtils.getRealPackageOrNull(pkg);
+ }
+ return null;
+ }
+
+ /** Returns {@code true} if the package has been renamed. Otherwise, {@code false}. */
+ public static boolean isPackageRenamed(@NonNull AndroidPackage pkg,
+ @Nullable String renamedPkgName) {
+ return pkg.getOriginalPackages().contains(renamedPkgName);
+ }
+
+ /**
+ * Renames the package if it was installed under a different name.
+ * <p>When we've already installed the package under an original name, update
+ * the new package so we can continue to have the old name.
+ */
+ public static void ensurePackageRenamed(@NonNull ParsedPackage parsedPackage,
+ @NonNull String renamedPackageName) {
+ if (!parsedPackage.getOriginalPackages().contains(renamedPackageName)
+ || parsedPackage.getPackageName().equals(renamedPackageName)) {
+ return;
+ }
+ parsedPackage.setPackageName(renamedPackageName);
+ }
+
+ /**
+ * Returns {@code true} if the given file contains code. Otherwise {@code false}.
+ */
+ public static boolean apkHasCode(String fileName) {
+ StrictJarFile jarFile = null;
+ try {
+ jarFile = new StrictJarFile(fileName,
+ false /*verify*/, false /*signatureSchemeRollbackProtectionsEnforced*/);
+ return jarFile.findEntry("classes.dex") != null;
+ } catch (IOException ignore) {
+ } finally {
+ try {
+ if (jarFile != null) {
+ jarFile.close();
+ }
+ } catch (IOException ignore) {
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Sets the enabled state of components configured through {@link SystemConfig}.
+ * This modifies the {@link PackageSetting} object.
+ *
+ * TODO(b/135203078): Move this to package parsing
+ **/
+ public static void configurePackageComponents(AndroidPackage pkg) {
+ final ArrayMap<String, Boolean> componentsEnabledStates = SystemConfig.getInstance()
+ .getComponentsEnabledStates(pkg.getPackageName());
+ if (componentsEnabledStates == null) {
+ return;
+ }
+
+ for (int i = ArrayUtils.size(pkg.getActivities()) - 1; i >= 0; i--) {
+ final ParsedActivity component = pkg.getActivities().get(i);
+ final Boolean enabled = componentsEnabledStates.get(component.getName());
+ if (enabled != null) {
+ ComponentMutateUtils.setEnabled(component, enabled);
+ }
+ }
+
+ for (int i = ArrayUtils.size(pkg.getReceivers()) - 1; i >= 0; i--) {
+ final ParsedActivity component = pkg.getReceivers().get(i);
+ final Boolean enabled = componentsEnabledStates.get(component.getName());
+ if (enabled != null) {
+ ComponentMutateUtils.setEnabled(component, enabled);
+ }
+ }
+
+ for (int i = ArrayUtils.size(pkg.getProviders()) - 1; i >= 0; i--) {
+ final ParsedProvider component = pkg.getProviders().get(i);
+ final Boolean enabled = componentsEnabledStates.get(component.getName());
+ if (enabled != null) {
+ ComponentMutateUtils.setEnabled(component, enabled);
+ }
+ }
+
+ for (int i = ArrayUtils.size(pkg.getServices()) - 1; i >= 0; i--) {
+ final ParsedService component = pkg.getServices().get(i);
+ final Boolean enabled = componentsEnabledStates.get(component.getName());
+ if (enabled != null) {
+ ComponentMutateUtils.setEnabled(component, enabled);
+ }
+ }
+ }
+
+ public static int getVendorPartitionVersion() {
+ final String version = SystemProperties.get("ro.vndk.version");
+ if (!version.isEmpty()) {
+ try {
+ return Integer.parseInt(version);
+ } catch (NumberFormatException ignore) {
+ if (ArrayUtils.contains(Build.VERSION.ACTIVE_CODENAMES, version)) {
+ return Build.VERSION_CODES.CUR_DEVELOPMENT;
+ }
+ }
+ }
+ return Build.VERSION_CODES.P;
+ }
+
+ /**
+ * Applies policy to the parsed package based upon the given policy flags.
+ * Ensures the package is in a good state.
+ * <p>
+ * Implementation detail: This method must NOT have any side effect. It would
+ * ideally be static, but, it requires locks to read system state.
+ */
+ public static void applyPolicy(ParsedPackage parsedPackage,
+ final @PackageManagerService.ScanFlags int scanFlags, AndroidPackage platformPkg,
+ boolean isUpdatedSystemApp) {
+ if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
+ parsedPackage.setSystem(true);
+ // TODO(b/135203078): Can this be done in PackageParser? Or just inferred when the flag
+ // is set during parse.
+ if (parsedPackage.isDirectBootAware()) {
+ parsedPackage.setAllComponentsDirectBootAware(true);
+ }
+ if (compressedFileExists(parsedPackage.getPath())) {
+ parsedPackage.setStub(true);
+ }
+ } else {
+ parsedPackage
+ // Non system apps cannot mark any broadcast as protected
+ .clearProtectedBroadcasts()
+ // non system apps can't be flagged as core
+ .setCoreApp(false)
+ // clear flags not applicable to regular apps
+ .setPersistent(false)
+ .setDefaultToDeviceProtectedStorage(false)
+ .setDirectBootAware(false)
+ // non system apps can't have permission priority
+ .capPermissionPriorities();
+ }
+ if ((scanFlags & SCAN_AS_PRIVILEGED) == 0) {
+ parsedPackage
+ .markNotActivitiesAsNotExportedIfSingleUser();
+ }
+
+ parsedPackage.setPrivileged((scanFlags & SCAN_AS_PRIVILEGED) != 0)
+ .setOem((scanFlags & SCAN_AS_OEM) != 0)
+ .setVendor((scanFlags & SCAN_AS_VENDOR) != 0)
+ .setProduct((scanFlags & SCAN_AS_PRODUCT) != 0)
+ .setSystemExt((scanFlags & SCAN_AS_SYSTEM_EXT) != 0)
+ .setOdm((scanFlags & SCAN_AS_ODM) != 0);
+
+ // Check if the package is signed with the same key as the platform package.
+ parsedPackage.setSignedWithPlatformKey(
+ (PLATFORM_PACKAGE_NAME.equals(parsedPackage.getPackageName())
+ || (platformPkg != null && compareSignatures(
+ platformPkg.getSigningDetails().getSignatures(),
+ parsedPackage.getSigningDetails().getSignatures()
+ ) == PackageManager.SIGNATURE_MATCH))
+ );
+
+ if (!parsedPackage.isSystem()) {
+ // Only system apps can use these features.
+ parsedPackage.clearOriginalPackages()
+ .clearAdoptPermissions();
+ }
+
+ PackageBackwardCompatibility.modifySharedLibraries(parsedPackage, isUpdatedSystemApp);
+ }
+
+ /**
+ * Applies the adjusted ABI calculated by
+ * {@link PackageAbiHelper#getAdjustedAbiForSharedUser(Set, AndroidPackage)} to all
+ * relevant packages and settings.
+ * @param sharedUserSetting The {@code SharedUserSetting} to adjust
+ * @param scannedPackage the package being scanned or null
+ * @param adjustedAbi the adjusted ABI calculated by {@link PackageAbiHelper}
+ * @return the list of code paths that belong to packages that had their ABIs adjusted.
+ */
+ public static List<String> applyAdjustedAbiToSharedUser(SharedUserSetting sharedUserSetting,
+ ParsedPackage scannedPackage, String adjustedAbi) {
+ if (scannedPackage != null) {
+ scannedPackage.setPrimaryCpuAbi(adjustedAbi);
+ }
+ List<String> changedAbiCodePath = null;
+ for (PackageSetting ps : sharedUserSetting.packages) {
+ if (scannedPackage == null
+ || !scannedPackage.getPackageName().equals(ps.getPackageName())) {
+ if (ps.getPrimaryCpuAbi() != null) {
+ continue;
+ }
+
+ ps.setPrimaryCpuAbi(adjustedAbi);
+ if (ps.getPkg() != null) {
+ if (!TextUtils.equals(adjustedAbi,
+ AndroidPackageUtils.getRawPrimaryCpuAbi(ps.getPkg()))) {
+ if (DEBUG_ABI_SELECTION) {
+ Slog.i(TAG,
+ "Adjusting ABI for " + ps.getPackageName() + " to "
+ + adjustedAbi + " (scannedPackage="
+ + (scannedPackage != null ? scannedPackage : "null")
+ + ")");
+ }
+ if (changedAbiCodePath == null) {
+ changedAbiCodePath = new ArrayList<>();
+ }
+ changedAbiCodePath.add(ps.getPathString());
+ }
+ }
+ }
+ }
+ return changedAbiCodePath;
+ }
+
+ public static void collectCertificatesLI(PackageSetting ps, ParsedPackage parsedPackage,
+ Settings.VersionInfo settingsVersionForPackage, boolean forceCollect,
+ boolean skipVerify, boolean isPreNMR1Upgrade)
+ throws PackageManagerException {
+ // When upgrading from pre-N MR1, verify the package time stamp using the package
+ // directory and not the APK file.
+ final long lastModifiedTime = isPreNMR1Upgrade
+ ? new File(parsedPackage.getPath()).lastModified()
+ : getLastModifiedTime(parsedPackage);
+ if (ps != null && !forceCollect
+ && ps.getPathString().equals(parsedPackage.getPath())
+ && ps.getLastModifiedTime() == lastModifiedTime
+ && !ReconcilePackageUtils.isCompatSignatureUpdateNeeded(settingsVersionForPackage)
+ && !ReconcilePackageUtils.isRecoverSignatureUpdateNeeded(
+ settingsVersionForPackage)) {
+ if (ps.getSigningDetails().getSignatures() != null
+ && ps.getSigningDetails().getSignatures().length != 0
+ && ps.getSigningDetails().getSignatureSchemeVersion()
+ != SigningDetails.SignatureSchemeVersion.UNKNOWN) {
+ // Optimization: reuse the existing cached signing data
+ // if the package appears to be unchanged.
+ parsedPackage.setSigningDetails(
+ new SigningDetails(ps.getSigningDetails()));
+ return;
+ }
+
+ Slog.w(TAG, "PackageSetting for " + ps.getPackageName()
+ + " is missing signatures. Collecting certs again to recover them.");
+ } else {
+ Slog.i(TAG, parsedPackage.getPath() + " changed; collecting certs"
+ + (forceCollect ? " (forced)" : ""));
+ }
+
+ try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
+ final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+ final ParseResult<SigningDetails> result = ParsingPackageUtils.getSigningDetails(
+ input, parsedPackage, skipVerify);
+ if (result.isError()) {
+ throw new PackageManagerException(
+ result.getErrorCode(), result.getErrorMessage(), result.getException());
+ }
+ parsedPackage.setSigningDetails(result.getResult());
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }
+
+ public static void setInstantAppForUser(PackageManagerServiceInjector injector,
+ PackageSetting pkgSetting, int userId, boolean instantApp, boolean fullApp) {
+ // no state specified; do nothing
+ if (!instantApp && !fullApp) {
+ return;
+ }
+ if (userId != UserHandle.USER_ALL) {
+ if (instantApp && !pkgSetting.getInstantApp(userId)) {
+ pkgSetting.setInstantApp(true /*instantApp*/, userId);
+ } else if (fullApp && pkgSetting.getInstantApp(userId)) {
+ pkgSetting.setInstantApp(false /*instantApp*/, userId);
+ }
+ } else {
+ for (int currentUserId : injector.getUserManagerInternal().getUserIds()) {
+ if (instantApp && !pkgSetting.getInstantApp(currentUserId)) {
+ pkgSetting.setInstantApp(true /*instantApp*/, currentUserId);
+ } else if (fullApp && pkgSetting.getInstantApp(currentUserId)) {
+ pkgSetting.setInstantApp(false /*instantApp*/, currentUserId);
+ }
+ }
+ }
+ }
+
+ /** Directory where installed application's 32-bit native libraries are copied. */
+ public static File getAppLib32InstallDir() {
+ return new File(Environment.getDataDirectory(), "app-lib");
+ }
+}
diff --git a/services/core/java/com/android/server/pm/VerificationParams.java b/services/core/java/com/android/server/pm/VerificationParams.java
index e1442dd..4334cbd 100644
--- a/services/core/java/com/android/server/pm/VerificationParams.java
+++ b/services/core/java/com/android/server/pm/VerificationParams.java
@@ -507,6 +507,13 @@
requiredVerifierPackage, verificationTimeout,
verifierUserId, false,
REASON_PACKAGE_VERIFIER, "package verifier");
+
+ if (streaming) {
+ // For streaming installations, count verification timeout from the broadcast.
+ startVerificationTimeoutCountdown(verificationId, streaming, response,
+ verificationTimeout);
+ }
+
mPm.mContext.sendOrderedBroadcastAsUser(verification, verifierUser,
android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
/* appOp= */ AppOpsManager.OP_NONE,
@@ -514,12 +521,12 @@
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- final Message msg = mPm.mHandler
- .obtainMessage(CHECK_PENDING_VERIFICATION);
- msg.arg1 = verificationId;
- msg.arg2 = streaming ? 1 : 0;
- msg.obj = response;
- mPm.mHandler.sendMessageDelayed(msg, verificationTimeout);
+ if (!streaming) {
+ // For NON-streaming installations, count verification timeout from
+ // the broadcast was processed by all receivers.
+ startVerificationTimeoutCountdown(verificationId, streaming, response,
+ verificationTimeout);
+ }
}
}, null, 0, null, null);
@@ -532,6 +539,15 @@
mWaitForVerificationToComplete = true;
}
+ private void startVerificationTimeoutCountdown(int verificationId, boolean streaming,
+ PackageVerificationResponse response, long verificationTimeout) {
+ final Message msg = mPm.mHandler.obtainMessage(CHECK_PENDING_VERIFICATION);
+ msg.arg1 = verificationId;
+ msg.arg2 = streaming ? 1 : 0;
+ msg.obj = response;
+ mPm.mHandler.sendMessageDelayed(msg, verificationTimeout);
+ }
+
/**
* Get the default verification agent response code.
*
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 1c93b99..e80a9b9 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2834,8 +2834,14 @@
mBaseDisplayDensity = baseDensity;
if (mMaxUiWidth > 0 && mBaseDisplayWidth > mMaxUiWidth) {
- mBaseDisplayHeight = (mMaxUiWidth * mBaseDisplayHeight) / mBaseDisplayWidth;
+ final float ratio = mMaxUiWidth / (float) mBaseDisplayWidth;
+ mBaseDisplayHeight = (int) (mBaseDisplayHeight * ratio);
mBaseDisplayWidth = mMaxUiWidth;
+ if (!mIsDensityForced) {
+ // Update the density proportionally so the size of the UI elements won't change
+ // from the user's perspective.
+ mBaseDisplayDensity = (int) (mBaseDisplayDensity * ratio);
+ }
if (DEBUG_DISPLAY) {
Slog.v(TAG_WM, "Applying config restraints:" + mBaseDisplayWidth + "x"
@@ -2892,6 +2898,13 @@
/** If the given width and height equal to initial size, the setting will be cleared. */
void setForcedSize(int width, int height) {
+ // Can't force size higher than the maximal allowed
+ if (mMaxUiWidth > 0 && width > mMaxUiWidth) {
+ final float ratio = mMaxUiWidth / (float) width;
+ height = (int) (height * ratio);
+ width = mMaxUiWidth;
+ }
+
mIsSizeForced = mInitialDisplayWidth != width || mInitialDisplayHeight != height;
if (mIsSizeForced) {
// Set some sort of reasonable bounds on the size of the display that we will try
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index 429edf1..2f4dd57 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -26,6 +26,10 @@
<xs:element name="displayConfiguration">
<xs:complexType>
<xs:sequence>
+ <xs:element type="densityMap" name="densityMap" minOccurs="0" maxOccurs="1">
+ <xs:annotation name="nullable"/>
+ <xs:annotation name="final"/>
+ </xs:element>
<xs:element type="nitsMap" name="screenBrightnessMap">
<xs:annotation name="nonnull"/>
<xs:annotation name="final"/>
@@ -181,5 +185,27 @@
</xs:sequence>
</xs:complexType>
+ <xs:complexType name="densityMap">
+ <xs:sequence>
+ <xs:element name="density" type="density" maxOccurs="unbounded" minOccurs="1">
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="density">
+ <xs:sequence>
+ <xs:element type="xs:nonNegativeInteger" name="width">
+ <xs:annotation name="nonnull"/>
+ <xs:annotation name="final"/>
+ </xs:element>
+ <xs:element type="xs:nonNegativeInteger" name="height">
+ <xs:annotation name="nonnull"/>
+ <xs:annotation name="final"/>
+ </xs:element>
+ <xs:element type="xs:nonNegativeInteger" name="density">
+ <xs:annotation name="nonnull"/>
+ <xs:annotation name="final"/>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
</xs:schema>
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index ad18602..5b2b87c 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -1,8 +1,24 @@
// Signature format: 2.0
package com.android.server.display.config {
+ public class Density {
+ ctor public Density();
+ method @NonNull public final java.math.BigInteger getDensity();
+ method @NonNull public final java.math.BigInteger getHeight();
+ method @NonNull public final java.math.BigInteger getWidth();
+ method public final void setDensity(@NonNull java.math.BigInteger);
+ method public final void setHeight(@NonNull java.math.BigInteger);
+ method public final void setWidth(@NonNull java.math.BigInteger);
+ }
+
+ public class DensityMap {
+ ctor public DensityMap();
+ method public java.util.List<com.android.server.display.config.Density> getDensity();
+ }
+
public class DisplayConfiguration {
ctor public DisplayConfiguration();
+ method @Nullable public final com.android.server.display.config.DensityMap getDensityMap();
method public com.android.server.display.config.HighBrightnessMode getHighBrightnessMode();
method public final com.android.server.display.config.SensorDetails getLightSensor();
method public final com.android.server.display.config.SensorDetails getProxSensor();
@@ -13,6 +29,7 @@
method public final java.math.BigDecimal getScreenBrightnessRampFastIncrease();
method public final java.math.BigDecimal getScreenBrightnessRampSlowDecrease();
method public final java.math.BigDecimal getScreenBrightnessRampSlowIncrease();
+ method public final void setDensityMap(@Nullable com.android.server.display.config.DensityMap);
method public void setHighBrightnessMode(com.android.server.display.config.HighBrightnessMode);
method public final void setLightSensor(com.android.server.display.config.SensorDetails);
method public final void setProxSensor(com.android.server.display.config.SensorDetails);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 45d9626..663e17b 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -151,6 +151,7 @@
import com.android.server.os.NativeTombstoneManagerService;
import com.android.server.os.SchedulingPolicyService;
import com.android.server.people.PeopleService;
+import com.android.server.pm.ApexManager;
import com.android.server.pm.CrossProfileAppsService;
import com.android.server.pm.DataLoaderManagerService;
import com.android.server.pm.DynamicCodeLoggingService;
@@ -220,6 +221,7 @@
import java.util.Date;
import java.util.LinkedList;
import java.util.Locale;
+import java.util.Map;
import java.util.Timer;
import java.util.TreeSet;
import java.util.concurrent.CountDownLatch;
@@ -918,6 +920,13 @@
startBootstrapServices(t);
startCoreServices(t);
startOtherServices(t);
+ // Apex services must be the last category of services to start. No other service must
+ // be starting after this point. This is to prevent unnessary stability issues when
+ // these apexes are updated outside of OTA; and to avoid breaking dependencies from
+ // system into apexes.
+ // TODO(satayev): lock mSystemServiceManager.startService to stop accepting new services
+ // after this step
+ startApexServices(t);
} catch (Throwable ex) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting system services", ex);
@@ -3051,6 +3060,27 @@
t.traceEnd(); // startOtherServices
}
+ /**
+ * Starts system services defined in apexes.
+ */
+ private void startApexServices(@NonNull TimingsTraceAndSlog t) {
+ t.traceBegin("startApexServices");
+ Map<String, String> services = ApexManager.getInstance().getApexSystemServices();
+ // TODO(satayev): filter out already started services
+ // TODO(satayev): introduce android:order for services coming the same apexes
+ for (String name : new TreeSet<>(services.keySet())) {
+ String jarPath = services.get(name);
+ t.traceBegin("starting " + name);
+ if (TextUtils.isEmpty(jarPath)) {
+ mSystemServiceManager.startService(name);
+ } else {
+ mSystemServiceManager.startServiceFromJar(name, jarPath);
+ }
+ t.traceEnd();
+ }
+ t.traceEnd(); // startApexServices
+ }
+
private boolean deviceHasConfigString(@NonNull Context context, @StringRes int resId) {
String serviceName = context.getString(resId);
return !TextUtils.isEmpty(serviceName);
diff --git a/services/selectiontoolbar/java/com/android/server/selectiontoolbar/DefaultSelectionToolbarRenderService.java b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/DefaultSelectionToolbarRenderService.java
new file mode 100644
index 0000000..c26965d
--- /dev/null
+++ b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/DefaultSelectionToolbarRenderService.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.selectiontoolbar;
+
+import android.service.selectiontoolbar.SelectionToolbarRenderService;
+import android.util.Log;
+import android.view.selectiontoolbar.ShowInfo;
+
+/**
+ * The default implementation of {@link SelectionToolbarRenderService}.
+ */
+public final class DefaultSelectionToolbarRenderService extends SelectionToolbarRenderService {
+
+ private static final String TAG = "DefaultSelectionToolbarRenderService";
+
+ @Override
+ public void onShow(ShowInfo showInfo,
+ SelectionToolbarRenderService.RemoteCallbackWrapper callbackWrapper) {
+ // TODO: Add implementation
+ Log.w(TAG, "onShow()");
+ }
+
+ @Override
+ public void onHide(long widgetToken) {
+ // TODO: Add implementation
+ Log.w(TAG, "onHide()");
+ }
+
+ @Override
+ public void onDismiss(long widgetToken) {
+ // TODO: Add implementation
+ Log.w(TAG, "onDismiss()");
+ }
+}
diff --git a/services/selectiontoolbar/java/com/android/server/selectiontoolbar/RemoteSelectionToolbarRenderService.java b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/RemoteSelectionToolbarRenderService.java
new file mode 100644
index 0000000..ced24e0
--- /dev/null
+++ b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/RemoteSelectionToolbarRenderService.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.selectiontoolbar;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.service.selectiontoolbar.ISelectionToolbarRenderService;
+import android.service.selectiontoolbar.SelectionToolbarRenderService;
+import android.view.selectiontoolbar.ISelectionToolbarCallback;
+import android.view.selectiontoolbar.ShowInfo;
+
+import com.android.internal.infra.AbstractRemoteService;
+import com.android.internal.infra.ServiceConnector;
+
+final class RemoteSelectionToolbarRenderService extends
+ ServiceConnector.Impl<ISelectionToolbarRenderService> {
+ private static final String TAG = "RemoteSelectionToolbarRenderService";
+
+ private static final long TIMEOUT_IDLE_UNBIND_MS =
+ AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS;
+
+ private final ComponentName mComponentName;
+
+
+ RemoteSelectionToolbarRenderService(Context context, ComponentName serviceName, int userId) {
+ super(context, new Intent(SelectionToolbarRenderService.SERVICE_INTERFACE).setComponent(
+ serviceName), 0, userId, ISelectionToolbarRenderService.Stub::asInterface);
+ mComponentName = serviceName;
+ // Bind right away.
+ connect();
+ }
+
+ @Override // from AbstractRemoteService
+ protected long getAutoDisconnectTimeoutMs() {
+ return TIMEOUT_IDLE_UNBIND_MS;
+ }
+
+ public ComponentName getComponentName() {
+ return mComponentName;
+ }
+
+ public void onShow(ShowInfo showInfo, ISelectionToolbarCallback callback) {
+ run((s) -> s.onShow(showInfo, callback));
+ }
+
+ public void onHide(long widgetToken) {
+ run((s) -> s.onHide(widgetToken));
+ }
+
+ public void onDismiss(long widgetToken) {
+ run((s) -> s.onDismiss(widgetToken));
+ }
+}
diff --git a/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerServiceImpl.java b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerServiceImpl.java
index 94bf712..235f547 100644
--- a/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerServiceImpl.java
+++ b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerServiceImpl.java
@@ -17,9 +17,18 @@
package com.android.server.selectiontoolbar;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.AppGlobals;
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.os.RemoteException;
+import android.util.Slog;
import android.view.selectiontoolbar.ISelectionToolbarCallback;
import android.view.selectiontoolbar.ShowInfo;
+import com.android.internal.annotations.GuardedBy;
import com.android.server.infra.AbstractPerUserSystemService;
final class SelectionToolbarManagerServiceImpl extends
@@ -28,20 +37,92 @@
private static final String TAG = "SelectionToolbarManagerServiceImpl";
+ @GuardedBy("mLock")
+ @Nullable
+ private RemoteSelectionToolbarRenderService mRemoteService;
+
protected SelectionToolbarManagerServiceImpl(@NonNull SelectionToolbarManagerService master,
@NonNull Object lock, int userId) {
super(master, lock, userId);
+ updateRemoteServiceLocked();
}
+ @GuardedBy("mLock")
+ @Override // from PerUserSystemService
+ protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
+ throws PackageManager.NameNotFoundException {
+ return getServiceInfoOrThrow(serviceComponent, mUserId);
+ }
+
+ @GuardedBy("mLock")
+ @Override // from PerUserSystemService
+ protected boolean updateLocked(boolean disabled) {
+ final boolean enabledChanged = super.updateLocked(disabled);
+ updateRemoteServiceLocked();
+ return enabledChanged;
+ }
+
+ /**
+ * Updates the reference to the remote service.
+ */
+ @GuardedBy("mLock")
+ private void updateRemoteServiceLocked() {
+ if (mRemoteService != null) {
+ Slog.d(TAG, "updateRemoteService(): destroying old remote service");
+ mRemoteService.unbind();
+ mRemoteService = null;
+ }
+ }
+
+ @GuardedBy("mLock")
void showToolbar(ShowInfo showInfo, ISelectionToolbarCallback callback) {
- // TODO: add implementation to bind service
+ final RemoteSelectionToolbarRenderService remoteService = ensureRemoteServiceLocked();
+ if (remoteService != null) {
+ remoteService.onShow(showInfo, callback);
+ }
}
+ @GuardedBy("mLock")
void hideToolbar(long widgetToken) {
- // TODO: add implementation to bind service
+ final RemoteSelectionToolbarRenderService remoteService = ensureRemoteServiceLocked();
+ if (remoteService != null) {
+ remoteService.onHide(widgetToken);
+ }
}
+ @GuardedBy("mLock")
void dismissToolbar(long widgetToken) {
- // TODO: add implementation to bind service
+ final RemoteSelectionToolbarRenderService remoteService = ensureRemoteServiceLocked();
+ if (remoteService != null) {
+ remoteService.onDismiss(widgetToken);
+ }
+ }
+
+ @GuardedBy("mLock")
+ @Nullable
+ private RemoteSelectionToolbarRenderService ensureRemoteServiceLocked() {
+ if (mRemoteService == null) {
+ final String serviceName = getComponentNameLocked();
+ final ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
+ mRemoteService = new RemoteSelectionToolbarRenderService(getContext(), serviceComponent,
+ mUserId);
+ }
+ return mRemoteService;
+ }
+
+ private static ServiceInfo getServiceInfoOrThrow(ComponentName comp, @UserIdInt int userId)
+ throws PackageManager.NameNotFoundException {
+ int flags = PackageManager.GET_META_DATA;
+
+ ServiceInfo si = null;
+ try {
+ si = AppGlobals.getPackageManager().getServiceInfo(comp, flags, userId);
+ } catch (RemoteException e) {
+ }
+ if (si == null) {
+ throw new PackageManager.NameNotFoundException("Could not get serviceInfo for "
+ + comp.flattenToShortString());
+ }
+ return si;
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DensityMapTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DensityMapTest.java
new file mode 100644
index 0000000..3f69f1b
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DensityMapTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import static com.android.server.display.DensityMap.Entry;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DensityMapTest {
+
+ @Test
+ public void testConstructor_withBadConfig_throwsException() {
+ assertThrows(IllegalStateException.class, () ->
+ DensityMap.createByOwning(new Entry[]{
+ new Entry(1080, 1920, 320),
+ new Entry(1080, 1920, 320)})
+ );
+
+ assertThrows(IllegalStateException.class, () ->
+ DensityMap.createByOwning(new Entry[]{
+ new Entry(1080, 1920, 320),
+ new Entry(1920, 1080, 120)})
+ );
+
+ assertThrows(IllegalStateException.class, () ->
+ DensityMap.createByOwning(new Entry[]{
+ new Entry(1080, 1920, 320),
+ new Entry(2160, 3840, 120)})
+ );
+
+ assertThrows(IllegalStateException.class, () ->
+ DensityMap.createByOwning(new Entry[]{
+ new Entry(1080, 1920, 320),
+ new Entry(3840, 2160, 120)})
+ );
+
+ // Two entries with the same diagonal
+ assertThrows(IllegalStateException.class, () ->
+ DensityMap.createByOwning(new Entry[]{
+ new Entry(500, 500, 123),
+ new Entry(100, 700, 456)})
+ );
+ }
+
+ @Test
+ public void testGetDensityForResolution_withResolutionMatch_returnsDensityFromConfig() {
+ DensityMap densityMap = DensityMap.createByOwning(new Entry[]{
+ new Entry(720, 1280, 213),
+ new Entry(1080, 1920, 320),
+ new Entry(2160, 3840, 640)});
+
+ assertEquals(213, densityMap.getDensityForResolution(720, 1280));
+ assertEquals(213, densityMap.getDensityForResolution(1280, 720));
+
+ assertEquals(320, densityMap.getDensityForResolution(1080, 1920));
+ assertEquals(320, densityMap.getDensityForResolution(1920, 1080));
+
+ assertEquals(640, densityMap.getDensityForResolution(2160, 3840));
+ assertEquals(640, densityMap.getDensityForResolution(3840, 2160));
+ }
+
+ @Test
+ public void testGetDensityForResolution_withDiagonalMatch_returnsDensityFromConfig() {
+ DensityMap densityMap = DensityMap.createByOwning(
+ new Entry[]{ new Entry(500, 500, 123)});
+
+ // 500x500 has the same diagonal as 100x700
+ assertEquals(123, densityMap.getDensityForResolution(100, 700));
+ }
+
+ @Test
+ public void testGetDensityForResolution_withOneEntry_withNoMatch_returnsExtrapolatedDensity() {
+ DensityMap densityMap = DensityMap.createByOwning(
+ new Entry[]{ new Entry(1080, 1920, 320)});
+
+ assertEquals(320, densityMap.getDensityForResolution(1081, 1920));
+ assertEquals(320, densityMap.getDensityForResolution(1080, 1921));
+
+ assertEquals(640, densityMap.getDensityForResolution(2160, 3840));
+ assertEquals(640, densityMap.getDensityForResolution(3840, 2160));
+
+ assertEquals(213, densityMap.getDensityForResolution(720, 1280));
+ assertEquals(213, densityMap.getDensityForResolution(1280, 720));
+ }
+
+ @Test
+ public void testGetDensityForResolution_withTwoEntries_withNoMatch_returnExtrapolatedDensity() {
+ DensityMap densityMap = DensityMap.createByOwning(new Entry[]{
+ new Entry(1080, 1920, 320),
+ new Entry(2160, 3840, 320)});
+
+ // Resolution is smaller than all entries
+ assertEquals(213, densityMap.getDensityForResolution(720, 1280));
+ assertEquals(213, densityMap.getDensityForResolution(1280, 720));
+
+ // Resolution is bigger than all entries
+ assertEquals(320 * 2, densityMap.getDensityForResolution(2160 * 2, 3840 * 2));
+ assertEquals(320 * 2, densityMap.getDensityForResolution(3840 * 2, 2160 * 2));
+ }
+
+ @Test
+ public void testGetDensityForResolution_withNoMatch_returnsInterpolatedDensity() {
+ {
+ DensityMap densityMap = DensityMap.createByOwning(new Entry[]{
+ new Entry(1080, 1920, 320),
+ new Entry(2160, 3840, 320)});
+
+ assertEquals(320, densityMap.getDensityForResolution(2000, 2000));
+ }
+
+ {
+ DensityMap densityMap = DensityMap.createByOwning(new Entry[]{
+ new Entry(720, 1280, 213),
+ new Entry(2160, 3840, 640)});
+
+ assertEquals(320, densityMap.getDensityForResolution(1080, 1920));
+ assertEquals(320, densityMap.getDensityForResolution(1920, 1080));
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
index 71d5b77..28f24f2 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -470,9 +470,7 @@
.addUsesPermission(
new ParsedUsesPermissionImpl(Manifest.permission.FACTORY_TEST, 0));
- final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(
- mMockPackageManager, mMockInjector);
- final ScanResult scanResult = scanPackageHelper.scanPackageOnlyLI(
+ final ScanResult scanResult = ScanPackageUtils.scanPackageOnlyLI(
createBasicScanRequestBuilder(basicPackage).build(),
mMockInjector,
true /*isUnderFactoryTest*/,
@@ -520,9 +518,7 @@
private ScanResult executeScan(
ScanRequest scanRequest) throws PackageManagerException {
- final ScanPackageHelper scanPackageHelper = new ScanPackageHelper(
- mMockPackageManager, mMockInjector);
- ScanResult result = scanPackageHelper.scanPackageOnlyLI(
+ ScanResult result = ScanPackageUtils.scanPackageOnlyLI(
scanRequest,
mMockInjector,
false /*isUnderFactoryTest*/,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index e8a27990..d49cf67 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -5112,6 +5112,11 @@
assertTrue(expected.containsKey(uid));
assertThat(expected.get(uid)).isEqualTo(
builder.getInt(PackageNotificationPreferences.IMPORTANCE_FIELD_NUMBER));
+
+ // pre-migration, the userSet field will always default to false
+ boolean userSet = builder.getBoolean(
+ PackageNotificationPreferences.USER_SET_IMPORTANCE_FIELD_NUMBER);
+ assertFalse(userSet);
}
}
}
@@ -5123,8 +5128,8 @@
// build a collection of app permissions that should be passed in but ignored
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
appPermissions.put(new Pair(1, "first"), new Pair(true, false)); // not in local prefs
- appPermissions.put(new Pair(3, "third"), new Pair(false, false)); // not in local prefs
- appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, false)); // in local prefs
+ appPermissions.put(new Pair(3, "third"), new Pair(false, true)); // not in local prefs
+ appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, true)); // in local prefs
// package preferences: PKG_O not banned based on local importance, and PKG_P is
mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
@@ -5132,11 +5137,11 @@
// expected output. format: uid -> importance, as only uid (and not package name)
// is in PackageNotificationPreferences
- ArrayMap<Integer, Integer> expected = new ArrayMap<>();
- expected.put(1, IMPORTANCE_DEFAULT);
- expected.put(3, IMPORTANCE_NONE);
- expected.put(UID_O, IMPORTANCE_NONE); // banned by permissions
- expected.put(UID_P, IMPORTANCE_NONE); // defaults to none
+ ArrayMap<Integer, Pair<Integer, Boolean>> expected = new ArrayMap<>();
+ expected.put(1, new Pair(IMPORTANCE_DEFAULT, false));
+ expected.put(3, new Pair(IMPORTANCE_NONE, true));
+ expected.put(UID_O, new Pair(IMPORTANCE_NONE, true)); // banned by permissions
+ expected.put(UID_P, new Pair(IMPORTANCE_NONE, false)); // defaults to none, false
ArrayList<StatsEvent> events = new ArrayList<>();
mHelper.pullPackagePreferencesStats(events, appPermissions);
@@ -5144,11 +5149,14 @@
for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) {
if (builder.getAtomId() == PACKAGE_NOTIFICATION_PREFERENCES) {
int uid = builder.getInt(PackageNotificationPreferences.UID_FIELD_NUMBER);
+ boolean userSet = builder.getBoolean(
+ PackageNotificationPreferences.USER_SET_IMPORTANCE_FIELD_NUMBER);
// if it's one of the expected ids, then make sure the importance matches
assertTrue(expected.containsKey(uid));
- assertThat(expected.get(uid)).isEqualTo(
+ assertThat(expected.get(uid).first).isEqualTo(
builder.getInt(PackageNotificationPreferences.IMPORTANCE_FIELD_NUMBER));
+ assertThat(expected.get(uid).second).isEqualTo(userSet);
}
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index deba835..dc0e028 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -681,7 +681,7 @@
final int maxWidth = 300;
final int resultingHeight = (maxWidth * baseHeight) / baseWidth;
- final int resultingDensity = baseDensity;
+ final int resultingDensity = (baseDensity * maxWidth) / baseWidth;
displayContent.setMaxUiWidth(maxWidth);
verifySizes(displayContent, maxWidth, resultingHeight, resultingDensity);
@@ -756,6 +756,33 @@
}
@Test
+ public void testSetForcedDensity() {
+ final DisplayContent displayContent = createDisplayNoUpdateDisplayInfo();
+ final int baseWidth = 1280;
+ final int baseHeight = 720;
+ final int baseDensity = 320;
+
+ displayContent.mInitialDisplayWidth = baseWidth;
+ displayContent.mInitialDisplayHeight = baseHeight;
+ displayContent.mInitialDisplayDensity = baseDensity;
+ displayContent.updateBaseDisplayMetrics(baseWidth, baseHeight, baseDensity);
+
+ final int forcedDensity = 600;
+
+ // Verify that forcing the density is honored and the size doesn't change.
+ displayContent.setForcedDensity(forcedDensity, 0 /* userId */);
+ verifySizes(displayContent, baseWidth, baseHeight, forcedDensity);
+
+ // Verify that forcing the density is idempotent.
+ displayContent.setForcedDensity(forcedDensity, 0 /* userId */);
+ verifySizes(displayContent, baseWidth, baseHeight, forcedDensity);
+
+ // Verify that forcing resolution won't affect the already forced density.
+ displayContent.setForcedSize(1800, 1200);
+ verifySizes(displayContent, 1800, 1200, forcedDensity);
+ }
+
+ @Test
public void testDisplayCutout_rot0() {
final DisplayContent dc = createNewDisplay();
dc.mInitialDisplayWidth = 200;
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 4bfb2d8..53261cb 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4466,6 +4466,29 @@
"subscription_group_uuid_string";
/**
+ * Controls the cellular usage setting.
+ *
+ * The usage setting indicates whether a device will remain attached to a network based on
+ * the primary use case for the service. A device will detach and search for a more-preferred
+ * network if the primary use case (voice or data) is not satisfied. Depending on the type
+ * of device, it may operate in a voice or data-centric mode by default.
+ *
+ * <p>Sets the usage setting in accordance with 3gpp 24.301 sec 4.3 and 3gpp 24.501 sec 4.3.
+ * Also refer to "UE's usage setting" as defined in 3gpp 24.301 section 3.1 and 3gpp 23.221
+ * Annex A.
+ *
+ * Either omit this key or pass a value of
+ * {@link SubscriptionManager#USAGE_SETTING_UNKNOWN unknown} to preserve the current setting.
+ *
+ * {@link SubscriptionManager#USAGE_SETTING_DEFAULT default},
+ * {@link SubscriptionManager#USAGE_SETTING_VOICE_CENTRIC voice-centric},
+ * or {@link SubscriptionManager#USAGE_SETTING_DATA_CENTRIC data-centric}.
+ * {@see SubscriptionInfo#getUsageSetting}
+ */
+ public static final String KEY_CELLULAR_USAGE_SETTING_INT =
+ "cellular_usage_setting_int";
+
+ /**
* Data switch validation minimal gap time, in milliseconds.
*
* Which means, if the same subscription on the same network (based on MCC+MNC+TAC+subId)
@@ -6087,6 +6110,8 @@
sDefaults.putStringArray(KEY_IWLAN_HANDOVER_POLICY_STRING_ARRAY, new String[]{
"source=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, "
+ "target=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, type=allowed"});
+ sDefaults.putInt(KEY_CELLULAR_USAGE_SETTING_INT,
+ SubscriptionManager.USAGE_SETTING_UNKNOWN);
}
/**
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index d11ad91..c36eb2f 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -37,6 +37,7 @@
import android.os.Parcel;
import android.os.ParcelUuid;
import android.os.Parcelable;
+import android.telephony.SubscriptionManager.UsageSetting;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -227,6 +228,11 @@
private final int mPortIndex;
/**
+ * Subscription's preferred usage setting.
+ */
+ private @UsageSetting int mUsageSetting = SubscriptionManager.USAGE_SETTING_UNKNOWN;
+
+ /**
* Public copy constructor.
* @hide
*/
@@ -284,6 +290,7 @@
cardId, isOpportunistic, groupUUID, isGroupDisabled, carrierId, profileClass,
subType, groupOwner, carrierConfigAccessRules, areUiccApplicationsEnabled, 0);
}
+
/**
* @hide
*/
@@ -295,6 +302,24 @@
int carrierId, int profileClass, int subType, @Nullable String groupOwner,
@Nullable UiccAccessRule[] carrierConfigAccessRules,
boolean areUiccApplicationsEnabled, int portIndex) {
+ this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
+ roaming, icon, mcc, mnc, countryIso, isEmbedded, nativeAccessRules, cardString,
+ cardId, isOpportunistic, groupUUID, isGroupDisabled, carrierId, profileClass,
+ subType, groupOwner, carrierConfigAccessRules, areUiccApplicationsEnabled,
+ portIndex, SubscriptionManager.USAGE_SETTING_DEFAULT);
+ }
+
+ /**
+ * @hide
+ */
+ public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
+ CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
+ Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
+ @Nullable UiccAccessRule[] nativeAccessRules, String cardString, int cardId,
+ boolean isOpportunistic, @Nullable String groupUUID, boolean isGroupDisabled,
+ int carrierId, int profileClass, int subType, @Nullable String groupOwner,
+ @Nullable UiccAccessRule[] carrierConfigAccessRules,
+ boolean areUiccApplicationsEnabled, int portIndex, @UsageSetting int usageSetting) {
this.mId = id;
this.mIccId = iccId;
this.mSimSlotIndex = simSlotIndex;
@@ -322,6 +347,7 @@
this.mCarrierConfigAccessRules = carrierConfigAccessRules;
this.mAreUiccApplicationsEnabled = areUiccApplicationsEnabled;
this.mPortIndex = portIndex;
+ this.mUsageSetting = usageSetting;
}
/**
* @return the subscription ID.
@@ -796,7 +822,18 @@
return mAreUiccApplicationsEnabled;
}
- public static final @android.annotation.NonNull Parcelable.Creator<SubscriptionInfo> CREATOR = new Parcelable.Creator<SubscriptionInfo>() {
+ /**
+ * Get the usage setting for this subscription.
+ *
+ * @return the usage setting used for this subscription.
+ */
+ public @UsageSetting int getUsageSetting() {
+ return mUsageSetting;
+ }
+
+ public static final @android.annotation.NonNull
+ Parcelable.Creator<SubscriptionInfo> CREATOR =
+ new Parcelable.Creator<SubscriptionInfo>() {
@Override
public SubscriptionInfo createFromParcel(Parcel source) {
int id = source.readInt();
@@ -828,12 +865,14 @@
UiccAccessRule[] carrierConfigAccessRules = source.createTypedArray(
UiccAccessRule.CREATOR);
boolean areUiccApplicationsEnabled = source.readBoolean();
+ int usageSetting = source.readInt();
SubscriptionInfo info = new SubscriptionInfo(id, iccId, simSlotIndex, displayName,
carrierName, nameSource, iconTint, number, dataRoaming, /* icon= */ null,
mcc, mnc, countryIso, isEmbedded, nativeAccessRules, cardString, cardId,
isOpportunistic, groupUUID, isGroupDisabled, carrierid, profileClass, subType,
- groupOwner, carrierConfigAccessRules, areUiccApplicationsEnabled, portId);
+ groupOwner, carrierConfigAccessRules, areUiccApplicationsEnabled,
+ portId, usageSetting);
info.setAssociatedPlmns(ehplmns, hplmns);
return info;
}
@@ -875,6 +914,7 @@
dest.writeString(mGroupOwner);
dest.writeTypedArray(mCarrierConfigAccessRules, flags);
dest.writeBoolean(mAreUiccApplicationsEnabled);
+ dest.writeInt(mUsageSetting);
}
@Override
@@ -919,7 +959,8 @@
+ " subscriptionType=" + mSubscriptionType
+ " groupOwner=" + mGroupOwner
+ " carrierConfigAccessRules=" + Arrays.toString(mCarrierConfigAccessRules)
- + " areUiccApplicationsEnabled=" + mAreUiccApplicationsEnabled + "}";
+ + " areUiccApplicationsEnabled=" + mAreUiccApplicationsEnabled
+ + " usageSetting=" + mUsageSetting + "}";
}
@Override
@@ -927,7 +968,8 @@
return Objects.hash(mId, mSimSlotIndex, mNameSource, mIconTint, mDataRoaming, mIsEmbedded,
mIsOpportunistic, mGroupUUID, mIccId, mNumber, mMcc, mMnc, mCountryIso, mCardString,
mCardId, mDisplayName, mCarrierName, mNativeAccessRules, mIsGroupDisabled,
- mCarrierId, mProfileClass, mGroupOwner, mAreUiccApplicationsEnabled, mPortIndex);
+ mCarrierId, mProfileClass, mGroupOwner, mAreUiccApplicationsEnabled, mPortIndex,
+ mUsageSetting);
}
@Override
@@ -967,6 +1009,7 @@
&& Arrays.equals(mNativeAccessRules, toCompare.mNativeAccessRules)
&& mProfileClass == toCompare.mProfileClass
&& Arrays.equals(mEhplmns, toCompare.mEhplmns)
- && Arrays.equals(mHplmns, toCompare.mHplmns);
+ && Arrays.equals(mHplmns, toCompare.mHplmns)
+ && mUsageSetting == toCompare.mUsageSetting;
}
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 821b74a..4e6a7a3 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1037,12 +1037,75 @@
public static final String UICC_APPLICATIONS_ENABLED = SimInfo.COLUMN_UICC_APPLICATIONS_ENABLED;
/**
- * Indicate which network type is allowed. By default it's enabled.
+ * Indicate which network type is allowed.
* @hide
*/
public static final String ALLOWED_NETWORK_TYPES =
SimInfo.COLUMN_ALLOWED_NETWORK_TYPES_FOR_REASONS;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"USAGE_SETTING_"},
+ value = {
+ USAGE_SETTING_UNKNOWN,
+ USAGE_SETTING_DEFAULT,
+ USAGE_SETTING_VOICE_CENTRIC,
+ USAGE_SETTING_DATA_CENTRIC})
+ public @interface UsageSetting {}
+
+ /**
+ * The usage setting is unknown.
+ *
+ * This will be the usage setting returned on devices that do not support querying the
+ * or setting the usage setting.
+ *
+ * It may also be provided by a carrier that wishes to provide a value to avoid making any
+ * settings changes.
+ */
+ public static final int USAGE_SETTING_UNKNOWN = -1;
+
+ /**
+ * Subscription uses the default setting.
+ *
+ * The value is based upon device capability and the other properties of the subscription.
+ *
+ * Most subscriptions will default to voice-centric when in a phone.
+ *
+ * An opportunistic subscription will default to data-centric.
+ *
+ * {@see SubscriptionInfo#isOpportunistic}
+ */
+ public static final int USAGE_SETTING_DEFAULT = 0;
+
+ /**
+ * This subscription is forced to voice-centric mode
+ *
+ * <p>Refer to voice-centric mode in 3gpp 24.301 sec 4.3 and 3gpp 24.501 sec 4.3.
+ * Also refer to "UE's usage setting" as defined in 3gpp 24.301 section 3.1 and 3gpp 23.221
+ * Annex A.
+ */
+ public static final int USAGE_SETTING_VOICE_CENTRIC = 1;
+
+ /**
+ * This subscription is forced to data-centric mode
+ *
+ * <p>Refer to data-centric mode in 3gpp 24.301 sec 4.3 and 3gpp 24.501 sec 4.3.
+ * Also refer to "UE's usage setting" as defined in 3gpp 24.301 section 3.1 and 3gpp 23.221
+ * Annex A.
+ */
+ public static final int USAGE_SETTING_DATA_CENTRIC = 2;
+
+ /**
+ * Indicate the preferred usage setting for the subscription.
+ *
+ * 0 - Default - If the value has not been explicitly set, it will be "default"
+ * 1 - Voice-centric
+ * 2 - Data-centric
+ *
+ * @hide
+ */
+ public static final String USAGE_SETTING = SimInfo.COLUMN_USAGE_SETTING;
+
/**
* Broadcast Action: The user has changed one of the default subs related to
* data, phone calls, or sms</p>
@@ -3950,4 +4013,33 @@
throw ex.rethrowAsRuntimeException();
}
}
+
+ /**
+ * Set the preferred usage setting.
+ *
+ * The cellular usage setting is a switch which controls the mode of operation for the cellular
+ * radio to either require or not require voice service. It is not managed via Android’s
+ * Settings.
+ *
+ * @param subscriptionId the subId of the subscription.
+ * @param usageSetting the requested usage setting.
+ *
+ * @throws IllegalStateException if a specific mode or setting the mode is not supported on a
+ * particular device.
+ *
+ * <p>Requires {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+ * or that the calling app has CarrierPrivileges for the given subscription.
+ *
+ * Note: This method will not allow the setting of USAGE_SETTING_UNKNOWN.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ void setUsageSetting(int subscriptionId, @UsageSetting int usageSetting) {
+ if (VDBG) logd("[setUsageSetting]+ setting:" + usageSetting + " subId:" + subscriptionId);
+ setSubscriptionPropertyHelper(subscriptionId, "setUsageSetting",
+ (iSub)-> iSub.setUsageSetting(
+ usageSetting, subscriptionId, mContext.getOpPackageName()));
+ }
}
+
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index c78e42f..2d16f9e 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -16448,4 +16448,76 @@
}
return null;
}
+
+ /**
+ * Callback to listen for when the set of packages with carrier privileges for a SIM changes.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface CarrierPrivilegesListener {
+ /**
+ * Called when the set of packages with carrier privileges has changed.
+ *
+ * <p>Of note, this callback will <b>not</b> be fired if a carrier triggers a SIM profile
+ * switch and the same set of packages remains privileged after the switch.
+ *
+ * <p>At registration, the callback will receive the current set of privileged packages.
+ *
+ * @param privilegedPackageNames The updated set of package names that have carrier
+ * privileges
+ * @param privilegedUids The updated set of UIDs that have carrier privileges
+ */
+ void onCarrierPrivilegesChanged(
+ @NonNull List<String> privilegedPackageNames, @NonNull int[] privilegedUids);
+ }
+
+ /**
+ * Registers a {@link CarrierPrivilegesListener} on the given {@code logicalSlotIndex} to
+ * receive callbacks when the set of packages with carrier privileges changes. The callback will
+ * immediately be called with the latest state.
+ *
+ * @param logicalSlotIndex The SIM slot to listen on
+ * @param executor The executor where {@code listener} will be invoked
+ * @param listener The callback to register
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void addCarrierPrivilegesListener(
+ int logicalSlotIndex,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull CarrierPrivilegesListener listener) {
+ if (mContext == null) {
+ throw new IllegalStateException("Telephony service is null");
+ } else if (executor == null || listener == null) {
+ throw new IllegalArgumentException(
+ "CarrierPrivilegesListener and executor must be non-null");
+ }
+ mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class);
+ if (mTelephonyRegistryMgr == null) {
+ throw new IllegalStateException("Telephony registry service is null");
+ }
+ mTelephonyRegistryMgr.addCarrierPrivilegesListener(logicalSlotIndex, executor, listener);
+ }
+
+ /**
+ * Unregisters an existing {@link CarrierPrivilegesListener}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public void removeCarrierPrivilegesListener(@NonNull CarrierPrivilegesListener listener) {
+ if (mContext == null) {
+ throw new IllegalStateException("Telephony service is null");
+ } else if (listener == null) {
+ throw new IllegalArgumentException("CarrierPrivilegesListener must be non-null");
+ }
+ mTelephonyRegistryMgr = mContext.getSystemService(TelephonyRegistryManager.class);
+ if (mTelephonyRegistryMgr == null) {
+ throw new IllegalStateException("Telephony registry service is null");
+ }
+ mTelephonyRegistryMgr.removeCarrierPrivilegesListener(listener);
+ }
}
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index 2f03475..892eb29 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -402,6 +402,21 @@
}
/**
+ * Notify the system that a given DataProfile was unthrottled.
+ *
+ * @param dataProfile DataProfile associated with an APN returned from the modem
+ */
+ public final void notifyDataProfileUnthrottled(@NonNull DataProfile dataProfile) {
+ synchronized (mApnUnthrottledCallbacks) {
+ for (IDataServiceCallback callback : mApnUnthrottledCallbacks) {
+ mHandler.obtainMessage(DATA_SERVICE_INDICATION_APN_UNTHROTTLED,
+ mSlotIndex, 0, new ApnUnthrottledIndication(dataProfile,
+ callback)).sendToTarget();
+ }
+ }
+ }
+
+ /**
* Called when the instance of data service is destroyed (e.g. got unbind or binder died)
* or when the data service provider is removed. The extended class should implement this
* method to perform cleanup works.
@@ -496,13 +511,20 @@
}
private static final class ApnUnthrottledIndication {
+ public final DataProfile dataProfile;
public final String apn;
public final IDataServiceCallback callback;
ApnUnthrottledIndication(String apn,
IDataServiceCallback callback) {
+ this.dataProfile = null;
this.apn = apn;
this.callback = callback;
}
+ ApnUnthrottledIndication(DataProfile dataProfile, IDataServiceCallback callback) {
+ this.dataProfile = dataProfile;
+ this.apn = null;
+ this.callback = callback;
+ }
}
private class DataServiceHandler extends Handler {
@@ -636,8 +658,13 @@
ApnUnthrottledIndication apnUnthrottledIndication =
(ApnUnthrottledIndication) message.obj;
try {
- apnUnthrottledIndication.callback
- .onApnUnthrottled(apnUnthrottledIndication.apn);
+ if (apnUnthrottledIndication.dataProfile != null) {
+ apnUnthrottledIndication.callback
+ .onDataProfileUnthrottled(apnUnthrottledIndication.dataProfile);
+ } else {
+ apnUnthrottledIndication.callback
+ .onApnUnthrottled(apnUnthrottledIndication.apn);
+ }
} catch (RemoteException e) {
loge("Failed to call onApnUnthrottled. " + e);
}
diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java
index d082715..051d6c5 100644
--- a/telephony/java/android/telephony/data/DataServiceCallback.java
+++ b/telephony/java/android/telephony/data/DataServiceCallback.java
@@ -260,8 +260,8 @@
/**
* Unthrottles the APN on the current transport. There is no matching "APN throttle" method.
- * Instead, the APN is throttled for the time specified in
- * {@link DataCallResponse#getRetryDurationMillis}.
+ * Instead, the APN is throttled when {@link IDataService#setupDataCall} fails within
+ * the time specified by {@link DataCallResponse#getRetryDurationMillis}.
* <p/>
* see: {@link DataCallResponse#getRetryDurationMillis}
*
@@ -279,4 +279,27 @@
Rlog.e(TAG, "onApnUnthrottled: callback is null!");
}
}
+
+ /**
+ * Unthrottles the DataProfile on the current transport.
+ * There is no matching "DataProfile throttle" method.
+ * Instead, the DataProfile is throttled when {@link IDataService#setupDataCall} fails within
+ * the time specified by {@link DataCallResponse#getRetryDurationMillis}.
+ * <p/>
+ * see: {@link DataCallResponse#getRetryDurationMillis}
+ *
+ * @param dataProfile DataProfile containing the APN to be throttled
+ */
+ public void onDataProfileUnthrottled(final @NonNull DataProfile dataProfile) {
+ if (mCallback != null) {
+ try {
+ if (DBG) Rlog.d(TAG, "onDataProfileUnthrottled");
+ mCallback.onDataProfileUnthrottled(dataProfile);
+ } catch (RemoteException e) {
+ Rlog.e(TAG, "onDataProfileUnthrottled: remote exception", e);
+ }
+ } else {
+ Rlog.e(TAG, "onDataProfileUnthrottled: callback is null!");
+ }
+ }
}
diff --git a/telephony/java/android/telephony/data/IDataServiceCallback.aidl b/telephony/java/android/telephony/data/IDataServiceCallback.aidl
index 9cc2fea..8205b5e 100644
--- a/telephony/java/android/telephony/data/IDataServiceCallback.aidl
+++ b/telephony/java/android/telephony/data/IDataServiceCallback.aidl
@@ -17,6 +17,7 @@
package android.telephony.data;
import android.telephony.data.DataCallResponse;
+import android.telephony.data.DataProfile;
/**
* The call back interface
@@ -33,4 +34,5 @@
void onHandoverStarted(int result);
void onHandoverCancelled(int result);
void onApnUnthrottled(in String apn);
+ void onDataProfileUnthrottled(in DataProfile dp);
}
diff --git a/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
index 9293a40..7a1a2af 100644
--- a/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
+++ b/telephony/java/android/telephony/ims/stub/CapabilityExchangeEventListener.java
@@ -93,8 +93,8 @@
* Notify the framework that the ImsService has refreshed the PUBLISH
* internally, which has resulted in a new PUBLISH result.
* <p>
- * This method must return both SUCCESS (200 OK) and FAILURE (300+) codes in order to
- * keep the AOSP stack up to date.
+ * This method must be called to notify the framework of SUCCESS (200 OK) and FAILURE (300+)
+ * codes in order to keep the AOSP stack up to date.
* @param reasonCode The SIP response code sent from the network.
* @param reasonPhrase The optional reason response from the network. If the
* network provided no reason with the sip code, the string should be empty.
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index a900c84..1e38b92 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -313,4 +313,15 @@
void setPhoneNumber(int subId, int source, String number,
String callingPackage, String callingFeatureId);
+
+ /**
+ * Set the Usage Setting for this subscription.
+ *
+ * @param usageSetting the usage setting for this subscription
+ * @param subId the unique SubscriptionInfo index in database
+ * @param callingPackage The package making the IPC.
+ *
+ * @throws SecurityException if doesn't have MODIFY_PHONE_STATE or Carrier Privileges
+ */
+ int setUsageSetting(int usageSetting, int subId, String callingPackage);
}