diff options
129 files changed, 4342 insertions, 1190 deletions
diff --git a/Android.bp b/Android.bp index 598f4fcaf620..f8e5e8e6f587 100644 --- a/Android.bp +++ b/Android.bp @@ -349,6 +349,7 @@ filegroup { ":framework-telecomm-sources", ":framework-telephony-common-sources", ":framework-telephony-sources", + ":framework-vcn-util-sources", ":framework-wifi-annotations", ":framework-wifi-non-updatable-sources", ":PacProcessor-aidl-sources", @@ -1373,7 +1374,6 @@ filegroup { // TODO(b/145644363): move this to under StubLibraries.bp or ApiDocs.bp metalava_framework_docs_args = "--manifest $(location core/res/AndroidManifest.xml) " + - "--ignore-classes-on-classpath " + "--hide-package com.android.server " + "--hide-package android.audio.policy.configuration.V7_0 " + "--error UnhiddenSystemApi " + diff --git a/ApiDocs.bp b/ApiDocs.bp index 3d6bdbf19264..5f4f3c2f175b 100644 --- a/ApiDocs.bp +++ b/ApiDocs.bp @@ -96,7 +96,9 @@ droidstubs { arg_files: [ "core/res/AndroidManifest.xml", ], - args: metalava_framework_docs_args, + args: metalava_framework_docs_args + + // Needed for hidden libcore annotations for now. + " --ignore-classes-on-classpath ", write_sdk_values: true, } @@ -106,7 +108,10 @@ droidstubs { arg_files: [ "core/res/AndroidManifest.xml", ], - args: metalava_framework_docs_args + " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\) ", + args: metalava_framework_docs_args + + // Needed for hidden libcore annotations for now. + " --ignore-classes-on-classpath " + + " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\) ", write_sdk_values: true, } diff --git a/StubLibraries.bp b/StubLibraries.bp index 12ee889387ec..05967558178e 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -50,7 +50,9 @@ stubs_defaults { ":art.module.public.api{.public.stubs.source}", "**/package.html", ], - sdk_version: "core_platform", + sdk_version: "none", + system_modules: "none", + java_version: "1.8", arg_files: ["core/res/AndroidManifest.xml"], // TODO(b/147699819, b/169090544): remove below aidl includes. aidl: { @@ -80,6 +82,7 @@ stubs_defaults { "android.hardware.usb.gadget-V1.0-java", "android.hardware.vibrator-V1.3-java", "framework-protos", + "stable.core.platform.api.stubs", // There are a few classes from modules used as type arguments that // need to be resolved by metalava. For now, we can use a previously // finalized stub library to resolve them. If a new class gets added, diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp index 8323d0ba2415..437180d3d1be 100644 --- a/cmds/idmap2/idmap2/Lookup.cpp +++ b/cmds/idmap2/idmap2/Lookup.cpp @@ -71,7 +71,7 @@ Result<ResourceId> WARN_UNUSED ParseResReference(const AssetManager2& am, const } // next, try to parse as a package:type/name string - if (auto resid = am.GetResourceId(res, "", fallback_package)) { + if (auto resid = am.GetResourceId(res, "", fallback_package); resid.ok()) { return *resid; } @@ -94,7 +94,7 @@ void PrintValue(AssetManager2* const am, const AssetManager2::SelectedValue& val case Res_value::TYPE_STRING: { const ResStringPool* pool = am->GetStringPoolForCookie(value.cookie); out->append("\""); - if (auto str = pool->string8ObjectAt(value.data)) { + if (auto str = pool->string8ObjectAt(value.data); str.ok()) { out->append(*str); } } break; diff --git a/cmds/idmap2/libidmap2/XmlParser.cpp b/cmds/idmap2/libidmap2/XmlParser.cpp index 7c55b64566f2..4030b83b3a41 100644 --- a/cmds/idmap2/libidmap2/XmlParser.cpp +++ b/cmds/idmap2/libidmap2/XmlParser.cpp @@ -98,7 +98,7 @@ Result<std::string> XmlParser::Node::GetAttributeStringValue(const std::string& switch ((*value).dataType) { case Res_value::TYPE_STRING: { - if (auto str = parser_.getStrings().string8ObjectAt((*value).data)) { + if (auto str = parser_.getStrings().string8ObjectAt((*value).data); str.ok()) { return std::string(str->string()); } break; diff --git a/core/api/current.txt b/core/api/current.txt index 1c50e14d0caa..1ed78bb759d2 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -10134,6 +10134,7 @@ package android.content { method public abstract android.content.Context createDisplayContext(@NonNull android.view.Display); method public abstract android.content.Context createPackageContext(String, int) throws android.content.pm.PackageManager.NameNotFoundException; method @NonNull public android.content.Context createWindowContext(int, @Nullable android.os.Bundle); + method @NonNull public android.content.Context createWindowContext(@NonNull android.view.Display, int, @Nullable android.os.Bundle); method public abstract String[] databaseList(); method public abstract boolean deleteDatabase(String); method public abstract boolean deleteFile(String); @@ -19931,7 +19932,6 @@ package android.media { public final class AudioPlaybackConfiguration implements android.os.Parcelable { method public int describeContents(); method public android.media.AudioAttributes getAudioAttributes(); - method @Nullable public android.media.AudioDeviceInfo getAudioDevice(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.media.AudioPlaybackConfiguration> CREATOR; } @@ -33635,8 +33635,8 @@ package android.provider { } protected static interface ContactsContract.DataColumns { - field public static final String CARRIER_PRESENCE = "carrier_presence"; - field public static final int CARRIER_PRESENCE_VT_CAPABLE = 1; // 0x1 + field @Deprecated public static final String CARRIER_PRESENCE = "carrier_presence"; + field @Deprecated public static final int CARRIER_PRESENCE_VT_CAPABLE = 1; // 0x1 field public static final String DATA1 = "data1"; field public static final String DATA10 = "data10"; field public static final String DATA11 = "data11"; @@ -40689,7 +40689,7 @@ package android.telephony { field public static final String KEY_USE_ACS_FOR_RCS_BOOL = "use_acs_for_rcs_bool"; field public static final String KEY_USE_HFA_FOR_PROVISIONING_BOOL = "use_hfa_for_provisioning_bool"; field public static final String KEY_USE_OTASP_FOR_PROVISIONING_BOOL = "use_otasp_for_provisioning_bool"; - field public static final String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool"; + field @Deprecated public static final String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool"; field public static final String KEY_USE_RCS_SIP_OPTIONS_BOOL = "use_rcs_sip_options_bool"; field public static final String KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL = "use_wfc_home_network_mode_in_roaming_network_bool"; field public static final String KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL = "voicemail_notification_persistent_bool"; @@ -40729,9 +40729,12 @@ package android.telephony { } public static final class CarrierConfigManager.Ims { + field public static final String KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL = "ims.enable_presence_capability_exchange_bool"; + field public static final String KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL = "ims.enable_presence_group_subscribe_bool"; field public static final String KEY_ENABLE_PRESENCE_PUBLISH_BOOL = "ims.enable_presence_publish_bool"; field public static final String KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL = "ims.ims_single_registration_required_bool"; field public static final String KEY_PREFIX = "ims."; + field public static final String KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL = "ims.rcs_bulk_capability_exchange_bool"; field public static final String KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT = "ims.wifi_off_deferring_time_millis_int"; } @@ -42296,6 +42299,7 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean doesSwitchMultiSimConfigTriggerReboot(); method public int getActiveModemCount(); method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public java.util.List<android.telephony.CellInfo> getAllCellInfo(); + method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public int getCallComposerStatus(); method public int getCallState(); method public int getCardIdForDefaultEuicc(); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @WorkerThread public android.os.PersistableBundle getCarrierConfig(); @@ -42392,6 +42396,7 @@ package android.telephony { method @Deprecated public String sendEnvelopeWithStatus(String); method @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void sendUssdRequest(String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler); method public void sendVisualVoicemailSms(String, int, String, android.app.PendingIntent); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCallComposerStatus(int); method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabledForReason(int, boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setForbiddenPlmns(@NonNull java.util.List<java.lang.String>); @@ -42432,6 +42437,8 @@ package android.telephony { field public static final int APPTYPE_USIM = 2; // 0x2 field public static final int AUTHTYPE_EAP_AKA = 129; // 0x81 field public static final int AUTHTYPE_EAP_SIM = 128; // 0x80 + field public static final int CALL_COMPOSER_STATUS_OFF = 0; // 0x0 + field public static final int CALL_COMPOSER_STATUS_ON = 1; // 0x1 field public static final int CALL_STATE_IDLE = 0; // 0x0 field public static final int CALL_STATE_OFFHOOK = 2; // 0x2 field public static final int CALL_STATE_RINGING = 1; // 0x1 diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 85da7c37805e..5679c8958bbc 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -8062,7 +8062,7 @@ package android.os { method @RequiresPermission(anyOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static void prepareForUnattendedUpdate(@NonNull android.content.Context, @NonNull String, @Nullable android.content.IntentSender) throws java.io.IOException; method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void processPackage(android.content.Context, java.io.File, android.os.RecoverySystem.ProgressListener, android.os.Handler) throws java.io.IOException; method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void processPackage(android.content.Context, java.io.File, android.os.RecoverySystem.ProgressListener) throws java.io.IOException; - method @RequiresPermission(anyOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static void rebootAndApply(@NonNull android.content.Context, @NonNull String, @NonNull String) throws java.io.IOException; + method @Deprecated @RequiresPermission(android.Manifest.permission.RECOVERY) public static void rebootAndApply(@NonNull android.content.Context, @NonNull String, @NonNull String) throws java.io.IOException; method @RequiresPermission(anyOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static void rebootAndApply(@NonNull android.content.Context, @NonNull String, boolean) throws java.io.IOException; method @RequiresPermission(allOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static void rebootWipeAb(android.content.Context, java.io.File, String) throws java.io.IOException; method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void scheduleUpdateOnBoot(android.content.Context, java.io.File) throws java.io.IOException; diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 124cf71edc9c..700d8ff36446 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -2468,8 +2468,9 @@ class ContextImpl extends Context { return context; } + @NonNull @Override - public @NonNull WindowContext createWindowContext(int type, Bundle options) { + public WindowContext createWindowContext(int type, @NonNull Bundle options) { if (getDisplay() == null) { throw new UnsupportedOperationException("WindowContext can only be created from " + "other visual contexts, such as Activity or one created with " @@ -2478,13 +2479,26 @@ class ContextImpl extends Context { return new WindowContext(this, type, options); } - ContextImpl createBaseWindowContext(IBinder token) { + @NonNull + @Override + public WindowContext createWindowContext(@NonNull Display display, int type, + @NonNull Bundle options) { + if (display == null) { + throw new IllegalArgumentException("Display must not be null"); + } + return new WindowContext(this, display, type, options); + } + + ContextImpl createBaseWindowContext(IBinder token, Display display) { ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mAttributionTag, mSplitName, token, mUser, mFlags, mClassLoader, null); // Window contexts receive configurations directly from the server and as such do not // need to override their display in ResourcesManager. context.mForceDisplayOverrideInResources = false; context.mContextType = CONTEXT_TYPE_WINDOW_CONTEXT; + if (display != null) { + context.mDisplay = display; + } return context; } diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index 99785e1e73f4..50853a3bcf9c 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -729,6 +729,23 @@ public final class LoadedApk { } } + private StrictMode.VmPolicy allowVmViolations() { + if (mActivityThread == null) { + // When LoadedApk is used without an ActivityThread (usually in a + // zygote context), don't call into StrictMode, as it initializes + // the binder subsystem, which we don't want. + return null; + } + + return StrictMode.allowVmViolations(); + } + + private void setVmPolicy(StrictMode.VmPolicy policy) { + if (mActivityThread != null && policy != null) { + StrictMode.setVmPolicy(policy); + } + } + private void createOrUpdateClassLoaderLocked(List<String> addedPaths) { if (mPackageName.equals("android")) { // Note: This branch is taken for system server and we don't need to setup @@ -984,13 +1001,20 @@ public final class LoadedApk { // Temporarily disable logging of disk reads on the Looper thread as this is necessary - // and the loader will access the directory anyway if we don't check it. - StrictMode.ThreadPolicy oldPolicy = allowThreadDiskReads(); + StrictMode.ThreadPolicy oldThreadPolicy = allowThreadDiskReads(); + + // Also disable logging of access to /data/user before CE storage is unlocked. The check + // below will return false (because the directory name we pass will not match the + // encrypted one), but that's correct. + StrictMode.VmPolicy oldVmPolicy = allowVmViolations(); + try { // We are constructing a classloader for a different package. It is likely, // but not certain, that we can't acccess its app data dir - so check. return new File(mDataDir).canExecute(); } finally { - setThreadPolicy(oldPolicy); + setThreadPolicy(oldThreadPolicy); + setVmPolicy(oldVmPolicy); } } diff --git a/core/java/android/app/WindowContext.java b/core/java/android/app/WindowContext.java index 5f72bac89d7b..14ed414da9d0 100644 --- a/core/java/android/app/WindowContext.java +++ b/core/java/android/app/WindowContext.java @@ -26,6 +26,7 @@ import android.content.ContextWrapper; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; +import android.view.Display; import android.view.IWindowManager; import android.view.WindowManagerGlobal; import android.view.WindowManagerImpl; @@ -59,13 +60,27 @@ public class WindowContext extends ContextWrapper { * @hide */ public WindowContext(@NonNull Context base, int type, @Nullable Bundle options) { + this(base, null /* display */, type, options); + } + + /** + * Default constructor. Will generate a {@link WindowTokenClient} and attach this context to + * the token. + * + * @param base Base {@link Context} for this new instance. + * @param display the {@link Display} to override. + * @param type Window type to be used with this context. + * @hide + */ + public WindowContext(@NonNull Context base, @Nullable Display display, int type, + @Nullable Bundle options) { // Correct base context will be built once the token is resolved, so passing 'null' here. super(null /* base */); mWms = WindowManagerGlobal.getWindowManagerService(); mToken = new WindowTokenClient(); - final ContextImpl contextImpl = createBaseWindowContext(base, mToken); + final ContextImpl contextImpl = createBaseWindowContext(base, mToken, display); attachBaseContext(contextImpl); contextImpl.setOuterContext(this); @@ -93,9 +108,10 @@ public class WindowContext extends ContextWrapper { Reference.reachabilityFence(this); } - private static ContextImpl createBaseWindowContext(Context outer, IBinder token) { + private static ContextImpl createBaseWindowContext(Context outer, IBinder token, + Display display) { final ContextImpl contextImpl = ContextImpl.getImpl(outer); - return contextImpl.createBaseWindowContext(token); + return contextImpl.createBaseWindowContext(token, display); } @Override diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java index a0d2977cf09a..ce2fd4fb60b2 100644 --- a/core/java/android/app/admin/DevicePolicyManagerInternal.java +++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java @@ -231,13 +231,7 @@ public abstract class DevicePolicyManagerInternal { * Returns the profile owner component for the given user, or {@code null} if there is not one. */ @Nullable - public abstract ComponentName getProfileOwnerAsUser(@UserIdInt int userId); - - /** - * Returns the user id of the device owner, or {@link UserHandle#USER_NULL} if there is not one. - */ - @UserIdInt - public abstract int getDeviceOwnerUserId(); + public abstract ComponentName getProfileOwnerAsUser(int userHandle); /** * Returns whether the given package is a device owner or a profile owner in the calling user. diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 1713a0c158c3..406fe8d5023c 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -2967,6 +2967,16 @@ public final class BluetoothAdapter { } }); } + synchronized (mBluetoothConnectionCallbackExecutorMap) { + if (!mBluetoothConnectionCallbackExecutorMap.isEmpty()) { + try { + mService.registerBluetoothConnectionCallback(mConnectionCallback); + } catch (RemoteException e) { + Log.e(TAG, "onBluetoothServiceUp: Failed to register bluetooth" + + "connection callback", e); + } + } + } } public void onBluetoothServiceDown() { @@ -3616,25 +3626,25 @@ public final class BluetoothAdapter { return false; } - // If the callback map is empty, we register the service-to-app callback - if (mBluetoothConnectionCallbackExecutorMap.isEmpty()) { - try { - mServiceLock.readLock().lock(); - if (mService != null) { - if (!mService.registerBluetoothConnectionCallback(mConnectionCallback)) { - return false; + synchronized (mBluetoothConnectionCallbackExecutorMap) { + // If the callback map is empty, we register the service-to-app callback + if (mBluetoothConnectionCallbackExecutorMap.isEmpty()) { + try { + mServiceLock.readLock().lock(); + if (mService != null) { + if (!mService.registerBluetoothConnectionCallback(mConnectionCallback)) { + return false; + } } + } catch (RemoteException e) { + Log.e(TAG, "", e); + mBluetoothConnectionCallbackExecutorMap.remove(callback); + } finally { + mServiceLock.readLock().unlock(); } - } catch (RemoteException e) { - Log.e(TAG, "", e); - mBluetoothConnectionCallbackExecutorMap.remove(callback); - } finally { - mServiceLock.readLock().unlock(); } - } - // Adds the passed in callback to our map of callbacks to executors - synchronized (mBluetoothConnectionCallbackExecutorMap) { + // Adds the passed in callback to our map of callbacks to executors if (mBluetoothConnectionCallbackExecutorMap.containsKey(callback)) { throw new IllegalArgumentException("This callback has already been registered"); } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index ac576a80b5d2..abe7fdae0bf7 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -5989,22 +5989,22 @@ public abstract class Context { * Creating a window context is an expensive operation. Misuse of this API may lead to a huge * performance drop. The best practice is to use the same window context when possible. * An approach is to create one window context with specific window type and display and - * use it everywhere it's needed.. + * use it everywhere it's needed. * </p> * * @param type Window type in {@link WindowManager.LayoutParams} - * @param options Bundle used to pass window-related options. - * @return A {@link Context} that can be used to create windows. - * @throws UnsupportedOperationException if this is called on a non-UI context, such as - * {@link android.app.Application Application} or {@link android.app.Service Service}. + * @param options A bundle used to pass window-related options + * @return A {@link Context} that can be used to create + * non-{@link android.app.Activity activity} windows. * * @see #getSystemService(String) * @see #getSystemService(Class) * @see #WINDOW_SERVICE * @see #LAYOUT_INFLATER_SERVICE * @see #WALLPAPER_SERVICE - * @throws UnsupportedOperationException if this {@link Context} does not attach to a display or - * the current number of window contexts without adding any view by + * @throws UnsupportedOperationException if this {@link Context} does not attach to a display, + * such as {@link android.app.Application Application} or {@link android.app.Service Service}, + * or the current number of window contexts without adding any view by * {@link WindowManager#addView} <b>exceeds five</b>. */ @UiContext @@ -6014,6 +6014,32 @@ public abstract class Context { } /** + * A special version of {@link #createWindowContext(int, Bundle)} which also takes + * {@link Display}. The only difference between this API and + * {@link #createWindowContext(int, Bundle)} is that this API can create window context from + * any context even if the context which is not associated to a {@link Display} instance. + * + * @param display The {@link Display} to associate with + * @param type Window type in {@link WindowManager.LayoutParams} + * @param options A bundle used to pass window-related options. + * @return A {@link Context} that can be used to create + * non-{@link android.app.Activity activity} windows. + * @throws IllegalArgumentException if the {@link Display} is {@code null}. + * + * @see #getSystemService(String) + * @see #getSystemService(Class) + * @see #WINDOW_SERVICE + * @see #LAYOUT_INFLATER_SERVICE + * @see #WALLPAPER_SERVICE + */ + @UiContext + @NonNull + public Context createWindowContext(@NonNull Display display, @WindowType int type, + @Nullable Bundle options) { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** * Return a new Context object for the current Context but attribute to a different tag. * In complex apps attribution tagging can be used to distinguish between separate logical * parts. diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index 56da3cb0eb02..e450c08de280 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -988,6 +988,13 @@ public class ContextWrapper extends Context { } @Override + @NonNull + public Context createWindowContext(@NonNull Display display, @WindowType int type, + @Nullable Bundle options) { + return mBase.createWindowContext(display, type, options); + } + + @Override public @NonNull Context createAttributionContext(@Nullable String attributionTag) { return mBase.createAttributionContext(attributionTag); } diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 6831eca32f72..df9a7c2cb586 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -1700,8 +1700,12 @@ public class InputMethodService extends AbstractInputMethodService { if (config.orientation != Configuration.ORIENTATION_LANDSCAPE) { return false; } - if (mInputEditorInfo != null - && (mInputEditorInfo.imeOptions & EditorInfo.IME_FLAG_NO_FULLSCREEN) != 0) { + if ((mInputEditorInfo != null + && (mInputEditorInfo.imeOptions & EditorInfo.IME_FLAG_NO_FULLSCREEN) != 0) + // If app window has portrait orientation, regardless of what display orientation + // is, IME shouldn't use fullscreen-mode. + || (mInputEditorInfo.internalImeOptions + & EditorInfo.IME_FLAG_APP_WINDOW_PORTRAIT) != 0) { return false; } return true; diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index fb0128363310..95a2f2efeb7d 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -31,6 +31,7 @@ import android.net.ISocketKeepaliveCallback; import android.net.ProxyInfo; import android.os.Bundle; import android.os.IBinder; +import android.os.INetworkActivityListener; import android.os.Messenger; import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; @@ -233,4 +234,10 @@ interface IConnectivityManager in PersistableBundle extras); void systemReady(); + + void registerNetworkActivityListener(in INetworkActivityListener l); + + void unregisterNetworkActivityListener(in INetworkActivityListener l); + + boolean isDefaultNetworkActive(); } diff --git a/core/java/android/net/vcn/VcnConfig.java b/core/java/android/net/vcn/VcnConfig.java index 148acf130857..d4a3fa7411b1 100644 --- a/core/java/android/net/vcn/VcnConfig.java +++ b/core/java/android/net/vcn/VcnConfig.java @@ -15,30 +15,104 @@ */ package android.net.vcn; +import static com.android.internal.annotations.VisibleForTesting.Visibility; + import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; +import android.os.PersistableBundle; +import android.util.ArraySet; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.Preconditions; +import com.android.server.vcn.util.PersistableBundleUtils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Objects; +import java.util.Set; /** * This class represents a configuration for a Virtual Carrier Network. * + * <p>Each {@link VcnGatewayConnectionConfig} instance added represents a connection that will be + * brought up on demand based on active {@link NetworkRequest}(s). + * + * @see VcnManager for more information on the Virtual Carrier Network feature * @hide */ public final class VcnConfig implements Parcelable { @NonNull private static final String TAG = VcnConfig.class.getSimpleName(); - private VcnConfig() { + private static final String GATEWAY_CONNECTION_CONFIGS_KEY = "mGatewayConnectionConfigs"; + @NonNull private final Set<VcnGatewayConnectionConfig> mGatewayConnectionConfigs; + + private VcnConfig(@NonNull Set<VcnGatewayConnectionConfig> tunnelConfigs) { + mGatewayConnectionConfigs = Collections.unmodifiableSet(tunnelConfigs); + validate(); } - // TODO: Implement getters, validators, etc /** - * Validates this configuration. + * Deserializes a VcnConfig from a PersistableBundle. * * @hide */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + public VcnConfig(@NonNull PersistableBundle in) { + final PersistableBundle gatewayConnectionConfigsBundle = + in.getPersistableBundle(GATEWAY_CONNECTION_CONFIGS_KEY); + mGatewayConnectionConfigs = + new ArraySet<>( + PersistableBundleUtils.toList( + gatewayConnectionConfigsBundle, VcnGatewayConnectionConfig::new)); + + validate(); + } + private void validate() { - // TODO: implement validation logic + Preconditions.checkCollectionNotEmpty( + mGatewayConnectionConfigs, "gatewayConnectionConfigs"); + } + + /** Retrieves the set of configured tunnels. */ + @NonNull + public Set<VcnGatewayConnectionConfig> getGatewayConnectionConfigs() { + return Collections.unmodifiableSet(mGatewayConnectionConfigs); + } + + /** + * Serializes this object to a PersistableBundle. + * + * @hide + */ + @NonNull + public PersistableBundle toPersistableBundle() { + final PersistableBundle result = new PersistableBundle(); + + final PersistableBundle gatewayConnectionConfigsBundle = + PersistableBundleUtils.fromList( + new ArrayList<>(mGatewayConnectionConfigs), + VcnGatewayConnectionConfig::toPersistableBundle); + result.putPersistableBundle(GATEWAY_CONNECTION_CONFIGS_KEY, gatewayConnectionConfigsBundle); + + return result; + } + + @Override + public int hashCode() { + return Objects.hash(mGatewayConnectionConfigs); + } + + @Override + public boolean equals(@Nullable Object other) { + if (!(other instanceof VcnConfig)) { + return false; + } + + final VcnConfig rhs = (VcnConfig) other; + return mGatewayConnectionConfigs.equals(rhs.mGatewayConnectionConfigs); } // Parcelable methods @@ -49,15 +123,16 @@ public final class VcnConfig implements Parcelable { } @Override - public void writeToParcel(Parcel out, int flags) {} + public void writeToParcel(Parcel out, int flags) { + out.writeParcelable(toPersistableBundle(), flags); + } @NonNull public static final Parcelable.Creator<VcnConfig> CREATOR = new Parcelable.Creator<VcnConfig>() { @NonNull public VcnConfig createFromParcel(Parcel in) { - // TODO: Ensure all methods are pulled from the parcels - return new VcnConfig(); + return new VcnConfig((PersistableBundle) in.readParcelable(null)); } @NonNull @@ -68,7 +143,23 @@ public final class VcnConfig implements Parcelable { /** This class is used to incrementally build {@link VcnConfig} objects. */ public static class Builder { - // TODO: Implement this builder + @NonNull + private final Set<VcnGatewayConnectionConfig> mGatewayConnectionConfigs = new ArraySet<>(); + + /** + * Adds a configuration for an individual gateway connection. + * + * @param gatewayConnectionConfig the configuration for an individual gateway connection + * @return this {@link Builder} instance, for chaining + */ + @NonNull + public Builder addGatewayConnectionConfig( + @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig) { + Objects.requireNonNull(gatewayConnectionConfig, "gatewayConnectionConfig was null"); + + mGatewayConnectionConfigs.add(gatewayConnectionConfig); + return this; + } /** * Builds and validates the VcnConfig. @@ -77,7 +168,7 @@ public final class VcnConfig implements Parcelable { */ @NonNull public VcnConfig build() { - return new VcnConfig(); + return new VcnConfig(mGatewayConnectionConfigs); } } } diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java index 8160edc87440..039360a69a3a 100644 --- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java +++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java @@ -15,7 +15,27 @@ */ package android.net.vcn; +import static android.net.NetworkCapabilities.NetCapability; + +import static com.android.internal.annotations.VisibleForTesting.Visibility; + +import android.annotation.IntRange; import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.NetworkCapabilities; +import android.os.PersistableBundle; +import android.util.ArraySet; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.Preconditions; +import com.android.server.vcn.util.PersistableBundleUtils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.TimeUnit; /** * This class represents a configuration for a connection to a Virtual Carrier Network gateway. @@ -49,38 +69,399 @@ import android.annotation.NonNull; * <li>{@link android.net.NetworkCapabilities.NET_CAPABILITY_MCX} * </ul> * + * <p>The meteredness and roaming of the VCN {@link Network} will be determined by that of the + * underlying Network(s). + * * @hide */ public final class VcnGatewayConnectionConfig { - private VcnGatewayConnectionConfig() { + // TODO: Use MIN_MTU_V6 once it is public, @hide + @VisibleForTesting(visibility = Visibility.PRIVATE) + static final int MIN_MTU_V6 = 1280; + + private static final Set<Integer> ALLOWED_CAPABILITIES; + + static { + Set<Integer> allowedCaps = new ArraySet<>(); + allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_MMS); + allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_SUPL); + allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_DUN); + allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_FOTA); + allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_IMS); + allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_CBS); + allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_IA); + allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_RCS); + allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_XCAP); + allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_EIMS); + allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_INTERNET); + allowedCaps.add(NetworkCapabilities.NET_CAPABILITY_MCX); + + ALLOWED_CAPABILITIES = Collections.unmodifiableSet(allowedCaps); + } + + private static final int DEFAULT_MAX_MTU = 1500; + + /** + * The maximum number of retry intervals that may be specified. + * + * <p>Limited to ensure an upper bound on config sizes. + */ + private static final int MAX_RETRY_INTERVAL_COUNT = 10; + + /** + * The minimum allowable repeating retry interval + * + * <p>To ensure the device is not constantly being woken up, this retry interval MUST be greater + * than this value. + * + * @see {@link Builder#setRetryInterval()} + */ + private static final long MINIMUM_REPEATING_RETRY_INTERVAL_MS = TimeUnit.MINUTES.toMillis(15); + + private static final long[] DEFAULT_RETRY_INTERVALS_MS = + new long[] { + TimeUnit.SECONDS.toMillis(1), + TimeUnit.SECONDS.toMillis(2), + TimeUnit.SECONDS.toMillis(5), + TimeUnit.SECONDS.toMillis(30), + TimeUnit.MINUTES.toMillis(1), + TimeUnit.MINUTES.toMillis(5), + TimeUnit.MINUTES.toMillis(15) + }; + + private static final String EXPOSED_CAPABILITIES_KEY = "mExposedCapabilities"; + @NonNull private final Set<Integer> mExposedCapabilities; + + private static final String UNDERLYING_CAPABILITIES_KEY = "mUnderlyingCapabilities"; + @NonNull private final Set<Integer> mUnderlyingCapabilities; + + // TODO: Add Ike/ChildSessionParams as a subclass - maybe VcnIkeGatewayConnectionConfig + + private static final String MAX_MTU_KEY = "mMaxMtu"; + private final int mMaxMtu; + + private static final String RETRY_INTERVAL_MS_KEY = "mRetryIntervalsMs"; + @NonNull private final long[] mRetryIntervalsMs; + + @VisibleForTesting(visibility = Visibility.PRIVATE) + public VcnGatewayConnectionConfig( + @NonNull Set<Integer> exposedCapabilities, + @NonNull Set<Integer> underlyingCapabilities, + @NonNull long[] retryIntervalsMs, + @IntRange(from = MIN_MTU_V6) int maxMtu) { + mExposedCapabilities = exposedCapabilities; + mUnderlyingCapabilities = underlyingCapabilities; + mRetryIntervalsMs = retryIntervalsMs; + mMaxMtu = maxMtu; + + validate(); + } + + /** @hide */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + public VcnGatewayConnectionConfig(@NonNull PersistableBundle in) { + final PersistableBundle exposedCapsBundle = + in.getPersistableBundle(EXPOSED_CAPABILITIES_KEY); + final PersistableBundle underlyingCapsBundle = + in.getPersistableBundle(UNDERLYING_CAPABILITIES_KEY); + + mExposedCapabilities = new ArraySet<>(PersistableBundleUtils.toList( + exposedCapsBundle, PersistableBundleUtils.INTEGER_DESERIALIZER)); + mUnderlyingCapabilities = new ArraySet<>(PersistableBundleUtils.toList( + underlyingCapsBundle, PersistableBundleUtils.INTEGER_DESERIALIZER)); + mRetryIntervalsMs = in.getLongArray(RETRY_INTERVAL_MS_KEY); + mMaxMtu = in.getInt(MAX_MTU_KEY); + validate(); } - // TODO: Implement getters, validators, etc + private void validate() { + Preconditions.checkArgument( + mExposedCapabilities != null && !mExposedCapabilities.isEmpty(), + "exposedCapsBundle was null or empty"); + for (Integer cap : getAllExposedCapabilities()) { + checkValidCapability(cap); + } + + Preconditions.checkArgument( + mUnderlyingCapabilities != null && !mUnderlyingCapabilities.isEmpty(), + "underlyingCapabilities was null or empty"); + for (Integer cap : getAllUnderlyingCapabilities()) { + checkValidCapability(cap); + } + + Objects.requireNonNull(mRetryIntervalsMs, "retryIntervalsMs was null"); + validateRetryInterval(mRetryIntervalsMs); + + Preconditions.checkArgument( + mMaxMtu >= MIN_MTU_V6, "maxMtu must be at least IPv6 min MTU (1280)"); + } + + private static void checkValidCapability(int capability) { + Preconditions.checkArgument( + ALLOWED_CAPABILITIES.contains(capability), + "NetworkCapability " + capability + "out of range"); + } + + private static void validateRetryInterval(@Nullable long[] retryIntervalsMs) { + Preconditions.checkArgument( + retryIntervalsMs != null + && retryIntervalsMs.length > 0 + && retryIntervalsMs.length <= MAX_RETRY_INTERVAL_COUNT, + "retryIntervalsMs was null, empty or exceed max interval count"); + + final long repeatingInterval = retryIntervalsMs[retryIntervalsMs.length - 1]; + if (repeatingInterval < MINIMUM_REPEATING_RETRY_INTERVAL_MS) { + throw new IllegalArgumentException( + "Repeating retry interval was too short, must be a minimum of 15 minutes: " + + repeatingInterval); + } + } /** - * Validates this configuration + * Returns all exposed capabilities. * * @hide */ - private void validate() { - // TODO: implement validation logic + @NonNull + public Set<Integer> getAllExposedCapabilities() { + return Collections.unmodifiableSet(mExposedCapabilities); + } + + /** + * Checks if this config is configured to support/expose a specific capability. + * + * @param capability the capability to check for + */ + public boolean hasExposedCapability(@NetCapability int capability) { + checkValidCapability(capability); + + return mExposedCapabilities.contains(capability); + } + + /** + * Returns all capabilities required of underlying networks. + * + * @hide + */ + @NonNull + public Set<Integer> getAllUnderlyingCapabilities() { + return Collections.unmodifiableSet(mUnderlyingCapabilities); } - // Parcelable methods + /** + * Checks if this config requires an underlying network to have the specified capability. + * + * @param capability the capability to check for + */ + public boolean requiresUnderlyingCapability(@NetCapability int capability) { + checkValidCapability(capability); + + return mUnderlyingCapabilities.contains(capability); + } - /** This class is used to incrementally build {@link VcnGatewayConnectionConfig} objects */ + /** Retrieves the configured retry intervals. */ + @NonNull + public long[] getRetryIntervalsMs() { + return Arrays.copyOf(mRetryIntervalsMs, mRetryIntervalsMs.length); + } + + /** Retrieves the maximum MTU allowed for this Gateway Connection. */ + @IntRange(from = MIN_MTU_V6) + public int getMaxMtu() { + return mMaxMtu; + } + + /** + * Converts this config to a PersistableBundle. + * + * @hide + */ + @NonNull + @VisibleForTesting(visibility = Visibility.PROTECTED) + public PersistableBundle toPersistableBundle() { + final PersistableBundle result = new PersistableBundle(); + + final PersistableBundle exposedCapsBundle = + PersistableBundleUtils.fromList( + new ArrayList<>(mExposedCapabilities), + PersistableBundleUtils.INTEGER_SERIALIZER); + final PersistableBundle underlyingCapsBundle = + PersistableBundleUtils.fromList( + new ArrayList<>(mUnderlyingCapabilities), + PersistableBundleUtils.INTEGER_SERIALIZER); + + result.putPersistableBundle(EXPOSED_CAPABILITIES_KEY, exposedCapsBundle); + result.putPersistableBundle(UNDERLYING_CAPABILITIES_KEY, underlyingCapsBundle); + result.putLongArray(RETRY_INTERVAL_MS_KEY, mRetryIntervalsMs); + result.putInt(MAX_MTU_KEY, mMaxMtu); + + return result; + } + + @Override + public int hashCode() { + return Objects.hash( + mExposedCapabilities, + mUnderlyingCapabilities, + Arrays.hashCode(mRetryIntervalsMs), + mMaxMtu); + } + + @Override + public boolean equals(@Nullable Object other) { + if (!(other instanceof VcnGatewayConnectionConfig)) { + return false; + } + + final VcnGatewayConnectionConfig rhs = (VcnGatewayConnectionConfig) other; + return mExposedCapabilities.equals(rhs.mExposedCapabilities) + && mUnderlyingCapabilities.equals(rhs.mUnderlyingCapabilities) + && Arrays.equals(mRetryIntervalsMs, rhs.mRetryIntervalsMs) + && mMaxMtu == rhs.mMaxMtu; + } + + /** This class is used to incrementally build {@link VcnGatewayConnectionConfig} objects. */ public static class Builder { - // TODO: Implement this builder + @NonNull private final Set<Integer> mExposedCapabilities = new ArraySet(); + @NonNull private final Set<Integer> mUnderlyingCapabilities = new ArraySet(); + @NonNull private long[] mRetryIntervalsMs = DEFAULT_RETRY_INTERVALS_MS; + private int mMaxMtu = DEFAULT_MAX_MTU; + + // TODO: (b/175829816) Consider VCN-exposed capabilities that may be transport dependent. + // Consider the case where the VCN might only expose MMS on WiFi, but defer to MMS + // when on Cell. + + /** + * Add a capability that this VCN Gateway Connection will support. + * + * @param exposedCapability the app-facing capability to be exposed by this VCN Gateway + * Connection (i.e., the capabilities that this VCN Gateway Connection will support). + * @return this {@link Builder} instance, for chaining + * @see VcnGatewayConnectionConfig for a list of capabilities may be exposed by a Gateway + * Connection + */ + public Builder addExposedCapability(@NetCapability int exposedCapability) { + checkValidCapability(exposedCapability); + + mExposedCapabilities.add(exposedCapability); + return this; + } + + /** + * Remove a capability that this VCN Gateway Connection will support. + * + * @param exposedCapability the app-facing capability to not be exposed by this VCN Gateway + * Connection (i.e., the capabilities that this VCN Gateway Connection will support) + * @return this {@link Builder} instance, for chaining + * @see VcnGatewayConnectionConfig for a list of capabilities may be exposed by a Gateway + * Connection + */ + public Builder removeExposedCapability(@NetCapability int exposedCapability) { + checkValidCapability(exposedCapability); + + mExposedCapabilities.remove(exposedCapability); + return this; + } + + /** + * Require a capability for Networks underlying this VCN Gateway Connection. + * + * @param underlyingCapability the capability that a network MUST have in order to be an + * underlying network for this VCN Gateway Connection. + * @return this {@link Builder} instance, for chaining + * @see VcnGatewayConnectionConfig for a list of capabilities may be required of underlying + * networks + */ + public Builder addRequiredUnderlyingCapability(@NetCapability int underlyingCapability) { + checkValidCapability(underlyingCapability); + + mUnderlyingCapabilities.add(underlyingCapability); + return this; + } + + /** + * Remove a requirement of a capability for Networks underlying this VCN Gateway Connection. + * + * <p>Calling this method will allow Networks that do NOT have this capability to be + * selected as an underlying network for this VCN Gateway Connection. However, underlying + * networks MAY still have the removed capability. + * + * @param underlyingCapability the capability that a network DOES NOT need to have in order + * to be an underlying network for this VCN Gateway Connection. + * @return this {@link Builder} instance, for chaining + * @see VcnGatewayConnectionConfig for a list of capabilities may be required of underlying + * networks + */ + public Builder removeRequiredUnderlyingCapability(@NetCapability int underlyingCapability) { + checkValidCapability(underlyingCapability); + + mUnderlyingCapabilities.remove(underlyingCapability); + return this; + } + + /** + * Set the retry interval between VCN establishment attempts upon successive failures. + * + * <p>The last retry interval will be repeated until safe mode is entered, or a connection + * is successfully established, at which point the retry timers will be reset. For power + * reasons, the last (repeated) retry interval MUST be at least 15 minutes. + * + * <p>Retry intervals MAY be subject to system power saving modes. That is to say that if + * the system enters a power saving mode, the retry may not occur until the device leaves + * the specified power saving mode. Intervals are sequential, and intervals will NOT be + * skipped if system power saving results in delaying retries (even if it exceed multiple + * retry intervals). + * + * <p>Each Gateway Connection will retry according to the retry intervals configured, but if + * safe mode is enabled, all Gateway Connection(s) will be disabled. + * + * @param retryIntervalsMs an array of between 1 and 10 millisecond intervals after which + * the VCN will attempt to retry a session initiation. The last (repeating) retry + * interval must be at least 15 minutes. Defaults to: {@code [1s, 2s, 5s, 30s, 1m, 5m, + * 15m]} + * @return this {@link Builder} instance, for chaining + * @see VcnManager for additional discussion on fail-safe mode + */ + @NonNull + public Builder setRetryInterval(@NonNull long[] retryIntervalsMs) { + validateRetryInterval(retryIntervalsMs); + + mRetryIntervalsMs = retryIntervalsMs; + return this; + } + + /** + * Sets the maximum MTU allowed for this VCN Gateway Connection. + * + * <p>This MTU is applied to the VCN Gateway Connection exposed Networks, and represents the + * MTU of the virtualized network. + * + * <p>The system may reduce the MTU below the maximum specified based on signals such as the + * MTU of the underlying networks (and adjusted for Gateway Connection overhead). + * + * @param maxMtu the maximum MTU allowed for this Gateway Connection. Must be greater than + * the IPv6 minimum MTU of 1280. Defaults to 1500. + * @return this {@link Builder} instance, for chaining + */ + @NonNull + public Builder setMaxMtu(@IntRange(from = MIN_MTU_V6) int maxMtu) { + Preconditions.checkArgument( + maxMtu >= MIN_MTU_V6, "maxMtu must be at least IPv6 min MTU (1280)"); + + mMaxMtu = maxMtu; + return this; + } /** - * Builds and validates the VcnGatewayConnectionConfig + * Builds and validates the VcnGatewayConnectionConfig. * * @return an immutable VcnGatewayConnectionConfig instance */ @NonNull public VcnGatewayConnectionConfig build() { - return new VcnGatewayConnectionConfig(); + return new VcnGatewayConnectionConfig( + mExposedCapabilities, mUnderlyingCapabilities, mRetryIntervalsMs, mMaxMtu); } } } diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java index 6769b9e46e4c..19c183f9fe9c 100644 --- a/core/java/android/net/vcn/VcnManager.java +++ b/core/java/android/net/vcn/VcnManager.java @@ -23,10 +23,37 @@ import android.annotation.SystemService; import android.content.Context; import android.os.ParcelUuid; import android.os.RemoteException; +import android.os.ServiceSpecificException; + +import java.io.IOException; /** * VcnManager publishes APIs for applications to configure and manage Virtual Carrier Networks. * + * <p>A VCN creates a virtualization layer to allow MVNOs to aggregate heterogeneous physical + * networks, unifying them as a single carrier network. This enables infrastructure flexibility on + * the part of MVNOs without impacting user connectivity, abstracting the physical network + * technologies as an implementation detail of their public network. + * + * <p>Each VCN virtualizes an Carrier's network by building tunnels to a carrier's core network over + * carrier-managed physical links and supports a IP mobility layer to ensure seamless transitions + * between the underlying networks. Each VCN is configured based on a Subscription Group (see {@link + * android.telephony.SubscriptionManager}) and aggregates all networks that are brought up based on + * a profile or suggestion in the specified Subscription Group. + * + * <p>The VCN can be configured to expose one or more {@link android.net.Network}(s), each with + * different capabilities, allowing for APN virtualization. + * + * <p>If a tunnel fails to connect, or otherwise encounters a fatal error, the VCN will attempt to + * reestablish the connection. If the tunnel still has not reconnected after a system-determined + * timeout, the VCN Safe Mode (see below) will be entered. + * + * <p>The VCN Safe Mode ensures that users (and carriers) have a fallback to restore system + * connectivity to update profiles, diagnose issues, contact support, or perform other remediation + * tasks. In Safe Mode, the system will allow underlying cellular networks to be used as default. + * Additionally, during Safe Mode, the VCN will continue to retry the connections, and will + * automatically exit Safe Mode if all active tunnels connect successfully. + * * @hide */ @SystemService(Context.VCN_MANAGEMENT_SERVICE) @@ -63,15 +90,20 @@ public final class VcnManager { * @param config the configuration parameters for the VCN * @throws SecurityException if the caller does not have carrier privileges, or is not running * as the primary user + * @throws IOException if the configuration failed to be persisted. A caller encountering this + * exception should attempt to retry (possibly after a delay). * @hide */ @RequiresPermission("carrier privileges") // TODO (b/72967236): Define a system-wide constant - public void setVcnConfig(@NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config) { + public void setVcnConfig(@NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config) + throws IOException { requireNonNull(subscriptionGroup, "subscriptionGroup was null"); requireNonNull(config, "config was null"); try { mService.setVcnConfig(subscriptionGroup, config); + } catch (ServiceSpecificException e) { + throw new IOException(e); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -88,14 +120,18 @@ public final class VcnManager { * @param subscriptionGroup the subscription group that the configuration should be applied to * @throws SecurityException if the caller does not have carrier privileges, or is not running * as the primary user + * @throws IOException if the configuration failed to be cleared. A caller encountering this + * exception should attempt to retry (possibly after a delay). * @hide */ @RequiresPermission("carrier privileges") // TODO (b/72967236): Define a system-wide constant - public void clearVcnConfig(@NonNull ParcelUuid subscriptionGroup) { + public void clearVcnConfig(@NonNull ParcelUuid subscriptionGroup) throws IOException { requireNonNull(subscriptionGroup, "subscriptionGroup was null"); try { mService.clearVcnConfig(subscriptionGroup); + } catch (ServiceSpecificException e) { + throw new IOException(e); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/os/IRecoverySystem.aidl b/core/java/android/os/IRecoverySystem.aidl index 5f8b93283269..205288303b1e 100644 --- a/core/java/android/os/IRecoverySystem.aidl +++ b/core/java/android/os/IRecoverySystem.aidl @@ -30,5 +30,6 @@ interface IRecoverySystem { boolean requestLskf(in String packageName, in IntentSender sender); boolean clearLskf(in String packageName); boolean isLskfCaptured(in String packageName); + boolean rebootWithLskfAssumeSlotSwitch(in String packageName, in String reason); boolean rebootWithLskf(in String packageName, in String reason, in boolean slotSwitch); } diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java index 5f3cfa35fbd0..93c1690e3813 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -687,8 +687,8 @@ public class RecoverySystem { } /** - * Request that the device reboot and apply the update that has been prepared. Callers are - * recommended to use {@link #rebootAndApply(Context, String, boolean)} instead. + * Request that the device reboot and apply the update that has been prepared. This API is + * deprecated, and is expected to be used by OTA only on devices running Android 11. * * @param context the Context to use. * @param updateToken this parameter is deprecated and won't be used. See details in @@ -699,18 +699,18 @@ public class RecoverySystem { * unattended reboot or if the {@code updateToken} did not match the previously * given token * @hide + * @deprecated Use {@link #rebootAndApply(Context, String, boolean)} instead */ @SystemApi - @RequiresPermission(anyOf = {android.Manifest.permission.RECOVERY, - android.Manifest.permission.REBOOT}) + @RequiresPermission(android.Manifest.permission.RECOVERY) public static void rebootAndApply(@NonNull Context context, @NonNull String updateToken, @NonNull String reason) throws IOException { if (updateToken == null) { throw new NullPointerException("updateToken == null"); } RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE); - // OTA is the sole user before S, and a slot switch is required for ota update. - if (!rs.rebootWithLskf(context.getPackageName(), reason, true)) { + // OTA is the sole user, who expects a slot switch. + if (!rs.rebootWithLskfAssumeSlotSwitch(context.getPackageName(), reason)) { throw new IOException("system not prepared to apply update"); } } @@ -738,7 +738,7 @@ public class RecoverySystem { * * @param context the Context to use. * @param reason the reboot reason to give to the {@link PowerManager} - * @param slotSwitch true if the caller intends to switch the slot on an A/B device. + * @param slotSwitch true if the caller expects the slot to be switched on A/B devices. * @throws IOException if the reboot couldn't proceed because the device wasn't ready for an * unattended reboot. * @hide @@ -1395,6 +1395,21 @@ public class RecoverySystem { } } + + /** + * Calls the recovery system service to reboot and apply update. This is the legacy API and + * expects a slot switch for A/B devices. + * + */ + private boolean rebootWithLskfAssumeSlotSwitch(String packageName, String reason) + throws IOException { + try { + return mService.rebootWithLskfAssumeSlotSwitch(packageName, reason); + } catch (RemoteException e) { + throw new IOException("could not reboot for update"); + } + } + /** * Internally, recovery treats each line of the command file as a separate * argv, so we only need to protect against newlines and nulls. diff --git a/core/java/android/os/incremental/OWNERS b/core/java/android/os/incremental/OWNERS new file mode 100644 index 000000000000..3795493b861f --- /dev/null +++ b/core/java/android/os/incremental/OWNERS @@ -0,0 +1,5 @@ +# Bug component: 554432 +alexbuy@google.com +schfan@google.com +toddke@google.com +zyy@google.com diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index b2b8db19ac17..376d9421e8d3 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -4313,13 +4313,23 @@ public final class ContactsContract { * <P> * Type: INTEGER (A bitmask of CARRIER_PRESENCE_* fields) * </P> + * + * @deprecated The contacts database will only show presence + * information on devices where + * {@link android.telephony.CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL} is true, + * otherwise use {@link android.telephony.ims.RcsUceAdapter}. */ + @Deprecated public static final String CARRIER_PRESENCE = "carrier_presence"; /** * Indicates that the entry is Video Telephony (VT) capable on the * current carrier. An allowed bitmask of {@link #CARRIER_PRESENCE}. + * + * @deprecated Same as {@link DataColumns#CARRIER_PRESENCE}. + * */ + @Deprecated public static final int CARRIER_PRESENCE_VT_CAPABLE = 0x01; /** diff --git a/core/java/android/service/attestation/OWNERS b/core/java/android/service/attestation/OWNERS new file mode 100644 index 000000000000..b9e7b996e5d8 --- /dev/null +++ b/core/java/android/service/attestation/OWNERS @@ -0,0 +1,2 @@ +chaviw@google.com +ogunwale@google.com diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java index 7dbf69369996..1cf25a77fa38 100644 --- a/core/java/android/view/inputmethod/EditorInfo.java +++ b/core/java/android/view/inputmethod/EditorInfo.java @@ -28,6 +28,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.content.res.Configuration; import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.os.LocaleList; @@ -293,6 +294,13 @@ public class EditorInfo implements InputType, Parcelable { public static final int IME_FLAG_FORCE_ASCII = 0x80000000; /** + * Flag of {@link #internalImeOptions}: flag is set when app window containing this + * {@link EditorInfo} is using {@link Configuration#ORIENTATION_PORTRAIT} mode. + * @hide + */ + public static final int IME_FLAG_APP_WINDOW_PORTRAIT = 0x80000000; + + /** * Generic unspecified type for {@link #imeOptions}. */ public static final int IME_NULL = 0x00000000; @@ -312,6 +320,7 @@ public class EditorInfo implements InputType, Parcelable { * 1 1 IME_ACTION_NEXT * 11 IME_ACTION_DONE * 111 IME_ACTION_PREVIOUS + * 1 IME_FLAG_APP_WINDOW_PORTRAIT * 1 IME_FLAG_NO_PERSONALIZED_LEARNING * 1 IME_FLAG_NO_FULLSCREEN * 1 IME_FLAG_NAVIGATE_PREVIOUS @@ -343,6 +352,20 @@ public class EditorInfo implements InputType, Parcelable { public String privateImeOptions = null; /** + * Masks for {@link internalImeOptions} + * + * <pre> + * 1 IME_FLAG_APP_WINDOW_PORTRAIT + * |-------|-------|-------|-------|</pre> + */ + + /** + * Same as {@link android.R.attr#imeOptions} but for framework's internal-use only. + * @hide + */ + public int internalImeOptions = IME_NULL; + + /** * In some cases an IME may be able to display an arbitrary label for * a command the user can perform, which you can specify here. This is * typically used as the label for the action to use in-line as a replacement diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 12c91fac0c65..977a0e89a143 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -17,6 +17,7 @@ package android.widget; import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; +import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.view.ContentInfo.FLAG_CONVERT_TO_PLAIN_TEXT; import static android.view.ContentInfo.SOURCE_AUTOFILL; import static android.view.ContentInfo.SOURCE_CLIPBOARD; @@ -8738,6 +8739,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_ENTER_ACTION; } } + if (getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT) { + outAttrs.internalImeOptions |= EditorInfo.IME_FLAG_APP_WINDOW_PORTRAIT; + } if (isMultilineInputType(outAttrs.inputType)) { // Multi-line text editors should always show an enter key. outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_ENTER_ACTION; diff --git a/core/java/android/window/DisplayAreaOrganizer.java b/core/java/android/window/DisplayAreaOrganizer.java index 8a1d4a0b4823..1254647379b5 100644 --- a/core/java/android/window/DisplayAreaOrganizer.java +++ b/core/java/android/window/DisplayAreaOrganizer.java @@ -164,15 +164,19 @@ public class DisplayAreaOrganizer extends WindowOrganizer { } /** - * Creates a persistent task display area. It will be added to be the top most task display area - * in the root. + * Creates a persistent {@link com.android.server.wm.TaskDisplayArea}. * * The new created TDA is organized by the organizer, and will be deleted on calling * {@link #deleteTaskDisplayArea(WindowContainerToken)} or {@link #unregisterOrganizer()}. * - * @param displayId the display to create the new task display area in. - * @param rootFeatureId the root display area to create the new task display area in. Caller can - * use {@link #FEATURE_ROOT} as the root of the logical display. + * @param displayId the display to create the new TDA in. + * @param parentFeatureId the parent to create the new TDA in. If it is a + * {@link com.android.server.wm.RootDisplayArea}, the new TDA will be + * placed as the topmost TDA. If it is another TDA, the new TDA will be + * placed as the topmost child. + * Caller can use {@link #FEATURE_ROOT} as the root of the logical + * display, or {@link #FEATURE_DEFAULT_TASK_CONTAINER} as the default + * TDA. * @param name the name for the new task display area. * @return the new created task display area. * @throws IllegalArgumentException if failed to create a new task display area. @@ -181,11 +185,11 @@ public class DisplayAreaOrganizer extends WindowOrganizer { @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) @CallSuper @NonNull - public DisplayAreaAppearedInfo createTaskDisplayArea(int displayId, int rootFeatureId, + public DisplayAreaAppearedInfo createTaskDisplayArea(int displayId, int parentFeatureId, @NonNull String name) { try { return getController().createTaskDisplayArea( - mInterface, displayId, rootFeatureId, name); + mInterface, displayId, parentFeatureId, name); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/window/IDisplayAreaOrganizerController.aidl b/core/java/android/window/IDisplayAreaOrganizerController.aidl index 26fa434506cf..6c097bbd80bb 100644 --- a/core/java/android/window/IDisplayAreaOrganizerController.aidl +++ b/core/java/android/window/IDisplayAreaOrganizerController.aidl @@ -40,21 +40,25 @@ interface IDisplayAreaOrganizerController { void unregisterOrganizer(in IDisplayAreaOrganizer organizer); /** - * Creates a persistent task display area. It will be added to be the top most task display area - * in the root. + * Creates a persistent {@link com.android.server.wm.TaskDisplayArea}. * * The new created TDA is organized by the organizer, and will be deleted on calling * {@link #deleteTaskDisplayArea(WindowContainerToken)} or {@link #unregisterOrganizer()}. * - * @param displayId the display to create the new task display area in. - * @param rootFeatureId the root display area to create the new task display area in. Caller can - * use {@link #FEATURE_ROOT} as the root of the logical display. + * @param displayId the display to create the new TDA in. + * @param parentFeatureId the parent to create the new TDA in. If it is a + * {@link com.android.server.wm.RootDisplayArea}, the new TDA will be + * placed as the topmost TDA. If it is another TDA, the new TDA will be + * placed as the topmost child. + * Caller can use {@link #FEATURE_ROOT} as the root of the logical + * display, or {@link #FEATURE_DEFAULT_TASK_CONTAINER} as the default + * TDA. * @param name the name for the new task display area. * @return the new created task display area. * @throws IllegalArgumentException if failed to create a new task display area. */ DisplayAreaAppearedInfo createTaskDisplayArea(in IDisplayAreaOrganizer organizer, int displayId, - int rootFeatureId, in String name); + int parentFeatureId, in String name); /** * Deletes a persistent task display area. It can only be one that created by an organizer. diff --git a/core/java/com/android/internal/os/KernelWakelockReader.java b/core/java/com/android/internal/os/KernelWakelockReader.java index e595db384cb9..c4168141f184 100644 --- a/core/java/com/android/internal/os/KernelWakelockReader.java +++ b/core/java/com/android/internal/os/KernelWakelockReader.java @@ -78,13 +78,16 @@ public class KernelWakelockReader { boolean useSystemSuspend = (new File(sSysClassWakeupDir)).exists(); if (useSystemSuspend) { - // Get both kernel and native wakelock stats from SystemSuspend - updateVersion(staleStats); - if (getWakelockStatsFromSystemSuspend(staleStats) == null) { - Slog.w(TAG, "Failed to get wakelock stats from SystemSuspend"); - return null; + // static read/write lock protection for sKernelWakelockUpdateVersion + synchronized (KernelWakelockReader.class) { + // Get both kernel and native wakelock stats from SystemSuspend + updateVersion(staleStats); + if (getWakelockStatsFromSystemSuspend(staleStats) == null) { + Slog.w(TAG, "Failed to get wakelock stats from SystemSuspend"); + return null; + } + return removeOldStats(staleStats); } - return removeOldStats(staleStats); } else { Arrays.fill(mKernelWakelockBuffer, (byte) 0); int len = 0; @@ -141,14 +144,17 @@ public class KernelWakelockReader { } } - updateVersion(staleStats); - // Get native wakelock stats from SystemSuspend - if (getWakelockStatsFromSystemSuspend(staleStats) == null) { - Slog.w(TAG, "Failed to get Native wakelock stats from SystemSuspend"); + // static read/write lock protection for sKernelWakelockUpdateVersion + synchronized (KernelWakelockReader.class) { + updateVersion(staleStats); + // Get native wakelock stats from SystemSuspend + if (getWakelockStatsFromSystemSuspend(staleStats) == null) { + Slog.w(TAG, "Failed to get Native wakelock stats from SystemSuspend"); + } + // Get kernel wakelock stats + parseProcWakelocks(mKernelWakelockBuffer, len, wakeup_sources, staleStats); + return removeOldStats(staleStats); } - // Get kernel wakelock stats - parseProcWakelocks(mKernelWakelockBuffer, len, wakeup_sources, staleStats); - return removeOldStats(staleStats); } } diff --git a/core/res/Android.bp b/core/res/Android.bp index f94a2b08e6c3..9ee5e5e18185 100644 --- a/core/res/Android.bp +++ b/core/res/Android.bp @@ -19,6 +19,13 @@ android_app { sdk_version: "core_platform", certificate: "platform", + // Disable dexpreopt and verify_uses_libraries check as the app + // contains no Java code to be dexpreopted. + enforce_uses_libs: false, + dex_preopt: { + enabled: false, + }, + // Soong special-cases framework-res to install this alongside // the libraries at /system/framework/framework-res.apk. diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 62a8ae539222..28f5e354b83f 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -393,6 +393,7 @@ <protected-broadcast android:name="android.net.wifi.action.PASSPOINT_OSU_PROVIDERS_LIST" /> <protected-broadcast android:name="android.net.wifi.action.PASSPOINT_SUBSCRIPTION_REMEDIATION" /> <protected-broadcast android:name="android.net.wifi.action.PASSPOINT_LAUNCH_OSU_VIEW" /> + <protected-broadcast android:name="android.net.wifi.action.REFRESH_USER_PROVISIONING" /> <protected-broadcast android:name="android.net.wifi.action.WIFI_NETWORK_SUGGESTION_POST_CONNECTION" /> <protected-broadcast android:name="android.net.wifi.action.WIFI_SCAN_AVAILABILITY_CHANGED" /> <protected-broadcast android:name="android.net.wifi.supplicant.CONNECTION_CHANGE" /> diff --git a/core/tests/coretests/src/android/widget/TextViewTest.java b/core/tests/coretests/src/android/widget/TextViewTest.java index 40ecf9a83981..66fdfff990f8 100644 --- a/core/tests/coretests/src/android/widget/TextViewTest.java +++ b/core/tests/coretests/src/android/widget/TextViewTest.java @@ -16,6 +16,9 @@ package android.widget; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; @@ -30,6 +33,7 @@ import android.text.GetChars; import android.text.Layout; import android.text.PrecomputedText; import android.view.View; +import android.view.inputmethod.EditorInfo; import android.widget.TextView.BufferType; import androidx.test.InstrumentationRegistry; @@ -254,6 +258,24 @@ public class TextViewTest { assertEquals("", mTextView.getTransformed().toString()); } + @Test + @UiThreadTest + public void testPortraitDoesntSupportFullscreenIme() { + mActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); + mTextView = new NullSetTextTextView(mActivity); + mTextView.requestFocus(); + assertEquals("IME_FLAG_NO_FULLSCREEN should be set", + mTextView.getImeOptions(), + mTextView.getImeOptions() & EditorInfo.IME_FLAG_NO_FULLSCREEN); + + mTextView.clearFocus(); + mActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); + mTextView = new NullSetTextTextView(mActivity); + mTextView.requestFocus(); + assertEquals("IME_FLAG_NO_FULLSCREEN should not be set", + 0, mTextView.getImeOptions() & EditorInfo.IME_FLAG_NO_FULLSCREEN); + } + private String createLongText() { int size = 600 * 1000; final StringBuilder builder = new StringBuilder(size); diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java index 67f4c8a16da6..bd4154210cbf 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java @@ -38,6 +38,7 @@ import org.junit.runners.Suite; BatteryStatsTimeBaseTest.class, BatteryStatsTimerTest.class, BatteryStatsUidTest.class, + BatteryUsageStatsTest.class, BatteryStatsUserLifecycleTests.class, BluetoothPowerCalculatorTest.class, BstatsCpuTimesValidationTest.class, diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java new file mode 100644 index 000000000000..96e9c4af21c2 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.os; + +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.fail; + +import android.os.BatteryConsumer; +import android.os.BatteryUsageStats; +import android.os.Parcel; +import android.os.SystemBatteryConsumer; +import android.os.UidBatteryConsumer; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.List; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class BatteryUsageStatsTest { + + @Test + public void testBuilder() { + BatteryUsageStats batteryUsageStats = buildBatteryUsageStats(); + validateBatteryUsageStats(batteryUsageStats); + } + + @Test + public void testParcelability() { + final BatteryUsageStats outBatteryUsageStats = buildBatteryUsageStats(); + final Parcel outParcel = Parcel.obtain(); + outParcel.writeParcelable(outBatteryUsageStats, 0); + final byte[] bytes = outParcel.marshall(); + outParcel.recycle(); + + final Parcel inParcel = Parcel.obtain(); + inParcel.unmarshall(bytes, 0, bytes.length); + inParcel.setDataPosition(0); + final BatteryUsageStats inBatteryUsageStats = + inParcel.readParcelable(getClass().getClassLoader()); + assertThat(inBatteryUsageStats).isNotNull(); + validateBatteryUsageStats(inBatteryUsageStats); + } + + private BatteryUsageStats buildBatteryUsageStats() { + final MockClocks clocks = new MockClocks(); + final MockBatteryStatsImpl batteryStats = new MockBatteryStatsImpl(clocks); + final BatteryStatsImpl.Uid batteryStatsUid = batteryStats.getUidStatsLocked(2000); + + final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(1, 1, true); + builder.setConsumedPower(100); + builder.setDischargePercentage(20); + + final UidBatteryConsumer.Builder uidBatteryConsumerBuilder = + builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid); + uidBatteryConsumerBuilder.setPackageWithHighestDrain("foo"); + uidBatteryConsumerBuilder.setConsumedPower(200); + uidBatteryConsumerBuilder.setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, 300); + uidBatteryConsumerBuilder.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 400); + uidBatteryConsumerBuilder.setConsumedPowerForCustomComponent( + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 500); + uidBatteryConsumerBuilder.setConsumedPowerForCustomComponent( + BatteryConsumer.FIRST_MODELED_POWER_COMPONENT_ID + + BatteryConsumer.POWER_COMPONENT_CPU, 510); + uidBatteryConsumerBuilder.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU, 600); + uidBatteryConsumerBuilder.setUsageDurationMillis( + BatteryConsumer.TIME_COMPONENT_CPU_FOREGROUND, 700); + uidBatteryConsumerBuilder.setUsageDurationForCustomComponentMillis( + BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID, 800); + + final SystemBatteryConsumer.Builder systemBatteryConsumerBuilder = + builder.getOrCreateSystemBatteryConsumerBuilder( + SystemBatteryConsumer.DRAIN_TYPE_CAMERA); + systemBatteryConsumerBuilder.setConsumedPower(10000); + systemBatteryConsumerBuilder.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 10100); + systemBatteryConsumerBuilder.setConsumedPowerForCustomComponent( + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200); + systemBatteryConsumerBuilder.setConsumedPowerForCustomComponent( + BatteryConsumer.FIRST_MODELED_POWER_COMPONENT_ID + + BatteryConsumer.POWER_COMPONENT_CPU, 10210); + systemBatteryConsumerBuilder.setUsageDurationMillis( + BatteryConsumer.TIME_COMPONENT_CPU, 10300); + systemBatteryConsumerBuilder.setUsageDurationForCustomComponentMillis( + BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID, 10400); + + return builder.build(); + } + + public void validateBatteryUsageStats(BatteryUsageStats batteryUsageStats) { + assertThat(batteryUsageStats.getConsumedPower()).isEqualTo(100); + assertThat(batteryUsageStats.getDischargePercentage()).isEqualTo(20); + + final List<UidBatteryConsumer> uidBatteryConsumers = + batteryUsageStats.getUidBatteryConsumers(); + for (UidBatteryConsumer uidBatteryConsumer : uidBatteryConsumers) { + if (uidBatteryConsumer.getUid() == 2000) { + assertThat(uidBatteryConsumer.getPackageWithHighestDrain()).isEqualTo("foo"); + assertThat(uidBatteryConsumer.getConsumedPower()).isEqualTo(200); + assertThat(uidBatteryConsumer.getConsumedPower( + BatteryConsumer.POWER_COMPONENT_USAGE)).isEqualTo(300); + assertThat(uidBatteryConsumer.getConsumedPower( + BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(400); + assertThat(uidBatteryConsumer.getConsumedPowerForCustomComponent( + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(500); + assertThat(uidBatteryConsumer.getConsumedPowerForCustomComponent( + BatteryConsumer.FIRST_MODELED_POWER_COMPONENT_ID + + BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(510); + assertThat(uidBatteryConsumer.getUsageDurationMillis( + BatteryConsumer.TIME_COMPONENT_CPU)).isEqualTo(600); + assertThat(uidBatteryConsumer.getUsageDurationMillis( + BatteryConsumer.TIME_COMPONENT_CPU_FOREGROUND)).isEqualTo(700); + assertThat(uidBatteryConsumer.getUsageDurationForCustomComponentMillis( + BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID)).isEqualTo(800); + } else { + fail("Unexpected UID " + uidBatteryConsumer.getUid()); + } + } + + final List<SystemBatteryConsumer> systemBatteryConsumers = + batteryUsageStats.getSystemBatteryConsumers(); + for (SystemBatteryConsumer systemBatteryConsumer : systemBatteryConsumers) { + if (systemBatteryConsumer.getDrainType() == SystemBatteryConsumer.DRAIN_TYPE_CAMERA) { + assertThat(systemBatteryConsumer.getConsumedPower()).isEqualTo(10000); + assertThat(systemBatteryConsumer.getConsumedPower( + BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(10100); + assertThat(systemBatteryConsumer.getConsumedPowerForCustomComponent( + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(10200); + assertThat(systemBatteryConsumer.getConsumedPowerForCustomComponent( + BatteryConsumer.FIRST_MODELED_POWER_COMPONENT_ID + + BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(10210); + assertThat(systemBatteryConsumer.getUsageDurationMillis( + BatteryConsumer.TIME_COMPONENT_CPU)).isEqualTo(10300); + assertThat(systemBatteryConsumer.getUsageDurationForCustomComponentMillis( + BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID)).isEqualTo(10400); + } else { + fail("Unexpected drain type " + systemBatteryConsumer.getDrainType()); + } + } + } +} diff --git a/data/keyboards/keyboards.mk b/data/keyboards/keyboards.mk index 68cbd29859bb..c7ce8cd6693a 100644 --- a/data/keyboards/keyboards.mk +++ b/data/keyboards/keyboards.mk @@ -14,13 +14,9 @@ # Warning: this is actually a product definition, to be inherited from -include $(LOCAL_PATH)/common.mk +PRODUCT_COPY_FILES := \ + $(call find-copy-subdir-files,*.kl,$(LOCAL_PATH),system/usr/keylayout) \ + $(call find-copy-subdir-files,*.kcm,$(LOCAL_PATH),system/usr/keychars) \ + $(call find-copy-subdir-files,*.idc,$(LOCAL_PATH),system/usr/idc) -PRODUCT_COPY_FILES := $(foreach file,$(framework_keylayouts),\ - $(file):system/usr/keylayout/$(notdir $(file))) -PRODUCT_COPY_FILES += $(foreach file,$(framework_keycharmaps),\ - $(file):system/usr/keychars/$(notdir $(file))) - -PRODUCT_COPY_FILES += $(foreach file,$(framework_keyconfigs),\ - $(file):system/usr/idc/$(notdir $(file))) diff --git a/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java index 0775a1a99886..56f7ea8725ee 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStore3DESCipherSpi.java @@ -41,7 +41,7 @@ import javax.crypto.spec.IvParameterSpec; * * @hide */ -public class AndroidKeyStore3DESCipherSpi extends AndroidKeyStoreCipherSpiBase { +public abstract class AndroidKeyStore3DESCipherSpi extends AndroidKeyStoreCipherSpiBase { private static final int BLOCK_SIZE_BYTES = 8; @@ -73,12 +73,22 @@ public class AndroidKeyStore3DESCipherSpi extends AndroidKeyStoreCipherSpiBase { public NoPadding() { super(KeymasterDefs.KM_PAD_NONE); } + + @Override + protected final String getTransform() { + return "DESede/ECB/NoPadding"; + } } public static class PKCS7Padding extends ECB { public PKCS7Padding() { super(KeymasterDefs.KM_PAD_PKCS7); } + + @Override + protected final String getTransform() { + return "DESede/ECB/PKCS7Padding"; + } } } @@ -91,12 +101,23 @@ public class AndroidKeyStore3DESCipherSpi extends AndroidKeyStoreCipherSpiBase { public NoPadding() { super(KeymasterDefs.KM_PAD_NONE); } + + @Override + protected final String getTransform() { + return "DESede/CBC/NoPadding"; + } + } public static class PKCS7Padding extends CBC { public PKCS7Padding() { super(KeymasterDefs.KM_PAD_PKCS7); } + + @Override + protected final String getTransform() { + return "DESede/CBC/PKCS7Padding"; + } } } @@ -288,7 +309,7 @@ public class AndroidKeyStore3DESCipherSpi extends AndroidKeyStoreCipherSpiBase { if (parameters != null) { for (KeyParameter p : parameters) { if (p.tag == KeymasterDefs.KM_TAG_NONCE) { - returnedIv = p.blob; + returnedIv = p.value.getBlob(); break; } } diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java index bc56f015f3bd..64da83778a45 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreAuthenticatedAESCipherSpi.java @@ -64,6 +64,11 @@ abstract class AndroidKeyStoreAuthenticatedAESCipherSpi extends AndroidKeyStoreC } @Override + protected final String getTransform() { + return "AES/GCM/NoPadding"; + } + + @Override protected final void resetAll() { mTagLengthBits = DEFAULT_TAG_LENGTH_BITS; super.resetAll(); @@ -325,7 +330,7 @@ abstract class AndroidKeyStoreAuthenticatedAESCipherSpi extends AndroidKeyStoreC if (parameters != null) { for (KeyParameter p : parameters) { if (p.tag == KeymasterDefs.KM_TAG_NONCE) { - returnedIv = p.blob; + returnedIv = p.value.getBlob(); break; } } diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreBCWorkaroundProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreBCWorkaroundProvider.java index dd943d422e62..9ad6f3adbd33 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreBCWorkaroundProvider.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreBCWorkaroundProvider.java @@ -254,13 +254,13 @@ class AndroidKeyStoreBCWorkaroundProvider extends Provider { private void putAsymmetricCipherImpl(String transformation, String implClass) { put("Cipher." + transformation, implClass); put("Cipher." + transformation + " SupportedKeyClasses", - KEYSTORE_PRIVATE_KEY_CLASS_NAME + "|" + KEYSTORE_PUBLIC_KEY_CLASS_NAME); + KEYSTORE_PRIVATE_KEY_CLASS_NAME); } private void putSignatureImpl(String algorithm, String implClass) { put("Signature." + algorithm, implClass); put("Signature." + algorithm + " SupportedKeyClasses", - KEYSTORE_PRIVATE_KEY_CLASS_NAME + "|" + KEYSTORE_PUBLIC_KEY_CLASS_NAME); + KEYSTORE_PRIVATE_KEY_CLASS_NAME); } public static String[] getSupportedEcdsaSignatureDigests() { diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java index a3b04abfba3f..2ee952cbc5fb 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java @@ -43,6 +43,7 @@ import java.security.PublicKey; import java.security.SecureRandom; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.InvalidKeySpecException; +import java.security.spec.MGF1ParameterSpec; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.ArrayList; @@ -57,6 +58,8 @@ import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.ShortBufferException; +import javax.crypto.spec.OAEPParameterSpec; +import javax.crypto.spec.PSource; import javax.crypto.spec.SecretKeySpec; /** @@ -99,6 +102,8 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor */ private Exception mCachedException; + private Cipher mCipher; + AndroidKeyStoreCipherSpiBase() { mOperation = null; mEncrypting = false; @@ -110,6 +115,7 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor mAdditionalAuthenticationDataStreamer = null; mAdditionalAuthenticationDataStreamerClosed = false; mCachedException = null; + mCipher = null; } @Override @@ -117,6 +123,45 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor throws InvalidKeyException { resetAll(); + if (!(key instanceof AndroidKeyStorePrivateKey + || key instanceof AndroidKeyStoreSecretKey)) { + try { + mCipher = Cipher.getInstance(getTransform()); + String transform = getTransform(); + + if ("RSA/ECB/OAEPWithSHA-224AndMGF1Padding".equals(transform)) { + OAEPParameterSpec spec = + new OAEPParameterSpec("SHA-224", "MGF1", + new MGF1ParameterSpec("SHA1"), PSource.PSpecified.DEFAULT); + mCipher.init(opmode, key, spec, random); + } else if ("RSA/ECB/OAEPWithSHA-256AndMGF1Padding".equals(transform)) { + OAEPParameterSpec spec = + new OAEPParameterSpec("SHA-256", "MGF1", + new MGF1ParameterSpec("SHA1"), PSource.PSpecified.DEFAULT); + mCipher.init(opmode, key, spec, random); + + } else if ("RSA/ECB/OAEPWithSHA-384AndMGF1Padding".equals(transform)) { + OAEPParameterSpec spec = + new OAEPParameterSpec("SHA-384", "MGF1", + new MGF1ParameterSpec("SHA1"), PSource.PSpecified.DEFAULT); + mCipher.init(opmode, key, spec, random); + + } else if ("RSA/ECB/OAEPWithSHA-512AndMGF1Padding".equals(transform)) { + OAEPParameterSpec spec = + new OAEPParameterSpec("SHA-512", "MGF1", + new MGF1ParameterSpec("SHA1"), PSource.PSpecified.DEFAULT); + mCipher.init(opmode, key, spec, random); + } else { + mCipher.init(opmode, key, random); + } + return; + } catch (NoSuchAlgorithmException + | NoSuchPaddingException + | InvalidAlgorithmParameterException e) { + throw new InvalidKeyException(e); + } + } + boolean success = false; try { init(opmode, key, random); @@ -139,6 +184,17 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { resetAll(); + if (!(key instanceof AndroidKeyStorePrivateKey + || key instanceof AndroidKeyStoreSecretKey)) { + try { + mCipher = Cipher.getInstance(getTransform()); + mCipher.init(opmode, key, params, random); + return; + } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { + throw new InvalidKeyException(e); + } + } + boolean success = false; try { init(opmode, key, random); @@ -157,6 +213,17 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { resetAll(); + if (!(key instanceof AndroidKeyStorePrivateKey + || key instanceof AndroidKeyStoreSecretKey)) { + try { + mCipher = Cipher.getInstance(getTransform()); + mCipher.init(opmode, key, params, random); + return; + } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { + throw new InvalidKeyException(e); + } + } + boolean success = false; try { init(opmode, key, random); @@ -214,6 +281,7 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor mAdditionalAuthenticationDataStreamer = null; mAdditionalAuthenticationDataStreamerClosed = false; mCachedException = null; + mCipher = null; } /** @@ -320,6 +388,10 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor @Override protected final byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) { + if (mCipher != null) { + return mCipher.update(input, inputOffset, inputLen); + } + if (mCachedException != null) { return null; } @@ -371,6 +443,9 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor @Override protected final int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) throws ShortBufferException { + if (mCipher != null) { + return mCipher.update(input, inputOffset, inputLen, output); + } byte[] outputCopy = engineUpdate(input, inputOffset, inputLen); if (outputCopy == null) { return 0; @@ -387,6 +462,10 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor @Override protected final int engineUpdate(ByteBuffer input, ByteBuffer output) throws ShortBufferException { + if (mCipher != null) { + return mCipher.update(input, output); + } + if (input == null) { throw new NullPointerException("input == null"); } @@ -423,6 +502,11 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor @Override protected final void engineUpdateAAD(byte[] input, int inputOffset, int inputLen) { + if (mCipher != null) { + mCipher.updateAAD(input, inputOffset, inputLen); + return; + } + if (mCachedException != null) { return; } @@ -459,6 +543,11 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor @Override protected final void engineUpdateAAD(ByteBuffer src) { + if (mCipher != null) { + mCipher.updateAAD(src); + return; + } + if (src == null) { throw new IllegalArgumentException("src == null"); } @@ -486,6 +575,10 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor @Override protected final byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen) throws IllegalBlockSizeException, BadPaddingException { + if (mCipher != null) { + return mCipher.doFinal(input, inputOffset, inputLen); + } + if (mCachedException != null) { throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(mCachedException); @@ -522,6 +615,10 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor protected final int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) throws ShortBufferException, IllegalBlockSizeException, BadPaddingException { + if (mCipher != null) { + return mCipher.doFinal(input, inputOffset, inputLen, output); + } + byte[] outputCopy = engineDoFinal(input, inputOffset, inputLen); if (outputCopy == null) { return 0; @@ -538,6 +635,10 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor @Override protected final int engineDoFinal(ByteBuffer input, ByteBuffer output) throws ShortBufferException, IllegalBlockSizeException, BadPaddingException { + if (mCipher != null) { + return mCipher.doFinal(input, output); + } + if (input == null) { throw new NullPointerException("input == null"); } @@ -575,6 +676,10 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor @Override protected final byte[] engineWrap(Key key) throws IllegalBlockSizeException, InvalidKeyException { + if (mCipher != null) { + return mCipher.wrap(key); + } + if (mKey == null) { throw new IllegalStateException("Not initilized"); } @@ -656,6 +761,10 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor @Override protected final Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, int wrappedKeyType) throws InvalidKeyException, NoSuchAlgorithmException { + if (mCipher != null) { + return mCipher.unwrap(wrappedKey, wrappedKeyAlgorithm, wrappedKeyType); + } + if (mKey == null) { throw new IllegalStateException("Not initilized"); } @@ -902,4 +1011,6 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor */ protected abstract void loadAlgorithmSpecificParametersFromBeginResult( KeyParameter[] parameters); + + protected abstract String getTransform(); } diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java index d1ef1df817e6..8289671de212 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreECDSASignatureSpi.java @@ -44,6 +44,11 @@ abstract class AndroidKeyStoreECDSASignatureSpi extends AndroidKeyStoreSignature } @Override + protected String getAlgorithm() { + return "NONEwithECDSA"; + } + + @Override protected KeyStoreCryptoOperationStreamer createMainDataStreamer( KeyStoreOperation operation) { return new TruncateToFieldSizeMessageStreamer( @@ -113,30 +118,50 @@ abstract class AndroidKeyStoreECDSASignatureSpi extends AndroidKeyStoreSignature public SHA1() { super(KeymasterDefs.KM_DIGEST_SHA1); } + @Override + protected String getAlgorithm() { + return "SHA1withECDSA"; + } } public final static class SHA224 extends AndroidKeyStoreECDSASignatureSpi { public SHA224() { super(KeymasterDefs.KM_DIGEST_SHA_2_224); } + @Override + protected String getAlgorithm() { + return "SHA224withECDSA"; + } } public final static class SHA256 extends AndroidKeyStoreECDSASignatureSpi { public SHA256() { super(KeymasterDefs.KM_DIGEST_SHA_2_256); } + @Override + protected String getAlgorithm() { + return "SHA256withECDSA"; + } } public final static class SHA384 extends AndroidKeyStoreECDSASignatureSpi { public SHA384() { super(KeymasterDefs.KM_DIGEST_SHA_2_384); } + @Override + protected String getAlgorithm() { + return "SHA384withECDSA"; + } } public final static class SHA512 extends AndroidKeyStoreECDSASignatureSpi { public SHA512() { super(KeymasterDefs.KM_DIGEST_SHA_2_512); } + @Override + protected String getAlgorithm() { + return "SHA512withECDSA"; + } } private final int mKeymasterDigest; diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java index b2e32a3175e3..403da189262d 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java @@ -31,9 +31,7 @@ import android.system.keystore2.KeyEntryResponse; import android.system.keystore2.KeyMetadata; import android.system.keystore2.ResponseCode; -import java.security.KeyFactory; import java.security.KeyPair; -import java.security.NoSuchAlgorithmException; import java.security.Provider; import java.security.ProviderException; import java.security.PublicKey; @@ -42,8 +40,6 @@ import java.security.Signature; import java.security.UnrecoverableKeyException; import java.security.interfaces.ECPublicKey; import java.security.interfaces.RSAPublicKey; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.X509EncodedKeySpec; import javax.crypto.Cipher; import javax.crypto.Mac; @@ -237,28 +233,11 @@ public class AndroidKeyStoreProvider extends Provider { throw new UnrecoverableKeyException("Failed to obtain X.509 form of public key." + " Keystore has no public certificate stored."); } - final byte[] x509EncodedPublicKey = metadata.certificate; + final byte[] x509PublicCert = metadata.certificate; - String jcaKeyAlgorithm; - try { - jcaKeyAlgorithm = KeyProperties.KeyAlgorithm.fromKeymasterAsymmetricKeyAlgorithm( - algorithm); - } catch (IllegalArgumentException e) { - throw (UnrecoverableKeyException) - new UnrecoverableKeyException("Failed to load private key") - .initCause(e); - } + PublicKey publicKey = AndroidKeyStoreSpi.toCertificate(x509PublicCert).getPublicKey(); - PublicKey publicKey; - try { - KeyFactory keyFactory = KeyFactory.getInstance(jcaKeyAlgorithm); - publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(x509EncodedPublicKey)); - } catch (NoSuchAlgorithmException e) { - throw new ProviderException( - "Failed to obtain " + jcaKeyAlgorithm + " KeyFactory", e); - } catch (InvalidKeySpecException e) { - throw new ProviderException("Invalid X.509 encoding of public key", e); - } + String jcaKeyAlgorithm = publicKey.getAlgorithm(); KeyStoreSecurityLevel securityLevel = iSecurityLevel; if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(jcaKeyAlgorithm)) { @@ -358,7 +337,7 @@ public class AndroidKeyStoreProvider extends Provider { KeyDescriptor descriptor = new KeyDescriptor(); if (namespace == KeyProperties.NAMESPACE_APPLICATION) { - descriptor.nspace = 0; // ignored; + descriptor.nspace = KeyProperties.NAMESPACE_APPLICATION; // ignored; descriptor.domain = Domain.APP; } else { descriptor.nspace = namespace; @@ -387,10 +366,10 @@ public class AndroidKeyStoreProvider extends Provider { for (Authorization a : response.metadata.authorizations) { switch (a.keyParameter.tag) { case KeymasterDefs.KM_TAG_ALGORITHM: - keymasterAlgorithm = a.keyParameter.integer; + keymasterAlgorithm = a.keyParameter.value.getAlgorithm(); break; case KeymasterDefs.KM_TAG_DIGEST: - if (keymasterDigest == -1) keymasterDigest = a.keyParameter.integer; + if (keymasterDigest == -1) keymasterDigest = a.keyParameter.value.getDigest(); break; } } @@ -407,7 +386,7 @@ public class AndroidKeyStoreProvider extends Provider { keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_EC) { return makeAndroidKeyStorePublicKeyFromKeyEntryResponse(descriptor, response.metadata, new KeyStoreSecurityLevel(response.iSecurityLevel), - keymasterAlgorithm); + keymasterAlgorithm).getPrivateKey(); } else { throw new UnrecoverableKeyException("Key algorithm unknown"); } diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java index 951f91887894..6ff9432905ed 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java @@ -158,7 +158,7 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase } /** - * RSA cipher with OAEP encryption padding. Only SHA-1 based MGF1 is supported as MGF. + * RSA cipher with OAEP encryption padding. */ abstract static class OAEPWithMGF1Padding extends AndroidKeyStoreRSACipherSpi { @@ -316,6 +316,25 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase protected final int getAdditionalEntropyAmountForFinish() { return (isEncrypting()) ? mDigestOutputSizeBytes : 0; } + + @Override + protected final String getTransform() { + switch (mKeymasterDigest) { + case KeymasterDefs.KM_DIGEST_SHA1: + return "RSA/ECB/OAEPWithSHA-1AndMGF1Padding"; + case KeymasterDefs.KM_DIGEST_SHA_2_224: + return "RSA/ECB/OAEPWithSHA-224AndMGF1Padding"; + case KeymasterDefs.KM_DIGEST_SHA_2_256: + return "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"; + case KeymasterDefs.KM_DIGEST_SHA_2_384: + return "RSA/ECB/OAEPWithSHA-384AndMGF1Padding"; + case KeymasterDefs.KM_DIGEST_SHA_2_512: + return "RSA/ECB/OAEPWithSHA-512AndMGF1Padding"; + default: + return "RSA/ECB/OAEPPadding"; + } + } + } public static class OAEPWithSHA1AndMGF1Padding extends OAEPWithMGF1Padding { @@ -358,6 +377,11 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase } @Override + protected String getTransform() { + return "RSA/ECB/" + KeyProperties.EncryptionPadding.fromKeymaster(mKeymasterPadding); + } + + @Override protected final void initKey(int opmode, Key key) throws InvalidKeyException { if (key == null) { throw new InvalidKeyException("Unsupported key: null"); diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java index ab7559116a41..931c2f864eba 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreRSASignatureSpi.java @@ -48,42 +48,70 @@ abstract class AndroidKeyStoreRSASignatureSpi extends AndroidKeyStoreSignatureSp public NONEWithPKCS1Padding() { super(KeymasterDefs.KM_DIGEST_NONE); } + @Override + protected String getAlgorithm() { + return "NONEwithRSA"; + } } public static final class MD5WithPKCS1Padding extends PKCS1Padding { public MD5WithPKCS1Padding() { super(KeymasterDefs.KM_DIGEST_MD5); } + @Override + protected String getAlgorithm() { + return "MD5withRSA"; + } } public static final class SHA1WithPKCS1Padding extends PKCS1Padding { public SHA1WithPKCS1Padding() { super(KeymasterDefs.KM_DIGEST_SHA1); } + @Override + protected String getAlgorithm() { + return "SHA1withRSA"; + } } public static final class SHA224WithPKCS1Padding extends PKCS1Padding { public SHA224WithPKCS1Padding() { super(KeymasterDefs.KM_DIGEST_SHA_2_224); } + @Override + protected String getAlgorithm() { + return "SHA224withRSA"; + } } public static final class SHA256WithPKCS1Padding extends PKCS1Padding { public SHA256WithPKCS1Padding() { super(KeymasterDefs.KM_DIGEST_SHA_2_256); } + @Override + protected String getAlgorithm() { + return "SHA256withRSA"; + } } public static final class SHA384WithPKCS1Padding extends PKCS1Padding { public SHA384WithPKCS1Padding() { super(KeymasterDefs.KM_DIGEST_SHA_2_384); } + @Override + protected String getAlgorithm() { + return "SHA384withRSA"; + } } public static final class SHA512WithPKCS1Padding extends PKCS1Padding { public SHA512WithPKCS1Padding() { super(KeymasterDefs.KM_DIGEST_SHA_2_512); } + @Override + protected String getAlgorithm() { + return "SHA512withRSA"; + } } abstract static class PSSPadding extends AndroidKeyStoreRSASignatureSpi { @@ -103,30 +131,50 @@ abstract class AndroidKeyStoreRSASignatureSpi extends AndroidKeyStoreSignatureSp public SHA1WithPSSPadding() { super(KeymasterDefs.KM_DIGEST_SHA1); } + @Override + protected String getAlgorithm() { + return "SHA1withRSA/PSS"; + } } public static final class SHA224WithPSSPadding extends PSSPadding { public SHA224WithPSSPadding() { super(KeymasterDefs.KM_DIGEST_SHA_2_224); } + @Override + protected String getAlgorithm() { + return "SHA224withRSA/PSS"; + } } public static final class SHA256WithPSSPadding extends PSSPadding { public SHA256WithPSSPadding() { super(KeymasterDefs.KM_DIGEST_SHA_2_256); } + @Override + protected String getAlgorithm() { + return "SHA256withRSA/PSS"; + } } public static final class SHA384WithPSSPadding extends PSSPadding { public SHA384WithPSSPadding() { super(KeymasterDefs.KM_DIGEST_SHA_2_384); } + @Override + protected String getAlgorithm() { + return "SHA384withRSA/PSS"; + } } public static final class SHA512WithPSSPadding extends PSSPadding { public SHA512WithPSSPadding() { super(KeymasterDefs.KM_DIGEST_SHA_2_512); } + @Override + protected String getAlgorithm() { + return "SHA512withRSA/PSS"; + } } private final int mKeymasterDigest; diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java index 9d3b9704d711..74503e1eecfa 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java @@ -102,7 +102,8 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { insideSecureHardware = KeyStore2ParameterUtils.isSecureHardware(a.securityLevel); securityLevel = a.securityLevel; - origin = KeyProperties.Origin.fromKeymaster(a.keyParameter.integer); + origin = KeyProperties.Origin.fromKeymaster( + a.keyParameter.value.getOrigin()); break; case KeymasterDefs.KM_TAG_KEY_SIZE: long keySizeUnsigned = KeyStore2ParameterUtils.getUnsignedInt(a); @@ -113,45 +114,51 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { keySize = (int) keySizeUnsigned; break; case KeymasterDefs.KM_TAG_PURPOSE: - purposes |= KeyProperties.Purpose.fromKeymaster(a.keyParameter.integer); + purposes |= KeyProperties.Purpose.fromKeymaster( + a.keyParameter.value.getKeyPurpose()); break; case KeymasterDefs.KM_TAG_PADDING: + int paddingMode = a.keyParameter.value.getPaddingMode(); try { - if (a.keyParameter.integer == KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN - || a.keyParameter.integer == KeymasterDefs.KM_PAD_RSA_PSS) { + if (paddingMode == KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN + || paddingMode == KeymasterDefs.KM_PAD_RSA_PSS) { @KeyProperties.SignaturePaddingEnum String padding = KeyProperties.SignaturePadding.fromKeymaster( - a.keyParameter.integer); + paddingMode); signaturePaddingsList.add(padding); } else { @KeyProperties.EncryptionPaddingEnum String jcaPadding = KeyProperties.EncryptionPadding.fromKeymaster( - a.keyParameter.integer); + paddingMode); encryptionPaddingsList.add(jcaPadding); } } catch (IllegalArgumentException e) { throw new ProviderException("Unsupported padding: " - + a.keyParameter.integer); + + paddingMode); } break; case KeymasterDefs.KM_TAG_DIGEST: - digestsList.add(KeyProperties.Digest.fromKeymaster(a.keyParameter.integer)); + digestsList.add(KeyProperties.Digest.fromKeymaster( + a.keyParameter.value.getDigest())); break; case KeymasterDefs.KM_TAG_BLOCK_MODE: blockModesList.add( - KeyProperties.BlockMode.fromKeymaster(a.keyParameter.integer) + KeyProperties.BlockMode.fromKeymaster( + a.keyParameter.value.getBlockMode()) ); break; case KeymasterDefs.KM_TAG_USER_AUTH_TYPE: + int authenticatorType = a.keyParameter.value.getHardwareAuthenticatorType(); if (KeyStore2ParameterUtils.isSecureHardware(a.securityLevel)) { - keymasterHwEnforcedUserAuthenticators = a.keyParameter.integer; + keymasterHwEnforcedUserAuthenticators = authenticatorType; } else { - keymasterSwEnforcedUserAuthenticators = a.keyParameter.integer; + keymasterSwEnforcedUserAuthenticators = authenticatorType; } break; case KeymasterDefs.KM_TAG_USER_SECURE_ID: keymasterSecureUserIds.add( - KeymasterArguments.toUint64(a.keyParameter.longInteger)); + KeymasterArguments.toUint64( + a.keyParameter.value.getLongInteger())); break; case KeymasterDefs.KM_TAG_ACTIVE_DATETIME: keyValidityStart = KeyStore2ParameterUtils.getDate(a); diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java index 9b4f01e744f7..96da1e00ade8 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSignatureSpiBase.java @@ -30,10 +30,12 @@ import libcore.util.EmptyArray; import java.nio.ByteBuffer; import java.security.InvalidKeyException; import java.security.InvalidParameterException; +import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.ProviderException; import java.security.PublicKey; import java.security.SecureRandom; +import java.security.Signature; import java.security.SignatureException; import java.security.SignatureSpi; import java.util.ArrayList; @@ -76,6 +78,13 @@ abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi */ private Exception mCachedException; + /** + * This signature object is used for public key operations, i.e, signatrue verification. + * The Android Keystore backend does not perform public key operations and defers to the + * Highest priority provider. + */ + private Signature mSignature; + AndroidKeyStoreSignatureSpiBase() { mOperation = null; mOperationChallenge = 0; @@ -84,6 +93,7 @@ abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi appRandom = null; mMessageStreamer = null; mCachedException = null; + mSignature = null; } @Override @@ -123,27 +133,13 @@ abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi protected final void engineInitVerify(PublicKey publicKey) throws InvalidKeyException { resetAll(); - boolean success = false; try { - if (publicKey == null) { - throw new InvalidKeyException("Unsupported key: null"); - } - AndroidKeyStoreKey keystoreKey; - if (publicKey instanceof AndroidKeyStorePublicKey) { - keystoreKey = (AndroidKeyStorePublicKey) publicKey; - } else { - throw new InvalidKeyException("Unsupported public key type: " + publicKey); - } - mSigning = false; - initKey(keystoreKey); - appRandom = null; - ensureKeystoreOperationInitialized(); - success = true; - } finally { - if (!success) { - resetAll(); - } + mSignature = Signature.getInstance(getAlgorithm()); + } catch (NoSuchAlgorithmException e) { + throw new InvalidKeyException(e); } + + mSignature.initVerify(publicKey); } /** @@ -251,6 +247,11 @@ abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi @Override protected final void engineUpdate(byte[] b, int off, int len) throws SignatureException { + if (mSignature != null) { + mSignature.update(b, off, len); + return; + } + if (mCachedException != null) { throw new SignatureException(mCachedException); } @@ -337,39 +338,10 @@ abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi @Override protected final boolean engineVerify(byte[] signature) throws SignatureException { - if (mCachedException != null) { - throw new SignatureException(mCachedException); - } - - try { - ensureKeystoreOperationInitialized(); - } catch (InvalidKeyException e) { - throw new SignatureException(e); - } - - boolean verified; - try { - byte[] output = mMessageStreamer.doFinal( - EmptyArray.BYTE, 0, 0, - signature); - if (output.length != 0) { - throw new ProviderException( - "Signature verification unexpected produced output: " + output.length - + " bytes"); - } - verified = true; - } catch (KeyStoreException e) { - switch (e.getErrorCode()) { - case KeymasterDefs.KM_ERROR_VERIFICATION_FAILED: - verified = false; - break; - default: - throw new SignatureException(e); - } + if (mSignature != null) { + return mSignature.verify(signature); } - - resetWhilePreservingInitState(); - return verified; + throw new IllegalStateException("Not initialised."); } @Override @@ -392,6 +364,13 @@ abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi } /** + * Implementations need to report the algorithm they implement so that we can delegate to the + * highest priority provider. + * @return Algorithm string. + */ + protected abstract String getAlgorithm(); + + /** * Returns {@code true} if this signature is initialized for signing, {@code false} if this * signature is initialized for verification. */ diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java index c42c7b2c41b0..5e7f6482ebed 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java @@ -219,7 +219,7 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi { return null; } - private static X509Certificate toCertificate(byte[] bytes) { + static X509Certificate toCertificate(byte[] bytes) { try { final CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); return (X509Certificate) certFactory.generateCertificate( diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java index 4d4b0d8f183b..5c048a127cb3 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreUnauthenticatedAESCipherSpi.java @@ -42,7 +42,7 @@ import javax.crypto.spec.IvParameterSpec; * * @hide */ -class AndroidKeyStoreUnauthenticatedAESCipherSpi extends AndroidKeyStoreCipherSpiBase { +abstract class AndroidKeyStoreUnauthenticatedAESCipherSpi extends AndroidKeyStoreCipherSpiBase { abstract static class ECB extends AndroidKeyStoreUnauthenticatedAESCipherSpi { protected ECB(int keymasterPadding) { @@ -53,12 +53,22 @@ class AndroidKeyStoreUnauthenticatedAESCipherSpi extends AndroidKeyStoreCipherSp public NoPadding() { super(KeymasterDefs.KM_PAD_NONE); } + + @Override + protected final String getTransform() { + return "AES/ECB/NoPadding"; + } } public static class PKCS7Padding extends ECB { public PKCS7Padding() { super(KeymasterDefs.KM_PAD_PKCS7); } + + @Override + protected final String getTransform() { + return "AES/ECB/PKCS7Padding"; + } } } @@ -71,12 +81,22 @@ class AndroidKeyStoreUnauthenticatedAESCipherSpi extends AndroidKeyStoreCipherSp public NoPadding() { super(KeymasterDefs.KM_PAD_NONE); } + + @Override + protected final String getTransform() { + return "AES/CBC/NoPadding"; + } } public static class PKCS7Padding extends CBC { public PKCS7Padding() { super(KeymasterDefs.KM_PAD_PKCS7); } + + @Override + protected final String getTransform() { + return "AES/CBC/PKCS7Padding"; + } } } @@ -89,6 +109,11 @@ class AndroidKeyStoreUnauthenticatedAESCipherSpi extends AndroidKeyStoreCipherSp public NoPadding() { super(KeymasterDefs.KM_PAD_NONE); } + + @Override + protected final String getTransform() { + return "AES/CTR/NoPadding"; + } } } @@ -275,7 +300,7 @@ class AndroidKeyStoreUnauthenticatedAESCipherSpi extends AndroidKeyStoreCipherSp if (parameters != null) { for (KeyParameter p : parameters) { if (p.tag == KeymasterDefs.KM_TAG_NONCE) { - returnedIv = p.blob; + returnedIv = p.value.getBlob(); break; } } diff --git a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java index 18c786aa3093..4c8ab8d6c713 100644 --- a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java +++ b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java @@ -19,7 +19,9 @@ package android.security.keystore2; import android.annotation.NonNull; import android.hardware.biometrics.BiometricManager; import android.hardware.security.keymint.KeyParameter; +import android.hardware.security.keymint.KeyParameterValue; import android.hardware.security.keymint.SecurityLevel; +import android.hardware.security.keymint.Tag; import android.security.GateKeeper; import android.security.keymaster.KeymasterDefs; import android.security.keystore.KeyProperties; @@ -50,7 +52,7 @@ public abstract class KeyStore2ParameterUtils { } KeyParameter p = new KeyParameter(); p.tag = tag; - p.boolValue = true; + p.value = KeyParameterValue.boolValue(true); return p; } @@ -62,14 +64,40 @@ public abstract class KeyStore2ParameterUtils { * @hide */ static @NonNull KeyParameter makeEnum(int tag, int v) { - int type = KeymasterDefs.getTagType(tag); - if (type != KeymasterDefs.KM_ENUM && type != KeymasterDefs.KM_ENUM_REP) { - throw new IllegalArgumentException("Not an enum or repeatable enum tag: " + tag); + KeyParameter kp = new KeyParameter(); + kp.tag = tag; + switch (tag) { + case Tag.PURPOSE: + kp.value = KeyParameterValue.keyPurpose(v); + break; + case Tag.ALGORITHM: + kp.value = KeyParameterValue.algorithm(v); + break; + case Tag.BLOCK_MODE: + kp.value = KeyParameterValue.blockMode(v); + break; + case Tag.DIGEST: + kp.value = KeyParameterValue.digest(v); + break; + case Tag.EC_CURVE: + kp.value = KeyParameterValue.ecCurve(v); + break; + case Tag.ORIGIN: + kp.value = KeyParameterValue.origin(v); + break; + case Tag.PADDING: + kp.value = KeyParameterValue.paddingMode(v); + break; + case Tag.USER_AUTH_TYPE: + kp.value = KeyParameterValue.hardwareAuthenticatorType(v); + break; + case Tag.HARDWARE_TYPE: + kp.value = KeyParameterValue.securityLevel(v); + break; + default: + throw new IllegalArgumentException("Not an enum or repeatable enum tag: " + tag); } - KeyParameter p = new KeyParameter(); - p.tag = tag; - p.integer = v; - return p; + return kp; } /** @@ -86,7 +114,7 @@ public abstract class KeyStore2ParameterUtils { } KeyParameter p = new KeyParameter(); p.tag = tag; - p.integer = v; + p.value = KeyParameterValue.integer(v); return p; } @@ -104,7 +132,7 @@ public abstract class KeyStore2ParameterUtils { } KeyParameter p = new KeyParameter(); p.tag = tag; - p.longInteger = v; + p.value = KeyParameterValue.longInteger(v); return p; } @@ -121,7 +149,7 @@ public abstract class KeyStore2ParameterUtils { } KeyParameter p = new KeyParameter(); p.tag = tag; - p.blob = b; + p.value = KeyParameterValue.blob(b); return p; } @@ -138,9 +166,10 @@ public abstract class KeyStore2ParameterUtils { } KeyParameter p = new KeyParameter(); p.tag = tag; - p.longInteger = date.getTime(); - if (p.longInteger < 0) { - throw new IllegalArgumentException("Date tag value out of range: " + p.longInteger); + p.value = KeyParameterValue.dateTime(date.getTime()); + if (p.value.getDateTime() < 0) { + throw new IllegalArgumentException("Date tag value out of range: " + + p.value.getDateTime()); } return p; } @@ -160,24 +189,24 @@ public abstract class KeyStore2ParameterUtils { throw new IllegalArgumentException("Not an int tag: " + param.keyParameter.tag); } // KM_UINT is 32 bits wide so we must suppress sign extension. - return ((long) param.keyParameter.integer) & 0xffffffffL; + return ((long) param.keyParameter.value.getInteger()) & 0xffffffffL; } static @NonNull Date getDate(@NonNull Authorization param) { if (KeymasterDefs.getTagType(param.keyParameter.tag) != KeymasterDefs.KM_DATE) { throw new IllegalArgumentException("Not a date tag: " + param.keyParameter.tag); } - if (param.keyParameter.longInteger < 0) { + if (param.keyParameter.value.getDateTime() < 0) { throw new IllegalArgumentException("Date Value too large: " - + param.keyParameter.longInteger); + + param.keyParameter.value.getDateTime()); } - return new Date(param.keyParameter.longInteger); + return new Date(param.keyParameter.value.getDateTime()); } static void forEachSetFlag(int flags, Consumer<Integer> consumer) { int offset = 0; while (flags != 0) { - if ((flags & 1) == 0) { + if ((flags & 1) == 1) { consumer.accept(1 << offset); } offset += 1; diff --git a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java index 3b11854bf7cb..f87a3d25f90c 100644 --- a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java +++ b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java @@ -57,7 +57,7 @@ abstract class KeyStoreCryptoOperationUtils { for (Authorization p : key.getAuthorizations()) { switch(p.keyParameter.tag) { case KeymasterDefs.KM_TAG_USER_SECURE_ID: - keySids.add(p.keyParameter.longInteger); + keySids.add(p.keyParameter.value.getLongInteger()); break; default: break; diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt index d580104ade19..c546a4d18027 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt @@ -20,7 +20,6 @@ import android.app.Instrumentation import androidx.test.uiautomator.By import androidx.test.uiautomator.Until import com.android.server.wm.flicker.helpers.FIND_TIMEOUT -import com.android.server.wm.flicker.helpers.waitForIME import com.android.wm.shell.flicker.TEST_APP_IME_ACTIVITY_LABEL import com.android.wm.shell.flicker.testapp.Components import org.junit.Assert @@ -39,14 +38,9 @@ open class ImeAppHelper( Assert.assertNotNull("Text field not found, this usually happens when the device " + "was left in an unknown state (e.g. in split screen)", editText) editText.click() - if (!uiDevice.waitForIME()) { - Assert.fail("IME did not appear") - } } fun closeIME() { uiDevice.pressBack() - // Using only the AccessibilityInfo it is not possible to identify if the IME is active - uiDevice.waitForIdle(1000) } }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt index 6b44ce6ace0c..866d654362ca 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt @@ -25,8 +25,10 @@ import com.android.server.wm.flicker.helpers.WindowUtils import com.android.server.wm.flicker.helpers.closePipWindow import com.android.server.wm.flicker.helpers.hasPipWindow import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen +import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import com.android.wm.shell.flicker.IME_WINDOW_NAME import com.android.wm.shell.flicker.helpers.ImeAppHelper +import com.android.wm.shell.flicker.testapp.Components import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -46,6 +48,8 @@ class PipKeyboardTest( rotation: Int ) : PipTestBase(rotationName, rotation) { private val keyboardApp = ImeAppHelper(instrumentation) + private val keyboardComponent = Components.ImeActivity().componentName + private val helper = WindowManagerStateHelper() private val keyboardScenario: FlickerBuilder get() = FlickerBuilder(instrumentation).apply { @@ -64,6 +68,8 @@ class PipKeyboardTest( // UiAutomator doesn't support to launch the multiple Activities in a task. // So use launchActivity() for the Keyboard Activity. keyboardApp.launchViaIntent() + helper.waitForAppTransitionIdle() + helper.waitForFullScreenApp(keyboardComponent) } } teardown { @@ -88,9 +94,11 @@ class PipKeyboardTest( transitions { // open the soft keyboard keyboardApp.openIME() + helper.waitImeWindowShown() // then close it again keyboardApp.closeIME() + helper.waitImeWindowGone() } assertions { windowManagerTrace { @@ -112,11 +120,13 @@ class PipKeyboardTest( transitions { // open the soft keyboard keyboardApp.openIME() + helper.waitImeWindowShown() } teardown { eachRun { // close the keyboard keyboardApp.closeIME() + helper.waitImeWindowGone() } } assertions { diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index a545b3d5e134..bec80a7d605e 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -670,7 +670,7 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal( } auto entry_flags = type_spec->GetFlagsForEntryIndex(entry_idx); - if (UNLIKELY(!entry_flags)) { + if (UNLIKELY(!entry_flags.has_value())) { return base::unexpected(entry_flags.error()); } type_flags |= entry_flags.value(); diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 4010e4e10317..bce70e2aae9e 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -897,12 +897,12 @@ base::expected<StringPiece, NullOrIOError> ResStringPool::string8At(size_t idx) // Decode the UTF-16 length. This is not used if we're not // converting to UTF-16 from UTF-8. const base::expected<size_t, IOError> u16len = decodeLength(&str); - if (UNLIKELY(!u16len)) { + if (UNLIKELY(!u16len.has_value())) { return base::unexpected(u16len.error()); } const base::expected<size_t, IOError> u8len = decodeLength(&str); - if (UNLIKELY(!u8len)) { + if (UNLIKELY(!u8len.has_value())) { return base::unexpected(u8len.error()); } diff --git a/libs/androidfw/ResourceUtils.cpp b/libs/androidfw/ResourceUtils.cpp index a34aa7239250..87fb2c038c9f 100644 --- a/libs/androidfw/ResourceUtils.cpp +++ b/libs/androidfw/ResourceUtils.cpp @@ -56,7 +56,8 @@ base::expected<AssetManager2::ResourceName, NullOrIOError> ToResourceName( .package_len = package_name.size(), }; - if (base::expected<StringPiece, NullOrIOError> type_str = type_string_ref.string8()) { + if (base::expected<StringPiece, NullOrIOError> type_str = type_string_ref.string8(); + type_str.ok()) { name.type = type_str->data(); name.type_len = type_str->size(); } else if (UNLIKELY(IsIOError(type_str))) { @@ -64,7 +65,8 @@ base::expected<AssetManager2::ResourceName, NullOrIOError> ToResourceName( } if (name.type == nullptr) { - if (base::expected<StringPiece16, NullOrIOError> type16_str = type_string_ref.string16()) { + if (base::expected<StringPiece16, NullOrIOError> type16_str = type_string_ref.string16(); + type16_str.ok()) { name.type16 = type16_str->data(); name.type_len = type16_str->size(); } else if (!type16_str.has_value()) { @@ -72,7 +74,8 @@ base::expected<AssetManager2::ResourceName, NullOrIOError> ToResourceName( } } - if (base::expected<StringPiece, NullOrIOError> entry_str = entry_string_ref.string8()) { + if (base::expected<StringPiece, NullOrIOError> entry_str = entry_string_ref.string8(); + entry_str.ok()) { name.entry = entry_str->data(); name.entry_len = entry_str->size(); } else if (UNLIKELY(IsIOError(entry_str))) { @@ -80,7 +83,8 @@ base::expected<AssetManager2::ResourceName, NullOrIOError> ToResourceName( } if (name.entry == nullptr) { - if (base::expected<StringPiece16, NullOrIOError> entry16_str = entry_string_ref.string16()) { + if (base::expected<StringPiece16, NullOrIOError> entry16_str = entry_string_ref.string16(); + entry16_str.ok()) { name.entry16 = entry16_str->data(); name.entry_len = entry16_str->size(); } else if (!entry16_str.has_value()) { diff --git a/media/java/android/media/AudioPlaybackConfiguration.java b/media/java/android/media/AudioPlaybackConfiguration.java index b9f449c20677..17305a501cd0 100644 --- a/media/java/android/media/AudioPlaybackConfiguration.java +++ b/media/java/android/media/AudioPlaybackConfiguration.java @@ -21,7 +21,6 @@ import static android.media.AudioAttributes.ALLOW_CAPTURE_BY_NONE; import android.annotation.IntDef; import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.SystemApi; import android.os.Binder; import android.os.IBinder; @@ -48,8 +47,6 @@ public final class AudioPlaybackConfiguration implements Parcelable { public static final int PLAYER_PIID_INVALID = -1; /** @hide */ public static final int PLAYER_UPID_INVALID = -1; - /** @hide */ - public static final int PLAYER_DEVICEID_INVALID = 0; // information about the implementation /** @@ -161,11 +158,6 @@ public final class AudioPlaybackConfiguration implements Parcelable { */ @SystemApi public static final int PLAYER_STATE_STOPPED = 4; - /** - * @hide - * The state used to update device id, does not actually change the state of the player - */ - public static final int PLAYER_UPDATE_DEVICE_ID = 5; /** @hide */ @IntDef({ @@ -174,8 +166,7 @@ public final class AudioPlaybackConfiguration implements Parcelable { PLAYER_STATE_IDLE, PLAYER_STATE_STARTED, PLAYER_STATE_PAUSED, - PLAYER_STATE_STOPPED, - PLAYER_UPDATE_DEVICE_ID + PLAYER_STATE_STOPPED }) @Retention(RetentionPolicy.SOURCE) public @interface PlayerState {} @@ -193,8 +184,6 @@ public final class AudioPlaybackConfiguration implements Parcelable { private int mPlayerState; private AudioAttributes mPlayerAttr; // never null - private int mDeviceId; - /** * Never use without initializing parameters afterwards */ @@ -212,7 +201,6 @@ public final class AudioPlaybackConfiguration implements Parcelable { mPlayerType = pic.mPlayerType; mClientUid = uid; mClientPid = pid; - mDeviceId = PLAYER_DEVICEID_INVALID; mPlayerState = PLAYER_STATE_IDLE; mPlayerAttr = pic.mAttributes; if ((sPlayerDeathMonitor != null) && (pic.mIPlayer != null)) { @@ -253,7 +241,6 @@ public final class AudioPlaybackConfiguration implements Parcelable { in.mPlayerAttr.getAllowedCapturePolicy() == ALLOW_CAPTURE_BY_ALL ? ALLOW_CAPTURE_BY_ALL : ALLOW_CAPTURE_BY_NONE) .build(); - anonymCopy.mDeviceId = in.mDeviceId; // anonymized data anonymCopy.mPlayerType = PLAYER_TYPE_UNKNOWN; anonymCopy.mClientUid = PLAYER_UPID_INVALID; @@ -291,25 +278,6 @@ public final class AudioPlaybackConfiguration implements Parcelable { } /** - * Returns information about the {@link AudioDeviceInfo} used for this playback. - * @return the audio playback device or null if the device is not available at the time of query - */ - public @Nullable AudioDeviceInfo getAudioDevice() { - if (mDeviceId == PLAYER_DEVICEID_INVALID) { - return null; - } - // TODO(175802592): change this to AudioManager.getDeviceForPortId() when available - AudioDeviceInfo[] devices = - AudioManager.getDevicesStatic(AudioManager.GET_DEVICES_OUTPUTS); - for (int i = 0; i < devices.length; i++) { - if (devices[i].getId() == mDeviceId) { - return devices[i]; - } - } - return null; - } - - /** * @hide * Return the type of player linked to this configuration. * <br>Note that player types not exposed in the system API will be represented as @@ -391,29 +359,13 @@ public final class AudioPlaybackConfiguration implements Parcelable { * @hide * Handle a player state change * @param event - * @param deviceId active device id or {@Code PLAYER_DEVICEID_INVALID} - * <br>Note device id is valid for {@code PLAYER_UPDATE_DEVICE_ID} or - * <br>{@code PLAYER_STATE_STARTED} events, as the device id will be reset to none when - * <br>pausing or stopping playback. It will be set to active device when playback starts or - * <br>it will be changed when PLAYER_UPDATE_DEVICE_ID is sent. The latter can happen if the - * <br>device changes in the middle of playback. * @return true if the state changed, false otherwise */ - public boolean handleStateEvent(int event, int deviceId) { - boolean changed = false; + public boolean handleStateEvent(int event) { + final boolean changed; synchronized (this) { - - // Do not update if it is only device id update - if (event != PLAYER_UPDATE_DEVICE_ID) { - changed = (mPlayerState != event); - mPlayerState = event; - } - - if (event == PLAYER_STATE_STARTED || event == PLAYER_UPDATE_DEVICE_ID) { - changed = changed || (mDeviceId != deviceId); - mDeviceId = deviceId; - } - + changed = (mPlayerState != event); + mPlayerState = event; if (changed && (event == PLAYER_STATE_RELEASED) && (mIPlayerShell != null)) { mIPlayerShell.release(); mIPlayerShell = null; @@ -484,7 +436,7 @@ public final class AudioPlaybackConfiguration implements Parcelable { @Override public int hashCode() { - return Objects.hash(mPlayerIId, mDeviceId, mPlayerType, mClientUid, mClientPid); + return Objects.hash(mPlayerIId, mPlayerType, mClientUid, mClientPid); } @Override @@ -495,7 +447,6 @@ public final class AudioPlaybackConfiguration implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mPlayerIId); - dest.writeInt(mDeviceId); dest.writeInt(mPlayerType); dest.writeInt(mClientUid); dest.writeInt(mClientPid); @@ -510,7 +461,6 @@ public final class AudioPlaybackConfiguration implements Parcelable { private AudioPlaybackConfiguration(Parcel in) { mPlayerIId = in.readInt(); - mDeviceId = in.readInt(); mPlayerType = in.readInt(); mClientUid = in.readInt(); mClientPid = in.readInt(); @@ -528,7 +478,6 @@ public final class AudioPlaybackConfiguration implements Parcelable { AudioPlaybackConfiguration that = (AudioPlaybackConfiguration) o; return ((mPlayerIId == that.mPlayerIId) - && (mDeviceId == that.mDeviceId) && (mPlayerType == that.mPlayerType) && (mClientUid == that.mClientUid) && (mClientPid == that.mClientPid)); @@ -537,7 +486,6 @@ public final class AudioPlaybackConfiguration implements Parcelable { @Override public String toString() { return "AudioPlaybackConfiguration piid:" + mPlayerIId - + " deviceId:" + mDeviceId + " type:" + toLogFriendlyPlayerType(mPlayerType) + " u/pid:" + mClientUid + "/" + mClientPid + " state:" + toLogFriendlyPlayerState(mPlayerState) @@ -623,7 +571,6 @@ public final class AudioPlaybackConfiguration implements Parcelable { case PLAYER_STATE_STARTED: return "started"; case PLAYER_STATE_PAUSED: return "paused"; case PLAYER_STATE_STOPPED: return "stopped"; - case PLAYER_UPDATE_DEVICE_ID: return "device"; default: return "unknown player state - FIXME"; } diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index e9a18e982ccc..e3b6fba18eb0 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -1824,7 +1824,6 @@ public class AudioTrack extends PlayerBase @Override protected void finalize() { - tryToDisableNativeRoutingCallback(); baseRelease(); native_finalize(); } @@ -2718,15 +2717,9 @@ public class AudioTrack extends PlayerBase } private void startImpl() { - synchronized (mRoutingChangeListeners) { - if (!mEnableSelfRoutingMonitor) { - testEnableNativeRoutingCallbacksLocked(); - mEnableSelfRoutingMonitor = true; - } - } synchronized(mPlayStateLock) { + baseStart(); native_start(); - baseStart(native_getRoutedDeviceId()); if (mPlayState == PLAYSTATE_PAUSED_STOPPING) { mPlayState = PLAYSTATE_STOPPING; } else { @@ -2764,7 +2757,6 @@ public class AudioTrack extends PlayerBase mPlayStateLock.notify(); } } - tryToDisableNativeRoutingCallback(); } /** @@ -3504,21 +3496,12 @@ public class AudioTrack extends PlayerBase return null; } - private void tryToDisableNativeRoutingCallback() { - synchronized (mRoutingChangeListeners) { - if (mEnableSelfRoutingMonitor) { - mEnableSelfRoutingMonitor = false; - testDisableNativeRoutingCallbacksLocked(); - } - } - } - /* * Call BEFORE adding a routing callback handler. */ @GuardedBy("mRoutingChangeListeners") private void testEnableNativeRoutingCallbacksLocked() { - if (mRoutingChangeListeners.size() == 0 && !mEnableSelfRoutingMonitor) { + if (mRoutingChangeListeners.size() == 0) { native_enableDeviceCallback(); } } @@ -3528,7 +3511,7 @@ public class AudioTrack extends PlayerBase */ @GuardedBy("mRoutingChangeListeners") private void testDisableNativeRoutingCallbacksLocked() { - if (mRoutingChangeListeners.size() == 0 && !mEnableSelfRoutingMonitor) { + if (mRoutingChangeListeners.size() == 0) { native_disableDeviceCallback(); } } @@ -3545,9 +3528,6 @@ public class AudioTrack extends PlayerBase private ArrayMap<AudioRouting.OnRoutingChangedListener, NativeRoutingEventHandlerDelegate> mRoutingChangeListeners = new ArrayMap<>(); - @GuardedBy("mRoutingChangeListeners") - private boolean mEnableSelfRoutingMonitor; - /** * Adds an {@link AudioRouting.OnRoutingChangedListener} to receive notifications of routing * changes on this AudioTrack. @@ -3647,7 +3627,6 @@ public class AudioTrack extends PlayerBase */ private void broadcastRoutingChange() { AudioManager.resetAudioPortGeneration(); - baseUpdateDeviceId(getRoutedDevice()); synchronized (mRoutingChangeListeners) { for (NativeRoutingEventHandlerDelegate delegate : mRoutingChangeListeners.values()) { delegate.notifyClient(); diff --git a/media/java/android/media/HwAudioSource.java b/media/java/android/media/HwAudioSource.java index bbf632a406ec..01a02f1d9f05 100644 --- a/media/java/android/media/HwAudioSource.java +++ b/media/java/android/media/HwAudioSource.java @@ -22,8 +22,6 @@ import android.annotation.SystemApi; import com.android.internal.util.Preconditions; -import java.util.ArrayList; - /** * The HwAudioSource represents the audio playback directly from a source audio device. * It currently supports {@link HwAudioSource#start()} and {@link HwAudioSource#stop()} only @@ -132,32 +130,10 @@ public class HwAudioSource extends PlayerBase { */ public void start() { Preconditions.checkState(!isPlaying(), "HwAudioSource is currently playing"); + baseStart(); mNativeHandle = AudioSystem.startAudioSource( mAudioDeviceInfo.getPort().activeConfig(), mAudioAttributes); - // FIXME: b/174876389 clean up device id reporting - baseStart(getDeviceId()); - } - - private int getDeviceId() { - ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>(); - if (AudioManager.listAudioPatches(patches) != AudioManager.SUCCESS) { - return 0; - } - - for (int i = 0; i < patches.size(); i++) { - AudioPatch patch = patches.get(i); - AudioPortConfig[] sources = patch.sources(); - AudioPortConfig[] sinks = patch.sinks(); - if ((sources != null) && (sources.length > 0)) { - for (int c = 0; c < sources.length; c++) { - if (sources[c].port().id() == mAudioDeviceInfo.getId()) { - return sinks[c].port().id(); - } - } - } - } - return 0; } /** diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index b6bb3a3df38b..bad0d19b723e 100755 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -63,7 +63,7 @@ interface IAudioService { oneway void playerAttributes(in int piid, in AudioAttributes attr); - oneway void playerEvent(in int piid, in int event, in int deviceId); + oneway void playerEvent(in int piid, in int event); oneway void releasePlayer(in int piid); diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java index e7e83ebb001f..9657b25e7c18 100644 --- a/media/java/android/media/MediaCodecInfo.java +++ b/media/java/android/media/MediaCodecInfo.java @@ -1865,10 +1865,6 @@ public final class MediaCodecInfo { && aligned.mMaxMacroBlockRate >= otherAligned.mMaxMacroBlockRate); } - /* package private */ boolean isEqualDimension(@NonNull PerformancePoint other) { - return mWidth == other.mWidth && mHeight == other.mHeight; - } - private @NonNull Size getCommonBlockSize(@NonNull PerformancePoint other) { return new Size( Math.max(mBlockSize.getWidth(), other.mBlockSize.getWidth()) * 16, @@ -2010,8 +2006,11 @@ public final class MediaCodecInfo { * codecs are active, should use that highest pixel count, and add the frame rates of * each individual codec. * <p class=note> - * Supported resolution could be further restricted for 32-bit processes due to - * the limited virtual memory space. + * 32-bit processes will not support resolutions larger than 4096x4096 due to + * the limited address space, but performance points will be presented as is. + * In other words, even though a component publishes a performance point for + * a resolution higher than 4096x4096, it does not mean that the resolution is supported + * for 32-bit processes. */ @Nullable public List<PerformancePoint> getSupportedPerformancePoints() { @@ -2215,28 +2214,6 @@ public final class MediaCodecInfo { (a.getMaxFrameRate() != b.getMaxFrameRate()) ? (a.getMaxFrameRate() < b.getMaxFrameRate() ? -1 : 1) : 0)); - // remove redundant points - for (int i = 1; i < ret.size(); ++i) { - PerformancePoint a = ret.get(i); - for (int j = 0; j < i; ++j) { - PerformancePoint b = ret.get(j); - if (b.isEqualDimension(a) && b.covers(a)) { - ret.set(i, null); - break; - } - } - } - int newSize = 0; - for (int i = 0; i < ret.size(); ++i) { - PerformancePoint a = ret.get(i); - if (a == null) { - continue; - } - ret.set(newSize, a); - ++newSize; - } - ret.setSize(newSize); - return Collections.unmodifiableList(ret); } diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index ceac2c9d5e05..f8311cd580a9 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -1352,6 +1352,7 @@ public class MediaPlayer extends PlayerBase } private void startImpl() { + baseStart(); stayAwake(true); _start(); } @@ -1377,6 +1378,7 @@ public class MediaPlayer extends PlayerBase public void stop() throws IllegalStateException { stayAwake(false); _stop(); + baseStop(); } private native void _stop() throws IllegalStateException; @@ -1390,6 +1392,7 @@ public class MediaPlayer extends PlayerBase public void pause() throws IllegalStateException { stayAwake(false); _pause(); + basePause(); } private native void _pause() throws IllegalStateException; @@ -1494,60 +1497,13 @@ public class MediaPlayer extends PlayerBase return null; } - - /** - * Sends device list change notification to all listeners. - */ - private void broadcastRoutingChange() { - AudioManager.resetAudioPortGeneration(); - synchronized (mRoutingChangeListeners) { - // Prevent the case where an event is triggered by registering a routing change - // listener via the media player. - if (mEnableSelfRoutingMonitor) { - baseUpdateDeviceId(getRoutedDevice()); - } - for (NativeRoutingEventHandlerDelegate delegate - : mRoutingChangeListeners.values()) { - delegate.notifyClient(); - } - } - } - /* - * Call BEFORE adding a routing callback handler. + * Call BEFORE adding a routing callback handler or AFTER removing a routing callback handler. */ @GuardedBy("mRoutingChangeListeners") - private void testEnableNativeRoutingCallbacksLocked() { - if (mRoutingChangeListeners.size() == 0 && !mEnableSelfRoutingMonitor) { - native_enableDeviceCallback(true); - } - } - - private void tryToEnableNativeRoutingCallback() { - synchronized (mRoutingChangeListeners) { - if (!mEnableSelfRoutingMonitor) { - testEnableNativeRoutingCallbacksLocked(); - mEnableSelfRoutingMonitor = true; - } - } - } - - private void tryToDisableNativeRoutingCallback() { - synchronized (mRoutingChangeListeners) { - if (mEnableSelfRoutingMonitor) { - mEnableSelfRoutingMonitor = false; - testDisableNativeRoutingCallbacksLocked(); - } - } - } - - /* - * Call AFTER removing a routing callback handler. - */ - @GuardedBy("mRoutingChangeListeners") - private void testDisableNativeRoutingCallbacksLocked() { - if (mRoutingChangeListeners.size() == 0 && !mEnableSelfRoutingMonitor) { - native_enableDeviceCallback(false); + private void enableNativeRoutingCallbacksLocked(boolean enabled) { + if (mRoutingChangeListeners.size() == 0) { + native_enableDeviceCallback(enabled); } } @@ -1560,9 +1516,6 @@ public class MediaPlayer extends PlayerBase private ArrayMap<AudioRouting.OnRoutingChangedListener, NativeRoutingEventHandlerDelegate> mRoutingChangeListeners = new ArrayMap<>(); - @GuardedBy("mRoutingChangeListeners") - private boolean mEnableSelfRoutingMonitor; - /** * Adds an {@link AudioRouting.OnRoutingChangedListener} to receive notifications of routing * changes on this MediaPlayer. @@ -1576,7 +1529,7 @@ public class MediaPlayer extends PlayerBase Handler handler) { synchronized (mRoutingChangeListeners) { if (listener != null && !mRoutingChangeListeners.containsKey(listener)) { - testEnableNativeRoutingCallbacksLocked(); + enableNativeRoutingCallbacksLocked(true); mRoutingChangeListeners.put( listener, new NativeRoutingEventHandlerDelegate(this, listener, handler != null ? handler : mEventHandler)); @@ -1595,8 +1548,8 @@ public class MediaPlayer extends PlayerBase synchronized (mRoutingChangeListeners) { if (mRoutingChangeListeners.containsKey(listener)) { mRoutingChangeListeners.remove(listener); + enableNativeRoutingCallbacksLocked(false); } - testDisableNativeRoutingCallbacksLocked(); } } @@ -3348,7 +3301,6 @@ public class MediaPlayer extends PlayerBase @Override protected void finalize() { - tryToDisableNativeRoutingCallback(); baseRelease(); native_finalize(); } @@ -3463,8 +3415,6 @@ public class MediaPlayer extends PlayerBase case MEDIA_STOPPED: { - tryToDisableNativeRoutingCallback(); - baseStop(); TimeProvider timeProvider = mTimeProvider; if (timeProvider != null) { timeProvider.onStopped(); @@ -3473,16 +3423,8 @@ public class MediaPlayer extends PlayerBase break; case MEDIA_STARTED: - { - baseStart(native_getRoutedDeviceId()); - tryToEnableNativeRoutingCallback(); - } - // fall through case MEDIA_PAUSED: { - if (msg.what == MEDIA_PAUSED) { - basePause(); - } TimeProvider timeProvider = mTimeProvider; if (timeProvider != null) { timeProvider.onPaused(msg.what == MEDIA_PAUSED); @@ -3648,8 +3590,14 @@ public class MediaPlayer extends PlayerBase break; case MEDIA_AUDIO_ROUTING_CHANGED: - broadcastRoutingChange(); - return; + AudioManager.resetAudioPortGeneration(); + synchronized (mRoutingChangeListeners) { + for (NativeRoutingEventHandlerDelegate delegate + : mRoutingChangeListeners.values()) { + delegate.notifyClient(); + } + } + return; case MEDIA_TIME_DISCONTINUITY: final OnMediaTimeDiscontinuityListener mediaTimeListener; @@ -3855,7 +3803,6 @@ public class MediaPlayer extends PlayerBase @Override public void onCompletion(MediaPlayer mp) { baseStop(); - tryToDisableNativeRoutingCallback(); } }; diff --git a/media/java/android/media/OWNERS b/media/java/android/media/OWNERS index cbc9ab7e4adf..cf06fad3e203 100644 --- a/media/java/android/media/OWNERS +++ b/media/java/android/media/OWNERS @@ -6,3 +6,4 @@ lajos@google.com olly@google.com andrewlewis@google.com sungsoo@google.com +jmtrivi@google.com diff --git a/media/java/android/media/PlayerBase.java b/media/java/android/media/PlayerBase.java index 58ae279d4df1..da69c6cbbb5c 100644 --- a/media/java/android/media/PlayerBase.java +++ b/media/java/android/media/PlayerBase.java @@ -90,8 +90,6 @@ public abstract class PlayerBase { private float mPanMultiplierR = 1.0f; @GuardedBy("mLock") private float mVolMultiplier = 1.0f; - @GuardedBy("mLock") - private int mDeviceId; /** * Constructor. Must be given audio attributes, as they are required for AppOps. @@ -154,35 +152,14 @@ public abstract class PlayerBase { } } - void baseUpdateDeviceId(@Nullable AudioDeviceInfo deviceInfo) { - int deviceId = 0; - if (deviceInfo != null) { - deviceId = deviceInfo.getId(); - } - int piid; - synchronized (mLock) { - piid = mPlayerIId; - mDeviceId = deviceId; - } - try { - getService().playerEvent(piid, - AudioPlaybackConfiguration.PLAYER_UPDATE_DEVICE_ID, deviceId); - } catch (RemoteException e) { - Log.e(TAG, "Error talking to audio service, " - + deviceId - + " device id will not be tracked for piid=" + piid, e); - } - } - - private void updateState(int state, int deviceId) { + private void updateState(int state) { final int piid; synchronized (mLock) { mState = state; piid = mPlayerIId; - mDeviceId = deviceId; } try { - getService().playerEvent(piid, state, deviceId); + getService().playerEvent(piid, state); } catch (RemoteException e) { Log.e(TAG, "Error talking to audio service, " + AudioPlaybackConfiguration.toLogFriendlyPlayerState(state) @@ -190,11 +167,9 @@ public abstract class PlayerBase { } } - void baseStart(int deviceId) { - if (DEBUG) { - Log.v(TAG, "baseStart() piid=" + mPlayerIId + " deviceId=" + deviceId); - } - updateState(AudioPlaybackConfiguration.PLAYER_STATE_STARTED, deviceId); + void baseStart() { + if (DEBUG) { Log.v(TAG, "baseStart() piid=" + mPlayerIId); } + updateState(AudioPlaybackConfiguration.PLAYER_STATE_STARTED); synchronized (mLock) { if (isRestricted_sync()) { playerSetVolume(true/*muting*/,0, 0); @@ -216,12 +191,12 @@ public abstract class PlayerBase { void basePause() { if (DEBUG) { Log.v(TAG, "basePause() piid=" + mPlayerIId); } - updateState(AudioPlaybackConfiguration.PLAYER_STATE_PAUSED, 0); + updateState(AudioPlaybackConfiguration.PLAYER_STATE_PAUSED); } void baseStop() { if (DEBUG) { Log.v(TAG, "baseStop() piid=" + mPlayerIId); } - updateState(AudioPlaybackConfiguration.PLAYER_STATE_STOPPED, 0); + updateState(AudioPlaybackConfiguration.PLAYER_STATE_STOPPED); } void baseSetPan(float pan) { diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java index 797caf36203b..3f685a4c934e 100644 --- a/media/java/android/media/SoundPool.java +++ b/media/java/android/media/SoundPool.java @@ -303,8 +303,7 @@ public class SoundPool extends PlayerBase { */ public final int play(int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate) { - // FIXME: b/174876164 implement device id for soundpool - baseStart(0); + baseStart(); return _play(soundID, leftVolume, rightVolume, priority, loop, rate); } diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index 440d5bc2f167..6fa94f1b7a7b 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -337,8 +337,8 @@ C2DataIdInfo::C2DataIdInfo(uint32_t index, uint64_t value) : C2Param(kParamSize, /////////////// MediaEvent /////////////////////// -MediaEvent::MediaEvent(sp<Filter> filter, hidl_handle avHandle, - uint64_t dataId, uint64_t dataSize, jobject obj) : mFilter(filter), +MediaEvent::MediaEvent(sp<FilterClient> filterClient, hidl_handle avHandle, + uint64_t dataId, uint64_t dataSize, jobject obj) : mFilterClient(filterClient), mDataId(dataId), mDataSize(dataSize), mBuffer(nullptr), mDataIdRefCnt(0), mAvHandleRefCnt(0), mIonHandle(nullptr) { JNIEnv *env = AndroidRuntime::getJNIEnv(); @@ -359,12 +359,13 @@ MediaEvent::~MediaEvent() { if (pC2Buffer != NULL) { pC2Buffer->unregisterOnDestroyNotify(&DestroyCallback, this); } + mFilterClient = NULL; } void MediaEvent::finalize() { - if (mAvHandleRefCnt == 0) { - mFilter->mFilterSp->releaseAvHandle( - hidl_handle(mAvHandle), mDataIdRefCnt == 0 ? mDataId : 0); + if (mAvHandleRefCnt == 0 && mFilterClient != NULL) { + mFilterClient->releaseAvHandle( + mAvHandle, mDataIdRefCnt == 0 ? mDataId : 0); native_handle_close(mAvHandle); } } @@ -382,21 +383,25 @@ jobject MediaEvent::getLinearBlock() { int numInts = 0; int memIndex; int dataSize; + SharedHandleInfo info = mFilterClient->getAvSharedHandleInfo(); + native_handle_t* avSharedHandle = info.sharedHandle; + uint64_t avSharedMemSize = info.size; + if (mAvHandle->numFds == 0) { - if (mFilter->mAvSharedHandle == NULL) { + if (avSharedHandle == NULL) { ALOGE("Shared AV memory handle is not initialized."); return NULL; } - if (mFilter->mAvSharedHandle->numFds == 0) { + if (avSharedHandle->numFds == 0) { ALOGE("Shared AV memory handle is empty."); return NULL; } - fd = mFilter->mAvSharedHandle->data[0]; - dataSize = mFilter->mAvSharedMemSize; - numInts = mFilter->mAvSharedHandle->numInts; + fd = avSharedHandle->data[0]; + dataSize = avSharedMemSize; + numInts = avSharedHandle->numInts; if (numInts > 0) { // If the first int in the shared native handle has value, use it as the index - memIndex = mFilter->mAvSharedHandle->data[mFilter->mAvSharedHandle->numFds]; + memIndex = avSharedHandle->data[avSharedHandle->numFds]; } } else { fd = mAvHandle->data[0]; @@ -407,11 +412,11 @@ jobject MediaEvent::getLinearBlock() { // event has value, use it as the index memIndex = mAvHandle->data[mAvHandle->numFds]; } else { - if (mFilter->mAvSharedHandle != NULL) { - numInts = mFilter->mAvSharedHandle->numInts; + if (avSharedHandle != NULL) { + numInts = avSharedHandle->numInts; if (numInts > 0) { // If the first int in the shared native handle has value, use it as the index - memIndex = mFilter->mAvSharedHandle->data[mFilter->mAvSharedHandle->numFds]; + memIndex = avSharedHandle->data[avSharedHandle->numFds]; } } } @@ -462,9 +467,9 @@ uint64_t MediaEvent::getAudioHandle() { return mDataId; } -/////////////// FilterCallback /////////////////////// +/////////////// FilterClientCallbackImpl /////////////////////// -jobjectArray FilterCallback::getSectionEvent( +jobjectArray FilterClientCallbackImpl::getSectionEvent( jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) { JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/SectionEvent"); @@ -486,7 +491,7 @@ jobjectArray FilterCallback::getSectionEvent( return arr; } -jobjectArray FilterCallback::getMediaEvent( +jobjectArray FilterClientCallbackImpl::getMediaEvent( jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) { JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/MediaEvent"); @@ -537,7 +542,7 @@ jobjectArray FilterCallback::getMediaEvent( if (mediaEvent.avMemory.getNativeHandle() != NULL || mediaEvent.avDataId != 0) { sp<MediaEvent> mediaEventSp = - new MediaEvent(mFilter, mediaEvent.avMemory, + new MediaEvent(mFilterClient, mediaEvent.avMemory, mediaEvent.avDataId, dataLength + offset, obj); mediaEventSp->mAvHandleRefCnt++; env->SetLongField(obj, eventContext, (jlong) mediaEventSp.get()); @@ -549,7 +554,7 @@ jobjectArray FilterCallback::getMediaEvent( return arr; } -jobjectArray FilterCallback::getPesEvent( +jobjectArray FilterClientCallbackImpl::getPesEvent( jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) { JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/PesEvent"); @@ -570,7 +575,7 @@ jobjectArray FilterCallback::getPesEvent( return arr; } -jobjectArray FilterCallback::getTsRecordEvent( +jobjectArray FilterClientCallbackImpl::getTsRecordEvent( jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events, const std::vector<DemuxFilterEventExt::Event>& eventsExt) { JNIEnv *env = AndroidRuntime::getJNIEnv(); @@ -623,7 +628,7 @@ jobjectArray FilterCallback::getTsRecordEvent( return arr; } -jobjectArray FilterCallback::getMmtpRecordEvent( +jobjectArray FilterClientCallbackImpl::getMmtpRecordEvent( jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events, const std::vector<DemuxFilterEventExt::Event>& eventsExt) { JNIEnv *env = AndroidRuntime::getJNIEnv(); @@ -665,7 +670,7 @@ jobjectArray FilterCallback::getMmtpRecordEvent( return arr; } -jobjectArray FilterCallback::getDownloadEvent( +jobjectArray FilterClientCallbackImpl::getDownloadEvent( jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) { JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/DownloadEvent"); @@ -689,7 +694,7 @@ jobjectArray FilterCallback::getDownloadEvent( return arr; } -jobjectArray FilterCallback::getIpPayloadEvent( +jobjectArray FilterClientCallbackImpl::getIpPayloadEvent( jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) { JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/IpPayloadEvent"); @@ -705,7 +710,7 @@ jobjectArray FilterCallback::getIpPayloadEvent( return arr; } -jobjectArray FilterCallback::getTemiEvent( +jobjectArray FilterClientCallbackImpl::getTemiEvent( jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events) { JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/TemiEvent"); @@ -728,7 +733,7 @@ jobjectArray FilterCallback::getTemiEvent( return arr; } -jobjectArray FilterCallback::getScramblingStatusEvent( +jobjectArray FilterClientCallbackImpl::getScramblingStatusEvent( jobjectArray& arr, const std::vector<DemuxFilterEventExt::Event>& eventsExt) { JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/ScramblingStatusEvent"); @@ -740,7 +745,7 @@ jobjectArray FilterCallback::getScramblingStatusEvent( return arr; } -jobjectArray FilterCallback::getIpCidChangeEvent( +jobjectArray FilterClientCallbackImpl::getIpCidChangeEvent( jobjectArray& arr, const std::vector<DemuxFilterEventExt::Event>& eventsExt) { JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/IpCidChangeEvent"); @@ -752,7 +757,7 @@ jobjectArray FilterCallback::getIpCidChangeEvent( return arr; } -jobjectArray FilterCallback::getRestartEvent( +jobjectArray FilterClientCallbackImpl::getRestartEvent( jobjectArray& arr, const std::vector<DemuxFilterEventExt::Event>& eventsExt) { JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/RestartEvent"); @@ -764,9 +769,9 @@ jobjectArray FilterCallback::getRestartEvent( return arr; } -Return<void> FilterCallback::onFilterEvent_1_1(const DemuxFilterEvent& filterEvent, +void FilterClientCallbackImpl::onFilterEvent_1_1(const DemuxFilterEvent& filterEvent, const DemuxFilterEventExt& filterEventExt) { - ALOGD("FilterCallback::onFilterEvent_1_1"); + ALOGD("FilterClientCallbackImpl::onFilterEvent_1_1"); JNIEnv *env = AndroidRuntime::getJNIEnv(); jobjectArray array; @@ -848,14 +853,13 @@ Return<void> FilterCallback::onFilterEvent_1_1(const DemuxFilterEvent& filterEve } } env->CallVoidMethod( - mFilter->mFilterObj, + mFilterObj, gFields.onFilterEventID, array); - return Void(); } -Return<void> FilterCallback::onFilterEvent(const DemuxFilterEvent& filterEvent) { - ALOGD("FilterCallback::onFilterEvent"); +void FilterClientCallbackImpl::onFilterEvent(const DemuxFilterEvent& filterEvent) { + ALOGD("FilterClientCallbackImpl::onFilterEvent"); std::vector<DemuxFilterEventExt::Event> emptyEventsExt; DemuxFilterEventExt emptyFilterEventExt { .events = emptyEventsExt, @@ -863,49 +867,20 @@ Return<void> FilterCallback::onFilterEvent(const DemuxFilterEvent& filterEvent) return onFilterEvent_1_1(filterEvent, emptyFilterEventExt); } -Return<void> FilterCallback::onFilterStatus(const DemuxFilterStatus status) { - ALOGD("FilterCallback::onFilterStatus"); +void FilterClientCallbackImpl::onFilterStatus(const DemuxFilterStatus status) { + ALOGD("FilterClientCallbackImpl::onFilterStatus"); JNIEnv *env = AndroidRuntime::getJNIEnv(); env->CallVoidMethod( - mFilter->mFilterObj, + mFilterObj, gFields.onFilterStatusID, (jint)status); - return Void(); -} - -void FilterCallback::setFilter(const sp<Filter> filter) { - ALOGD("FilterCallback::setFilter"); - // JNI Object - mFilter = filter; -} - -FilterCallback::~FilterCallback() {} - -/////////////// Filter /////////////////////// - -Filter::Filter(sp<IFilter> sp, jobject obj) : mFilterSp(sp) { - JNIEnv *env = AndroidRuntime::getJNIEnv(); - mFilterObj = env->NewWeakGlobalRef(obj); -} - -Filter::~Filter() { - JNIEnv *env = AndroidRuntime::getJNIEnv(); - - env->DeleteWeakGlobalRef(mFilterObj); - mFilterObj = NULL; - EventFlag::deleteEventFlag(&mFilterMQEventFlag); } -int Filter::close() { - Result r = mFilterSp->close(); - if (r == Result::SUCCESS) { - EventFlag::deleteEventFlag(&mFilterMQEventFlag); - } - return (int)r; -} - -sp<IFilter> Filter::getIFilter() { - return mFilterSp; +void FilterClientCallbackImpl::setFilter(jweak filterObj, sp<FilterClient> filterClient) { + ALOGD("FilterClientCallbackImpl::setFilter"); + // Java Object + mFilterObj = filterObj; + mFilterClient = filterClient; } /////////////// TimeFilter /////////////////////// @@ -927,22 +902,23 @@ sp<ITimeFilter> TimeFilter::getITimeFilter() { return mTimeFilterSp; } -/////////////// FrontendCallback /////////////////////// +/////////////// FrontendClientCallbackImpl /////////////////////// -FrontendCallback::FrontendCallback(jweak tunerObj, FrontendId id) : mObject(tunerObj), mId(id) {} +FrontendClientCallbackImpl::FrontendClientCallbackImpl( + jweak tunerObj, FrontendId id) : mObject(tunerObj), mId(id) {} -Return<void> FrontendCallback::onEvent(FrontendEventType frontendEventType) { - ALOGD("FrontendCallback::onEvent, type=%d", frontendEventType); +void FrontendClientCallbackImpl::onEvent(FrontendEventType frontendEventType) { + ALOGD("FrontendClientCallbackImpl::onEvent, type=%d", frontendEventType); JNIEnv *env = AndroidRuntime::getJNIEnv(); env->CallVoidMethod( mObject, gFields.onFrontendEventID, (jint)frontendEventType); - return Void(); } -Return<void> FrontendCallback::onScanMessage(FrontendScanMessageType type, const FrontendScanMessage& message) { - ALOGD("FrontendCallback::onScanMessage, type=%d", type); +void FrontendClientCallbackImpl::onScanMessage( + FrontendScanMessageType type, const FrontendScanMessage& message) { + ALOGD("FrontendClientCallbackImpl::onScanMessage, type=%d", type); JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass clazz = env->FindClass("android/media/tv/tuner/Tuner"); switch(type) { @@ -1050,13 +1026,15 @@ Return<void> FrontendCallback::onScanMessage(FrontendScanMessageType type, const mObject, env->GetMethodID(clazz, "onDvbsStandard", "(I)V"), standard); - } else if (std.getDiscriminator() == FrontendScanMessage::Standard::hidl_discriminator::tStd) { + } else if (std.getDiscriminator() == + FrontendScanMessage::Standard::hidl_discriminator::tStd) { standard = (jint) std.tStd(); env->CallVoidMethod( mObject, env->GetMethodID(clazz, "onDvbtStandard", "(I)V"), standard); - } else if (std.getDiscriminator() == FrontendScanMessage::Standard::hidl_discriminator::sifStd) { + } else if (std.getDiscriminator() == + FrontendScanMessage::Standard::hidl_discriminator::sifStd) { standard = (jint) std.sifStd(); env->CallVoidMethod( mObject, @@ -1081,17 +1059,17 @@ Return<void> FrontendCallback::onScanMessage(FrontendScanMessageType type, const } env->CallVoidMethod( mObject, - env->GetMethodID(clazz, "onAtsc3PlpInfos", "([Landroid/media/tv/tuner/frontend/Atsc3PlpInfo;)V"), + env->GetMethodID(clazz, "onAtsc3PlpInfos", + "([Landroid/media/tv/tuner/frontend/Atsc3PlpInfo;)V"), array); break; } } - return Void(); } -Return<void> FrontendCallback::onScanMessageExt1_1(FrontendScanMessageTypeExt1_1 type, +void FrontendClientCallbackImpl::onScanMessageExt1_1(FrontendScanMessageTypeExt1_1 type, const FrontendScanMessageExt1_1& message) { - ALOGD("FrontendCallback::onScanMessageExt1_1, type=%d", type); + ALOGD("FrontendClientCallbackImpl::onScanMessageExt1_1, type=%d", type); JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass clazz = env->FindClass("android/media/tv/tuner/Tuner"); switch(type) { @@ -1165,7 +1143,6 @@ Return<void> FrontendCallback::onScanMessageExt1_1(FrontendScanMessageTypeExt1_1 default: break; } - return Void(); } /////////////// Tuner /////////////////////// @@ -1202,7 +1179,11 @@ JTuner::~JTuner() { env->DeleteWeakGlobalRef(mObject); env->DeleteGlobalRef(mClass); mTuner = NULL; + mFe = NULL; + mDemux = NULL; mTunerClient = NULL; + mFeClient = NULL; + mDemuxClient = NULL; mClass = NULL; mObject = NULL; } @@ -1252,9 +1233,11 @@ jobject JTuner::getFrontendIds() { return obj; } -jobject JTuner::openFrontendById(int id) { +jobject JTuner::openFrontendByHandle(int feHandle) { sp<IFrontend> fe; Result res; + uint32_t id = getResourceIdFromHandle(feHandle); + mTuner->openFrontendById(id, [&](Result r, const sp<IFrontend>& frontend) { fe = frontend; res = r; @@ -1269,11 +1252,25 @@ jobject JTuner::openFrontendById(int id) { if (mDemux != NULL) { mDemux->setFrontendDataSource(mFeId); } - sp<FrontendCallback> feCb = new FrontendCallback(mObject, id); - fe->setCallback(feCb); jint jId = (jint) id; + // TODO: Handle reopening frontend with different handle + sp<FrontendClient> feClient = mTunerClient->openFrontend(feHandle); + if (feClient == NULL) { + ALOGE("Failed to open frontend"); + return NULL; + } + mFeClient = feClient; + + mFeId = mFeClient->getId(); + jId = (jint) id; + if (mDemuxClient != NULL) { + mDemuxClient->setFrontendDataSource(mFeClient); + } + sp<FrontendClientCallbackImpl> feClientCb = new FrontendClientCallbackImpl(mObject, id); + mFeClient->setCallback(feClientCb); + JNIEnv *env = AndroidRuntime::getJNIEnv(); // TODO: add more fields to frontend return env->NewObject( @@ -1589,30 +1586,19 @@ jobject JTuner::openLnbByName(jstring name) { } int JTuner::tune(const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1) { - if (mFe == NULL) { + if (mFeClient == nullptr) { ALOGE("frontend is not initialized"); return (int)Result::INVALID_STATE; } - Result result; - sp<::android::hardware::tv::tuner::V1_1::IFrontend> fe_1_1 = - ::android::hardware::tv::tuner::V1_1::IFrontend::castFrom(mFe); - if (fe_1_1 == NULL) { - ALOGD("1.1 frontend is not found. Using 1.0 instead."); - result = mFe->tune(settings); - return (int)result; - } - - result = fe_1_1->tune_1_1(settings, settingsExt1_1); - return (int)result; + return (int) mFeClient->tune(settings,settingsExt1_1); } int JTuner::stopTune() { - if (mFe == NULL) { + if (mFeClient == nullptr) { ALOGE("frontend is not initialized"); return (int)Result::INVALID_STATE; } - Result result = mFe->stopTune(); - return (int)result; + return (int) mFeClient->stopTune(); } int JTuner::scan(const FrontendSettings& settings, FrontendScanType scanType, @@ -1662,28 +1648,47 @@ int JTuner::setLna(bool enable) { } Result JTuner::openDemux() { - if (mTuner == nullptr) { + if (mTuner == nullptr || mTunerClient == nullptr) { return Result::NOT_INITIALIZED; } - if (mDemux != nullptr) { - return Result::SUCCESS; + + Result res = Result::SUCCESS; + + if (mDemux == nullptr) { + uint32_t id; + sp<IDemux> demuxSp; + mTuner->openDemux([&](Result r, uint32_t demuxId, const sp<IDemux>& demux) { + demuxSp = demux; + id = demuxId; + res = r; + ALOGD("open demux, id = %d", demuxId); + }); + if (res == Result::SUCCESS) { + mDemux = demuxSp; + mDemuxId = id; + if (mFe != NULL) { + mDemux->setFrontendDataSource(mFeId); + } + } else { + return res; + } } - Result res; - uint32_t id; - sp<IDemux> demuxSp; - mTuner->openDemux([&](Result r, uint32_t demuxId, const sp<IDemux>& demux) { - demuxSp = demux; - id = demuxId; - res = r; - ALOGD("open demux, id = %d", demuxId); - }); - if (res == Result::SUCCESS) { - mDemux = demuxSp; - mDemuxId = id; - if (mFe != NULL) { - mDemux->setFrontendDataSource(mFeId); + + // TODO: replace demux opening with mTunerClient->openDemux(handle) + // when DemuxClient is fully ready + if (mDemuxClient == nullptr) { + sp<DemuxClient> demuxClient = new DemuxClient(); + if (demuxClient == NULL) { + ALOGE("Failed to open demux"); + return Result::UNKNOWN_ERROR; + } + mDemuxClient = demuxClient; + mDemuxClient->setHidlDemux(mDemux); + if (mFeClient != NULL) { + mDemuxClient->setFrontendDataSource(mFeClient); } } + return res; } @@ -1701,23 +1706,31 @@ jint JTuner::close() { return (jint) res; } } + + if (mFeClient != NULL) { + res = mFeClient->close(); + if (res != Result::SUCCESS) { + return (jint) res; + } + mFeClient = NULL; + } + if (mDemuxClient != NULL) { + res = mDemuxClient->close(); + if (res != Result::SUCCESS) { + return (jint) res; + } + mDemuxClient = NULL; + } return (jint) res; } -jobject JTuner::getAvSyncHwId(sp<Filter> filter) { - if (mDemux == NULL) { +jobject JTuner::getAvSyncHwId(sp<FilterClient> filterClient) { + if (mDemuxClient == NULL) { return NULL; } - uint32_t avSyncHwId; - Result res; - sp<IFilter> iFilterSp = filter->getIFilter(); - mDemux->getAvSyncHwId(iFilterSp, - [&](Result r, uint32_t id) { - res = r; - avSyncHwId = id; - }); - if (res == Result::SUCCESS) { + int avSyncHwId = mDemuxClient->getAvSyncHwId(filterClient); + if (avSyncHwId >= 0) { JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass integerClazz = env->FindClass("java/lang/Integer"); jmethodID intInit = env->GetMethodID(integerClazz, "<init>", "(I)V"); @@ -1727,17 +1740,11 @@ jobject JTuner::getAvSyncHwId(sp<Filter> filter) { } jobject JTuner::getAvSyncTime(jint id) { - if (mDemux == NULL) { + if (mDemuxClient == NULL) { return NULL; } - uint64_t time; - Result res; - mDemux->getAvSyncTime(static_cast<uint32_t>(id), - [&](Result r, uint64_t ts) { - res = r; - time = ts; - }); - if (res == Result::SUCCESS) { + long time = mDemuxClient->getAvSyncTime((int)id); + if (time >= 0) { JNIEnv *env = AndroidRuntime::getJNIEnv(); jclass longClazz = env->FindClass("java/lang/Long"); jmethodID longInit = env->GetMethodID(longClazz, "<init>", "(J)V"); @@ -1747,13 +1754,13 @@ jobject JTuner::getAvSyncTime(jint id) { } int JTuner::connectCiCam(jint id) { - if (mDemux == NULL) { + if (mDemuxClient == NULL) { Result r = openDemux(); if (r != Result::SUCCESS) { return (int) r; } } - Result r = mDemux->connectCiCam(static_cast<uint32_t>(id)); + Result r = mDemuxClient->connectCiCam((int)id); return (int) r; } @@ -1779,13 +1786,13 @@ int JTuner::linkCiCam(int id) { } int JTuner::disconnectCiCam() { - if (mDemux == NULL) { + if (mDemuxClient == NULL) { Result r = openDemux(); if (r != Result::SUCCESS) { return (int) r; } } - Result r = mDemux->disconnectCiCam(); + Result r = mDemuxClient->disconnectCiCam(); return (int) r; } @@ -1832,34 +1839,25 @@ jobject JTuner::openDescrambler() { } jobject JTuner::openFilter(DemuxFilterType type, int bufferSize) { - if (mDemux == NULL) { + if (mDemux == NULL || mDemuxClient == NULL) { if (openDemux() != Result::SUCCESS) { return NULL; } } - sp<IFilter> iFilterSp; - sp<::android::hardware::tv::tuner::V1_1::IFilter> iFilterSp_1_1; - sp<FilterCallback> callback = new FilterCallback(); - Result res; - mDemux->openFilter(type, bufferSize, callback, - [&](Result r, const sp<IFilter>& filter) { - iFilterSp = filter; - res = r; - }); - if (res != Result::SUCCESS || iFilterSp == NULL) { + sp<FilterClient> filterClient; + sp<FilterClientCallbackImpl> callback = new FilterClientCallbackImpl(); + filterClient = mDemuxClient->openFilter(type, bufferSize, callback); + if (filterClient == NULL) { ALOGD("Failed to open filter, type = %d", type.mainType); return NULL; } uint64_t fId; - iFilterSp->getId([&](Result, uint32_t filterId) { - fId = filterId; - }); - iFilterSp_1_1 = ::android::hardware::tv::tuner::V1_1::IFilter::castFrom(iFilterSp); - if (iFilterSp_1_1 != NULL) { - iFilterSp_1_1->getId64Bit([&](Result, uint64_t filterId64Bit) { - fId = filterId64Bit; - }); + Result res = filterClient->getId64Bit(fId); + if (res != Result::SUCCESS) { + uint32_t id; + filterClient->getId(id); + fId = static_cast<uint64_t>(id); } JNIEnv *env = AndroidRuntime::getJNIEnv(); @@ -1869,35 +1867,9 @@ jobject JTuner::openFilter(DemuxFilterType type, int bufferSize) { gFields.filterInitID, (jlong) fId); - sp<Filter> filterSp = new Filter(iFilterSp, filterObj); - filterSp->incStrong(filterObj); - env->SetLongField(filterObj, gFields.filterContext, (jlong)filterSp.get()); - filterSp->mIsMediaFilter = false; - filterSp->mAvSharedHandle = NULL; - callback->setFilter(filterSp); - - if (type.mainType == DemuxFilterMainType::MMTP) { - if (type.subType.mmtpFilterType() == DemuxMmtpFilterType::AUDIO || - type.subType.mmtpFilterType() == DemuxMmtpFilterType::VIDEO) { - filterSp->mIsMediaFilter = true; - } - } - - if (type.mainType == DemuxFilterMainType::TS) { - if (type.subType.tsFilterType() == DemuxTsFilterType::AUDIO || - type.subType.tsFilterType() == DemuxTsFilterType::VIDEO) { - filterSp->mIsMediaFilter = true; - } - } - - if (iFilterSp_1_1 != NULL && filterSp->mIsMediaFilter) { - iFilterSp_1_1->getAvSharedHandle([&](Result r, hidl_handle avMemory, uint64_t avMemSize) { - if (r == Result::SUCCESS) { - filterSp->mAvSharedHandle = native_handle_clone(avMemory.getNativeHandle()); - filterSp->mAvSharedMemSize = avMemSize; - } - }); - } + filterClient->incStrong(filterObj); + env->SetLongField(filterObj, gFields.filterContext, (jlong)filterClient.get()); + callback->setFilter(env->NewWeakGlobalRef(filterObj), filterClient); return filterObj; } @@ -2616,6 +2588,15 @@ jint JTuner::closeFrontend() { if (r == Result::SUCCESS) { mFe = NULL; mFe_1_1 = NULL; + } else { + return (jint) r; + } + + if (mFeClient != NULL) { + r = mFeClient->close(); + } + if (r == Result::SUCCESS) { + mFeClient = NULL; } return (jint) r; } @@ -2627,10 +2608,18 @@ jint JTuner::closeDemux() { } if (r == Result::SUCCESS) { mDemux = NULL; + } else { + return (jint) r; + } + + if (mDemuxClient != NULL) { + r = mDemuxClient->close(); + } + if (r == Result::SUCCESS) { + mDemuxClient = NULL; } return (jint) r; } - } // namespace android //////////////////////////////////////////////////////////////////////////////// @@ -3271,8 +3260,8 @@ static FrontendSettingsExt1_1 getFrontendSettingsExt1_1(JNIEnv *env, int type, j return settingsExt1_1; } -static sp<Filter> getFilter(JNIEnv *env, jobject filter) { - return (Filter *)env->GetLongField(filter, gFields.filterContext); +static sp<FilterClient> getFilterClient(JNIEnv *env, jobject filter) { + return (FilterClient *)env->GetLongField(filter, gFields.filterContext); } static DvrSettings getDvrSettings(JNIEnv *env, jobject settings, bool isRecorder) { @@ -3383,7 +3372,7 @@ static void android_media_tv_Tuner_native_init(JNIEnv *env) { static void android_media_tv_Tuner_native_setup(JNIEnv *env, jobject thiz) { sp<JTuner> tuner = new JTuner(env, thiz); - setTuner(env,thiz, tuner); + setTuner(env, thiz, tuner); } static jint android_media_tv_Tuner_native_get_tuner_version(JNIEnv *env, jobject thiz) { @@ -3399,8 +3388,7 @@ static jobject android_media_tv_Tuner_get_frontend_ids(JNIEnv *env, jobject thiz static jobject android_media_tv_Tuner_open_frontend_by_handle( JNIEnv *env, jobject thiz, jint handle) { sp<JTuner> tuner = getTuner(env, thiz); - uint32_t id = getResourceIdFromHandle(handle); - return tuner->openFrontendById(id); + return tuner->openFrontendByHandle(handle); } static int android_media_tv_Tuner_tune(JNIEnv *env, jobject thiz, jint type, jobject settings) { @@ -3446,13 +3434,13 @@ static jobject android_media_tv_Tuner_get_frontend_status( static jobject android_media_tv_Tuner_get_av_sync_hw_id( JNIEnv *env, jobject thiz, jobject filter) { - sp<Filter> filterSp = getFilter(env, filter); - if (filterSp == NULL) { - ALOGD("Failed to get sync ID. Filter not found"); + sp<FilterClient> filterClient = getFilterClient(env, filter); + if (filterClient == NULL) { + ALOGD("Failed to get sync ID. Filter client not found"); return NULL; } sp<JTuner> tuner = getTuner(env, thiz); - return tuner->getAvSyncHwId(filterSp); + return tuner->getAvSyncHwId(filterClient); } static jobject android_media_tv_Tuner_get_av_sync_time(JNIEnv *env, jobject thiz, jint id) { @@ -3913,26 +3901,13 @@ static DemuxFilterSettings getFilterConfiguration( } static Result configureIpFilterContextId( - JNIEnv *env, sp<IFilter> iFilterSp, jobject ipFilterConfigObj) { + JNIEnv *env, sp<FilterClient> filterClient, jobject ipFilterConfigObj) { jclass clazz = env->FindClass( "android/media/tv/tuner/filter/IpFilterConfiguration"); uint32_t cid = env->GetIntField(ipFilterConfigObj, env->GetFieldID( clazz, "mIpFilterContextId", "I")); - Result res = Result::SUCCESS; - if (cid != static_cast<uint32_t>(Constant::INVALID_IP_FILTER_CONTEXT_ID)) { - sp<::android::hardware::tv::tuner::V1_1::IFilter> iFilterSp_1_1; - iFilterSp_1_1 = ::android::hardware::tv::tuner::V1_1::IFilter::castFrom(iFilterSp); - if (iFilterSp_1_1 != NULL) { - res = iFilterSp_1_1->configureIpCid(cid); - if (res != Result::SUCCESS) { - return res; - } - } else { - ALOGW("configureIpCid is not supported with the current HAL implementation."); - } - } - return res; + return filterClient->configureIpFilterContextId(cid); } static jint copyData(JNIEnv *env, std::unique_ptr<MQ>& mq, EventFlag* flag, jbyteArray buffer, @@ -3975,21 +3950,20 @@ static bool isAvFilterSettings(DemuxFilterSettings filterSettings) { static jint android_media_tv_Tuner_configure_filter( JNIEnv *env, jobject filter, int type, int subtype, jobject settings) { ALOGD("configure filter type=%d, subtype=%d", type, subtype); - sp<Filter> filterSp = getFilter(env, filter); - sp<IFilter> iFilterSp = filterSp->getIFilter(); - if (iFilterSp == NULL) { + sp<FilterClient> filterClient = getFilterClient(env, filter); + if (filterClient == NULL) { ALOGD("Failed to configure filter: filter not found"); return (jint) Result::NOT_INITIALIZED; } DemuxFilterSettings filterSettings = getFilterConfiguration(env, type, subtype, settings); - Result res = iFilterSp->configure(filterSettings); + Result res = filterClient->configure(filterSettings); if (res != Result::SUCCESS) { return (jint) res; } if (static_cast<DemuxFilterMainType>(type) == DemuxFilterMainType::IP) { - res = configureIpFilterContextId(env, iFilterSp, settings); + res = configureIpFilterContextId(env, filterClient, settings); if (res != Result::SUCCESS) { return (jint) res; } @@ -3997,49 +3971,19 @@ static jint android_media_tv_Tuner_configure_filter( AvStreamType streamType; if (isAvFilterSettings(filterSettings) && getAvStreamType(env, settings, streamType)) { - sp<::android::hardware::tv::tuner::V1_1::IFilter> iFilterSp_1_1; - iFilterSp_1_1 = ::android::hardware::tv::tuner::V1_1::IFilter::castFrom(iFilterSp); - if (iFilterSp_1_1 != NULL) { - res = iFilterSp_1_1->configureAvStreamType(streamType); - } else { - ALOGW("configureAvStreamType is not supported with the current HAL implementation."); - } - if (res != Result::SUCCESS) { - return (jint) res; - } - } - - MQDescriptorSync<uint8_t> filterMQDesc; - Result getQueueDescResult = Result::UNKNOWN_ERROR; - if (filterSp->mFilterMQ == NULL) { - iFilterSp->getQueueDesc( - [&](Result r, const MQDescriptorSync<uint8_t>& desc) { - filterMQDesc = desc; - getQueueDescResult = r; - ALOGD("getFilterQueueDesc"); - }); - if (getQueueDescResult == Result::SUCCESS) { - filterSp->mFilterMQ = std::make_unique<MQ>(filterMQDesc, true); - EventFlag::createEventFlag( - filterSp->mFilterMQ->getEventFlagWord(), &(filterSp->mFilterMQEventFlag)); - } + res = filterClient->configureAvStreamType(streamType); } - return (jint) getQueueDescResult; + return (jint) res; } static jint android_media_tv_Tuner_get_filter_id(JNIEnv* env, jobject filter) { - sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter(); - if (iFilterSp == NULL) { - ALOGD("Failed to get filter ID: filter not found"); + sp<FilterClient> filterClient = getFilterClient(env, filter); + if (filterClient == NULL) { + ALOGD("Failed to get filter ID: filter client not found"); return (int) Result::NOT_INITIALIZED; } - Result res; uint32_t id; - iFilterSp->getId( - [&](Result r, uint32_t filterId) { - res = r; - id = filterId; - }); + Result res = filterClient->getId(id); if (res != Result::SUCCESS) { return (jint) Constant::INVALID_FILTER_ID; } @@ -4047,30 +3991,13 @@ static jint android_media_tv_Tuner_get_filter_id(JNIEnv* env, jobject filter) { } static jlong android_media_tv_Tuner_get_filter_64bit_id(JNIEnv* env, jobject filter) { - sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter(); - if (iFilterSp == NULL) { - ALOGD("Failed to get filter ID: filter not found"); - return static_cast<jlong>( - ::android::hardware::tv::tuner::V1_1::Constant64Bit::INVALID_FILTER_ID_64BIT); + sp<FilterClient> filterClient = getFilterClient(env, filter); + if (filterClient == NULL) { + ALOGD("Failed to get filter ID 64 bit: filter client not found"); + return (int) Result::NOT_INITIALIZED; } - - sp<::android::hardware::tv::tuner::V1_1::IFilter> iFilterSp_1_1; - iFilterSp_1_1 = ::android::hardware::tv::tuner::V1_1::IFilter::castFrom(iFilterSp); - Result res; uint64_t id; - - if (iFilterSp_1_1 != NULL) { - iFilterSp_1_1->getId64Bit( - [&](Result r, uint64_t filterId64Bit) { - res = r; - id = filterId64Bit; - }); - } else { - ALOGW("getId64Bit is not supported with the current HAL implementation."); - return static_cast<jlong>( - ::android::hardware::tv::tuner::V1_1::Constant64Bit::INVALID_FILTER_ID_64BIT); - } - + Result res = filterClient->getId64Bit(id); return (res == Result::SUCCESS) ? static_cast<jlong>(id) : static_cast<jlong>( ::android::hardware::tv::tuner::V1_1::Constant64Bit::INVALID_FILTER_ID_64BIT); @@ -4078,96 +4005,96 @@ static jlong android_media_tv_Tuner_get_filter_64bit_id(JNIEnv* env, jobject fil static jint android_media_tv_Tuner_configure_monitor_event( JNIEnv* env, jobject filter, int monitorEventType) { - sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter(); - if (iFilterSp == NULL) { - ALOGD("Failed to configure scrambling event: filter not found"); - return (jint) Result::NOT_INITIALIZED; - } - - sp<::android::hardware::tv::tuner::V1_1::IFilter> iFilterSp_1_1; - iFilterSp_1_1 = ::android::hardware::tv::tuner::V1_1::IFilter::castFrom(iFilterSp); - Result res; - - if (iFilterSp_1_1 != NULL) { - res = iFilterSp_1_1->configureMonitorEvent(monitorEventType); - } else { - ALOGW("configureScramblingEvent is not supported with the current HAL implementation."); - return (jint) Result::INVALID_STATE; + sp<FilterClient> filterClient = getFilterClient(env, filter); + if (filterClient == NULL) { + ALOGD("Failed to configure scrambling event: filter client not found"); + return (int) Result::NOT_INITIALIZED; } - + Result res = filterClient->configureMonitorEvent(monitorEventType); return (jint) res; } static jint android_media_tv_Tuner_set_filter_data_source( JNIEnv* env, jobject filter, jobject srcFilter) { - sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter(); - if (iFilterSp == NULL) { - ALOGD("Failed to set filter data source: filter not found"); - return (jint) Result::NOT_INITIALIZED; + sp<FilterClient> filterClient = getFilterClient(env, filter); + if (filterClient == NULL) { + ALOGD("Failed to set filter data source: filter client not found"); + return (int) Result::NOT_INITIALIZED; } - Result r; + Result res; if (srcFilter == NULL) { - r = iFilterSp->setDataSource(NULL); + res = filterClient->setDataSource(NULL); } else { - sp<IFilter> srcSp = getFilter(env, srcFilter)->getIFilter(); - if (iFilterSp == NULL) { + sp<FilterClient> srcClient = getFilterClient(env, srcFilter); + if (srcClient == NULL) { ALOGD("Failed to set filter data source: src filter not found"); return (jint) Result::INVALID_ARGUMENT; } - r = iFilterSp->setDataSource(srcSp); + res = filterClient->setDataSource(srcClient); } - return (jint) r; + return (jint) res; } static jint android_media_tv_Tuner_start_filter(JNIEnv *env, jobject filter) { - sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter(); - if (iFilterSp == NULL) { - ALOGD("Failed to start filter: filter not found"); - return (jint) Result::NOT_INITIALIZED; + sp<FilterClient> filterClient = getFilterClient(env, filter); + if (filterClient == NULL) { + ALOGD("Failed to start filter: filter client not found"); + return (int) Result::NOT_INITIALIZED; } - Result r = iFilterSp->start(); - return (jint) r; + return (jint) filterClient->start(); } static jint android_media_tv_Tuner_stop_filter(JNIEnv *env, jobject filter) { - sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter(); - if (iFilterSp == NULL) { - ALOGD("Failed to stop filter: filter not found"); - return (jint) Result::NOT_INITIALIZED; + sp<FilterClient> filterClient = getFilterClient(env, filter); + if (filterClient == NULL) { + ALOGD("Failed to stop filter: filter client not found"); + return (int) Result::NOT_INITIALIZED; } - Result r = iFilterSp->stop(); - return (jint) r; + return (jint) filterClient->stop(); } static jint android_media_tv_Tuner_flush_filter(JNIEnv *env, jobject filter) { - sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter(); - if (iFilterSp == NULL) { - ALOGD("Failed to flush filter: filter not found"); - return (jint) Result::NOT_INITIALIZED; + sp<FilterClient> filterClient = getFilterClient(env, filter); + if (filterClient == NULL) { + ALOGD("Failed to flush filter: filter client not found"); + return (int) Result::NOT_INITIALIZED; } - Result r = iFilterSp->flush(); - return (jint) r; + return (jint) filterClient->flush(); } static jint android_media_tv_Tuner_read_filter_fmq( JNIEnv *env, jobject filter, jbyteArray buffer, jlong offset, jlong size) { - sp<Filter> filterSp = getFilter(env, filter); - if (filterSp == NULL) { + sp<FilterClient> filterClient = getFilterClient(env, filter); + if (filterClient == NULL) { jniThrowException(env, "java/lang/IllegalStateException", - "Failed to read filter FMQ: filter not found"); + "Failed to read filter FMQ: filter client not found"); return 0; } - return copyData(env, filterSp->mFilterMQ, filterSp->mFilterMQEventFlag, buffer, offset, size); + + jboolean isCopy; + jbyte *dst = env->GetByteArrayElements(buffer, &isCopy); + ALOGD("copyData, isCopy=%d", isCopy); + if (dst == nullptr) { + jniThrowRuntimeException(env, "Failed to GetByteArrayElements"); + return 0; + } + int realReadSize = filterClient->read(reinterpret_cast<uint8_t*>(dst) + offset, size); + env->ReleaseByteArrayElements(buffer, dst, 0); + if (realReadSize < 0) { + return (jint) Result::UNKNOWN_ERROR; + } + return (jint) Result::SUCCESS; } static jint android_media_tv_Tuner_close_filter(JNIEnv *env, jobject filter) { - sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter(); - if (iFilterSp == NULL) { - ALOGD("Failed to close filter: filter not found"); - return (jint) Result::NOT_INITIALIZED; + sp<FilterClient> filterClient = getFilterClient(env, filter); + if (filterClient == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", + "Failed to close filter: filter client not found"); + return 0; } - Result r = iFilterSp->close(); - return (jint) r; + + return (jint) filterClient->close(); } static sp<TimeFilter> getTimeFilter(JNIEnv *env, jobject filter) { @@ -4275,7 +4202,8 @@ static jint android_media_tv_Tuner_descrambler_add_pid( if (descramblerSp == NULL) { return (jint) Result::NOT_INITIALIZED; } - sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter(); + // TODO: use filter client once descramblerClient is ready + sp<IFilter> iFilterSp = getFilterClient(env, filter)->getHalFilter(); Result result = descramblerSp->addPid(getDemuxPid((int)pidType, (int)pid), iFilterSp); return (jint) result; } @@ -4286,7 +4214,8 @@ static jint android_media_tv_Tuner_descrambler_remove_pid( if (descramblerSp == NULL) { return (jint) Result::NOT_INITIALIZED; } - sp<IFilter> iFilterSp = getFilter(env, filter)->getIFilter(); + // TODO: use filter client once descramblerClient is ready + sp<IFilter> iFilterSp = getFilterClient(env, filter)->getHalFilter(); Result result = descramblerSp->removePid(getDemuxPid((int)pidType, (int)pid), iFilterSp); return (jint) result; } @@ -4358,12 +4287,13 @@ static jint android_media_tv_Tuner_attach_filter(JNIEnv *env, jobject dvr, jobje if (dvrSp == NULL) { return (jint) Result::NOT_INITIALIZED; } - sp<Filter> filterSp = getFilter(env, filter); - if (filterSp == NULL) { + sp<FilterClient> filterClient = getFilterClient(env, filter); + if (filterClient == NULL) { return (jint) Result::INVALID_ARGUMENT; } sp<IDvr> iDvrSp = dvrSp->getIDvr(); - sp<IFilter> iFilterSp = filterSp->getIFilter(); + // TODO: use filter client once dvrClient is ready + sp<IFilter> iFilterSp = filterClient->getHalFilter(); Result result = iDvrSp->attachFilter(iFilterSp); return (jint) result; } @@ -4373,12 +4303,13 @@ static jint android_media_tv_Tuner_detach_filter(JNIEnv *env, jobject dvr, jobje if (dvrSp == NULL) { return (jint) Result::NOT_INITIALIZED; } - sp<Filter> filterSp = getFilter(env, filter); - if (filterSp == NULL) { + sp<FilterClient> filterClient = getFilterClient(env, filter); + if (filterClient == NULL) { return (jint) Result::INVALID_ARGUMENT; } sp<IDvr> iDvrSp = dvrSp->getIDvr(); - sp<IFilter> iFilterSp = filterSp->getIFilter(); + // TODO: use filter client once dvrClient is ready + sp<IFilter> iFilterSp = filterClient->getHalFilter(); Result result = iDvrSp->detachFilter(iFilterSp); return (jint) result; } diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h index bec834cac238..414922852377 100644 --- a/media/jni/android_media_tv_Tuner.h +++ b/media/jni/android_media_tv_Tuner.h @@ -34,6 +34,11 @@ #include <utils/Mutex.h> #include <utils/RefBase.h> +#include "tuner/DemuxClient.h" +#include "tuner/FilterClient.h" +#include "tuner/FilterClientCallback.h" +#include "tuner/FrontendClient.h" +#include "tuner/FrontendClientCallback.h" #include "tuner/TunerClient.h" #include "jni.h" @@ -95,6 +100,7 @@ struct Lnb : public RefBase { Lnb(sp<ILnb> sp, jobject obj); ~Lnb(); sp<ILnb> getILnb(); + // TODO: remove after migrate to client lib sp<ILnb> mLnbSp; jweak mLnbObj; }; @@ -115,6 +121,7 @@ struct Dvr : public RefBase { jint close(); MQ& getDvrMQ(); sp<IDvr> getIDvr(); + // TODO: remove after migrate to client lib sp<IDvr> mDvrSp; jweak mDvrObj; std::unique_ptr<MQ> mDvrMQ; @@ -123,29 +130,15 @@ struct Dvr : public RefBase { int mFd; }; -struct Filter : public RefBase { - Filter(sp<IFilter> sp, jobject obj); - ~Filter(); - int close(); - sp<IFilter> getIFilter(); - sp<IFilter> mFilterSp; - std::unique_ptr<MQ> mFilterMQ; - EventFlag* mFilterMQEventFlag; - jweak mFilterObj; - native_handle_t* mAvSharedHandle; - uint64_t mAvSharedMemSize; - bool mIsMediaFilter; -}; - struct MediaEvent : public RefBase { - MediaEvent(sp<Filter> filter, hidl_handle avHandle, uint64_t dataId, + MediaEvent(sp<FilterClient> filterClient, hidl_handle avHandle, uint64_t dataId, uint64_t dataSize, jobject obj); ~MediaEvent(); jobject getLinearBlock(); uint64_t getAudioHandle(); void finalize(); - sp<Filter> mFilter; + sp<FilterClient> mFilterClient; native_handle_t* mAvHandle; uint64_t mDataId; uint64_t mDataSize; @@ -159,16 +152,16 @@ struct MediaEvent : public RefBase { std::weak_ptr<C2Buffer> mC2Buffer; }; -struct FilterCallback : public IFilterCallback { - ~FilterCallback(); - virtual Return<void> onFilterEvent_1_1(const DemuxFilterEvent& filterEvent, +struct FilterClientCallbackImpl : public FilterClientCallback { + virtual void onFilterEvent_1_1(const DemuxFilterEvent& filterEvent, const DemuxFilterEventExt& filterEventExt); - virtual Return<void> onFilterEvent(const DemuxFilterEvent& filterEvent); - virtual Return<void> onFilterStatus(const DemuxFilterStatus status); + virtual void onFilterEvent(const DemuxFilterEvent& filterEvent); + virtual void onFilterStatus(const DemuxFilterStatus status); - void setFilter(const sp<Filter> filter); + void setFilter(jweak filterObj, sp<FilterClient> filterClient); private: - sp<Filter> mFilter; + jweak mFilterObj; + sp<FilterClient> mFilterClient; jobjectArray getSectionEvent( jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events); jobjectArray getMediaEvent( @@ -195,13 +188,13 @@ private: jobjectArray& arr, const std::vector<DemuxFilterEventExt::Event>& eventsExt); }; -struct FrontendCallback : public IFrontendCallback { - FrontendCallback(jweak tunerObj, FrontendId id); +struct FrontendClientCallbackImpl : public FrontendClientCallback { + FrontendClientCallbackImpl(jweak tunerObj, FrontendId id); - virtual Return<void> onEvent(FrontendEventType frontendEventType); - virtual Return<void> onScanMessage( + virtual void onEvent(FrontendEventType frontendEventType); + virtual void onScanMessage( FrontendScanMessageType type, const FrontendScanMessage& message); - virtual Return<void> onScanMessageExt1_1( + virtual void onScanMessageExt1_1( FrontendScanMessageTypeExt1_1 type, const FrontendScanMessageExt1_1& messageExt); jweak mObject; @@ -212,22 +205,24 @@ struct TimeFilter : public RefBase { TimeFilter(sp<ITimeFilter> sp, jweak obj); ~TimeFilter(); sp<ITimeFilter> getITimeFilter(); + // TODO: remove after migrate to client lib sp<ITimeFilter> mTimeFilterSp; jweak mTimeFilterObj; }; struct JTuner : public RefBase { JTuner(JNIEnv *env, jobject thiz); + // TODO: modify after migrate to client lib sp<ITuner> getTunerService(); int getTunerVersion(); - jobject getAvSyncHwId(sp<Filter> filter); + jobject getAvSyncHwId(sp<FilterClient> filter); jobject getAvSyncTime(jint id); int connectCiCam(jint id); int linkCiCam(jint id); int disconnectCiCam(); int unlinkCiCam(jint id); jobject getFrontendIds(); - jobject openFrontendById(int id); + jobject openFrontendByHandle(int feHandle); jint closeFrontendById(int id); jobject getFrontendInfo(int id); int tune(const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1); @@ -257,15 +252,22 @@ protected: private: jclass mClass; jweak mObject; + // TODO: remove after migrate to client lib static sp<ITuner> mTuner; static sp<::android::hardware::tv::tuner::V1_1::ITuner> mTuner_1_1; static sp<TunerClient> mTunerClient; + // TODO: remove after migrate to client lib sp<IFrontend> mFe; + // TODO: remove after migrate to client lib sp<::android::hardware::tv::tuner::V1_1::IFrontend> mFe_1_1; + sp<FrontendClient> mFeClient; int mFeId; hidl_vec<LnbId> mLnbIds; + // TODO: remove after migrate to client lib sp<ILnb> mLnb; + // TODO: remove after migrate to client lib sp<IDemux> mDemux; + sp<DemuxClient> mDemuxClient; uint32_t mDemuxId; static jobject getAnalogFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps); static jobject getAtsc3FrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps); @@ -279,6 +281,9 @@ private: static jobject getDtmbFrontendCaps(JNIEnv *env, int id); bool isV1_1ExtendedStatusType(jint type); + static uint32_t getResourceIdFromHandle(jint handle) { + return (handle & 0x00ff0000) >> 16; + } }; class C2DataIdInfo : public C2Param { diff --git a/media/jni/tuner/DemuxClient.cpp b/media/jni/tuner/DemuxClient.cpp index 11acb5ebf52e..b237a242d03e 100644 --- a/media/jni/tuner/DemuxClient.cpp +++ b/media/jni/tuner/DemuxClient.cpp @@ -68,14 +68,12 @@ sp<FilterClient> DemuxClient::openFilter(DemuxFilterType type, int bufferSize, sp<HidlFilterCallback> callback = new HidlFilterCallback(cb); sp<IFilter> hidlFilter = openHidlFilter(type, bufferSize, callback); if (hidlFilter != NULL) { - sp<FilterClient> filterClient = new FilterClient(); + sp<FilterClient> filterClient = new FilterClient(type); filterClient->setHidlFilter(hidlFilter); return filterClient; } } - // TODO: handle share av memory handle - return NULL; } @@ -141,7 +139,7 @@ Result DemuxClient::disconnectCiCam() { } Result DemuxClient::close() { - // pending aidl interface + // TODO: pending aidl interface if (mDemux != NULL) { Result res = mDemux->close(); diff --git a/media/jni/tuner/FilterClient.cpp b/media/jni/tuner/FilterClient.cpp index a71cae60e464..0aab5fe7de86 100644 --- a/media/jni/tuner/FilterClient.cpp +++ b/media/jni/tuner/FilterClient.cpp @@ -22,6 +22,9 @@ #include "FilterClient.h" using ::android::hardware::tv::tuner::V1_0::DemuxQueueNotifyBits; +using ::android::hardware::tv::tuner::V1_0::DemuxFilterMainType; +using ::android::hardware::tv::tuner::V1_0::DemuxMmtpFilterType; +using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterType; namespace android { @@ -29,20 +32,25 @@ namespace android { // TODO: pending aidl interface // TODO: add filter callback -FilterClient::FilterClient() { +FilterClient::FilterClient(DemuxFilterType type) { //mTunerFilter = tunerFilter; + mAvSharedHandle = NULL; + checkIsMediaFilter(type); } FilterClient::~FilterClient() { //mTunerFilter = NULL; mFilter = NULL; mFilter_1_1 = NULL; + mAvSharedHandle = NULL; + mAvSharedMemSize = 0; } // TODO: remove after migration to Tuner Service is done. void FilterClient::setHidlFilter(sp<IFilter> filter) { mFilter = filter; mFilter_1_1 = ::android::hardware::tv::tuner::V1_1::IFilter::castFrom(mFilter); + handleAvShareMemory(); } int FilterClient::read(uint8_t* buffer, int size) { @@ -59,6 +67,22 @@ int FilterClient::read(uint8_t* buffer, int size) { return -1; } +SharedHandleInfo FilterClient::getAvSharedHandleInfo() { + SharedHandleInfo info{ + .sharedHandle = NULL, + .size = 0, + }; + + // TODO: pending aidl interface + + if (mFilter_1_1 != NULL) { + info.sharedHandle = mAvSharedHandle; + info.size = mAvSharedMemSize; + } + + return info; +} + Result FilterClient::configure(DemuxFilterSettings configure) { // TODO: pending aidl interface @@ -187,7 +211,11 @@ Result FilterClient::close() { // TODO: pending aidl interface if (mFilter != NULL) { - return mFilter->close(); + Result res = mFilter->close(); + if (res == Result::SUCCESS) { + mFilter = NULL; + } + return res; } return Result::INVALID_STATE; @@ -261,4 +289,31 @@ int FilterClient::copyData(uint8_t* buffer, int size) { return size; } + +void FilterClient::checkIsMediaFilter(DemuxFilterType type) { + if (type.mainType == DemuxFilterMainType::MMTP) { + if (type.subType.mmtpFilterType() == DemuxMmtpFilterType::AUDIO || + type.subType.mmtpFilterType() == DemuxMmtpFilterType::VIDEO) { + mIsMediaFilter = true; + } + } + + if (type.mainType == DemuxFilterMainType::TS) { + if (type.subType.tsFilterType() == DemuxTsFilterType::AUDIO || + type.subType.tsFilterType() == DemuxTsFilterType::VIDEO) { + mIsMediaFilter = true; + } + } +} + +void FilterClient::handleAvShareMemory() { + if (mFilter_1_1 != NULL && mIsMediaFilter) { + mFilter_1_1->getAvSharedHandle([&](Result r, hidl_handle avMemory, uint64_t avMemSize) { + if (r == Result::SUCCESS) { + mAvSharedHandle = native_handle_clone(avMemory.getNativeHandle()); + mAvSharedMemSize = avMemSize; + } + }); + } +} } // namespace android diff --git a/media/jni/tuner/FilterClient.h b/media/jni/tuner/FilterClient.h index 4af74b06d00b..976b2f5a107d 100644 --- a/media/jni/tuner/FilterClient.h +++ b/media/jni/tuner/FilterClient.h @@ -34,6 +34,7 @@ using ::android::hardware::Return; using ::android::hardware::Void; using ::android::hardware::hidl_handle; using ::android::hardware::tv::tuner::V1_0::DemuxFilterSettings; +using ::android::hardware::tv::tuner::V1_0::DemuxFilterType; using ::android::hardware::tv::tuner::V1_0::IFilter; using ::android::hardware::tv::tuner::V1_0::Result; using ::android::hardware::tv::tuner::V1_1::AvStreamType; @@ -45,6 +46,11 @@ using MQ = MessageQueue<uint8_t, kSynchronizedReadWrite>; namespace android { +struct SharedHandleInfo { + native_handle_t* sharedHandle; + uint64_t size; +}; + // TODO: pending aidl interface /*class TunerFilterCallback : public BnTunerFilterCallback { @@ -75,7 +81,7 @@ struct FilterClient : public RefBase { public: // TODO: pending aidl interface - FilterClient(); + FilterClient(DemuxFilterType type); ~FilterClient(); // TODO: remove after migration to Tuner Service is done. @@ -89,6 +95,11 @@ public: int read(uint8_t* buffer, int size); /** + * Get the a/v shared memory handle information + */ + SharedHandleInfo getAvSharedHandleInfo(); + + /** * Configure the filter. */ Result configure(DemuxFilterSettings configure); @@ -161,6 +172,8 @@ public: private: Result getFilterMq(); int copyData(uint8_t* buffer, int size); + void checkIsMediaFilter(DemuxFilterType type); + void handleAvShareMemory(); /** * An AIDL Tuner Filter Singleton assigned at the first time when the Tuner Client @@ -189,6 +202,10 @@ private: sp<FilterClientCallback> mCallback; //shared_ptr<TunerFilterCallback> mAidlCallback; sp<HidlFilterCallback> mHidlCallback; + + native_handle_t* mAvSharedHandle; + uint64_t mAvSharedMemSize; + bool mIsMediaFilter; }; } // namespace android diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java index fc16eb6b4277..61af5a74b00a 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java @@ -22,16 +22,6 @@ package com.android.settingslib.media; public class MediaOutputSliceConstants { /** - * Key for the Media output setting. - */ - public static final String KEY_MEDIA_OUTPUT = "media_output"; - - /** - * Key for the Media output group setting. - */ - public static final String KEY_MEDIA_OUTPUT_GROUP = "media_output_group"; - - /** * Key for the Remote Media slice. */ public static final String KEY_REMOTE_MEDIA = "remote_media"; @@ -47,19 +37,6 @@ public class MediaOutputSliceConstants { public static final String KEY_SESSION_INFO_ID = "key_session_info_id"; /** - * Activity Action: Show a settings dialog containing {@link MediaDevice} to transfer media. - */ - public static final String ACTION_MEDIA_OUTPUT = - "com.android.settings.panel.action.MEDIA_OUTPUT"; - - /** - * Activity Action: Show a settings dialog containing {@link MediaDevice} to handle media group - * operation. - */ - public static final String ACTION_MEDIA_OUTPUT_GROUP = - "com.android.settings.panel.action.MEDIA_OUTPUT_GROUP"; - - /** * A string extra specifying a media package name. */ public static final String EXTRA_PACKAGE_NAME = @@ -72,12 +49,6 @@ public class MediaOutputSliceConstants { "com.android.systemui.action.LAUNCH_MEDIA_OUTPUT_DIALOG"; /** - * An intent action to dismiss media output dialog. - */ - public static final String ACTION_DISMISS_MEDIA_OUTPUT_DIALOG = - "com.android.systemui.action.DISMISS_MEDIA_OUTPUT_DIALOG"; - - /** * Settings package name. */ public static final String SETTINGS_PACKAGE_NAME = "com.android.settings"; diff --git a/packages/SystemUI/res/drawable/ic_fingerprint.xml b/packages/SystemUI/res/drawable/ic_fingerprint.xml new file mode 100644 index 000000000000..e5f33602164e --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_fingerprint.xml @@ -0,0 +1,59 @@ +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License +--> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="32dp" + android:height="32dp" + android:tint="?attr/wallpaperTextColor" + android:alpha=".75" + android:viewportHeight="32.0" + android:viewportWidth="32.0"> + <path + android:fillColor="#ffffff" + android:pathData="M23.7,5.9c-0.1,0.0 -0.2,0.0 -0.3,-0.1C21.0,4.5 18.6,3.9 16.0,3.9c-2.5,0.0 + -4.6,0.6 -6.9,1.9C8.8,6.0 8.3,5.9 8.1,5.5C7.9,5.2 8.0,4.7 8.4,4.5c2.5,-1.4 4.9,-2.1 7.7, + -2.1c2.8,0.0 5.4,0.7 8.0,2.1c0.4,0.2 0.5,0.6 0.3,1.0C24.2,5.7 24.0,5.9 23.7,5.9z" /> + <path + android:fillColor="#ffffff" + android:pathData="M5.3,13.2c-0.1,0.0 -0.3,0.0 -0.4,-0.1c-0.3,-0.2 -0.4,-0.7 -0.2,-1.0c1.3, + -1.9 2.9,-3.4 4.9,-4.5c4.1,-2.2 9.3,-2.2 13.4,0.0c1.9,1.1 3.6,2.5 4.9,4.4c0.2,0.3 0.1,0.8 + -0.2,1.0c-0.3,0.2 -0.8,0.1 -1.0,-0.2c-1.2,-1.7 -2.6,-3.0 -4.3,-4.0c-3.7,-2.0 -8.3,-2.0 + -12.0,0.0c-1.7,0.9 -3.2,2.3 -4.3,4.0C5.7,13.1 5.5,13.2 5.3,13.2z" /> + <path + android:fillColor="#ffffff" + android:pathData="M13.3,29.6c-0.2,0.0 -0.4,-0.1 -0.5,-0.2c-1.1,-1.2 -1.7,-2.0 -2.6, + -3.6c-0.9,-1.7 -1.4,-3.7 -1.4,-5.9c0.0,-4.1 3.3,-7.4 7.4,-7.4c4.1,0.0 7.4,3.3 7.4,7.4c0.0, + 0.4 -0.3,0.7 -0.7,0.7s-0.7,-0.3 -0.7,-0.7c0.0,-3.3 -2.7,-5.9 -5.9,-5.9c-3.3,0.0 -5.9, + 2.7 -5.9,5.9c0.0,2.0 0.4,3.8 1.2,5.2c0.8,1.6 1.4,2.2 2.4,3.3c0.3,0.3 0.3,0.8 0.0,1.0 + C13.7,29.5 13.5,29.6 13.3,29.6z" /> + <path + android:fillColor="#ffffff" + android:pathData="M22.6,27.1c-1.6,0.0 -2.9,-0.4 -4.1,-1.2c-1.9,-1.4 -3.1,-3.6 -3.1, + -6.0c0.0,-0.4 0.3,-0.7 0.7,-0.7s0.7,0.3 0.7,0.7c0.0,1.9 0.9,3.7 2.5,4.8c0.9,0.6 1.9, + 1.0 3.2,1.0c0.3,0.0 0.8,0.0 1.3,-0.1c0.4,-0.1 0.8,0.2 0.8,0.6c0.1,0.4 -0.2,0.8 -0.6, + 0.8C23.4,27.1 22.8,27.1 22.6,27.1z" /> + <path + android:fillColor="#ffffff" + android:pathData="M20.0,29.9c-0.1,0.0 -0.1,0.0 -0.2,0.0c-2.1,-0.6 -3.4,-1.4 -4.8,-2.9c-1.8, + -1.9 -2.8,-4.4 -2.8,-7.1c0.0,-2.2 1.8,-4.1 4.1,-4.1c2.2,0.0 4.1,1.8 4.1,4.1c0.0,1.4 1.2, + 2.6 2.6,2.6c1.4,0.0 2.6,-1.2 2.6,-2.6c0.0,-5.1 -4.2,-9.3 -9.3,-9.3c-3.6,0.0 -6.9,2.1 -8.4, + 5.4C7.3,17.1 7.0,18.4 7.0,19.8c0.0,1.1 0.1,2.7 0.9,4.9c0.1,0.4 -0.1,0.8 -0.4,0.9c-0.4, + 0.1 -0.8,-0.1 -0.9,-0.4c-0.6,-1.8 -0.9,-3.6 -0.9,-5.4c0.0,-1.6 0.3,-3.1 0.9,-4.4c1.7, + -3.8 5.6,-6.3 9.8,-6.3c5.9,0.0 10.7,4.8 10.7,10.7c0.0,2.2 -1.8,4.1 -4.1,4.1s-4.0, + -1.8 -4.0,-4.1c0.0,-1.4 -1.2,-2.6 -2.6,-2.6c-1.4,0.0 -2.6,1.2 -2.6,2.6c0.0,2.3 0.9, + 4.5 2.4,6.1c1.2,1.3 2.4,2.0 4.2,2.5c0.4,0.1 0.6,0.5 0.5,0.9C20.6,29.7 20.3,29.9 20.0, + 29.9z" /> +</vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml index 84b9e3de28ef..2c08f5db0323 100644 --- a/packages/SystemUI/res/layout/status_bar_notification_row.xml +++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml @@ -18,6 +18,7 @@ <!-- extends FrameLayout --> <com.android.systemui.statusbar.notification.row.ExpandableNotificationRow xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/expandableNotificationRow" android:layout_width="match_parent" android:layout_height="wrap_content" android:focusable="true" diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 2071cfaf2bd9..b43496cb55dc 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -1901,6 +1901,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } /** + * Whether to show the lock icon on lock screen and bouncer. This depends on the enrolled + * biometrics to the device. + */ + public boolean shouldShowLockIcon() { + return isFaceAuthEnabledForUser(KeyguardUpdateMonitor.getCurrentUser()) + && !isUdfpsEnrolled(); + } + + /** * @return true if there's at least one udfps enrolled */ public boolean isUdfpsEnrolled() { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index a15a5aa86aa2..65a6f29892f0 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -135,7 +135,7 @@ class UdfpsController implements DozeReceiver { @SuppressLint("ClickableViewAccessibility") private final UdfpsView.OnTouchListener mOnTouchListener = (v, event) -> { UdfpsView view = (UdfpsView) v; - final boolean isFingerDown = view.isScrimShowing(); + final boolean isFingerDown = view.isShowScrimAndDot(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_MOVE: diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java index 5a61379f3ea3..663a0da81ad5 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java @@ -27,6 +27,7 @@ import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; +import android.graphics.drawable.Drawable; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.text.TextUtils; import android.util.AttributeSet; @@ -70,6 +71,7 @@ public class UdfpsView extends View implements DozeReceiver, // mInsetsListener to restrict the touchable region and allow the touches outside of the sensor // to propagate to the rest of the UI. @NonNull private final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsListener; + @NonNull private final Drawable mFingerprintDrawable; // Used to obtain the sensor location. @NonNull private FingerprintSensorPropertiesInternal mSensorProps; @@ -79,7 +81,7 @@ public class UdfpsView extends View implements DozeReceiver, private float mBurnInOffsetX; private float mBurnInOffsetY; - private boolean mIsScrimShowing; + private boolean mShowScrimAndDot; private boolean mIsHbmSupported; @Nullable private String mDebugMessage; @@ -112,15 +114,16 @@ public class UdfpsView extends View implements DozeReceiver, mSensorPaint = new Paint(0 /* flags */); mSensorPaint.setAntiAlias(true); mSensorPaint.setColor(Color.WHITE); - mSensorPaint.setStyle(Paint.Style.STROKE); - mSensorPaint.setStrokeWidth(SENSOR_OUTLINE_WIDTH); mSensorPaint.setShadowLayer(SENSOR_SHADOW_RADIUS, 0, 0, Color.BLACK); + mSensorPaint.setStyle(Paint.Style.FILL); mDebugTextPaint = new Paint(); mDebugTextPaint.setAntiAlias(true); mDebugTextPaint.setColor(Color.BLUE); mDebugTextPaint.setTextSize(DEBUG_TEXT_SIZE_PX); + mFingerprintDrawable = getResources().getDrawable(R.drawable.ic_fingerprint, null); + mTouchableRegion = new Rect(); // When the device is rotated, it's important that mTouchableRegion is updated before // this listener is called. This listener is usually called shortly after onLayout. @@ -130,7 +133,7 @@ public class UdfpsView extends View implements DozeReceiver, internalInsetsInfo.touchableRegion.set(mTouchableRegion); }; - mIsScrimShowing = false; + mShowScrimAndDot = false; } void setSensorProperties(@NonNull FingerprintSensorPropertiesInternal properties) { @@ -181,6 +184,13 @@ public class UdfpsView extends View implements DozeReceiver, default: // Do nothing to stay in portrait mode. } + + int margin = (int) (mSensorRect.bottom - mSensorRect.top) / 5; + mFingerprintDrawable.setBounds( + (int) mSensorRect.left + margin, + (int) mSensorRect.top + margin, + (int) mSensorRect.right - margin, + (int) mSensorRect.bottom - margin); } @Override @@ -198,6 +208,8 @@ public class UdfpsView extends View implements DozeReceiver, // is finished, mTouchableRegion will be used by mInsetsListener to compute the touch // insets. mSensorRect.roundOut(mTouchableRegion); + + } @Override @@ -218,7 +230,7 @@ public class UdfpsView extends View implements DozeReceiver, protected void onDraw(Canvas canvas) { super.onDraw(canvas); - if (mIsScrimShowing && mIsHbmSupported) { + if (mShowScrimAndDot && mIsHbmSupported) { // Only draw the scrim if HBM is supported. canvas.drawRect(mScrimRect, mScrimPaint); } @@ -229,7 +241,15 @@ public class UdfpsView extends View implements DozeReceiver, if (!TextUtils.isEmpty(mDebugMessage)) { canvas.drawText(mDebugMessage, 0, 160, mDebugTextPaint); } - canvas.drawOval(mSensorRect, mSensorPaint); + + if (mShowScrimAndDot) { + // draw dot (white circle) + canvas.drawOval(mSensorRect, mSensorPaint); + } else { + // draw fingerprint icon + mFingerprintDrawable.draw(canvas); + } + canvas.restore(); } @@ -264,19 +284,17 @@ public class UdfpsView extends View implements DozeReceiver, mScrimPaint.setAlpha(alpha); } - boolean isScrimShowing() { - return mIsScrimShowing; + boolean isShowScrimAndDot() { + return mShowScrimAndDot; } void showScrimAndDot() { - mIsScrimShowing = true; - mSensorPaint.setStyle(Paint.Style.FILL); + mShowScrimAndDot = true; invalidate(); } void hideScrimAndDot() { - mIsScrimShowing = false; - mSensorPaint.setStyle(Paint.Style.STROKE); + mShowScrimAndDot = false; invalidate(); } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessController.kt index a7d17e128ff3..b4137fa80f19 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessController.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessController.kt @@ -64,7 +64,8 @@ open class FaceAuthScreenBrightnessController( private val globalSettings: GlobalSettings, private val systemSettings: SystemSettings, private val mainHandler: Handler, - private val dumpManager: DumpManager + private val dumpManager: DumpManager, + private val enabled: Boolean ) : Dumpable { private var userDefinedBrightness: Float = 1f @@ -86,7 +87,7 @@ open class FaceAuthScreenBrightnessController( return } // TODO enable only when receiving a low-light error - overridingBrightness = running + overridingBrightness = if (enabled) running else false } } private lateinit var whiteOverlay: View @@ -188,6 +189,7 @@ open class FaceAuthScreenBrightnessController( println("brightnessAnimationDuration: $brightnessAnimationDuration") println("maxScreenBrightness: $maxScreenBrightness") println("userDefinedBrightness: $userDefinedBrightness") + println("enabled: $enabled") } } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java index d7b5eea84c36..626abfcc85fd 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java @@ -142,8 +142,9 @@ public class KeyguardModule { return Optional.empty(); } + // currently disabled (doesn't ramp up brightness or use scrim) see b/175918367 return Optional.of(new FaceAuthScreenBrightnessController( notificationShadeWindowController, keyguardUpdateMonitor, resources, - globalSetting, systemSettings, handler, dumpManager)); + globalSetting, systemSettings, handler, dumpManager, false)); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index 7bac007ae478..20efa32d63c6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -197,7 +197,6 @@ public class NotificationShelf extends ActivatableNotificationView implements viewState.hasItemsInStableShelf = lastViewState.inShelf; viewState.hidden = !mAmbientState.isShadeExpanded() || mAmbientState.isQsCustomizerShowing(); - viewState.maxShelfEnd = maxShelfEnd; } else { viewState.hidden = true; viewState.location = ExpandableViewState.LOCATION_GONE; @@ -823,10 +822,6 @@ public class NotificationShelf extends ActivatableNotificationView implements return - (getIntrinsicHeight() - mStatusBarHeight) / 2; } - public int getNotificationMergeSize() { - return getIntrinsicHeight(); - } - @Override public boolean hasNoContentHeight() { return true; @@ -1018,7 +1013,6 @@ public class NotificationShelf extends ActivatableNotificationView implements private class ShelfState extends ExpandableViewState { private float openedAmount; private boolean hasItemsInStableShelf; - private float maxShelfEnd; @Override public void applyToView(View view) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java index 4ca9c5db013c..db0713cf0b7a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -289,8 +289,6 @@ public class KeyguardBouncer { SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__HIDDEN); mDismissCallbackRegistry.notifyDismissCancelled(); } - mExpansion = EXPANSION_HIDDEN; - dispatchExpansionChanged(); mIsScrimmed = false; mFalsingCollector.onBouncerHidden(); mCallback.onBouncerVisiblityChanged(false /* shown */); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java index b9e8d74d9b85..6da5d1b90cd9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java @@ -164,10 +164,10 @@ public class KeyguardClockPositionAlgorithm { public void setup(int statusBarMinHeight, int maxShadeBottom, int notificationStackHeight, float panelExpansion, int parentHeight, int keyguardStatusHeight, int clockPreferredY, boolean hasCustomClock, boolean hasVisibleNotifs, float dark, float emptyDragAmount, - boolean bypassEnabled, int unlockedStackScrollerPadding, boolean udfpsEnrolled, + boolean bypassEnabled, int unlockedStackScrollerPadding, boolean showLockIcon, float qsExpansion) { - mMinTopMargin = statusBarMinHeight + (udfpsEnrolled ? mContainerTopPaddingWithoutLockIcon : - mContainerTopPaddingWithLockIcon); + mMinTopMargin = statusBarMinHeight + (showLockIcon + ? mContainerTopPaddingWithLockIcon : mContainerTopPaddingWithoutLockIcon); mMaxShadeBottom = maxShadeBottom; mNotificationStackHeight = notificationStackHeight; mPanelExpansion = panelExpansion; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java index 6bdc303b4786..ab0366e07b9c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java @@ -501,7 +501,7 @@ public class LockscreenLockIconController { if (mBlockUpdates && canBlockUpdates()) { shouldUpdate = false; } - if (shouldUpdate && mLockIcon != null) { + if (shouldUpdate && mLockIcon != null && mLockIcon.getVisibility() != GONE) { mLockIcon.update(state, mStatusBarStateController.isDozing(), mKeyguardJustShown); } @@ -549,16 +549,14 @@ public class LockscreenLockIconController { return false; } - if (mKeyguardUpdateMonitor.isUdfpsEnrolled()) { - boolean changed = mLockIcon.getVisibility() == GONE; + if (!mKeyguardUpdateMonitor.shouldShowLockIcon()) { + boolean changed = mLockIcon.getVisibility() != GONE; mLockIcon.setVisibility(GONE); return changed; } boolean onAodOrDocked = mStatusBarStateController.isDozing() || mDocked; - boolean invisible = onAodOrDocked || mWakeAndUnlockRunning || mShowingLaunchAffordance - || (mKeyguardSecurityModel.getSecurityMode(KeyguardUpdateMonitor.getCurrentUser()) - == KeyguardSecurityModel.SecurityMode.None); + boolean invisible = onAodOrDocked || mWakeAndUnlockRunning || mShowingLaunchAffordance; boolean fingerprintOrBypass = mFingerprintUnlock || mKeyguardBypassController.getBypassEnabled(); if (fingerprintOrBypass && !mBouncerShowingScrimmed) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index 8d3b12868dd7..ba08e76e6f66 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -912,7 +912,8 @@ public class NotificationPanelViewController extends PanelViewController { clockPreferredY, hasCustomClock(), hasVisibleNotifications, mInterpolatedDarkAmount, mEmptyDragAmount, bypassEnabled, getUnlockedStackScrollerPadding(), - mUpdateMonitor.isUdfpsEnrolled(), getQsExpansionFraction()); + mUpdateMonitor.shouldShowLockIcon(), + getQsExpansionFraction()); mClockPositionAlgorithm.run(mClockPositionResult); mKeyguardStatusViewController.updatePosition( mClockPositionResult.clockX, mClockPositionResult.clockY, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java index 272c494b1af4..22fd93ed10ed 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java @@ -25,11 +25,15 @@ import android.os.UserHandle; import androidx.annotation.NonNull; +import com.android.systemui.Dumpable; +import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dump.DumpManager; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Date; import javax.inject.Inject; @@ -38,7 +42,7 @@ import javax.inject.Inject; */ @SysUISingleton public class NextAlarmControllerImpl extends BroadcastReceiver - implements NextAlarmController { + implements NextAlarmController, Dumpable { private final ArrayList<NextAlarmChangeCallback> mChangeCallbacks = new ArrayList<>(); @@ -48,18 +52,34 @@ public class NextAlarmControllerImpl extends BroadcastReceiver /** */ @Inject - public NextAlarmControllerImpl(Context context) { - mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + public NextAlarmControllerImpl( + AlarmManager alarmManager, + BroadcastDispatcher broadcastDispatcher, + DumpManager dumpManager) { + dumpManager.registerDumpable("NextAlarmController", this); + mAlarmManager = alarmManager; IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_USER_SWITCHED); filter.addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED); - context.registerReceiverAsUser(this, UserHandle.ALL, filter, null, null); + broadcastDispatcher.registerReceiver(this, filter, null, UserHandle.ALL); updateNextAlarm(); } + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println("NextAlarmController state:"); - pw.print(" mNextAlarm="); pw.println(mNextAlarm); + pw.print("mNextAlarm="); + if (mNextAlarm != null) { + pw.println(new Date(mNextAlarm.getTriggerTime())); + pw.print(" PendingIntentPkg="); + pw.println(mNextAlarm.getShowIntent().getCreatorPackage()); + } else { + pw.println("null"); + } + + pw.println("Registered Callbacks:"); + for (NextAlarmChangeCallback callback : mChangeCallbacks) { + pw.print(" "); pw.println(callback.toString()); + } } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index 648c31924a20..7f8372e4bc44 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -175,7 +175,7 @@ public class UdfpsControllerTest extends SysuiTestCase { @Test public void fingerDown() throws RemoteException { // Configure UdfpsView to accept the ACTION_DOWN event - when(mUdfpsView.isScrimShowing()).thenReturn(false); + when(mUdfpsView.isShowScrimAndDot()).thenReturn(false); when(mUdfpsView.isValidTouch(anyFloat(), anyFloat(), anyFloat())).thenReturn(true); // GIVEN that the overlay is showing diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessControllerTest.kt index 73a7ca93507d..cb05a6b21b3a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessControllerTest.kt @@ -83,7 +83,7 @@ class FaceAuthScreenBrightnessControllerTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) faceAuthScreenBrightnessController = object : FaceAuthScreenBrightnessController( notificationShadeWindowController, keyguardUpdateMonitor, resources, globalSettings, - systemSettings, mainHandler, dumpManager) { + systemSettings, mainHandler, dumpManager, true) { override fun createAnimator(start: Float, end: Float) = animator } `when`(systemSettings.getFloat(eq(SCREEN_BRIGHTNESS_FLOAT))).thenReturn(INITIAL_BRIGHTNESS) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java index 1ac793730f02..95a35050c09e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java @@ -91,6 +91,7 @@ public class LockscreenIconControllerTest extends SysuiTestCase { public void setUp() { MockitoAnnotations.initMocks(this); + when(mKeyguardUpdateMonitor.shouldShowLockIcon()).thenReturn(true); when(mLockIcon.getContext()).thenReturn(mContext); mLockIconController = new LockscreenLockIconController( mLockscreenGestureLogger, mKeyguardUpdateMonitor, mLockPatternUtils, @@ -145,12 +146,10 @@ public class LockscreenIconControllerTest extends SysuiTestCase { } @Test - public void testVisibility_noBouncer() { - // no security (ie: no lock screen OR swipe to unlock) - when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn( - KeyguardSecurityModel.SecurityMode.None); + public void testVisibility_doNotShowLockIcon() { + when(mKeyguardUpdateMonitor.shouldShowLockIcon()).thenReturn(false); mOnAttachStateChangeListener.onViewAttachedToWindow(mLockIcon); - verify(mLockIcon).updateIconVisibility(false); + verify(mLockIcon).setVisibility(View.GONE); } } diff --git a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java index 52a82dd2a156..5ee30fb728ad 100644 --- a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java +++ b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java @@ -51,7 +51,6 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; -import java.nio.charset.StandardCharsets; public class WallpaperBackupAgent extends BackupAgent { private static final String TAG = "WallpaperBackup"; @@ -323,8 +322,7 @@ public class WallpaperBackupAgent extends BackupAgent { private Rect parseCropHint(File wallpaperInfo, String sectionTag) { Rect cropHint = new Rect(); try (FileInputStream stream = new FileInputStream(wallpaperInfo)) { - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(stream, StandardCharsets.UTF_8.name()); + XmlPullParser parser = Xml.resolvePullParser(stream); int type; do { @@ -351,8 +349,7 @@ public class WallpaperBackupAgent extends BackupAgent { private ComponentName parseWallpaperComponent(File wallpaperInfo, String sectionTag) { ComponentName name = null; try (FileInputStream stream = new FileInputStream(wallpaperInfo)) { - final XmlPullParser parser = Xml.newPullParser(); - parser.setInput(stream, StandardCharsets.UTF_8.name()); + final XmlPullParser parser = Xml.resolvePullParser(stream); int type; do { diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 9b9919981935..2e1fbb77cd01 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -144,6 +144,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; +import android.os.INetworkActivityListener; import android.os.INetworkManagementService; import android.os.Looper; import android.os.Message; @@ -1380,8 +1381,11 @@ public class ConnectivityService extends IConnectivityManager.Stub return; } final String action = blocked ? "BLOCKED" : "UNBLOCKED"; + final NetworkRequest satisfiedRequest = nri.getSatisfiedRequest(); + final int requestId = satisfiedRequest != null + ? satisfiedRequest.requestId : nri.mRequests.get(0).requestId; mNetworkInfoBlockingLogs.log(String.format( - "%s %d(%d) on netId %d", action, nri.mUid, nri.request.requestId, net.getNetId())); + "%s %d(%d) on netId %d", action, nri.mUid, requestId, net.getNetId())); } /** @@ -1709,16 +1713,17 @@ public class ConnectivityService extends IConnectivityManager.Stub return newNc; } - Binder.withCleanCallingIdentity( - () -> { - if (!mLocationPermissionChecker.checkLocationPermission( - callerPkgName, null /* featureId */, callerUid, null /* message */)) { - // Caller does not have the requisite location permissions. Reset the - // owner's UID in the NetworkCapabilities. - newNc.setOwnerUid(INVALID_UID); - } - } - ); + final long token = Binder.clearCallingIdentity(); + try { + if (!mLocationPermissionChecker.checkLocationPermission( + callerPkgName, null /* featureId */, callerUid, null /* message */)) { + // Caller does not have the requisite location permissions. Reset the + // owner's UID in the NetworkCapabilities. + newNc.setOwnerUid(INVALID_UID); + } + } finally { + Binder.restoreCallingIdentity(token); + } return newNc; } @@ -2336,6 +2341,31 @@ public class ConnectivityService extends IConnectivityManager.Stub } /** + * Start listening for default data network activity state changes. + */ + @Override + public void registerNetworkActivityListener(@NonNull INetworkActivityListener l) { + // TODO: Replace network activity listener registry in ConnectivityManager from NMS to here + } + + /** + * Stop listening for default data network activity state changes. + */ + @Override + public void unregisterNetworkActivityListener(@NonNull INetworkActivityListener l) { + // TODO: Replace network activity listener registry in ConnectivityManager from NMS to here + } + + /** + * Check whether the default network radio is currently active. + */ + @Override + public boolean isDefaultNetworkActive() { + // TODO: Replace isNetworkActive() in NMS. + return false; + } + + /** * Setup data activity tracking for the given network. * * Every {@code setupDataActivityTracking} should be paired with a @@ -2705,7 +2735,7 @@ public class ConnectivityService extends IConnectivityManager.Stub * Return an array of all current NetworkRequest sorted by request id. */ @VisibleForTesting - protected NetworkRequestInfo[] requestsSortedById() { + NetworkRequestInfo[] requestsSortedById() { NetworkRequestInfo[] requests = new NetworkRequestInfo[0]; requests = mNetworkRequests.values().toArray(requests); // Sort the array based off the NRI containing the min requestId in its requests. @@ -3555,30 +3585,58 @@ public class ConnectivityService extends IConnectivityManager.Stub return false; } for (NetworkRequestInfo nri : mNetworkRequests.values()) { - if (reason == UnneededFor.LINGER && nri.request.isBackgroundRequest()) { + if (reason == UnneededFor.LINGER + && !nri.isMultilayerRequest() + && nri.mRequests.get(0).isBackgroundRequest()) { // Background requests don't affect lingering. continue; } - // If this Network is already the highest scoring Network for a request, or if - // there is hope for it to become one if it validated, then it is needed. - if (nri.request.isRequest() && nai.satisfies(nri.request) && - (nai.isSatisfyingRequest(nri.request.requestId) || - // Note that this catches two important cases: - // 1. Unvalidated cellular will not be reaped when unvalidated WiFi - // is currently satisfying the request. This is desirable when - // cellular ends up validating but WiFi does not. - // 2. Unvalidated WiFi will not be reaped when validated cellular - // is currently satisfying the request. This is desirable when - // WiFi ends up validating and out scoring cellular. - nri.mSatisfier.getCurrentScore() - < nai.getCurrentScoreAsValidated())) { + if (isNetworkPotentialSatisfier(nai, nri)) { return false; } } return true; } + private boolean isNetworkPotentialSatisfier( + @NonNull final NetworkAgentInfo candidate, @NonNull final NetworkRequestInfo nri) { + // listen requests won't keep up a network satisfying it. If this is not a multilayer + // request, we can return immediately. For multilayer requests, we have to check to see if + // any of the multilayer requests may have a potential satisfier. + if (!nri.isMultilayerRequest() && nri.mRequests.get(0).isListen()) { + return false; + } + for (final NetworkRequest req : nri.mRequests) { + // As non-multilayer listen requests have already returned, the below would only happen + // for a multilayer request therefore continue to the next request if available. + if (req.isListen()) { + continue; + } + // If this Network is already the highest scoring Network for a request, or if + // there is hope for it to become one if it validated, then it is needed. + if (candidate.satisfies(req)) { + // As soon as a network is found that satisfies a request, return. Specifically for + // multilayer requests, returning as soon as a NetworkAgentInfo satisfies a request + // is important so as to not evaluate lower priority requests further in + // nri.mRequests. + final boolean isNetworkNeeded = candidate.isSatisfyingRequest(req.requestId) + // Note that this catches two important cases: + // 1. Unvalidated cellular will not be reaped when unvalidated WiFi + // is currently satisfying the request. This is desirable when + // cellular ends up validating but WiFi does not. + // 2. Unvalidated WiFi will not be reaped when validated cellular + // is currently satisfying the request. This is desirable when + // WiFi ends up validating and out scoring cellular. + || nri.mSatisfier.getCurrentScore() + < candidate.getCurrentScoreAsValidated(); + return isNetworkNeeded; + } + } + + return false; + } + private NetworkRequestInfo getNriForAppRequest( NetworkRequest request, int callingUid, String requestedOperation) { final NetworkRequestInfo nri = mNetworkRequests.get(request); @@ -3875,8 +3933,12 @@ public class ConnectivityService extends IConnectivityManager.Stub new CaptivePortal(new CaptivePortalImpl(network).asBinder())); appIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK); - Binder.withCleanCallingIdentity(() -> - mContext.startActivityAsUser(appIntent, UserHandle.CURRENT)); + final long token = Binder.clearCallingIdentity(); + try { + mContext.startActivityAsUser(appIntent, UserHandle.CURRENT); + } finally { + Binder.restoreCallingIdentity(token); + } } private class CaptivePortalImpl extends ICaptivePortal.Stub { @@ -5464,6 +5526,10 @@ public class ConnectivityService extends IConnectivityManager.Stub this(r, null); } + boolean isMultilayerRequest() { + return mRequests.size() > 1; + } + private List<NetworkRequest> initializeRequests(NetworkRequest r) { final ArrayList<NetworkRequest> tempRequests = new ArrayList<>(); tempRequests.add(new NetworkRequest(r)); @@ -5505,7 +5571,7 @@ public class ConnectivityService extends IConnectivityManager.Stub public void binderDied() { log("ConnectivityService NetworkRequestInfo binderDied(" + mRequests + ", " + mBinder + ")"); - releaseNetworkRequest(mRequests); + releaseNetworkRequests(mRequests); } @Override @@ -5538,13 +5604,15 @@ public class ConnectivityService extends IConnectivityManager.Stub mAppOpsManager.checkPackage(callerUid, callerPackageName); } - private ArrayList<Integer> getSignalStrengthThresholds(NetworkAgentInfo nai) { + private ArrayList<Integer> getSignalStrengthThresholds(@NonNull final NetworkAgentInfo nai) { final SortedSet<Integer> thresholds = new TreeSet<>(); synchronized (nai) { - for (NetworkRequestInfo nri : mNetworkRequests.values()) { - if (nri.request.networkCapabilities.hasSignalStrength() && - nai.satisfiesImmutableCapabilitiesOf(nri.request)) { - thresholds.add(nri.request.networkCapabilities.getSignalStrength()); + for (final NetworkRequestInfo nri : mNetworkRequests.values()) { + for (final NetworkRequest req : nri.mRequests) { + if (req.networkCapabilities.hasSignalStrength() + && nai.satisfiesImmutableCapabilitiesOf(req)) { + thresholds.add(req.networkCapabilities.getSignalStrength()); + } } } } @@ -5831,7 +5899,7 @@ public class ConnectivityService extends IConnectivityManager.Stub return mNextNetworkProviderId.getAndIncrement(); } - private void releaseNetworkRequest(List<NetworkRequest> networkRequests) { + private void releaseNetworkRequests(List<NetworkRequest> networkRequests) { for (int i = 0; i < networkRequests.size(); i++) { releaseNetworkRequest(networkRequests.get(i)); } @@ -7763,10 +7831,13 @@ public class ConnectivityService extends IConnectivityManager.Stub final int userId = UserHandle.getCallingUserId(); - Binder.withCleanCallingIdentity(() -> { + final long token = Binder.clearCallingIdentity(); + try { final IpMemoryStore ipMemoryStore = IpMemoryStore.getMemoryStore(mContext); ipMemoryStore.factoryReset(); - }); + } finally { + Binder.restoreCallingIdentity(token); + } // Turn airplane mode off setAirplaneMode(false); diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java index 655d8abf3e84..e8687e57a07b 100644 --- a/services/core/java/com/android/server/TestNetworkService.java +++ b/services/core/java/com/android/server/TestNetworkService.java @@ -107,23 +107,23 @@ class TestNetworkService extends ITestNetworkManager.Stub { String ifacePrefix = isTun ? TEST_TUN_PREFIX : TEST_TAP_PREFIX; String iface = ifacePrefix + sTestTunIndex.getAndIncrement(); - return Binder.withCleanCallingIdentity( - () -> { - try { - ParcelFileDescriptor tunIntf = - ParcelFileDescriptor.adoptFd(jniCreateTunTap(isTun, iface)); - for (LinkAddress addr : linkAddrs) { - mNetd.interfaceAddAddress( - iface, - addr.getAddress().getHostAddress(), - addr.getPrefixLength()); - } - - return new TestNetworkInterface(tunIntf, iface); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - }); + final long token = Binder.clearCallingIdentity(); + try { + ParcelFileDescriptor tunIntf = + ParcelFileDescriptor.adoptFd(jniCreateTunTap(isTun, iface)); + for (LinkAddress addr : linkAddrs) { + mNetd.interfaceAddAddress( + iface, + addr.getAddress().getHostAddress(), + addr.getPrefixLength()); + } + + return new TestNetworkInterface(tunIntf, iface); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } finally { + Binder.restoreCallingIdentity(token); + } } /** @@ -317,7 +317,12 @@ class TestNetworkService extends ITestNetworkManager.Stub { try { // This requires NETWORK_STACK privileges. - Binder.withCleanCallingIdentity(() -> mNMS.setInterfaceUp(iface)); + final long token = Binder.clearCallingIdentity(); + try { + mNMS.setInterfaceUp(iface); + } finally { + Binder.restoreCallingIdentity(token); + } // Synchronize all accesses to mTestNetworkTracker to prevent the case where: // 1. TestNetworkAgent successfully binds to death of binder diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index 165b6a1b08f1..74e38510770b 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -25,24 +25,44 @@ import android.net.NetworkProvider; import android.net.NetworkRequest; import android.net.vcn.IVcnManagementService; import android.net.vcn.VcnConfig; +import android.os.Binder; +import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.ParcelUuid; +import android.os.PersistableBundle; +import android.os.Process; +import android.os.ServiceSpecificException; +import android.os.UserHandle; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.util.ArrayMap; +import android.util.Slog; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; +import com.android.server.vcn.util.PersistableBundleUtils; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; /** * VcnManagementService manages Virtual Carrier Network profiles and lifecycles. * * <pre>The internal structure of the VCN Management subsystem is as follows: * - * +------------------------+ 1:1 +--------------------------------+ - * | VcnManagementService | ------------ Creates -------------> | TelephonySubscriptionManager | - * | | | | - * | Manages configs and | | Tracks subscriptions, carrier | - * | VcnInstance lifecycles | <--- Notifies of subscription & --- | privilege changes, caches maps | - * +------------------------+ carrier privilege changes +--------------------------------+ + * +-------------------------+ 1:1 +--------------------------------+ + * | VcnManagementService | ------------ Creates ------------> | TelephonySubscriptionManager | + * | | | | + * | Manages configs and | | Tracks subscriptions, carrier | + * | Vcn instance lifecycles | <--- Notifies of subscription & -- | privilege changes, caches maps | + * +-------------------------+ carrier privilege changes +--------------------------------+ * | 1:N ^ * | | * | +-------------------------------+ @@ -54,19 +74,19 @@ import com.android.internal.annotations.VisibleForTesting.Visibility; * | mode state changes * v | * +-----------------------------------------------------------------------+ - * | VcnInstance | + * | Vcn | * | | - * | Manages tunnel lifecycles based on fulfillable NetworkRequest(s) | - * | and overall safe-mode | + * | Manages GatewayConnection lifecycles based on fulfillable | + * | NetworkRequest(s) and overall safe-mode | * +-----------------------------------------------------------------------+ * | 1:N ^ * Creates to fulfill | - * NetworkRequest(s), tears Notifies of VcnTunnel + * NetworkRequest(s), tears Notifies of VcnGatewayConnection * down when no longer needed teardown (e.g. Network reaped) * | and safe-mode timer changes * v | * +-----------------------------------------------------------------------+ - * | VcnTunnel | + * | VcnGatewayConnection | * | | * | Manages a single (IKEv2) tunnel session and NetworkAgent, | * | handles mobility events, (IPsec) Tunnel setup and safe-mode timers | @@ -92,20 +112,72 @@ public class VcnManagementService extends IVcnManagementService.Stub { public static final boolean VDBG = false; // STOPSHIP: if true + @VisibleForTesting(visibility = Visibility.PRIVATE) + static final String VCN_CONFIG_FILE = "/data/system/vcn/configs.xml"; + /* Binder context for this service */ @NonNull private final Context mContext; @NonNull private final Dependencies mDeps; @NonNull private final Looper mLooper; + @NonNull private final Handler mHandler; @NonNull private final VcnNetworkProvider mNetworkProvider; + @GuardedBy("mLock") + @NonNull + private final Map<ParcelUuid, VcnConfig> mConfigs = new ArrayMap<>(); + + @NonNull private final Object mLock = new Object(); + + @NonNull private final PersistableBundleUtils.LockingReadWriteHelper mConfigDiskRwHelper; + @VisibleForTesting(visibility = Visibility.PRIVATE) VcnManagementService(@NonNull Context context, @NonNull Dependencies deps) { mContext = requireNonNull(context, "Missing context"); mDeps = requireNonNull(deps, "Missing dependencies"); mLooper = mDeps.getLooper(); + mHandler = new Handler(mLooper); mNetworkProvider = new VcnNetworkProvider(mContext, mLooper); + + mConfigDiskRwHelper = mDeps.newPersistableBundleLockingReadWriteHelper(VCN_CONFIG_FILE); + + // Run on handler to ensure I/O does not block system server startup + mHandler.post(() -> { + PersistableBundle configBundle = null; + try { + configBundle = mConfigDiskRwHelper.readFromDisk(); + } catch (IOException e1) { + Slog.e(TAG, "Failed to read configs from disk; retrying", e1); + + // Retry immediately. The IOException may have been transient. + try { + configBundle = mConfigDiskRwHelper.readFromDisk(); + } catch (IOException e2) { + Slog.wtf(TAG, "Failed to read configs from disk", e2); + return; + } + } + + if (configBundle != null) { + final Map<ParcelUuid, VcnConfig> configs = + PersistableBundleUtils.toMap( + configBundle, + PersistableBundleUtils::toParcelUuid, + VcnConfig::new); + + synchronized (mLock) { + for (Entry<ParcelUuid, VcnConfig> entry : configs.entrySet()) { + // Ensure no new configs are overwritten; a carrier app may have added a new + // config. + if (!mConfigs.containsKey(entry.getKey())) { + mConfigs.put(entry.getKey(), entry.getValue()); + } + } + // TODO: Trigger re-evaluation of active VCNs; start/stop VCNs as needed. + } + } + }); } // Package-visibility for SystemServer to create instances. @@ -130,16 +202,81 @@ public class VcnManagementService extends IVcnManagementService.Stub { } return mHandlerThread.getLooper(); } + + /** + * Retrieves the caller's UID + * + * <p>This call MUST be made before calling {@link Binder#clearCallingIdentity}, otherwise + * this will not work properly. + * + * @return + */ + public int getBinderCallingUid() { + return Binder.getCallingUid(); + } + + /** + * Creates and returns a new {@link PersistableBundle.LockingReadWriteHelper} + * + * @param path the file path to read/write from/to. + * @return the {@link PersistableBundleUtils.LockingReadWriteHelper} instance + */ + public PersistableBundleUtils.LockingReadWriteHelper + newPersistableBundleLockingReadWriteHelper(@NonNull String path) { + return new PersistableBundleUtils.LockingReadWriteHelper(path); + } } /** Notifies the VcnManagementService that external dependencies can be set up. */ public void systemReady() { - // TODO: Retrieve existing profiles from KeyStore - mContext.getSystemService(ConnectivityManager.class) .registerNetworkProvider(mNetworkProvider); } + private void enforcePrimaryUser() { + final int uid = mDeps.getBinderCallingUid(); + if (uid == Process.SYSTEM_UID) { + throw new IllegalStateException( + "Calling identity was System Server. Was Binder calling identity cleared?"); + } + + if (!UserHandle.getUserHandleForUid(uid).isSystem()) { + throw new SecurityException( + "VcnManagementService can only be used by callers running as the primary user"); + } + } + + private void enforceCallingUserAndCarrierPrivilege(ParcelUuid subscriptionGroup) { + // Only apps running in the primary (system) user are allowed to configure the VCN. This is + // in line with Telephony's behavior with regards to binding to a Carrier App provided + // CarrierConfigService. + enforcePrimaryUser(); + + // TODO (b/172619301): Check based on events propagated from CarrierPrivilegesTracker + final SubscriptionManager subMgr = mContext.getSystemService(SubscriptionManager.class); + final List<SubscriptionInfo> subscriptionInfos = new ArrayList<>(); + Binder.withCleanCallingIdentity( + () -> { + subscriptionInfos.addAll(subMgr.getSubscriptionsInGroup(subscriptionGroup)); + }); + + final TelephonyManager telMgr = mContext.getSystemService(TelephonyManager.class); + for (SubscriptionInfo info : subscriptionInfos) { + // Check subscription is active first; much cheaper/faster check, and an app (currently) + // cannot be carrier privileged for inactive subscriptions. + if (subMgr.isValidSlotIndex(info.getSimSlotIndex()) + && telMgr.hasCarrierPrivileges(info.getSubscriptionId())) { + // TODO (b/173717728): Allow configuration for inactive, but manageable + // subscriptions. + // TODO (b/173718661): Check for whole subscription groups at a time. + return; + } + } + + throw new SecurityException( + "Carrier privilege required for subscription group to set VCN Config"); + } + /** * Sets a VCN config for a given subscription group. * @@ -150,7 +287,17 @@ public class VcnManagementService extends IVcnManagementService.Stub { requireNonNull(subscriptionGroup, "subscriptionGroup was null"); requireNonNull(config, "config was null"); - // TODO: Store VCN configuration, trigger startup as necessary + enforceCallingUserAndCarrierPrivilege(subscriptionGroup); + + synchronized (mLock) { + mConfigs.put(subscriptionGroup, config); + + // Must be done synchronously to ensure that writes do not happen out-of-order. + writeConfigsToDiskLocked(); + } + + // TODO: Clear Binder calling identity + // TODO: Trigger startup as necessary } /** @@ -162,7 +309,40 @@ public class VcnManagementService extends IVcnManagementService.Stub { public void clearVcnConfig(@NonNull ParcelUuid subscriptionGroup) { requireNonNull(subscriptionGroup, "subscriptionGroup was null"); - // TODO: Clear VCN configuration, trigger teardown as necessary + enforceCallingUserAndCarrierPrivilege(subscriptionGroup); + + synchronized (mLock) { + mConfigs.remove(subscriptionGroup); + + // Must be done synchronously to ensure that writes do not happen out-of-order. + writeConfigsToDiskLocked(); + } + + // TODO: Clear Binder calling identity + // TODO: Trigger teardown as necessary + } + + @GuardedBy("mLock") + private void writeConfigsToDiskLocked() { + try { + PersistableBundle bundle = + PersistableBundleUtils.fromMap( + mConfigs, + PersistableBundleUtils::fromParcelUuid, + VcnConfig::toPersistableBundle); + mConfigDiskRwHelper.writeToDisk(bundle); + } catch (IOException e) { + Slog.e(TAG, "Failed to save configs to disk", e); + throw new ServiceSpecificException(0, "Failed to save configs"); + } + } + + /** Get current configuration list for testing purposes */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + Map<ParcelUuid, VcnConfig> getConfigs() { + synchronized (mLock) { + return Collections.unmodifiableMap(mConfigs); + } } /** diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index 12fe3eda78a4..fffa814566fb 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -2928,6 +2928,7 @@ final class ActivityManagerShellCommand extends ShellCommand { final PlatformCompat platformCompat = (PlatformCompat) ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE); String toggleValue = getNextArgRequired(); + boolean killPackage = !"--no-kill".equals(getNextOption()); boolean toggleAll = false; int targetSdkVersion = -1; long changeId = -1; @@ -2979,7 +2980,11 @@ final class ActivityManagerShellCommand extends ShellCommand { CompatibilityChangeConfig overrides = new CompatibilityChangeConfig( new Compatibility.ChangeConfig(enabled, disabled)); - platformCompat.setOverrides(overrides, packageName); + if (killPackage) { + platformCompat.setOverrides(overrides, packageName); + } else { + platformCompat.setOverridesForTest(overrides, packageName); + } pw.println("Enabled change " + changeId + " for " + packageName + "."); } return 0; @@ -2998,13 +3003,21 @@ final class ActivityManagerShellCommand extends ShellCommand { CompatibilityChangeConfig overrides = new CompatibilityChangeConfig( new Compatibility.ChangeConfig(enabled, disabled)); - platformCompat.setOverrides(overrides, packageName); + if (killPackage) { + platformCompat.setOverrides(overrides, packageName); + } else { + platformCompat.setOverridesForTest(overrides, packageName); + } pw.println("Disabled change " + changeId + " for " + packageName + "."); } return 0; case "reset": if (toggleAll) { - platformCompat.clearOverrides(packageName); + if (killPackage) { + platformCompat.clearOverrides(packageName); + } else { + platformCompat.clearOverridesForTest(packageName); + } pw.println("Reset all changes for " + packageName + " to default value."); return 0; } @@ -3410,15 +3423,18 @@ final class ActivityManagerShellCommand extends ShellCommand { pw.println(" write"); pw.println(" Write all pending state to storage."); pw.println(" compat [COMMAND] [...]: sub-commands for toggling app-compat changes."); - pw.println(" enable|disable|reset <CHANGE_ID|CHANGE_NAME> <PACKAGE_NAME>"); + pw.println(" enable|disable [--no-kill] <CHANGE_ID|CHANGE_NAME> <PACKAGE_NAME>"); + pw.println(" Toggles a change either by id or by name for <PACKAGE_NAME>."); + pw.println(" It kills <PACKAGE_NAME> (to allow the toggle to take effect) unless --no-kill is provided."); + pw.println(" reset <CHANGE_ID|CHANGE_NAME> <PACKAGE_NAME>"); pw.println(" Toggles a change either by id or by name for <PACKAGE_NAME>."); pw.println(" It kills <PACKAGE_NAME> (to allow the toggle to take effect)."); - pw.println(" enable-all|disable-all <targetSdkVersion> <PACKAGE_NAME"); + pw.println(" enable-all|disable-all <targetSdkVersion> <PACKAGE_NAME>"); pw.println(" Toggles all changes that are gated by <targetSdkVersion>."); - pw.println(" reset-all <PACKAGE_NAME>"); + pw.println(" reset-all [--no-kill] <PACKAGE_NAME>"); pw.println(" Removes all existing overrides for all changes for "); pw.println(" <PACKAGE_NAME> (back to default behaviour)."); - pw.println(" It kills <PACKAGE_NAME> (to allow the toggle to take effect)."); + pw.println(" It kills <PACKAGE_NAME> (to allow the toggle to take effect) unless --no-kill is provided."); pw.println(" memory-factor [command] [...]: sub-commands for overriding memory pressure factor"); pw.println(" set <NORMAL|MODERATE|LOW|CRITICAL>"); pw.println(" Overrides memory pressure factor. May also supply a raw int level"); diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 083e970cc434..2f7c5234d982 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -254,9 +254,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub mHandler = new Handler(mHandlerThread.getLooper()); // TODO(b/173077356): Replace directly calling the HAL with PowerStatsService queries - // Make sure to init Hal Wrapper before creating BatteryStatsImpl. - mPowerStatsHALWrapper = new PowerStatsHALWrapper.PowerStatsHALWrapperImpl(); - mPowerStatsHALWrapper.initialize(); + mPowerStatsHALWrapper = PowerStatsHALWrapper.getPowerStatsHalImpl(); final MeasuredEnergyArray initialEnergies = getEnergyConsumptionData(); final boolean[] supportedBuckets = getSupportedEnergyBuckets(initialEnergies); diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 4444de0c00c4..ada67b1d59af 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -8906,14 +8906,8 @@ public class AudioService extends IAudioService.Stub mPlaybackMonitor.playerAttributes(piid, attr, Binder.getCallingUid()); } - /** - * Update player event - * @param piid Player id to update - * @param event The new player event - * @param deviceId The new player device id - */ - public void playerEvent(int piid, int event, int deviceId) { - mPlaybackMonitor.playerEvent(piid, event, deviceId, Binder.getCallingUid()); + public void playerEvent(int piid, int event) { + mPlaybackMonitor.playerEvent(piid, event, Binder.getCallingUid()); } public void playerHasOpPlayAudio(int piid, boolean hasOpPlayAudio) { diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java index 36c67cdbac4b..a5778836aa6e 100644 --- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java +++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java @@ -233,25 +233,15 @@ public final class PlaybackActivityMonitor } } - /** - * Update player event - * @param piid Player id to update - * @param event The new player event - * @param deviceId The new player device id - * @param binderUid Calling binder uid - */ - public void playerEvent(int piid, int event, int deviceId, int binderUid) { - if (DEBUG) { - Log.v(TAG, String.format("playerEvent(piid=%d, deviceId=%d, event=%d)", - piid, deviceId, event)); - } + public void playerEvent(int piid, int event, int binderUid) { + if (DEBUG) { Log.v(TAG, String.format("playerEvent(piid=%d, event=%d)", piid, event)); } final boolean change; synchronized(mPlayerLock) { final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid)); if (apc == null) { return; } - sEventLogger.log(new PlayerEvent(piid, event, deviceId)); + sEventLogger.log(new PlayerEvent(piid, event)); if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { for (Integer uidInteger: mBannedUids) { if (checkBanPlayer(apc, uidInteger.intValue())) { @@ -269,7 +259,7 @@ public final class PlaybackActivityMonitor if (checkConfigurationCaller(piid, apc, binderUid)) { //TODO add generation counter to only update to the latest state checkVolumeForPrivilegedAlarm(apc, event); - change = apc.handleStateEvent(event, deviceId); + change = apc.handleStateEvent(event); } else { Log.e(TAG, "Error handling event " + event); change = false; @@ -299,8 +289,7 @@ public final class PlaybackActivityMonitor mPlayers.remove(new Integer(piid)); mDuckingManager.removeReleased(apc); checkVolumeForPrivilegedAlarm(apc, AudioPlaybackConfiguration.PLAYER_STATE_RELEASED); - change = apc.handleStateEvent(AudioPlaybackConfiguration.PLAYER_STATE_RELEASED, - AudioPlaybackConfiguration.PLAYER_DEVICEID_INVALID); + change = apc.handleStateEvent(AudioPlaybackConfiguration.PLAYER_STATE_RELEASED); } } if (change) { @@ -879,19 +868,16 @@ public final class PlaybackActivityMonitor // only keeping the player interface ID as it uniquely identifies the player in the event final int mPlayerIId; final int mState; - final int mDeviceId; - PlayerEvent(int piid, int state, int deviceId) { + PlayerEvent(int piid, int state) { mPlayerIId = piid; mState = state; - mDeviceId = deviceId; } @Override public String eventToString() { return new StringBuilder("player piid:").append(mPlayerIId).append(" state:") - .append(AudioPlaybackConfiguration.toLogFriendlyPlayerState(mState)) - .append(" DeviceId:").append(mDeviceId).toString(); + .append(AudioPlaybackConfiguration.toLogFriendlyPlayerState(mState)).toString(); } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java index 78e875b864f4..a26662dfd970 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java @@ -133,7 +133,7 @@ public class FaceUserState extends BiometricUserState<Face> { if (tagName.equals(TAG_FACE)) { String name = parser.getAttributeValue(null, ATTR_NAME); int faceId = parser.getAttributeInt(null, ATTR_FACE_ID); - int deviceId = parser.getAttributeInt(null, ATTR_DEVICE_ID); + long deviceId = parser.getAttributeLong(null, ATTR_DEVICE_ID); mBiometrics.add(new Face(name, faceId, deviceId)); } } diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java index ff3193182501..a8aa9aada607 100644 --- a/services/core/java/com/android/server/compat/CompatChange.java +++ b/services/core/java/com/android/server/compat/CompatChange.java @@ -143,6 +143,9 @@ public final class CompatChange extends CompatibilityChangeInfo { * @return {@code true} if the change should be enabled for the package. */ boolean isEnabled(ApplicationInfo app) { + if (app == null) { + return defaultValue(); + } if (mPackageOverrides != null && mPackageOverrides.containsKey(app.packageName)) { return mPackageOverrides.get(app.packageName); } @@ -156,6 +159,15 @@ public final class CompatChange extends CompatibilityChangeInfo { } /** + * Returns the default value for the change id, assuming there are no overrides. + * + * @return {@code false} if it's a default disabled change, {@code true} otherwise. + */ + boolean defaultValue() { + return !getDisabled(); + } + + /** * Checks whether a change has an override for a package. * @param packageName name of the package * @return true if there is such override diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java index d80c58b39768..8511118cc840 100644 --- a/services/core/java/com/android/server/compat/CompatConfig.java +++ b/services/core/java/com/android/server/compat/CompatConfig.java @@ -392,6 +392,14 @@ final class CompatConfig { return alreadyKnown; } + boolean defaultChangeIdValue(long changeId) { + CompatChange c = mChanges.get(changeId); + if (c == null) { + return true; + } + return c.defaultValue(); + } + @VisibleForTesting void clearChanges() { synchronized (mChanges) { diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java index 77d5411f5f7f..4bd01d465e62 100644 --- a/services/core/java/com/android/server/compat/PlatformCompat.java +++ b/services/core/java/com/android/server/compat/PlatformCompat.java @@ -120,10 +120,12 @@ public class PlatformCompat extends IPlatformCompat.Stub { * permission check. */ public boolean isChangeEnabledInternal(long changeId, ApplicationInfo appInfo) { - boolean value = isChangeEnabledInternalNoLogging(changeId, appInfo); - reportChange(changeId, appInfo.uid, - value ? ChangeReporter.STATE_ENABLED : ChangeReporter.STATE_DISABLED); - return value; + boolean enabled = isChangeEnabledInternalNoLogging(changeId, appInfo); + if (appInfo != null) { + reportChange(changeId, appInfo.uid, + enabled ? ChangeReporter.STATE_ENABLED : ChangeReporter.STATE_DISABLED); + } + return enabled; } @Override @@ -131,9 +133,6 @@ public class PlatformCompat extends IPlatformCompat.Stub { @UserIdInt int userId) { checkCompatChangeReadAndLogPermission(); ApplicationInfo appInfo = getApplicationInfo(packageName, userId); - if (appInfo == null) { - return true; - } return isChangeEnabled(changeId, appInfo); } @@ -142,7 +141,7 @@ public class PlatformCompat extends IPlatformCompat.Stub { checkCompatChangeReadAndLogPermission(); String[] packages = mContext.getPackageManager().getPackagesForUid(uid); if (packages == null || packages.length == 0) { - return true; + return mCompatConfig.defaultChangeIdValue(changeId); } boolean enabled = true; for (String packageName : packages) { diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java index 20458b4db558..cc510fbc38bd 100644 --- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java +++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java @@ -39,6 +39,10 @@ import android.util.proto.ProtoOutputStream; import com.android.server.location.ClientBrokerProto; +import java.util.Collections; +import java.util.Iterator; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; @@ -119,6 +123,13 @@ public class ContextHubClientBroker extends IContextHubClient.Stub private final boolean mHasAccessContextHubPermission; /* + * The set of nanoapp IDs that represent the group of nanoapps this client has a messaging + * channel with, i.e. has sent or received messages from this particular nanoapp. + */ + private final Set<Long> mMessageChannelNanoappIdSet = + Collections.newSetFromMap(new ConcurrentHashMap<Long, Boolean>()); + + /* * Helper class to manage registered PendingIntent requests from the client. */ private class PendingIntentRequest { @@ -134,7 +145,8 @@ public class ContextHubClientBroker extends IContextHubClient.Stub private boolean mValid = false; - PendingIntentRequest() {} + PendingIntentRequest() { + } PendingIntentRequest(PendingIntent pendingIntent, long nanoAppId) { mPendingIntent = pendingIntent; @@ -177,7 +189,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub mPackage = mContext.getPackageManager().getNameForUid(Binder.getCallingUid()); mHasAccessContextHubPermission = context.checkCallingPermission( - Manifest.permission.ACCESS_CONTEXT_HUB) == PERMISSION_GRANTED; + Manifest.permission.ACCESS_CONTEXT_HUB) == PERMISSION_GRANTED; } /* package */ ContextHubClientBroker( @@ -193,7 +205,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub mPackage = pendingIntent.getCreatorPackage(); mHasAccessContextHubPermission = context.checkCallingPermission( - Manifest.permission.ACCESS_CONTEXT_HUB) == PERMISSION_GRANTED; + Manifest.permission.ACCESS_CONTEXT_HUB) == PERMISSION_GRANTED; } /** @@ -209,6 +221,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub int result; if (isRegistered()) { + mMessageChannelNanoappIdSet.add(message.getNanoAppId()); ContextHubMsg messageToNanoApp = ContextHubServiceUtil.createHidlContextHubMessage(mHostEndPointId, message); @@ -269,6 +282,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub * @param message the message that came from a nanoapp */ /* package */ void sendMessageToClient(NanoAppMessage message) { + mMessageChannelNanoappIdSet.add(message.getNanoAppId()); invokeCallback(callback -> callback.onMessageFromNanoApp(message)); Supplier<Intent> supplier = @@ -413,7 +427,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub /** * Sends an intent to any existing PendingIntent * - * @param supplier method to create the extra Intent + * @param supplier method to create the extra Intent * @param nanoAppId the ID of the nanoapp which this event is for */ private synchronized void sendPendingIntent(Supplier<Intent> supplier, long nanoAppId) { @@ -427,7 +441,7 @@ public class ContextHubClientBroker extends IContextHubClient.Stub * Sends a PendingIntent with extra Intent data * * @param pendingIntent the PendingIntent - * @param intent the extra Intent data + * @param intent the extra Intent data */ private void doSendPendingIntent(PendingIntent pendingIntent, Intent intent) { try { @@ -500,6 +514,17 @@ public class ContextHubClientBroker extends IContextHubClient.Stub } else { out += "package: " + mPackage; } + if (mMessageChannelNanoappIdSet.size() > 0) { + out += " messageChannelNanoappSet: ("; + Iterator<Long> it = mMessageChannelNanoappIdSet.iterator(); + while (it.hasNext()) { + out += "0x" + Long.toHexString(it.next()); + if (it.hasNext()) { + out += ","; + } + } + out += ")"; + } out += "]"; return out; diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 225c998d4c50..ccbf73ca9ab0 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -4760,31 +4760,13 @@ public class UserManagerService extends IUserManager.Stub { final boolean hasParent = user.profileGroupId != user.id && user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID; if (verbose) { - final DevicePolicyManagerInternal dpm = getDevicePolicyManagerInternal(); - String deviceOwner = ""; - String profileOwner = ""; - if (dpm != null) { - final long ident = Binder.clearCallingIdentity(); - try { - if (dpm.getDeviceOwnerUserId() == user.id) { - deviceOwner = " (device-owner)"; - } - if (dpm.getProfileOwnerAsUser(user.id) != null) { - profileOwner = " (profile-owner)"; - } - } finally { - Binder.restoreCallingIdentity(ident); - } - } - pw.printf("%d: id=%d, name=%s, flags=%s%s%s%s%s%s%s%s%s\n", i, user.id, - user.name, + pw.printf("%d: id=%d, name=%s, flags=%s%s%s%s%s%s%s\n", i, user.id, user.name, UserInfo.flagsToString(user.flags), hasParent ? " (parentId=" + user.profileGroupId + ")" : "", running ? " (running)" : "", user.partial ? " (partial)" : "", user.preCreated ? " (pre-created)" : "", user.convertedFromPreCreated ? " (converted)" : "", - deviceOwner, profileOwner, current ? " (current)" : ""); } else { // NOTE: the standard "list users" command is used by integration tests and @@ -4878,21 +4860,6 @@ public class UserManagerService extends IUserManager.Stub { if (userInfo.convertedFromPreCreated) { pw.print(" <converted>"); } - final DevicePolicyManagerInternal dpm = getDevicePolicyManagerInternal(); - if (dpm != null) { - final long ident = Binder.clearCallingIdentity(); - try { - if (dpm.getDeviceOwnerUserId() == userId) { - pw.print(" <device-owner>"); - } - if (dpm.getProfileOwnerAsUser(userId) != null) { - pw.print(" <profile-owner>"); - } - } finally { - Binder.restoreCallingIdentity(ident); - } - } - pw.println(); pw.print(" Type: "); pw.println(userInfo.userType); pw.print(" Flags: "); pw.print(userInfo.flags); pw.print(" ("); diff --git a/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java b/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java index 88e5f69e02c4..79c039291f57 100644 --- a/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java +++ b/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java @@ -16,7 +16,11 @@ package com.android.server.powerstats; +import android.hardware.power.stats.ChannelInfo; +import android.hardware.power.stats.EnergyMeasurement; import android.hardware.power.stats.IPowerStats; +import android.hardware.power.stats.PowerEntityInfo; +import android.hardware.power.stats.StateResidencyResult; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; @@ -32,6 +36,7 @@ import java.util.function.Supplier; */ public final class PowerStatsHALWrapper { private static final String TAG = PowerStatsHALWrapper.class.getSimpleName(); + private static final boolean DEBUG = false; /** * IPowerStatsHALWrapper defines the interface to the PowerStatsHAL. @@ -122,17 +127,30 @@ public final class PowerStatsHALWrapper { * * @return true if connection to power stats HAL was correctly established. */ - boolean initialize(); + boolean isInitialized(); } /** - * PowerStatsHALWrapperImpl is the implementation of the IPowerStatsHALWrapper - * used by the PowerStatsService. Other implementations will be used by the testing - * framework and will be passed into the PowerStatsService through an injector. + * PowerStatsHALWrapper20Impl is the implementation of the IPowerStatsHALWrapper + * used by the PowerStatsService on devices that support only PowerStats HAL 2.0. + * Other implementations will be used by the testing framework and will be passed + * into the PowerStatsService through an injector. */ - public static final class PowerStatsHALWrapperImpl implements IPowerStatsHALWrapper { + public static final class PowerStatsHAL20WrapperImpl implements IPowerStatsHALWrapper { private static Supplier<IPowerStats> sVintfPowerStats; + public PowerStatsHAL20WrapperImpl() { + Supplier<IPowerStats> service = new VintfHalCache(); + sVintfPowerStats = null; + + if (service.get() == null) { + if (DEBUG) Slog.d(TAG, "PowerStats HAL 2.0 not available on this device."); + sVintfPowerStats = null; + } else { + sVintfPowerStats = service; + } + } + @Override public android.hardware.power.stats.PowerEntityInfo[] getPowerEntityInfo() { android.hardware.power.stats.PowerEntityInfo[] powerEntityInfoHAL = null; @@ -141,7 +159,7 @@ public final class PowerStatsHALWrapper { try { powerEntityInfoHAL = sVintfPowerStats.get().getPowerEntityInfo(); } catch (RemoteException e) { - Slog.e(TAG, "Failed to get power entity info from PowerStats HAL"); + if (DEBUG) Slog.d(TAG, "Failed to get power entity info from PowerStats HAL"); } } @@ -158,7 +176,7 @@ public final class PowerStatsHALWrapper { stateResidencyResultHAL = sVintfPowerStats.get().getStateResidency(powerEntityIds); } catch (RemoteException e) { - Slog.e(TAG, "Failed to get state residency from PowerStats HAL"); + if (DEBUG) Slog.d(TAG, "Failed to get state residency from PowerStats HAL"); } } @@ -173,7 +191,9 @@ public final class PowerStatsHALWrapper { try { energyConsumerInfoHAL = sVintfPowerStats.get().getEnergyConsumerInfo(); } catch (RemoteException e) { - Slog.e(TAG, "Failed to get energy consumer info from PowerStats HAL"); + if (DEBUG) { + Slog.d(TAG, "Failed to get energy consumer info from PowerStats HAL"); + } } } @@ -190,7 +210,9 @@ public final class PowerStatsHALWrapper { energyConsumedHAL = sVintfPowerStats.get().getEnergyConsumed(energyConsumerIds); } catch (RemoteException e) { - Slog.e(TAG, "Failed to get energy consumer results from PowerStats HAL"); + if (DEBUG) { + Slog.d(TAG, "Failed to get energy consumer results from PowerStats HAL"); + } } } @@ -205,7 +227,7 @@ public final class PowerStatsHALWrapper { try { energyMeterInfoHAL = sVintfPowerStats.get().getEnergyMeterInfo(); } catch (RemoteException e) { - Slog.e(TAG, "Failed to get energy meter info from PowerStats HAL"); + if (DEBUG) Slog.d(TAG, "Failed to get energy meter info from PowerStats HAL"); } } @@ -221,7 +243,7 @@ public final class PowerStatsHALWrapper { energyMeasurementHAL = sVintfPowerStats.get().readEnergyMeters(channelIds); } catch (RemoteException e) { - Slog.e(TAG, "Failed to get energy measurements from PowerStats HAL"); + if (DEBUG) Slog.d(TAG, "Failed to get energy measurements from PowerStats HAL"); } } @@ -229,17 +251,90 @@ public final class PowerStatsHALWrapper { } @Override - public boolean initialize() { - Supplier<IPowerStats> service = new VintfHalCache(); + public boolean isInitialized() { + return (sVintfPowerStats != null); + } + } - if (service.get() == null) { - sVintfPowerStats = null; - return false; + /** + * PowerStatsHALWrapper10Impl is the implementation of the IPowerStatsHALWrapper + * used by the PowerStatsService on devices that support only PowerStats HAL 1.0. + * Other implementations will be used by the testing framework and will be passed + * into the PowerStatsService through an injector. + */ + public static final class PowerStatsHAL10WrapperImpl implements IPowerStatsHALWrapper { + private boolean mIsInitialized; + + // PowerStatsHAL 1.0 native functions exposed by JNI layer. + private static native boolean nativeInit(); + private static native PowerEntityInfo[] nativeGetPowerEntityInfo(); + private static native StateResidencyResult[] nativeGetStateResidency(int[] powerEntityIds); + private static native ChannelInfo[] nativeGetEnergyMeterInfo(); + private static native EnergyMeasurement[] nativeReadEnergyMeters(int[] channelIds); + + public PowerStatsHAL10WrapperImpl() { + if (nativeInit()) { + mIsInitialized = true; } else { - sVintfPowerStats = service; - return true; + if (DEBUG) Slog.d(TAG, "PowerStats HAL 1.0 not available on this device."); + mIsInitialized = false; } } + + @Override + public android.hardware.power.stats.PowerEntityInfo[] getPowerEntityInfo() { + return nativeGetPowerEntityInfo(); + } + + @Override + public android.hardware.power.stats.StateResidencyResult[] getStateResidency( + int[] powerEntityIds) { + return nativeGetStateResidency(powerEntityIds); + } + + @Override + public int[] getEnergyConsumerInfo() { + if (DEBUG) Slog.d(TAG, "Energy consumer info is not supported"); + return null; + } + + @Override + public android.hardware.power.stats.EnergyConsumerResult[] getEnergyConsumed( + int[] energyConsumerIds) { + if (DEBUG) Slog.d(TAG, "Energy consumer results are not supported"); + return null; + } + + @Override + public android.hardware.power.stats.ChannelInfo[] getEnergyMeterInfo() { + return nativeGetEnergyMeterInfo(); + } + + @Override + public android.hardware.power.stats.EnergyMeasurement[] readEnergyMeters(int[] channelIds) { + return nativeReadEnergyMeters(channelIds); + } + + @Override + public boolean isInitialized() { + return mIsInitialized; + } + } + + /** + * Returns an instance of an IPowerStatsHALWrapper. If PowerStats HAL 2.0 is supported on the + * device, return a PowerStatsHAL20WrapperImpl, else return a PowerStatsHAL10WrapperImpl. + * + * @return an instance of an IPowerStatsHALWrapper where preference is given to PowerStats HAL + * 2.0. + */ + public static IPowerStatsHALWrapper getPowerStatsHalImpl() { + PowerStatsHAL20WrapperImpl powerStatsHAL20WrapperImpl = new PowerStatsHAL20WrapperImpl(); + if (powerStatsHAL20WrapperImpl.isInitialized()) { + return powerStatsHAL20WrapperImpl; + } else { + return new PowerStatsHAL10WrapperImpl(); + } } private static class VintfHalCache implements Supplier<IPowerStats>, IBinder.DeathRecipient { diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java index 1150d4bbe770..ce50e5833c45 100644 --- a/services/core/java/com/android/server/powerstats/PowerStatsService.java +++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java @@ -29,7 +29,6 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DumpUtils; import com.android.server.SystemService; import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper; -import com.android.server.powerstats.PowerStatsHALWrapper.PowerStatsHALWrapperImpl; import com.android.server.powerstats.ProtoStreamUtils.ChannelInfoUtils; import com.android.server.powerstats.ProtoStreamUtils.EnergyConsumerIdUtils; import com.android.server.powerstats.ProtoStreamUtils.PowerEntityInfoUtils; @@ -78,7 +77,7 @@ public class PowerStatsService extends SystemService { } IPowerStatsHALWrapper createPowerStatsHALWrapperImpl() { - return new PowerStatsHALWrapperImpl(); + return PowerStatsHALWrapper.getPowerStatsHalImpl(); } PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath, @@ -143,7 +142,7 @@ public class PowerStatsService extends SystemService { private void onSystemServiceReady() { mPowerStatsHALWrapper = mInjector.createPowerStatsHALWrapperImpl(); - if (mPowerStatsHALWrapper.initialize()) { + if (mPowerStatsHALWrapper.isInitialized()) { if (DEBUG) Slog.d(TAG, "Starting PowerStatsService"); // Only start logger and triggers if initialization is successful. diff --git a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java index 5a4256ac0264..5e23b86e0adb 100644 --- a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java +++ b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java @@ -46,23 +46,31 @@ public class ProtoStreamUtils { static class PowerEntityInfoUtils { public static void print(PowerEntityInfo[] powerEntityInfo) { + if (powerEntityInfo == null) return; + for (int i = 0; i < powerEntityInfo.length; i++) { Slog.d(TAG, "PowerEntityId: " + powerEntityInfo[i].powerEntityId + ", PowerEntityName: " + powerEntityInfo[i].powerEntityName); - for (int j = 0; j < powerEntityInfo[i].states.length; j++) { - Slog.d(TAG, " StateId: " + powerEntityInfo[i].states[j].stateId - + ", StateName: " + powerEntityInfo[i].states[j].stateName); + if (powerEntityInfo[i].states != null) { + for (int j = 0; j < powerEntityInfo[i].states.length; j++) { + Slog.d(TAG, " StateId: " + powerEntityInfo[i].states[j].stateId + + ", StateName: " + powerEntityInfo[i].states[j].stateName); + } } } } public static void dumpsys(PowerEntityInfo[] powerEntityInfo, PrintWriter pw) { + if (powerEntityInfo == null) return; + for (int i = 0; i < powerEntityInfo.length; i++) { pw.println("PowerEntityId: " + powerEntityInfo[i].powerEntityId + ", PowerEntityName: " + powerEntityInfo[i].powerEntityName); - for (int j = 0; j < powerEntityInfo[i].states.length; j++) { - pw.println(" StateId: " + powerEntityInfo[i].states[j].stateId - + ", StateName: " + powerEntityInfo[i].states[j].stateName); + if (powerEntityInfo[i].states != null) { + for (int j = 0; j < powerEntityInfo[i].states.length; j++) { + pw.println(" StateId: " + powerEntityInfo[i].states[j].stateId + + ", StateName: " + powerEntityInfo[i].states[j].stateName); + } } } } @@ -70,6 +78,8 @@ public class ProtoStreamUtils { static class StateResidencyResultUtils { public static void print(StateResidencyResult[] stateResidencyResult) { + if (stateResidencyResult == null) return; + for (int i = 0; i < stateResidencyResult.length; i++) { Slog.d(TAG, "PowerEntityId: " + stateResidencyResult[i].powerEntityId); for (int j = 0; j < stateResidencyResult[i].stateResidencyData.length; j++) { @@ -90,6 +100,8 @@ public class ProtoStreamUtils { public static void packProtoMessage(ChannelInfo[] channelInfo, ProtoOutputStream pos) { long token; + if (channelInfo == null) return; + for (int i = 0; i < channelInfo.length; i++) { token = pos.start(PowerStatsServiceMeterProto.CHANNEL_INFO); pos.write(ChannelInfoProto.CHANNEL_ID, channelInfo[i].channelId); @@ -100,6 +112,8 @@ public class ProtoStreamUtils { } public static void print(ChannelInfo[] channelInfo) { + if (channelInfo == null) return; + for (int i = 0; i < channelInfo.length; i++) { Slog.d(TAG, "ChannelId: " + channelInfo[i].channelId + ", ChannelName: " + channelInfo[i].channelName); @@ -107,6 +121,8 @@ public class ProtoStreamUtils { } public static void dumpsys(ChannelInfo[] channelInfo, PrintWriter pw) { + if (channelInfo == null) return; + for (int i = 0; i < channelInfo.length; i++) { pw.println("ChannelId: " + channelInfo[i].channelId + ", ChannelName: " + channelInfo[i].channelName); @@ -125,6 +141,8 @@ public class ProtoStreamUtils { ProtoOutputStream pos) { long token; + if (energyMeasurement == null) return; + for (int i = 0; i < energyMeasurement.length; i++) { token = pos.start(PowerStatsServiceMeterProto.ENERGY_MEASUREMENT); pos.write(EnergyMeasurementProto.CHANNEL_ID, energyMeasurement[i].channelId); @@ -200,6 +218,8 @@ public class ProtoStreamUtils { } public static void print(EnergyMeasurement[] energyMeasurement) { + if (energyMeasurement == null) return; + for (int i = 0; i < energyMeasurement.length; i++) { Slog.d(TAG, "ChannelId: " + energyMeasurement[i].channelId + ", Timestamp (ms): " + energyMeasurement[i].timestampMs @@ -212,6 +232,8 @@ public class ProtoStreamUtils { public static void packProtoMessage(int[] energyConsumerId, ProtoOutputStream pos) { long token; + if (energyConsumerId == null) return; + for (int i = 0; i < energyConsumerId.length; i++) { token = pos.start(PowerStatsServiceModelProto.ENERGY_CONSUMER_ID); pos.write(EnergyConsumerIdProto.ENERGY_CONSUMER_ID, energyConsumerId[i]); @@ -220,12 +242,16 @@ public class ProtoStreamUtils { } public static void print(int[] energyConsumerId) { + if (energyConsumerId == null) return; + for (int i = 0; i < energyConsumerId.length; i++) { Slog.d(TAG, "EnergyConsumerId: " + energyConsumerId[i]); } } public static void dumpsys(int[] energyConsumerId, PrintWriter pw) { + if (energyConsumerId == null) return; + for (int i = 0; i < energyConsumerId.length; i++) { pw.println("EnergyConsumerId: " + energyConsumerId[i]); } @@ -243,6 +269,8 @@ public class ProtoStreamUtils { ProtoOutputStream pos) { long token; + if (energyConsumerResult == null) return; + for (int i = 0; i < energyConsumerResult.length; i++) { token = pos.start(PowerStatsServiceModelProto.ENERGY_CONSUMER_RESULT); pos.write(EnergyConsumerResultProto.ENERGY_CONSUMER_ID, @@ -321,6 +349,8 @@ public class ProtoStreamUtils { } public static void print(EnergyConsumerResult[] energyConsumerResult) { + if (energyConsumerResult == null) return; + for (int i = 0; i < energyConsumerResult.length; i++) { Slog.d(TAG, "EnergyConsumerId: " + energyConsumerResult[i].energyConsumerId + ", Timestamp (ms): " + energyConsumerResult[i].timestampMs diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java index 990055ebda9a..5c01e43af5a9 100644 --- a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java +++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java @@ -32,6 +32,8 @@ import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; import android.os.SystemProperties; +import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Slog; import com.android.internal.annotations.GuardedBy; @@ -49,10 +51,6 @@ import java.io.FileDescriptor; import java.io.FileWriter; import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; /** * The recovery system service is responsible for coordinating recovery related @@ -84,9 +82,9 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo private final Context mContext; @GuardedBy("this") - private final Map<String, IntentSender> mCallerPendingRequest = new HashMap<>(); + private final ArrayMap<String, IntentSender> mCallerPendingRequest = new ArrayMap<>(); @GuardedBy("this") - private final Set<String> mCallerPreparedForReboot = new HashSet<>(); + private final ArraySet<String> mCallerPreparedForReboot = new ArraySet<>(); /** * Need to prepare for resume on reboot. @@ -121,7 +119,7 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo @IntDef({ ROR_NEED_PREPARATION, ROR_SKIP_PREPARATION_AND_NOTIFY, ROR_SKIP_PREPARATION_NOT_NOTIFY }) - @interface ResumeOnRebootActionsOnRequest {} + private @interface ResumeOnRebootActionsOnRequest {} /** * The action to perform upon resume on reboot clear request for a given client. @@ -129,7 +127,7 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo @IntDef({ROR_NOT_REQUESTED, ROR_REQUESTED_NEED_CLEAR, ROR_REQUESTED_SKIP_CLEAR}) - @interface ResumeOnRebootActionsOnClear{} + private @interface ResumeOnRebootActionsOnClear{} static class Injector { protected final Context mContext; @@ -342,9 +340,8 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo != PackageManager.PERMISSION_GRANTED && mContext.checkCallingOrSelfPermission(android.Manifest.permission.REBOOT) != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Caller or self must have " - + android.Manifest.permission.RECOVERY + " or " - + android.Manifest.permission.REBOOT + " for resume on reboot."); + throw new SecurityException("Caller must have " + android.Manifest.permission.RECOVERY + + " or " + android.Manifest.permission.REBOOT + " for resume on reboot."); } } @@ -414,10 +411,14 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo Slog.w(TAG, "onPreparedForReboot called when some clients have prepared."); } + if (mCallerPendingRequest.isEmpty()) { + Slog.w(TAG, "onPreparedForReboot called but no client has requested."); + } + // Send intents to notify callers - for (Map.Entry<String, IntentSender> entry : mCallerPendingRequest.entrySet()) { - sendPreparedForRebootIntentIfNeeded(entry.getValue()); - mCallerPreparedForReboot.add(entry.getKey()); + for (int i = 0; i < mCallerPendingRequest.size(); i++) { + sendPreparedForRebootIntentIfNeeded(mCallerPendingRequest.valueAt(i)); + mCallerPreparedForReboot.add(mCallerPendingRequest.keyAt(i)); } mCallerPendingRequest.clear(); } @@ -475,9 +476,7 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo return needClear ? ROR_REQUESTED_NEED_CLEAR : ROR_REQUESTED_SKIP_CLEAR; } - @Override // Binder call - public boolean rebootWithLskf(String packageName, String reason, boolean slotSwitch) { - enforcePermissionForResumeOnReboot(); + private boolean rebootWithLskfImpl(String packageName, String reason, boolean slotSwitch) { if (packageName == null) { Slog.w(TAG, "Missing packageName when rebooting with lskf."); return false; @@ -498,11 +497,29 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo return true; } + @Override // Binder call for the legacy rebootWithLskf + public boolean rebootWithLskfAssumeSlotSwitch(String packageName, String reason) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null); + return rebootWithLskfImpl(packageName, reason, true); + } + @Override // Binder call - public synchronized boolean isLskfCaptured(String packageName) { + public boolean rebootWithLskf(String packageName, String reason, boolean slotSwitch) { enforcePermissionForResumeOnReboot(); - if (!mCallerPreparedForReboot.contains(packageName)) { - Slog.i(TAG, "Reboot requested before prepare completed for caller " + packageName); + return rebootWithLskfImpl(packageName, reason, slotSwitch); + } + + @Override // Binder call + public boolean isLskfCaptured(String packageName) { + enforcePermissionForResumeOnReboot(); + boolean captured; + synchronized (this) { + captured = mCallerPreparedForReboot.contains(packageName); + } + + if (!captured) { + Slog.i(TAG, "Reboot requested before prepare completed for caller " + + packageName); return false; } return true; diff --git a/services/core/java/com/android/server/vcn/Android.bp b/services/core/java/com/android/server/vcn/Android.bp new file mode 100644 index 000000000000..5ed204fd7640 --- /dev/null +++ b/services/core/java/com/android/server/vcn/Android.bp @@ -0,0 +1,4 @@ +filegroup { + name: "framework-vcn-util-sources", + srcs: ["util/**/*.java"], +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java new file mode 100644 index 000000000000..c0608072df9d --- /dev/null +++ b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java @@ -0,0 +1,314 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.vcn; + +import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED; +import static android.telephony.CarrierConfigManager.EXTRA_SLOT_INDEX; +import static android.telephony.CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX; +import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX; +import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Handler; +import android.os.HandlerExecutor; +import android.os.ParcelUuid; +import android.os.PersistableBundle; +import android.telephony.CarrierConfigManager; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener; +import android.util.ArraySet; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.annotations.VisibleForTesting.Visibility; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Set; + +/** + * TelephonySubscriptionTracker provides a caching layer for tracking active subscription groups. + * + * <p>This class performs two roles: + * + * <ol> + * <li>De-noises subscription changes by ensuring that only changes in active and ready + * subscription groups are acted upon + * <li>Caches mapping between subIds and subscription groups + * </ol> + * + * <p>An subscription group is active and ready if any of its contained subIds has had BOTH the + * {@link CarrierConfigManager#isConfigForIdentifiedCarrier()} return true, AND the subscription is + * listed as active per SubscriptionManager#getAllSubscriptionInfoList(). + * + * <p>Note that due to the asynchronous nature of callbacks and broadcasts, the output of this class + * is (only) eventually consistent. + * + * @hide + */ +public class TelephonySubscriptionTracker extends BroadcastReceiver { + @NonNull private static final String TAG = TelephonySubscriptionTracker.class.getSimpleName(); + private static final boolean LOG_DBG = false; // STOPSHIP if true + + @NonNull private final Context mContext; + @NonNull private final Handler mHandler; + @NonNull private final TelephonySubscriptionTrackerCallback mCallback; + @NonNull private final Dependencies mDeps; + + @NonNull private final SubscriptionManager mSubscriptionManager; + @NonNull private final CarrierConfigManager mCarrierConfigManager; + + // TODO (Android T+): Add ability to handle multiple subIds per slot. + @NonNull private final Map<Integer, Integer> mReadySubIdsBySlotId = new HashMap<>(); + @NonNull private final OnSubscriptionsChangedListener mSubscriptionChangedListener; + + @NonNull private TelephonySubscriptionSnapshot mCurrentSnapshot; + + public TelephonySubscriptionTracker( + @NonNull Context context, + @NonNull Handler handler, + @NonNull TelephonySubscriptionTrackerCallback callback) { + this(context, handler, callback, new Dependencies()); + } + + @VisibleForTesting(visibility = Visibility.PRIVATE) + TelephonySubscriptionTracker( + @NonNull Context context, + @NonNull Handler handler, + @NonNull TelephonySubscriptionTrackerCallback callback, + @NonNull Dependencies deps) { + mContext = Objects.requireNonNull(context, "Missing context"); + mHandler = Objects.requireNonNull(handler, "Missing handler"); + mCallback = Objects.requireNonNull(callback, "Missing callback"); + mDeps = Objects.requireNonNull(deps, "Missing deps"); + + mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class); + mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class); + + mSubscriptionChangedListener = + new OnSubscriptionsChangedListener() { + @Override + public void onSubscriptionsChanged() { + handleSubscriptionsChanged(); + } + }; + } + + /** Registers the receivers, and starts tracking subscriptions. */ + public void register() { + mContext.registerReceiver( + this, new IntentFilter(ACTION_CARRIER_CONFIG_CHANGED), null, mHandler); + mSubscriptionManager.addOnSubscriptionsChangedListener( + new HandlerExecutor(mHandler), mSubscriptionChangedListener); + } + + /** Unregisters the receivers, and stops tracking subscriptions. */ + public void unregister() { + mContext.unregisterReceiver(this); + mSubscriptionManager.removeOnSubscriptionsChangedListener(mSubscriptionChangedListener); + } + + /** + * Handles subscription changes, correlating available subscriptions and loaded carrier configs + * + * <p>The subscription change listener is registered with a HandlerExecutor backed by mHandler, + * so callbacks & broadcasts are all serialized on mHandler, avoiding the need for locking. + */ + public void handleSubscriptionsChanged() { + final Set<ParcelUuid> activeSubGroups = new ArraySet<>(); + final Map<Integer, ParcelUuid> newSubIdToGroupMap = new HashMap<>(); + + final List<SubscriptionInfo> allSubs = mSubscriptionManager.getAllSubscriptionInfoList(); + if (allSubs == null) { + return; // Telephony crashed; no way to verify subscriptions. + } + + // If allSubs is empty, no subscriptions exist. Cache will be cleared by virtue of no active + // subscriptions + for (SubscriptionInfo subInfo : allSubs) { + if (subInfo.getGroupUuid() == null) { + continue; + } + + // Build subId -> subGrp cache + newSubIdToGroupMap.put(subInfo.getSubscriptionId(), subInfo.getGroupUuid()); + + // Update subscription groups that are both ready, and active. For a group to be + // considered active, both of the following must be true: + // + // 1. A final CARRIER_CONFIG_CHANGED (where config is for an identified carrier) + // broadcast must have been received for the subId + // 2. A active subscription (is loaded into a SIM slot) must be part of the subscription + // group. + if (subInfo.getSimSlotIndex() != INVALID_SIM_SLOT_INDEX + && mReadySubIdsBySlotId.values().contains(subInfo.getSubscriptionId())) { + activeSubGroups.add(subInfo.getGroupUuid()); + } + } + + final TelephonySubscriptionSnapshot newSnapshot = + new TelephonySubscriptionSnapshot(newSubIdToGroupMap, activeSubGroups); + + // If snapshot was meaningfully updated, fire the callback + if (!newSnapshot.equals(mCurrentSnapshot)) { + mCurrentSnapshot = newSnapshot; + mHandler.post( + () -> { + mCallback.onNewSnapshot(newSnapshot); + }); + } + } + + /** + * Broadcast receiver for ACTION_CARRIER_CONFIG_CHANGED + * + * <p>The broadcast receiver is registered with mHandler, so callbacks & broadcasts are all + * serialized on mHandler, avoiding the need for locking. + */ + @Override + public void onReceive(Context context, Intent intent) { + // Accept sticky broadcasts; if CARRIER_CONFIG_CHANGED was previously broadcast and it + // already was for an identified carrier, we can stop waiting for initial load to complete + if (!ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) { + return; + } + + final int subId = intent.getIntExtra(EXTRA_SUBSCRIPTION_INDEX, INVALID_SUBSCRIPTION_ID); + final int slotId = intent.getIntExtra(EXTRA_SLOT_INDEX, INVALID_SIM_SLOT_INDEX); + + if (slotId == INVALID_SIM_SLOT_INDEX) { + return; + } + + if (SubscriptionManager.isValidSubscriptionId(subId)) { + final PersistableBundle carrierConfigs = mCarrierConfigManager.getConfigForSubId(subId); + if (mDeps.isConfigForIdentifiedCarrier(carrierConfigs)) { + Slog.v(TAG, String.format("SubId %s ready for SlotId %s", subId, slotId)); + mReadySubIdsBySlotId.put(slotId, subId); + handleSubscriptionsChanged(); + } + } else { + Slog.v(TAG, "Slot unloaded: " + slotId); + mReadySubIdsBySlotId.remove(slotId); + handleSubscriptionsChanged(); + } + } + + @VisibleForTesting(visibility = Visibility.PRIVATE) + void setReadySubIdsBySlotId(Map<Integer, Integer> readySubIdsBySlotId) { + mReadySubIdsBySlotId.putAll(readySubIdsBySlotId); + } + + @VisibleForTesting(visibility = Visibility.PRIVATE) + Map<Integer, Integer> getReadySubIdsBySlotId() { + return Collections.unmodifiableMap(mReadySubIdsBySlotId); + } + + /** TelephonySubscriptionSnapshot is a class containing info about active subscriptions */ + public static class TelephonySubscriptionSnapshot { + private final Map<Integer, ParcelUuid> mSubIdToGroupMap; + private final Set<ParcelUuid> mActiveGroups; + + @VisibleForTesting(visibility = Visibility.PRIVATE) + TelephonySubscriptionSnapshot( + @NonNull Map<Integer, ParcelUuid> subIdToGroupMap, + @NonNull Set<ParcelUuid> activeGroups) { + mSubIdToGroupMap = Collections.unmodifiableMap( + Objects.requireNonNull(subIdToGroupMap, "subIdToGroupMap was null")); + mActiveGroups = Collections.unmodifiableSet( + Objects.requireNonNull(activeGroups, "activeGroups was null")); + } + + /** Returns the active subscription groups */ + @NonNull + public Set<ParcelUuid> getActiveSubscriptionGroups() { + return mActiveGroups; + } + + /** Returns the Subscription Group for a given subId. */ + @Nullable + public ParcelUuid getGroupForSubId(int subId) { + return mSubIdToGroupMap.get(subId); + } + + /** + * Returns all the subIds in a given group, including available, but inactive subscriptions. + */ + @NonNull + public Set<Integer> getAllSubIdsInGroup(ParcelUuid subGrp) { + final Set<Integer> subIds = new ArraySet<>(); + + for (Entry<Integer, ParcelUuid> entry : mSubIdToGroupMap.entrySet()) { + if (subGrp.equals(entry.getValue())) { + subIds.add(entry.getKey()); + } + } + + return subIds; + } + + @Override + public int hashCode() { + return Objects.hash(mSubIdToGroupMap, mActiveGroups); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof TelephonySubscriptionSnapshot)) { + return false; + } + + final TelephonySubscriptionSnapshot other = (TelephonySubscriptionSnapshot) obj; + + return mSubIdToGroupMap.equals(other.mSubIdToGroupMap) + && mActiveGroups.equals(other.mActiveGroups); + } + } + + /** + * Interface for listening to changes in subscriptions + * + * @see TelephonySubscriptionTracker + */ + public interface TelephonySubscriptionTrackerCallback { + /** + * Called when subscription information changes, and a new subscription snapshot was taken + * + * @param snapshot the snapshot of subscription information. + */ + void onNewSnapshot(@NonNull TelephonySubscriptionSnapshot snapshot); + } + + /** External static dependencies for test injection */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + public static class Dependencies { + /** Checks if the given bundle is for an identified carrier */ + public boolean isConfigForIdentifiedCarrier(PersistableBundle bundle) { + return CarrierConfigManager.isConfigForIdentifiedCarrier(bundle); + } + } +} diff --git a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java index c475da354dda..53f700938bf1 100644 --- a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java +++ b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java @@ -134,7 +134,7 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl @Override public DisplayAreaAppearedInfo createTaskDisplayArea(IDisplayAreaOrganizer organizer, - int displayId, int rootFeatureId, String name) { + int displayId, int parentFeatureId, String name) { enforceTaskPermission("createTaskDisplayArea()"); final long uid = Binder.getCallingUid(); final long origId = Binder.clearCallingIdentity(); @@ -149,13 +149,26 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl + displayId); } - final DisplayArea root = display.getItemFromDisplayAreas(da -> - da.asRootDisplayArea() != null && da.mFeatureId == rootFeatureId - ? da + // The parentFeatureId can be either a RootDisplayArea or a TaskDisplayArea. + // Check if there is a RootDisplayArea with the given parentFeatureId. + final RootDisplayArea parentRoot = display.getItemFromDisplayAreas(da -> + da.asRootDisplayArea() != null && da.mFeatureId == parentFeatureId + ? da.asRootDisplayArea() : null); - if (root == null) { - throw new IllegalArgumentException("Can't find RootDisplayArea with featureId=" - + rootFeatureId); + final TaskDisplayArea parentTda; + if (parentRoot == null) { + // There is no RootDisplayArea matching the parentFeatureId. + // Check if there is a TaskDisplayArea with the given parentFeatureId. + parentTda = display.getItemFromTaskDisplayAreas(taskDisplayArea -> + taskDisplayArea.mFeatureId == parentFeatureId + ? taskDisplayArea + : null); + } else { + parentTda = null; + } + if (parentRoot == null && parentTda == null) { + throw new IllegalArgumentException( + "Can't find a parent DisplayArea with featureId=" + parentFeatureId); } final int taskDisplayAreaFeatureId = mNextTaskDisplayAreaFeatureId++; @@ -166,10 +179,13 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl // Oh well... } - final TaskDisplayArea tda = createTaskDisplayArea(root.asRootDisplayArea(), name, - taskDisplayAreaFeatureId); - return organizeDisplayArea(organizer, tda, + final TaskDisplayArea tda = parentRoot != null + ? createTaskDisplayArea(parentRoot, name, taskDisplayAreaFeatureId) + : createTaskDisplayArea(parentTda, name, taskDisplayAreaFeatureId); + final DisplayAreaAppearedInfo tdaInfo = organizeDisplayArea(organizer, tda, "DisplayAreaOrganizerController.createTaskDisplayArea"); + mOrganizersByFeatureIds.put(taskDisplayAreaFeatureId, organizer); + return tdaInfo; } } finally { Binder.restoreCallingIdentity(origId); @@ -196,6 +212,7 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl + "TaskDisplayArea=" + taskDisplayArea); } + mOrganizersByFeatureIds.remove(taskDisplayArea.mFeatureId); deleteTaskDisplayArea(taskDisplayArea); } } finally { @@ -253,6 +270,9 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl new SurfaceControl(displayArea.getSurfaceControl(), callsite)); } + /** + * Creates a {@link TaskDisplayArea} as the topmost TDA below the given {@link RootDisplayArea}. + */ private TaskDisplayArea createTaskDisplayArea(RootDisplayArea root, String name, int taskDisplayAreaFeatureId) { final TaskDisplayArea taskDisplayArea = new TaskDisplayArea(root.mDisplayContent, @@ -283,6 +303,21 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl return taskDisplayArea; } + /** + * Creates a {@link TaskDisplayArea} as the topmost child of the given {@link TaskDisplayArea}. + */ + private TaskDisplayArea createTaskDisplayArea(TaskDisplayArea parentTda, String name, + int taskDisplayAreaFeatureId) { + final TaskDisplayArea taskDisplayArea = new TaskDisplayArea(parentTda.mDisplayContent, + parentTda.mWmService, name, taskDisplayAreaFeatureId, + true /* createdByOrganizer */); + + // Insert the TaskDisplayArea on the top. + parentTda.addChild(taskDisplayArea, WindowContainer.POSITION_TOP); + + return taskDisplayArea; + } + private void deleteTaskDisplayArea(TaskDisplayArea taskDisplayArea) { taskDisplayArea.setOrganizer(null); mService.mRootWindowContainer.mTaskSupervisor.beginDeferResume(); diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 2c4eb1b92086..77ca4e9eddab 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -40,6 +40,7 @@ cc_library_static { "com_android_server_locksettings_SyntheticPasswordManager.cpp", "com_android_server_net_NetworkStatsService.cpp", "com_android_server_power_PowerManagerService.cpp", + "com_android_server_powerstats_PowerStatsService.cpp", "com_android_server_security_VerityUtils.cpp", "com_android_server_SerialService.cpp", "com_android_server_soundtrigger_middleware_AudioSessionProviderImpl.cpp", diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS index 7fc55656fc80..995cfe9fc2de 100644 --- a/services/core/jni/OWNERS +++ b/services/core/jni/OWNERS @@ -23,6 +23,7 @@ per-file com_android_server_locksettings_* = file:/services/core/java/com/androi per-file com_android_server_net_* = file:/services/core/java/com/android/server/net/OWNERS per-file com_android_server_pm_* = file:/services/core/java/com/android/server/pm/OWNERS per-file com_android_server_power_* = file:/services/core/java/com/android/server/power/OWNERS +per-file com_android_server_powerstats_* = file:/services/core/java/com/android/server/powerstats/OWNERS per-file com_android_server_se_* = file:/core/java/android/se/OWNERS per-file com_android_server_security_* = file:/core/java/android/security/OWNERS per-file com_android_server_tv_* = file:/media/java/android/media/tv/OWNERS diff --git a/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp b/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp new file mode 100644 index 000000000000..f5b851f7aea5 --- /dev/null +++ b/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp @@ -0,0 +1,440 @@ +/* + * 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. + */ + +#define LOG_TAG "PowerStatsService" + +#include <android/hardware/power/stats/1.0/IPowerStats.h> +#include <jni.h> +#include <nativehelper/JNIHelp.h> + +#include <log/log.h> + +using android::hardware::hidl_vec; +using android::hardware::Return; +using android::hardware::power::stats::V1_0::EnergyData; +using android::hardware::power::stats::V1_0::RailInfo; +using android::hardware::power::stats::V1_0::Status; + +// ChannelInfo +static jclass class_CI; +static jmethodID method_CI_init; +static jfieldID field_CI_channelId; +static jfieldID field_CI_channelName; + +// EnergyMeasurement +static jclass class_EM; +static jmethodID method_EM_init; +static jfieldID field_EM_channelId; +static jfieldID field_EM_timestampMs; +static jfieldID field_EM_durationMs; +static jfieldID field_EM_energyUWs; + +// StateInfo +static jclass class_SI; +static jmethodID method_SI_init; +static jfieldID field_SI_stateId; +static jfieldID field_SI_stateName; + +// PowerEntityInfo +static jclass class_PEI; +static jmethodID method_PEI_init; +static jfieldID field_PEI_powerEntityId; +static jfieldID field_PEI_powerEntityName; +static jfieldID field_PEI_states; + +// StateResidency +static jclass class_SR; +static jmethodID method_SR_init; +static jfieldID field_SR_stateId; +static jfieldID field_SR_totalTimeInStateMs; +static jfieldID field_SR_totalStateEntryCount; +static jfieldID field_SR_lastEntryTimestampMs; + +// StateResidencyResult +static jclass class_SRR; +static jmethodID method_SRR_init; +static jfieldID field_SRR_powerEntityId; +static jfieldID field_SRR_stateResidencyData; + +namespace android { + +static std::mutex gPowerStatsHalMutex; +static sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHalV1_0_ptr = nullptr; + +static void deinitPowerStats() { + gPowerStatsHalV1_0_ptr = nullptr; +} + +struct PowerStatsHalDeathRecipient : virtual public hardware::hidl_death_recipient { + virtual void serviceDied(uint64_t cookie, + const wp<android::hidl::base::V1_0::IBase> &who) override { + // The HAL just died. Reset all handles to HAL services. + std::lock_guard<std::mutex> lock(gPowerStatsHalMutex); + deinitPowerStats(); + } +}; + +sp<PowerStatsHalDeathRecipient> gPowerStatsHalDeathRecipient = new PowerStatsHalDeathRecipient(); + +static bool connectToPowerStatsHal() { + if (gPowerStatsHalV1_0_ptr == nullptr) { + gPowerStatsHalV1_0_ptr = android::hardware::power::stats::V1_0::IPowerStats::getService(); + + if (gPowerStatsHalV1_0_ptr == nullptr) { + ALOGE("Unable to get power.stats HAL service."); + return false; + } + + // Link death recipient to power.stats service handle + hardware::Return<bool> linked = + gPowerStatsHalV1_0_ptr->linkToDeath(gPowerStatsHalDeathRecipient, 0); + if (!linked.isOk()) { + ALOGE("Transaction error in linking to power.stats HAL death: %s", + linked.description().c_str()); + deinitPowerStats(); + return false; + } else if (!linked) { + ALOGW("Unable to link to power.stats HAL death notifications"); + return false; + } + } + return true; +} + +static bool checkResult(const Return<void> &ret, const char *function) { + if (!ret.isOk()) { + ALOGE("%s failed: requested HAL service not available. Description: %s", function, + ret.description().c_str()); + if (ret.isDeadObject()) { + deinitPowerStats(); + } + return false; + } + return true; +} + +static jobjectArray nativeGetPowerEntityInfo(JNIEnv *env, jclass clazz) { + std::lock_guard<std::mutex> lock(gPowerStatsHalMutex); + + if (!connectToPowerStatsHal()) { + ALOGE("nativeGetPowerEntityInfo failed to connect to power.stats HAL"); + return nullptr; + } + + jobjectArray powerEntityInfoArray = nullptr; + Return<void> ret = gPowerStatsHalV1_0_ptr->getPowerEntityInfo( + [&env, &powerEntityInfoArray](auto infos, auto status) { + if (status != Status::SUCCESS) { + ALOGE("Error getting power entity info"); + } else { + powerEntityInfoArray = env->NewObjectArray(infos.size(), class_PEI, nullptr); + for (int i = 0; i < infos.size(); i++) { + jstring powerEntityName = + env->NewStringUTF(infos[i].powerEntityName.c_str()); + jobject powerEntityInfo = env->NewObject(class_PEI, method_PEI_init); + env->SetIntField(powerEntityInfo, field_PEI_powerEntityId, + infos[i].powerEntityId); + env->SetObjectField(powerEntityInfo, field_PEI_powerEntityName, + powerEntityName); + env->SetObjectArrayElement(powerEntityInfoArray, i, powerEntityInfo); + env->DeleteLocalRef(powerEntityName); + env->DeleteLocalRef(powerEntityInfo); + } + } + }); + if (!checkResult(ret, __func__)) { + return nullptr; + } + + ret = gPowerStatsHalV1_0_ptr->getPowerEntityStateInfo( + {}, [&env, &powerEntityInfoArray](auto infos, auto status) { + if (status != Status::SUCCESS) { + ALOGE("Error getting power entity state info"); + } else { + for (int i = 0; i < infos.size(); i++) { + jobjectArray stateInfoArray = + env->NewObjectArray(infos[i].states.size(), class_SI, nullptr); + for (int j = 0; j < infos[i].states.size(); j++) { + jstring powerEntityStateName = env->NewStringUTF( + infos[i].states[j].powerEntityStateName.c_str()); + jobject stateInfo = env->NewObject(class_SI, method_SI_init); + env->SetIntField(stateInfo, field_SI_stateId, + infos[i].states[j].powerEntityStateId); + env->SetObjectField(stateInfo, field_SI_stateName, + powerEntityStateName); + env->SetObjectArrayElement(stateInfoArray, j, stateInfo); + env->DeleteLocalRef(powerEntityStateName); + env->DeleteLocalRef(stateInfo); + } + + for (int j = 0; j < env->GetArrayLength(powerEntityInfoArray); j++) { + jobject powerEntityInfo = + env->GetObjectArrayElement(powerEntityInfoArray, j); + if (env->GetIntField(powerEntityInfo, field_PEI_powerEntityId) == + infos[i].powerEntityId) { + env->SetObjectField(powerEntityInfo, field_PEI_states, + stateInfoArray); + env->SetObjectArrayElement(powerEntityInfoArray, j, + powerEntityInfo); + break; + } + } + } + } + }); + if (!checkResult(ret, __func__)) { + return nullptr; + } + + return powerEntityInfoArray; +} + +static jobjectArray nativeGetStateResidency(JNIEnv *env, jclass clazz, jintArray powerEntityIds) { + std::lock_guard<std::mutex> lock(gPowerStatsHalMutex); + + if (!connectToPowerStatsHal()) { + ALOGE("nativeGetStateResidency failed to connect to power.stats HAL"); + return nullptr; + } + + size_t powerEntityIdCount = env->GetArrayLength(powerEntityIds); + hidl_vec<uint32_t> powerEntityIdVector(powerEntityIdCount); + + jint *powerEntityIdElements = env->GetIntArrayElements(powerEntityIds, 0); + for (int i = 0; i < powerEntityIdCount; i++) { + powerEntityIdVector[i] = powerEntityIdElements[i]; + } + env->ReleaseIntArrayElements(powerEntityIds, powerEntityIdElements, 0); + + jobjectArray stateResidencyResultArray = nullptr; + Return<void> ret = gPowerStatsHalV1_0_ptr->getPowerEntityStateResidencyData( + powerEntityIdVector, [&env, &stateResidencyResultArray](auto results, auto status) { + if (status != Status::SUCCESS) { + ALOGE("Error getting power entity state residency data"); + } else { + stateResidencyResultArray = + env->NewObjectArray(results.size(), class_SRR, nullptr); + for (int i = 0; i < results.size(); i++) { + jobjectArray stateResidencyArray = + env->NewObjectArray(results[i].stateResidencyData.size(), class_SR, + nullptr); + for (int j = 0; j < results[i].stateResidencyData.size(); j++) { + jobject stateResidency = env->NewObject(class_SR, method_SR_init); + env->SetIntField(stateResidency, field_SR_stateId, + results[i].stateResidencyData[j].powerEntityStateId); + env->SetLongField(stateResidency, field_SR_totalTimeInStateMs, + results[i].stateResidencyData[j].totalTimeInStateMs); + env->SetLongField(stateResidency, field_SR_totalStateEntryCount, + results[i] + .stateResidencyData[j] + .totalStateEntryCount); + env->SetLongField(stateResidency, field_SR_lastEntryTimestampMs, + results[i] + .stateResidencyData[j] + .lastEntryTimestampMs); + env->SetObjectArrayElement(stateResidencyArray, j, stateResidency); + env->DeleteLocalRef(stateResidency); + } + jobject stateResidencyResult = env->NewObject(class_SRR, method_SRR_init); + env->SetIntField(stateResidencyResult, field_SRR_powerEntityId, + results[i].powerEntityId); + env->SetObjectField(stateResidencyResult, field_SRR_stateResidencyData, + stateResidencyArray); + env->SetObjectArrayElement(stateResidencyResultArray, i, + stateResidencyResult); + env->DeleteLocalRef(stateResidencyResult); + } + } + }); + if (!checkResult(ret, __func__)) { + return nullptr; + } + + return stateResidencyResultArray; +} + +static jobjectArray nativeGetEnergyMeterInfo(JNIEnv *env, jclass clazz) { + std::lock_guard<std::mutex> lock(gPowerStatsHalMutex); + + if (!connectToPowerStatsHal()) { + ALOGE("nativeGetEnergyMeterInfo failed to connect to power.stats HAL"); + return nullptr; + } + + jobjectArray channelInfoArray = nullptr; + Return<void> ret = gPowerStatsHalV1_0_ptr->getRailInfo( + [&env, &channelInfoArray](auto railInfo, auto status) { + if (status != Status::SUCCESS) { + ALOGW("Error getting rail info"); + } else { + channelInfoArray = env->NewObjectArray(railInfo.size(), class_CI, nullptr); + for (int i = 0; i < railInfo.size(); i++) { + jstring channelName = env->NewStringUTF(railInfo[i].railName.c_str()); + jobject channelInfo = env->NewObject(class_CI, method_CI_init); + env->SetIntField(channelInfo, field_CI_channelId, railInfo[i].index); + env->SetObjectField(channelInfo, field_CI_channelName, channelName); + env->SetObjectArrayElement(channelInfoArray, i, channelInfo); + env->DeleteLocalRef(channelName); + env->DeleteLocalRef(channelInfo); + } + } + }); + + if (!checkResult(ret, __func__)) { + ALOGE("getRailInfo failed"); + return nullptr; + } + + return channelInfoArray; +} + +static jobjectArray nativeReadEnergyMeters(JNIEnv *env, jclass clazz, jintArray channelIds) { + std::lock_guard<std::mutex> lock(gPowerStatsHalMutex); + + if (!connectToPowerStatsHal()) { + ALOGE("nativeGetEnergy failed to connect to power.stats HAL"); + } + + size_t channelIdCount = env->GetArrayLength(channelIds); + hidl_vec<uint32_t> channelIdVector(channelIdCount); + + jint *channelIdElements = env->GetIntArrayElements(channelIds, 0); + for (int i = 0; i < channelIdCount; i++) { + channelIdVector[i] = channelIdElements[i]; + } + env->ReleaseIntArrayElements(channelIds, channelIdElements, 0); + + jobjectArray energyMeasurementArray = nullptr; + Return<void> ret = + gPowerStatsHalV1_0_ptr + ->getEnergyData(channelIdVector, + [&env, &energyMeasurementArray](auto energyData, auto status) { + if (status != Status::SUCCESS) { + ALOGW("Error getting energy data"); + } else { + energyMeasurementArray = + env->NewObjectArray(energyData.size(), class_EM, + nullptr); + for (int i = 0; i < energyData.size(); i++) { + jobject energyMeasurement = + env->NewObject(class_EM, method_EM_init); + env->SetIntField(energyMeasurement, + field_EM_channelId, + energyData[i].index); + env->SetLongField(energyMeasurement, + field_EM_timestampMs, + energyData[i].timestamp); + env->SetLongField(energyMeasurement, + field_EM_durationMs, -1); + env->SetLongField(energyMeasurement, + field_EM_energyUWs, + energyData[i].energy); + env->SetObjectArrayElement(energyMeasurementArray, + i, energyMeasurement); + env->DeleteLocalRef(energyMeasurement); + } + } + }); + + if (!checkResult(ret, __func__)) { + ALOGE("getEnergyData failed"); + return nullptr; + } + + return energyMeasurementArray; +} + +static jboolean nativeInit(JNIEnv *env, jclass clazz) { + std::lock_guard<std::mutex> lock(gPowerStatsHalMutex); + + // ChannelInfo + jclass temp = env->FindClass("android/hardware/power/stats/ChannelInfo"); + class_CI = (jclass)env->NewGlobalRef(temp); + method_CI_init = env->GetMethodID(class_CI, "<init>", "()V"); + field_CI_channelId = env->GetFieldID(class_CI, "channelId", "I"); + field_CI_channelName = env->GetFieldID(class_CI, "channelName", "Ljava/lang/String;"); + + // EnergyMeasurement + temp = env->FindClass("android/hardware/power/stats/EnergyMeasurement"); + class_EM = (jclass)env->NewGlobalRef(temp); + method_EM_init = env->GetMethodID(class_EM, "<init>", "()V"); + field_EM_channelId = env->GetFieldID(class_EM, "channelId", "I"); + field_EM_timestampMs = env->GetFieldID(class_EM, "timestampMs", "J"); + field_EM_durationMs = env->GetFieldID(class_EM, "durationMs", "J"); + field_EM_energyUWs = env->GetFieldID(class_EM, "energyUWs", "J"); + + // StateInfo + temp = env->FindClass("android/hardware/power/stats/StateInfo"); + class_SI = (jclass)env->NewGlobalRef(temp); + method_SI_init = env->GetMethodID(class_SI, "<init>", "()V"); + field_SI_stateId = env->GetFieldID(class_SI, "stateId", "I"); + field_SI_stateName = env->GetFieldID(class_SI, "stateName", "Ljava/lang/String;"); + + // PowerEntityInfo + temp = env->FindClass("android/hardware/power/stats/PowerEntityInfo"); + class_PEI = (jclass)env->NewGlobalRef(temp); + method_PEI_init = env->GetMethodID(class_PEI, "<init>", "()V"); + field_PEI_powerEntityId = env->GetFieldID(class_PEI, "powerEntityId", "I"); + field_PEI_powerEntityName = env->GetFieldID(class_PEI, "powerEntityName", "Ljava/lang/String;"); + field_PEI_states = + env->GetFieldID(class_PEI, "states", "[Landroid/hardware/power/stats/StateInfo;"); + + // StateResidency + temp = env->FindClass("android/hardware/power/stats/StateResidency"); + class_SR = (jclass)env->NewGlobalRef(temp); + method_SR_init = env->GetMethodID(class_SR, "<init>", "()V"); + field_SR_stateId = env->GetFieldID(class_SR, "stateId", "I"); + field_SR_totalTimeInStateMs = env->GetFieldID(class_SR, "totalTimeInStateMs", "J"); + field_SR_totalStateEntryCount = env->GetFieldID(class_SR, "totalStateEntryCount", "J"); + field_SR_lastEntryTimestampMs = env->GetFieldID(class_SR, "lastEntryTimestampMs", "J"); + + // StateResidencyResult + temp = env->FindClass("android/hardware/power/stats/StateResidencyResult"); + class_SRR = (jclass)env->NewGlobalRef(temp); + method_SRR_init = env->GetMethodID(class_SRR, "<init>", "()V"); + field_SRR_powerEntityId = env->GetFieldID(class_SRR, "powerEntityId", "I"); + field_SRR_stateResidencyData = + env->GetFieldID(class_SRR, "stateResidencyData", + "[Landroid/hardware/power/stats/StateResidency;"); + + if (!connectToPowerStatsHal()) { + ALOGE("nativeInit failed to connect to power.stats HAL"); + return false; + } + + return true; +} + +static const JNINativeMethod method_table[] = { + {"nativeInit", "()Z", (void *)nativeInit}, + {"nativeGetPowerEntityInfo", "()[Landroid/hardware/power/stats/PowerEntityInfo;", + (void *)nativeGetPowerEntityInfo}, + {"nativeGetStateResidency", "([I)[Landroid/hardware/power/stats/StateResidencyResult;", + (void *)nativeGetStateResidency}, + {"nativeGetEnergyMeterInfo", "()[Landroid/hardware/power/stats/ChannelInfo;", + (void *)nativeGetEnergyMeterInfo}, + {"nativeReadEnergyMeters", "([I)[Landroid/hardware/power/stats/EnergyMeasurement;", + (void *)nativeReadEnergyMeters}, +}; + +int register_android_server_PowerStatsService(JNIEnv *env) { + return jniRegisterNativeMethods(env, + "com/android/server/powerstats/" + "PowerStatsHALWrapper$PowerStatsHAL10WrapperImpl", + method_table, NELEM(method_table)); +} + +}; // namespace android diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index 5a0d08aeb6b3..85ef39470cb3 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -29,6 +29,7 @@ int register_android_server_ConsumerIrService(JNIEnv *env); int register_android_server_InputManager(JNIEnv* env); int register_android_server_LightsService(JNIEnv* env); int register_android_server_PowerManagerService(JNIEnv* env); +int register_android_server_PowerStatsService(JNIEnv* env); int register_android_server_storage_AppFuse(JNIEnv* env); int register_android_server_SerialService(JNIEnv* env); int register_android_server_SystemServer(JNIEnv* env); @@ -82,6 +83,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) register_android_server_broadcastradio_BroadcastRadioService(env); register_android_server_broadcastradio_Tuner(vm, env); register_android_server_PowerManagerService(env); + register_android_server_PowerStatsService(env); register_android_server_SerialService(env); register_android_server_InputManager(env); register_android_server_LightsService(env); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index f2c65e230ec2..0c00e3d63f88 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -11766,13 +11766,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override - public ComponentName getProfileOwnerAsUser(@UserIdInt int userId) { - return DevicePolicyManagerService.this.getProfileOwnerAsUser(userId); - } - - @Override - public int getDeviceOwnerUserId() { - return DevicePolicyManagerService.this.getDeviceOwnerUserId(); + public ComponentName getProfileOwnerAsUser(int userHandle) { + return DevicePolicyManagerService.this.getProfileOwnerAsUser(userHandle); } @Override diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java index 9f895c678dd0..99693d28f378 100644 --- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java @@ -36,7 +36,6 @@ import android.os.HandlerThread; import android.util.Log; import android.util.SparseArray; import android.util.TypedXmlPullParser; -import android.util.TypedXmlSerializer; import android.util.Xml; import androidx.test.InstrumentationRegistry; @@ -53,7 +52,6 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.nio.charset.StandardCharsets; /** * Tests app ops version upgrades @@ -183,8 +181,7 @@ public class AppOpsUpgradeTest { boolean parse() { try (FileInputStream stream = new FileInputStream(mFile)) { - TypedXmlPullParser parser = Xml.newFastPullParser(); - parser.setInput(stream, StandardCharsets.UTF_8.name()); + TypedXmlPullParser parser = Xml.resolvePullParser(stream); int type; while ((type = parser.next()) != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) { diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/OWNERS b/services/tests/mockingservicestests/src/com/android/server/pm/OWNERS new file mode 100644 index 000000000000..d825dfd7cf00 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/pm/OWNERS @@ -0,0 +1 @@ +include /services/core/java/com/android/server/pm/OWNERS diff --git a/services/tests/servicestests/src/com/android/server/audio/OWNERS b/services/tests/servicestests/src/com/android/server/audio/OWNERS new file mode 100644 index 000000000000..894a1f5c25d0 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/audio/OWNERS @@ -0,0 +1 @@ +include /services/core/java/com/android/server/audio/OWNERS diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java index e5883708e921..8c63bfcf1407 100644 --- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java @@ -190,6 +190,22 @@ public class CompatConfigTest { } @Test + public void testIsChangeEnabledForInvalidApp() throws Exception { + final long disabledChangeId = 1234L; + final long enabledChangeId = 1235L; + final long targetSdkChangeId = 1236L; + CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) + .addEnabledChangeWithId(enabledChangeId) + .addDisabledChangeWithId(disabledChangeId) + .addEnableSinceSdkChangeWithId(42, targetSdkChangeId) + .build(); + + assertThat(compatConfig.isChangeEnabled(enabledChangeId, null)).isTrue(); + assertThat(compatConfig.isChangeEnabled(disabledChangeId, null)).isFalse(); + assertThat(compatConfig.isChangeEnabled(targetSdkChangeId, null)).isTrue(); + } + + @Test public void testPreventAddOverride() throws Exception { final long changeId = 1234L; CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) diff --git a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java index b26d1efef2a8..08d4caacd777 100644 --- a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java @@ -209,7 +209,7 @@ public class PowerStatsServiceTest { } @Override - public boolean initialize() { + public boolean isInitialized() { return true; } } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java index 1198ee2ba5f4..5597be93c1f1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.view.Display.DEFAULT_DISPLAY; +import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER; import static android.window.DisplayAreaOrganizer.FEATURE_ROOT; import static android.window.DisplayAreaOrganizer.FEATURE_RUNTIME_TASK_CONTAINER_FIRST; import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; @@ -117,7 +118,7 @@ public class DisplayAreaOrganizerTest extends WindowTestsBase { } @Test - public void testCreateTaskDisplayArea() { + public void testCreateTaskDisplayArea_topBelowRoot() { final String newTdaName = "testTda"; final IDisplayAreaOrganizer organizer = createMockOrganizer(new Binder()); final DisplayAreaAppearedInfo tdaInfo = mOrganizerController.createTaskDisplayArea( @@ -142,6 +143,30 @@ public class DisplayAreaOrganizerTest extends WindowTestsBase { } @Test + public void testCreateTaskDisplayArea_topBelowAnotherTaskDisplayArea() { + final String newTdaName = "testTda"; + final TaskDisplayArea parentTda = mDisplayContent.getDefaultTaskDisplayArea(); + final IDisplayAreaOrganizer organizer = createMockOrganizer(new Binder()); + final DisplayAreaAppearedInfo tdaInfo = mOrganizerController.createTaskDisplayArea( + organizer, DEFAULT_DISPLAY, FEATURE_DEFAULT_TASK_CONTAINER, newTdaName); + + final WindowContainer wc = parentTda.getChildAt(parentTda.getChildCount() - 1); + + // A new TaskDisplayArea is created on the top. + assertThat(wc).isInstanceOf(TaskDisplayArea.class); + assertThat(tdaInfo.getDisplayAreaInfo().displayId).isEqualTo(DEFAULT_DISPLAY); + assertThat(tdaInfo.getDisplayAreaInfo().token) + .isEqualTo(wc.mRemoteToken.toWindowContainerToken()); + + final TaskDisplayArea tda = wc.asTaskDisplayArea(); + + assertThat(tda.getName()).isEqualTo(newTdaName); + assertThat(tda.mFeatureId).isEqualTo(tdaInfo.getDisplayAreaInfo().featureId); + assertThat(tda.mCreatedByOrganizer).isTrue(); + assertThat(tda.mOrganizer).isEqualTo(organizer); + } + + @Test public void testCreateTaskDisplayArea_incrementalTdaFeatureId() { final String newTdaName = "testTda"; final IDisplayAreaOrganizer organizer = createMockOrganizer(new Binder()); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index d645e86d3f37..56345c032670 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -2050,7 +2050,13 @@ public class CarrierConfigManager { * via {@link android.telecom.PhoneAccount#CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE} * and can choose to hide or show the video calling icon based on whether a contact supports * video. + * + * @deprecated No longer used in framework code, however it may still be used by applications + * that have not updated their code. This config should still be set to {@code true} if + * {@link Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL} is set to {@code true} and + * {@link Ims#KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL} is set to {@code true}. */ + @Deprecated public static final String KEY_USE_RCS_PRESENCE_BOOL = "use_rcs_presence_bool"; /** @@ -3866,13 +3872,51 @@ public class CarrierConfigManager { * <p> * If this key's value is set to false, the procedure for RCS contact capability exchange * via SIP SUBSCRIBE/NOTIFY will also be disabled internally, and - * {@link #KEY_USE_RCS_PRESENCE_BOOL} must also be set to false to ensure apps do not - * improperly think that capability exchange via SIP PUBLISH is enabled. + * {@link Ims#KEY_ENABLE_PRESENCE_PUBLISH_BOOL} must also be set to false to ensure + * apps do not improperly think that capability exchange via SIP PUBLISH is enabled. * <p> The default value for this key is {@code false}. */ public static final String KEY_ENABLE_PRESENCE_PUBLISH_BOOL = KEY_PREFIX + "enable_presence_publish_bool"; + /** + * Flag indicating whether or not this carrier supports the exchange of phone numbers with + * the carrier's RCS presence server in order to retrieve the RCS capabilities of requested + * contacts used in the RCS User Capability Exchange (UCE) procedure. See RCC.71, section 3 + * for more information. + * <p> + * When presence is supported, the device uses the SIP SUBSCRIBE/NOTIFY procedure internally + * to retrieve the requested RCS capabilities. See + * {@link android.telephony.ims.RcsUceAdapter} for more information on how RCS capabilities + * can be retrieved from the carrier's network. + */ + public static final String KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL = + KEY_PREFIX + "enable_presence_capability_exchange_bool"; + + + /** + * Flag indicating whether or not the carrier expects the RCS UCE service to periodically + * refresh the RCS capabilities cache of the user's contacts as well as request the + * capabilities of call contacts when the SIM card is first inserted or when a new contact + * is added, removed, or modified. This corresponds to the RCC.07 A.19 + * "DISABLE INITIAL ADDRESS BOOK SCAN" parameter. + * <p> + * If this flag is disabled, the capabilities cache will not be refreshed internally at all + * and will only be updated if the cached capabilities are stale when an application + * requests them. + */ + public static final String KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL = + KEY_PREFIX + "rcs_bulk_capability_exchange_bool"; + + /** + * Flag indicating whether or not the carrier supports capability exchange with a list of + * contacts. When {@code true}, the device will batch together multiple requests and + * construct a RLMI document in the SIP SUBSCRIBE request (see RFC 4662). If {@code false}, + * the request will be split up into one SIP SUBSCRIBE request per contact. + */ + public static final String KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL = + KEY_PREFIX + "enable_presence_group_subscribe_bool"; + private Ims() {} private static PersistableBundle getDefaults() { @@ -3880,6 +3924,9 @@ public class CarrierConfigManager { defaults.putInt(KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT, 4000); defaults.putBoolean(KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, false); defaults.putBoolean(KEY_ENABLE_PRESENCE_PUBLISH_BOOL, false); + defaults.putBoolean(KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL, false); + defaults.putBoolean(KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL, false); + defaults.putBoolean(KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL, true); return defaults; } } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 31a12493b33c..d4565f32641a 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -8671,6 +8671,77 @@ public class TelephonyManager { return Collections.EMPTY_LIST; } + /** + * Call composer status OFF from user setting. + */ + public static final int CALL_COMPOSER_STATUS_OFF = 0; + + /** + * Call composer status ON from user setting. + */ + public static final int CALL_COMPOSER_STATUS_ON = 1; + + /** @hide */ + @IntDef(prefix = {"CALL_COMPOSER_STATUS_"}, + value = { + CALL_COMPOSER_STATUS_ON, + CALL_COMPOSER_STATUS_OFF, + }) + public @interface CallComposerStatus {} + + /** + * Set the user-set status for enriched calling with call composer. + * + * @param status user-set status for enriched calling with call composer; + * it must be a value of either {@link #CALL_COMPOSER_STATUS_ON} + * or {@link #CALL_COMPOSER_STATUS_OFF}. + * + * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} + * + * @throws IllegalArgumentException if requested state is invalid. + * @throws SecurityException if the caller does not have the permission. + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void setCallComposerStatus(@CallComposerStatus int status) { + if (status != CALL_COMPOSER_STATUS_ON && status != CALL_COMPOSER_STATUS_OFF) { + throw new IllegalArgumentException("requested status is invalid"); + } + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.setCallComposerStatus(getSubId(), status); + } + } catch (RemoteException ex) { + Log.e(TAG, "Error calling ITelephony#setCallComposerStatus", ex); + ex.rethrowFromSystemServer(); + } + } + + /** + * Get the user-set status for enriched calling with call composer. + * + * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the + * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} + * + * @throws SecurityException if the caller does not have the permission. + * + * @return the user-set status for enriched calling with call composer either + * {@link #CALL_COMPOSER_STATUS_ON} or {@link #CALL_COMPOSER_STATUS_OFF}. + */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public @CallComposerStatus int getCallComposerStatus() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getCallComposerStatus(getSubId()); + } + } catch (RemoteException ex) { + Log.e(TAG, "Error calling ITelephony#getCallComposerStatus", ex); + ex.rethrowFromSystemServer(); + } + return CALL_COMPOSER_STATUS_OFF; + } /** @hide */ @SystemApi diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java index 885ff9be550c..ad461c091493 100644 --- a/telephony/java/android/telephony/ims/ImsRcsManager.java +++ b/telephony/java/android/telephony/ims/ImsRcsManager.java @@ -30,7 +30,6 @@ import android.os.ServiceSpecificException; import android.provider.Settings; import android.telephony.AccessNetworkConstants; import android.telephony.BinderCacheManager; -import android.telephony.CarrierConfigManager; import android.telephony.TelephonyFrameworkInitializer; import android.telephony.ims.aidl.IImsCapabilityCallback; import android.telephony.ims.aidl.IImsRcsController; @@ -62,9 +61,10 @@ public class ImsRcsManager { * been enabled by the user can be queried using {@link RcsUceAdapter#isUceSettingEnabled()}. * <p> * This intent will always be handled by the system, however the application should only send - * this Intent if the carrier supports RCS contact discovery, which can be queried using the key - * {@link CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL}. Otherwise, the RCS contact discovery - * opt-in dialog will not be shown. + * this Intent if the carrier supports bulk RCS contact exchange, which will be true if either + * key {@link android.telephony.CarrierConfigManager.Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL} + * or {@link android.telephony.CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL} is set to true. + * Otherwise, the RCS contact discovery opt-in dialog will not be shown. * <p> * Input: A mandatory {@link Settings#EXTRA_SUB_ID} extra containing the subscription that the * setting will be be shown for. @@ -396,6 +396,7 @@ public class ImsRcsManager { * rather the subscription is capable of this service over IMS. * @see #isAvailable(int) * @see android.telephony.CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL + * @see android.telephony.CarrierConfigManager.Ims#KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL * @throws ImsException if the IMS service is not available when calling this method. * See {@link ImsException#getCode()} for more information on the error codes. * @hide diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index ff240a0302b2..de4da3829e95 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -128,6 +128,15 @@ interface ITelephony { */ boolean isRadioOnForSubscriberWithFeature(int subId, String callingPackage, String callingFeatureId); + /** + * Set the user-set status for enriched calling with call composer. + */ + void setCallComposerStatus(int subId, int status); + + /** + * Get the user-set status for enriched calling with call composer. + */ + int getCallComposerStatus(int subId); /** * Supply a pin to unlock the SIM for particular subId. diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java index cf3b03cae72e..f7cebd12fcff 100644 --- a/test-mock/src/android/test/mock/MockContext.java +++ b/test-mock/src/android/test/mock/MockContext.java @@ -817,6 +817,11 @@ public class MockContext extends Context { } @Override + public @NonNull Context createWindowContext(Display display, int type, Bundle options) { + throw new UnsupportedOperationException(); + } + + @Override public boolean isRestricted() { throw new UnsupportedOperationException(); } diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 20b05b54f95d..799b64e7d63b 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -8109,7 +8109,16 @@ public class ConnectivityServiceTest { @Test public void testDumpDoesNotCrash() { - StringWriter stringWriter = new StringWriter(); + // Filing a couple requests prior to testing the dump. + final TestNetworkCallback genericNetworkCallback = new TestNetworkCallback(); + final TestNetworkCallback wifiNetworkCallback = new TestNetworkCallback(); + final NetworkRequest genericRequest = new NetworkRequest.Builder() + .clearCapabilities().build(); + final NetworkRequest wifiRequest = new NetworkRequest.Builder() + .addTransportType(TRANSPORT_WIFI).build(); + mCm.registerNetworkCallback(genericRequest, genericNetworkCallback); + mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback); + final StringWriter stringWriter = new StringWriter(); mService.dump(new FileDescriptor(), new PrintWriter(stringWriter), new String[0]); @@ -8131,11 +8140,11 @@ public class ConnectivityServiceTest { mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback); mCm.registerNetworkCallback(cellRequest, cellNetworkCallback); - ConnectivityService.NetworkRequestInfo[] nriOutput = mService.requestsSortedById(); + final ConnectivityService.NetworkRequestInfo[] nriOutput = mService.requestsSortedById(); assertTrue(nriOutput.length > 1); for (int i = 0; i < nriOutput.length - 1; i++) { - boolean isRequestIdInOrder = + final boolean isRequestIdInOrder = nriOutput[i].mRequests.get(0).requestId < nriOutput[i + 1].mRequests.get(0).requestId; assertTrue(isRequestIdInOrder); diff --git a/tests/vcn/java/android/net/vcn/VcnConfigTest.java b/tests/vcn/java/android/net/vcn/VcnConfigTest.java new file mode 100644 index 000000000000..77944deb26f1 --- /dev/null +++ b/tests/vcn/java/android/net/vcn/VcnConfigTest.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.vcn; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import android.os.Parcel; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Collections; +import java.util.Set; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class VcnConfigTest { + private static final Set<VcnGatewayConnectionConfig> GATEWAY_CONNECTION_CONFIGS = + Collections.singleton(VcnGatewayConnectionConfigTest.buildTestConfig()); + + // Public visibility for VcnManagementServiceTest + public static VcnConfig buildTestConfig() { + VcnConfig.Builder builder = new VcnConfig.Builder(); + + for (VcnGatewayConnectionConfig gatewayConnectionConfig : GATEWAY_CONNECTION_CONFIGS) { + builder.addGatewayConnectionConfig(gatewayConnectionConfig); + } + + return builder.build(); + } + + @Test + public void testBuilderRequiresGatewayConnectionConfig() { + try { + new VcnConfig.Builder().build(); + fail("Expected exception due to no VcnGatewayConnectionConfigs provided"); + } catch (IllegalArgumentException e) { + } + } + + @Test + public void testBuilderAndGetters() { + final VcnConfig config = buildTestConfig(); + + assertEquals(GATEWAY_CONNECTION_CONFIGS, config.getGatewayConnectionConfigs()); + } + + @Test + public void testPersistableBundle() { + final VcnConfig config = buildTestConfig(); + + assertEquals(config, new VcnConfig(config.toPersistableBundle())); + } + + @Test + public void testParceling() { + final VcnConfig config = buildTestConfig(); + + Parcel parcel = Parcel.obtain(); + config.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + + assertEquals(config, VcnConfig.CREATOR.createFromParcel(parcel)); + } +} diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java new file mode 100644 index 000000000000..e98b6ef2b3a6 --- /dev/null +++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.vcn; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import android.net.NetworkCapabilities; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.TimeUnit; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class VcnGatewayConnectionConfigTest { + private static final int[] EXPOSED_CAPS = + new int[] { + NetworkCapabilities.NET_CAPABILITY_INTERNET, NetworkCapabilities.NET_CAPABILITY_MMS + }; + private static final int[] UNDERLYING_CAPS = new int[] {NetworkCapabilities.NET_CAPABILITY_DUN}; + private static final long[] RETRY_INTERVALS_MS = + new long[] { + TimeUnit.SECONDS.toMillis(5), + TimeUnit.SECONDS.toMillis(30), + TimeUnit.MINUTES.toMillis(1), + TimeUnit.MINUTES.toMillis(5), + TimeUnit.MINUTES.toMillis(15), + TimeUnit.MINUTES.toMillis(30) + }; + private static final int MAX_MTU = 1360; + + // Package protected for use in VcnConfigTest + static VcnGatewayConnectionConfig buildTestConfig() { + final VcnGatewayConnectionConfig.Builder builder = + new VcnGatewayConnectionConfig.Builder() + .setRetryInterval(RETRY_INTERVALS_MS) + .setMaxMtu(MAX_MTU); + + for (int caps : EXPOSED_CAPS) { + builder.addExposedCapability(caps); + } + + for (int caps : UNDERLYING_CAPS) { + builder.addRequiredUnderlyingCapability(caps); + } + + return builder.build(); + } + + @Test + public void testBuilderRequiresNonEmptyExposedCaps() { + try { + new VcnGatewayConnectionConfig.Builder() + .addRequiredUnderlyingCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .build(); + + fail("Expected exception due to invalid exposed capabilities"); + } catch (IllegalArgumentException e) { + } + } + + @Test + public void testBuilderRequiresNonEmptyUnderlyingCaps() { + try { + new VcnGatewayConnectionConfig.Builder() + .addExposedCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .build(); + + fail("Expected exception due to invalid required underlying capabilities"); + } catch (IllegalArgumentException e) { + } + } + + @Test + public void testBuilderRequiresNonNullRetryInterval() { + try { + new VcnGatewayConnectionConfig.Builder().setRetryInterval(null); + fail("Expected exception due to invalid retryIntervalMs"); + } catch (IllegalArgumentException e) { + } + } + + @Test + public void testBuilderRequiresNonEmptyRetryInterval() { + try { + new VcnGatewayConnectionConfig.Builder().setRetryInterval(new long[0]); + fail("Expected exception due to invalid retryIntervalMs"); + } catch (IllegalArgumentException e) { + } + } + + @Test + public void testBuilderRequiresValidMtu() { + try { + new VcnGatewayConnectionConfig.Builder() + .setMaxMtu(VcnGatewayConnectionConfig.MIN_MTU_V6 - 1); + fail("Expected exception due to invalid mtu"); + } catch (IllegalArgumentException e) { + } + } + + @Test + public void testBuilderAndGetters() { + final VcnGatewayConnectionConfig config = buildTestConfig(); + + for (int cap : EXPOSED_CAPS) { + config.hasExposedCapability(cap); + } + for (int cap : UNDERLYING_CAPS) { + config.requiresUnderlyingCapability(cap); + } + + assertArrayEquals(RETRY_INTERVALS_MS, config.getRetryIntervalsMs()); + assertEquals(MAX_MTU, config.getMaxMtu()); + } + + @Test + public void testPersistableBundle() { + final VcnGatewayConnectionConfig config = buildTestConfig(); + + assertEquals(config, new VcnGatewayConnectionConfig(config.toPersistableBundle())); + } +} diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java index c91fdbffd760..1cc953239fed 100644 --- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java +++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java @@ -16,42 +16,123 @@ package com.android.server; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.Mockito.any; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import android.content.Context; import android.net.ConnectivityManager; +import android.net.vcn.VcnConfig; +import android.net.vcn.VcnConfigTest; +import android.os.ParcelUuid; +import android.os.PersistableBundle; +import android.os.Process; +import android.os.UserHandle; import android.os.test.TestLooper; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.server.vcn.util.PersistableBundleUtils; + import org.junit.Test; import org.junit.runner.RunWith; +import java.io.FileNotFoundException; +import java.util.Collections; +import java.util.Map; +import java.util.UUID; + /** Tests for {@link VcnManagementService}. */ @RunWith(AndroidJUnit4.class) @SmallTest public class VcnManagementServiceTest { + private static final ParcelUuid TEST_UUID_1 = new ParcelUuid(new UUID(0, 0)); + private static final ParcelUuid TEST_UUID_2 = new ParcelUuid(new UUID(1, 1)); + private static final VcnConfig TEST_VCN_CONFIG = VcnConfigTest.buildTestConfig(); + private static final Map<ParcelUuid, VcnConfig> TEST_VCN_CONFIG_MAP = + Collections.unmodifiableMap(Collections.singletonMap(TEST_UUID_1, TEST_VCN_CONFIG)); + + private static final SubscriptionInfo TEST_SUBSCRIPTION_INFO = + new SubscriptionInfo( + 1 /* id */, + "" /* iccId */, + 0 /* simSlotIndex */, + "Carrier" /* displayName */, + "Carrier" /* carrierName */, + 0 /* nameSource */, + 255 /* iconTint */, + "12345" /* number */, + 0 /* roaming */, + null /* icon */, + "0" /* mcc */, + "0" /* mnc */, + "0" /* countryIso */, + false /* isEmbedded */, + null /* nativeAccessRules */, + null /* cardString */, + false /* isOpportunistic */, + TEST_UUID_1.toString() /* groupUUID */, + 0 /* carrierId */, + 0 /* profileClass */); + private final Context mMockContext = mock(Context.class); private final VcnManagementService.Dependencies mMockDeps = mock(VcnManagementService.Dependencies.class); private final TestLooper mTestLooper = new TestLooper(); private final ConnectivityManager mConnMgr = mock(ConnectivityManager.class); + private final TelephonyManager mTelMgr = mock(TelephonyManager.class); + private final SubscriptionManager mSubMgr = mock(SubscriptionManager.class); private final VcnManagementService mVcnMgmtSvc; + private final PersistableBundleUtils.LockingReadWriteHelper mConfigReadWriteHelper = + mock(PersistableBundleUtils.LockingReadWriteHelper.class); - public VcnManagementServiceTest() { - doReturn(Context.CONNECTIVITY_SERVICE) - .when(mMockContext) - .getSystemServiceName(ConnectivityManager.class); - doReturn(mConnMgr).when(mMockContext).getSystemService(Context.CONNECTIVITY_SERVICE); + public VcnManagementServiceTest() throws Exception { + setupSystemService(mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class); + setupSystemService(mTelMgr, Context.TELEPHONY_SERVICE, TelephonyManager.class); + setupSystemService( + mSubMgr, Context.TELEPHONY_SUBSCRIPTION_SERVICE, SubscriptionManager.class); doReturn(mTestLooper.getLooper()).when(mMockDeps).getLooper(); + doReturn(Process.FIRST_APPLICATION_UID).when(mMockDeps).getBinderCallingUid(); + doReturn(mConfigReadWriteHelper) + .when(mMockDeps) + .newPersistableBundleLockingReadWriteHelper(any()); + + final PersistableBundle bundle = + PersistableBundleUtils.fromMap( + TEST_VCN_CONFIG_MAP, + PersistableBundleUtils::fromParcelUuid, + VcnConfig::toPersistableBundle); + doReturn(bundle).when(mConfigReadWriteHelper).readFromDisk(); + + setupMockedCarrierPrivilege(true); mVcnMgmtSvc = new VcnManagementService(mMockContext, mMockDeps); } + private void setupSystemService(Object service, String name, Class<?> serviceClass) { + doReturn(name).when(mMockContext).getSystemServiceName(serviceClass); + doReturn(service).when(mMockContext).getSystemService(name); + } + + private void setupMockedCarrierPrivilege(boolean isPrivileged) { + doReturn(Collections.singletonList(TEST_SUBSCRIPTION_INFO)) + .when(mSubMgr) + .getSubscriptionsInGroup(any()); + doReturn(isPrivileged) + .when(mTelMgr) + .hasCarrierPrivileges(eq(TEST_SUBSCRIPTION_INFO.getSubscriptionId())); + } + @Test public void testSystemReady() throws Exception { mVcnMgmtSvc.systemReady(); @@ -59,4 +140,119 @@ public class VcnManagementServiceTest { verify(mConnMgr) .registerNetworkProvider(any(VcnManagementService.VcnNetworkProvider.class)); } + + @Test + public void testNonSystemServerRealConfigFileAccessPermission() throws Exception { + // Attempt to build a real instance of the dependencies, and verify we cannot write to the + // file. + VcnManagementService.Dependencies deps = new VcnManagementService.Dependencies(); + PersistableBundleUtils.LockingReadWriteHelper configReadWriteHelper = + deps.newPersistableBundleLockingReadWriteHelper( + VcnManagementService.VCN_CONFIG_FILE); + + // Even tests should not be able to read/write configs from disk; SELinux policies restrict + // it to only the system server. + // Reading config should always return null since the file "does not exist", and writing + // should throw an IOException. + assertNull(configReadWriteHelper.readFromDisk()); + + try { + configReadWriteHelper.writeToDisk(new PersistableBundle()); + fail("Expected IOException due to SELinux policy"); + } catch (FileNotFoundException expected) { + } + } + + @Test + public void testLoadVcnConfigsOnStartup() throws Exception { + mTestLooper.dispatchAll(); + + assertEquals(TEST_VCN_CONFIG_MAP, mVcnMgmtSvc.getConfigs()); + verify(mConfigReadWriteHelper).readFromDisk(); + } + + @Test + public void testSetVcnConfigRequiresNonSystemServer() throws Exception { + doReturn(Process.SYSTEM_UID).when(mMockDeps).getBinderCallingUid(); + + try { + mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, VcnConfigTest.buildTestConfig()); + fail("Expected IllegalStateException exception for system server"); + } catch (IllegalStateException expected) { + } + } + + @Test + public void testSetVcnConfigRequiresSystemUser() throws Exception { + doReturn(UserHandle.getUid(UserHandle.MIN_SECONDARY_USER_ID, Process.FIRST_APPLICATION_UID)) + .when(mMockDeps) + .getBinderCallingUid(); + + try { + mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, VcnConfigTest.buildTestConfig()); + fail("Expected security exception for non system user"); + } catch (SecurityException expected) { + } + } + + @Test + public void testSetVcnConfigRequiresCarrierPrivileges() throws Exception { + setupMockedCarrierPrivilege(false); + + try { + mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, VcnConfigTest.buildTestConfig()); + fail("Expected security exception for missing carrier privileges"); + } catch (SecurityException expected) { + } + } + + @Test + public void testSetVcnConfig() throws Exception { + // Use a different UUID to simulate a new VCN config. + mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG); + assertEquals(TEST_VCN_CONFIG, mVcnMgmtSvc.getConfigs().get(TEST_UUID_2)); + verify(mConfigReadWriteHelper).writeToDisk(any(PersistableBundle.class)); + } + + @Test + public void testClearVcnConfigRequiresNonSystemServer() throws Exception { + doReturn(Process.SYSTEM_UID).when(mMockDeps).getBinderCallingUid(); + + try { + mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1); + fail("Expected IllegalStateException exception for system server"); + } catch (IllegalStateException expected) { + } + } + + @Test + public void testClearVcnConfigRequiresSystemUser() throws Exception { + doReturn(UserHandle.getUid(UserHandle.MIN_SECONDARY_USER_ID, Process.FIRST_APPLICATION_UID)) + .when(mMockDeps) + .getBinderCallingUid(); + + try { + mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1); + fail("Expected security exception for non system user"); + } catch (SecurityException expected) { + } + } + + @Test + public void testClearVcnConfigRequiresCarrierPrivileges() throws Exception { + setupMockedCarrierPrivilege(false); + + try { + mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1); + fail("Expected security exception for missing carrier privileges"); + } catch (SecurityException expected) { + } + } + + @Test + public void testClearVcnConfig() throws Exception { + mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1); + assertTrue(mVcnMgmtSvc.getConfigs().isEmpty()); + verify(mConfigReadWriteHelper).writeToDisk(any(PersistableBundle.class)); + } } diff --git a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java new file mode 100644 index 000000000000..17b8f64a13fa --- /dev/null +++ b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java @@ -0,0 +1,333 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.vcn; + +import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED; +import static android.telephony.CarrierConfigManager.EXTRA_SLOT_INDEX; +import static android.telephony.CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX; +import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX; +import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; + +import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; +import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionTrackerCallback; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +import android.annotation.NonNull; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Handler; +import android.os.HandlerExecutor; +import android.os.ParcelUuid; +import android.os.test.TestLooper; +import android.telephony.CarrierConfigManager; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener; +import android.util.ArraySet; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +/** Tests for TelephonySubscriptionTracker */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class TelephonySubscriptionTrackerTest { + private static final ParcelUuid TEST_PARCEL_UUID = new ParcelUuid(UUID.randomUUID()); + private static final int TEST_SIM_SLOT_INDEX = 1; + private static final int TEST_SUBSCRIPTION_ID_1 = 2; + private static final SubscriptionInfo TEST_SUBINFO_1 = mock(SubscriptionInfo.class); + private static final int TEST_SUBSCRIPTION_ID_2 = 3; + private static final SubscriptionInfo TEST_SUBINFO_2 = mock(SubscriptionInfo.class); + private static final Map<Integer, ParcelUuid> TEST_SUBID_TO_GROUP_MAP; + + static { + final Map<Integer, ParcelUuid> subIdToGroupMap = new HashMap<>(); + subIdToGroupMap.put(TEST_SUBSCRIPTION_ID_1, TEST_PARCEL_UUID); + subIdToGroupMap.put(TEST_SUBSCRIPTION_ID_2, TEST_PARCEL_UUID); + TEST_SUBID_TO_GROUP_MAP = Collections.unmodifiableMap(subIdToGroupMap); + } + + @NonNull private final Context mContext; + @NonNull private final TestLooper mTestLooper; + @NonNull private final Handler mHandler; + @NonNull private final TelephonySubscriptionTracker.Dependencies mDeps; + + @NonNull private final SubscriptionManager mSubscriptionManager; + @NonNull private final CarrierConfigManager mCarrierConfigManager; + + @NonNull private TelephonySubscriptionTrackerCallback mCallback; + @NonNull private TelephonySubscriptionTracker mTelephonySubscriptionTracker; + + public TelephonySubscriptionTrackerTest() { + mContext = mock(Context.class); + mTestLooper = new TestLooper(); + mHandler = new Handler(mTestLooper.getLooper()); + mDeps = mock(TelephonySubscriptionTracker.Dependencies.class); + + mSubscriptionManager = mock(SubscriptionManager.class); + mCarrierConfigManager = mock(CarrierConfigManager.class); + + doReturn(Context.TELEPHONY_SUBSCRIPTION_SERVICE) + .when(mContext) + .getSystemServiceName(SubscriptionManager.class); + doReturn(mSubscriptionManager) + .when(mContext) + .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); + + doReturn(Context.CARRIER_CONFIG_SERVICE) + .when(mContext) + .getSystemServiceName(CarrierConfigManager.class); + doReturn(mCarrierConfigManager) + .when(mContext) + .getSystemService(Context.CARRIER_CONFIG_SERVICE); + + // subId 1, 2 are in same subGrp, only subId 1 is active + doReturn(TEST_PARCEL_UUID).when(TEST_SUBINFO_1).getGroupUuid(); + doReturn(TEST_PARCEL_UUID).when(TEST_SUBINFO_2).getGroupUuid(); + doReturn(TEST_SIM_SLOT_INDEX).when(TEST_SUBINFO_1).getSimSlotIndex(); + doReturn(INVALID_SIM_SLOT_INDEX).when(TEST_SUBINFO_2).getSimSlotIndex(); + doReturn(TEST_SUBSCRIPTION_ID_1).when(TEST_SUBINFO_1).getSubscriptionId(); + doReturn(TEST_SUBSCRIPTION_ID_2).when(TEST_SUBINFO_2).getSubscriptionId(); + } + + @Before + public void setUp() throws Exception { + mCallback = mock(TelephonySubscriptionTrackerCallback.class); + mTelephonySubscriptionTracker = + new TelephonySubscriptionTracker(mContext, mHandler, mCallback, mDeps); + mTelephonySubscriptionTracker.register(); + + doReturn(true).when(mDeps).isConfigForIdentifiedCarrier(any()); + doReturn(Arrays.asList(TEST_SUBINFO_1, TEST_SUBINFO_2)) + .when(mSubscriptionManager) + .getAllSubscriptionInfoList(); + } + + private IntentFilter getIntentFilter() { + final ArgumentCaptor<IntentFilter> captor = ArgumentCaptor.forClass(IntentFilter.class); + verify(mContext).registerReceiver(any(), captor.capture(), any(), any()); + + return captor.getValue(); + } + + private OnSubscriptionsChangedListener getOnSubscriptionsChangedListener() { + final ArgumentCaptor<OnSubscriptionsChangedListener> captor = + ArgumentCaptor.forClass(OnSubscriptionsChangedListener.class); + verify(mSubscriptionManager).addOnSubscriptionsChangedListener(any(), captor.capture()); + + return captor.getValue(); + } + + private Intent buildTestBroadcastIntent(boolean hasValidSubscription) { + Intent intent = new Intent(ACTION_CARRIER_CONFIG_CHANGED); + intent.putExtra(EXTRA_SLOT_INDEX, TEST_SIM_SLOT_INDEX); + intent.putExtra( + EXTRA_SUBSCRIPTION_INDEX, + hasValidSubscription ? TEST_SUBSCRIPTION_ID_1 : INVALID_SUBSCRIPTION_ID); + + return intent; + } + + private TelephonySubscriptionSnapshot buildExpectedSnapshot(Set<ParcelUuid> activeSubGroups) { + return buildExpectedSnapshot(TEST_SUBID_TO_GROUP_MAP, activeSubGroups); + } + + private TelephonySubscriptionSnapshot buildExpectedSnapshot( + Map<Integer, ParcelUuid> subIdToGroupMap, Set<ParcelUuid> activeSubGroups) { + return new TelephonySubscriptionSnapshot(subIdToGroupMap, activeSubGroups); + } + + private void verifyNoActiveSubscriptions() { + verify(mCallback).onNewSnapshot( + argThat(snapshot -> snapshot.getActiveSubscriptionGroups().isEmpty())); + } + + private void setupReadySubIds() { + mTelephonySubscriptionTracker.setReadySubIdsBySlotId( + Collections.singletonMap(TEST_SIM_SLOT_INDEX, TEST_SUBSCRIPTION_ID_1)); + } + + @Test + public void testRegister() throws Exception { + verify(mContext) + .registerReceiver( + eq(mTelephonySubscriptionTracker), + any(IntentFilter.class), + any(), + eq(mHandler)); + final IntentFilter filter = getIntentFilter(); + assertEquals(1, filter.countActions()); + assertTrue(filter.hasAction(ACTION_CARRIER_CONFIG_CHANGED)); + + verify(mSubscriptionManager) + .addOnSubscriptionsChangedListener(any(HandlerExecutor.class), any()); + assertNotNull(getOnSubscriptionsChangedListener()); + } + + @Test + public void testUnregister() throws Exception { + mTelephonySubscriptionTracker.unregister(); + + verify(mContext).unregisterReceiver(eq(mTelephonySubscriptionTracker)); + + final OnSubscriptionsChangedListener listener = getOnSubscriptionsChangedListener(); + verify(mSubscriptionManager).removeOnSubscriptionsChangedListener(eq(listener)); + } + + @Test + public void testOnSubscriptionsChangedFired_NoReadySubIds() throws Exception { + final OnSubscriptionsChangedListener listener = getOnSubscriptionsChangedListener(); + listener.onSubscriptionsChanged(); + mTestLooper.dispatchAll(); + + verifyNoActiveSubscriptions(); + } + + @Test + public void testOnSubscriptionsChangedFired_WithReadySubIds() throws Exception { + setupReadySubIds(); + + final OnSubscriptionsChangedListener listener = getOnSubscriptionsChangedListener(); + listener.onSubscriptionsChanged(); + mTestLooper.dispatchAll(); + + final Set<ParcelUuid> activeSubGroups = Collections.singleton(TEST_PARCEL_UUID); + verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(activeSubGroups))); + } + + @Test + public void testReceiveBroadcast_ConfigReadyWithSubscriptions() throws Exception { + mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true)); + mTestLooper.dispatchAll(); + + final Set<ParcelUuid> activeSubGroups = Collections.singleton(TEST_PARCEL_UUID); + verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(activeSubGroups))); + } + + @Test + public void testReceiveBroadcast_ConfigReadyNoSubscriptions() throws Exception { + doReturn(new ArrayList<SubscriptionInfo>()) + .when(mSubscriptionManager) + .getAllSubscriptionInfoList(); + + mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true)); + mTestLooper.dispatchAll(); + + // Expect an empty snapshot + verify(mCallback).onNewSnapshot( + eq(buildExpectedSnapshot(Collections.emptyMap(), Collections.emptySet()))); + } + + @Test + public void testReceiveBroadcast_SlotCleared() throws Exception { + setupReadySubIds(); + + mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(false)); + mTestLooper.dispatchAll(); + + verifyNoActiveSubscriptions(); + assertTrue(mTelephonySubscriptionTracker.getReadySubIdsBySlotId().isEmpty()); + } + + @Test + public void testReceiveBroadcast_ConfigNotReady() throws Exception { + doReturn(false).when(mDeps).isConfigForIdentifiedCarrier(any()); + + mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true)); + mTestLooper.dispatchAll(); + + // No interactions expected; config was not loaded + verifyNoMoreInteractions(mCallback); + } + + @Test + public void testSubscriptionsClearedAfterValidTriggersCallbacks() throws Exception { + final Set<ParcelUuid> activeSubGroups = Collections.singleton(TEST_PARCEL_UUID); + + mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true)); + mTestLooper.dispatchAll(); + verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(activeSubGroups))); + assertNotNull( + mTelephonySubscriptionTracker.getReadySubIdsBySlotId().get(TEST_SIM_SLOT_INDEX)); + + doReturn(Collections.emptyList()).when(mSubscriptionManager).getAllSubscriptionInfoList(); + mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true)); + mTestLooper.dispatchAll(); + verify(mCallback).onNewSnapshot( + eq(buildExpectedSnapshot(Collections.emptyMap(), Collections.emptySet()))); + } + + @Test + public void testSlotClearedAfterValidTriggersCallbacks() throws Exception { + final Set<ParcelUuid> activeSubGroups = Collections.singleton(TEST_PARCEL_UUID); + + mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true)); + mTestLooper.dispatchAll(); + verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(activeSubGroups))); + assertNotNull( + mTelephonySubscriptionTracker.getReadySubIdsBySlotId().get(TEST_SIM_SLOT_INDEX)); + + mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(false)); + mTestLooper.dispatchAll(); + verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(Collections.emptySet()))); + assertNull(mTelephonySubscriptionTracker.getReadySubIdsBySlotId().get(TEST_SIM_SLOT_INDEX)); + } + + @Test + public void testTelephonySubscriptionSnapshotGetGroupForSubId() throws Exception { + final TelephonySubscriptionSnapshot snapshot = + new TelephonySubscriptionSnapshot(TEST_SUBID_TO_GROUP_MAP, Collections.emptySet()); + + assertEquals(TEST_PARCEL_UUID, snapshot.getGroupForSubId(TEST_SUBSCRIPTION_ID_1)); + assertEquals(TEST_PARCEL_UUID, snapshot.getGroupForSubId(TEST_SUBSCRIPTION_ID_2)); + } + + @Test + public void testTelephonySubscriptionSnapshotGetAllSubIdsInGroup() throws Exception { + final TelephonySubscriptionSnapshot snapshot = + new TelephonySubscriptionSnapshot(TEST_SUBID_TO_GROUP_MAP, Collections.emptySet()); + + assertEquals( + new ArraySet<>(Arrays.asList(TEST_SUBSCRIPTION_ID_1, TEST_SUBSCRIPTION_ID_2)), + snapshot.getAllSubIdsInGroup(TEST_PARCEL_UUID)); + } +} diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp index 28330db966af..d7a8e6fe6ada 100644 --- a/tools/aapt2/util/Util.cpp +++ b/tools/aapt2/util/Util.cpp @@ -548,14 +548,14 @@ bool ExtractResFilePathParts(const StringPiece& path, StringPiece* out_prefix, } StringPiece16 GetString16(const android::ResStringPool& pool, size_t idx) { - if (auto str = pool.stringAt(idx)) { + if (auto str = pool.stringAt(idx); str.ok()) { return *str; } return StringPiece16(); } std::string GetString(const android::ResStringPool& pool, size_t idx) { - if (auto str = pool.string8At(idx)) { + if (auto str = pool.string8At(idx); str.ok()) { return ModifiedUtf8ToUtf8(str->to_string()); } return Utf16ToUtf8(GetString16(pool, idx)); diff --git a/tools/validatekeymaps/Main.cpp b/tools/validatekeymaps/Main.cpp index 950473d223fd..991b28071515 100644 --- a/tools/validatekeymaps/Main.cpp +++ b/tools/validatekeymaps/Main.cpp @@ -96,7 +96,7 @@ static bool validateFile(const char* filename) { case FileType::KEY_LAYOUT: { base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(filename); - if (!ret) { + if (!ret.ok()) { error("Error %s parsing key layout file.\n\n", ret.error().message().c_str()); return false; } @@ -106,7 +106,7 @@ static bool validateFile(const char* filename) { case FileType::KEY_CHARACTER_MAP: { base::Result<std::shared_ptr<KeyCharacterMap>> ret = KeyCharacterMap::load(filename, KeyCharacterMap::Format::ANY); - if (!ret) { + if (!ret.ok()) { error("Error %s parsing key character map file.\n\n", ret.error().message().c_str()); return false; |