diff options
109 files changed, 2305 insertions, 1016 deletions
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index 9abb308534df..2fd2e33bbc37 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -9,6 +9,7 @@ clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp core/jni/ libs/input/ services/core/jni/ + services/incremental/ [Hook Scripts] checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT} diff --git a/api/test-current.txt b/api/test-current.txt index 03f4dc5d2f87..629ce4e6470e 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -1504,6 +1504,13 @@ package android.media { ctor public AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String); } + public class AudioSystem { + method public static float getMasterBalance(); + method public static final int getNumStreamTypes(); + method public static int setMasterBalance(float); + field public static final int STREAM_DEFAULT = -1; // 0xffffffff + } + public static final class AudioTrack.MetricsConstants { field public static final String ATTRIBUTES = "android.media.audiotrack.attributes"; field public static final String CHANNEL_MASK = "android.media.audiotrack.channelMask"; @@ -4707,7 +4714,6 @@ package android.util { field public static final String FFLAG_OVERRIDE_PREFIX = "sys.fflag.override."; field public static final String FFLAG_PREFIX = "sys.fflag."; field public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid"; - field public static final String NOTIF_CONVO_BYPASS_SHORTCUT_REQ = "settings_notif_convo_bypass_shortcut_req"; field public static final String PERSIST_PREFIX = "persist.sys.fflag.override."; field public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press"; field public static final String SEAMLESS_TRANSFER = "settings_seamless_transfer"; diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h index 850e2b010f8c..9e21c777e6ff 100644 --- a/cmds/statsd/src/logd/LogEvent.h +++ b/cmds/statsd/src/logd/LogEvent.h @@ -218,9 +218,6 @@ private: * The below three variables are only valid during the execution of * parseBuffer. There are no guarantees about the state of these variables * before/after. - * - * TODO (b/150312423): These shouldn't be member variables. We should pass - * them around as parameters. */ uint8_t* mBuf; uint32_t mRemainingLen; // number of valid bytes left in the buffer being parsed diff --git a/cmds/statsd/src/shell/ShellSubscriber.cpp b/cmds/statsd/src/shell/ShellSubscriber.cpp index 1cee4d9babdc..bed836a1bd90 100644 --- a/cmds/statsd/src/shell/ShellSubscriber.cpp +++ b/cmds/statsd/src/shell/ShellSubscriber.cpp @@ -152,7 +152,6 @@ void ShellSubscriber::startPull(int64_t myToken) { mPullerMgr->Pull(pullInfo.mPullerMatcher.atom_id(), uids, &data); VLOG("pulled %zu atoms with id %d", data.size(), pullInfo.mPullerMatcher.atom_id()); - // TODO(b/150969574): Don't write to a pipe while holding a lock. if (!writePulledAtomsLocked(data, pullInfo.mPullerMatcher)) { mSubscriptionInfo->mClientAlive = false; mSubscriptionShouldEnd.notify_one(); diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index d4f511280553..67334f554df7 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -1819,6 +1819,13 @@ public abstract class AccessibilityService extends Service { /** * Returns a list of system actions available in the system right now. + * <p> + * System actions that correspond to the global action constants will have matching action IDs. + * For example, an with id {@link #GLOBAL_ACTION_BACK} will perform the back action. + * </p> + * <p> + * These actions should be called by {@link #performGlobalAction}. + * </p> * * @return A list of available system actions. */ diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index f461a1708373..7a593498e967 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -7158,7 +7158,8 @@ public class Notification implements Parcelable CharSequence conversationTitle = !TextUtils.isEmpty(super.mBigContentTitle) ? super.mBigContentTitle : mConversationTitle; - if (!TextUtils.isEmpty(conversationTitle) && !hasOnlyWhiteSpaceSenders()) { + if (mConversationType == CONVERSATION_TYPE_LEGACY + && !TextUtils.isEmpty(conversationTitle) && !hasOnlyWhiteSpaceSenders()) { return conversationTitle; } return null; diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java index 80fa87152d78..41f04f73aa87 100644 --- a/core/java/android/app/admin/DevicePolicyManagerInternal.java +++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java @@ -176,19 +176,31 @@ public abstract class DevicePolicyManagerInternal { * for cross-profile communication, via {@link * DevicePolicyManager#setCrossProfilePackages(ComponentName, Set)}.</li> * <li>The default package names that are allowed to request user consent for cross-profile - * communication without being explicitly enabled by the admin , via {@link - * DevicePolicyManager#setDefaultCrossProfilePackages(ComponentName, UserHandle, Set)}.</li> + * communication without being explicitly enabled by the admin, via + * {@link com.android.internal.R.array#cross_profile_apps} and + * {@link com.android.internal.R.array#vendor_cross_profile_apps}.</li> * </ul> * * @return the combined set of whitelisted package names set via * {@link DevicePolicyManager#setCrossProfilePackages(ComponentName, Set)} and - * {@link DevicePolicyManager#setDefaultCrossProfilePackages(ComponentName, UserHandle, Set)} + * {@link com.android.internal.R.array#cross_profile_apps} and + * {@link com.android.internal.R.array#vendor_cross_profile_apps} * * @hide */ public abstract List<String> getAllCrossProfilePackages(); /** + * Returns the default package names set by the OEM that are allowed to request user consent for + * cross-profile communication without being explicitly enabled by the admin, via + * {@link com.android.internal.R.array#cross_profile_apps} and + * {@link com.android.internal.R.array#vendor_cross_profile_apps}. + * + * @hide + */ + public abstract List<String> getDefaultCrossProfilePackages(); + + /** * Sends the {@code intent} to the packages with cross profile capabilities. * * <p>This means the application must have the {@code crossProfile} property and the diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java index 7578ede2648d..144a07eb4ea3 100644 --- a/core/java/android/content/pm/CrossProfileApps.java +++ b/core/java/android/content/pm/CrossProfileApps.java @@ -435,6 +435,9 @@ public class CrossProfileApps { * <p>This differs from {@link #canConfigureInteractAcrossProfiles(String)} since it will * not return {@code false} if the app is not whitelisted or not installed in the other profile. * + * <p>Note that platform-signed apps that are automatically granted the permission and are not + * whitelisted by the OEM will not be included in this list. + * * @hide */ public boolean canUserAttemptToConfigureInteractAcrossProfiles(String packageName) { diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index f48d78ac9cc3..9a2e07e9cfbd 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -1542,6 +1542,14 @@ public abstract class PackageManager { */ public static final int INSTALL_FAILED_PROCESS_NOT_DEFINED = -122; + /** + * Installation parse return code: system is in a minimal boot state, and the parser only + * allows the package with {@code coreApp} manifest attribute to be a valid application. + * + * @hide + */ + public static final int INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED = -123; + /** @hide */ @IntDef(flag = true, prefix = { "DELETE_" }, value = { DELETE_KEEP_DATA, diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java index 12328cf32fb3..c94d428f4475 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java @@ -22,6 +22,7 @@ import static android.content.pm.PackageManager.FEATURE_WATCH; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK; +import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION; import static android.os.Build.VERSION_CODES.DONUT; import static android.os.Build.VERSION_CODES.O; @@ -229,7 +230,8 @@ public class ParsingPackageUtils { final PackageParser.PackageLite lite = ApkLiteParseUtils.parseClusterPackageLite(packageDir, 0); if (mOnlyCoreApps && !lite.coreApp) { - return input.error("Not a coreApp: " + packageDir); + return input.error(INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED, + "Not a coreApp: " + packageDir); } // Build the split dependency tree. @@ -291,7 +293,8 @@ public class ParsingPackageUtils { final PackageParser.PackageLite lite = ApkLiteParseUtils.parseMonolithicPackageLite(apkFile, flags); if (mOnlyCoreApps && !lite.coreApp) { - return input.error("Not a coreApp: " + apkFile); + return input.error(INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED, + "Not a coreApp: " + apkFile); } final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags); diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java index c42dacc187a9..e9bcefef50c7 100644 --- a/core/java/android/net/NetworkAgent.java +++ b/core/java/android/net/NetworkAgent.java @@ -556,12 +556,12 @@ public abstract class NetworkAgent { @NonNull public Network register() { if (VDBG) log("Registering NetworkAgent"); - final ConnectivityManager cm = (ConnectivityManager) mInitialConfiguration.context - .getSystemService(Context.CONNECTIVITY_SERVICE); synchronized (mRegisterLock) { if (mNetwork != null) { throw new IllegalStateException("Agent already registered"); } + final ConnectivityManager cm = (ConnectivityManager) mInitialConfiguration.context + .getSystemService(Context.CONNECTIVITY_SERVICE); mNetwork = cm.registerNetworkAgent(new Messenger(mHandler), new NetworkInfo(mInitialConfiguration.info), mInitialConfiguration.properties, mInitialConfiguration.capabilities, diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java index 51f01cac8138..653a5594f495 100644 --- a/core/java/android/os/PowerManagerInternal.java +++ b/core/java/android/os/PowerManagerInternal.java @@ -102,9 +102,9 @@ public abstract class PowerManagerInternal { * * This method must only be called by the window manager. * - * @param brightness The overridden brightness, or -1 to disable the override. + * @param brightness The overridden brightness, or Float.NaN to disable the override. */ - public abstract void setScreenBrightnessOverrideFromWindowManager(int brightness); + public abstract void setScreenBrightnessOverrideFromWindowManager(float brightness); /** * Used by the window manager to override the user activity timeout based on the diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index e2d0c49d9d13..920302c75011 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -13757,6 +13757,16 @@ public final class Settings { "show_notification_channel_warnings"; /** + * When enabled, requires all notifications in the conversation section to be backed + * by a long-lived sharing shortcut + * + * The value 1 - require a shortcut, 0 - do not require a shortcut + * @hide + */ + public static final String REQUIRE_SHORTCUTS_FOR_CONVERSATIONS = + "require_shortcuts_for_conversations"; + + /** * Whether cell is enabled/disabled * @hide */ diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java index 1beeb6554ee1..21b83c660446 100644 --- a/core/java/android/service/notification/StatusBarNotification.java +++ b/core/java/android/service/notification/StatusBarNotification.java @@ -17,8 +17,6 @@ package android.service.notification; import static android.app.NotificationChannel.PLACEHOLDER_CONVERSATION_ID; -import static android.util.FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ; -import static android.util.FeatureFlagUtils.isEnabled; import android.annotation.NonNull; import android.app.Notification; @@ -33,6 +31,7 @@ import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.os.UserHandle; +import android.provider.Settings; import android.text.TextUtils; import com.android.internal.logging.InstanceId; @@ -477,9 +476,10 @@ public class StatusBarNotification implements Parcelable { */ public String getShortcutId(Context context) { String conversationId = getNotification().getShortcutId(); - if (isEnabled(context, NOTIF_CONVO_BYPASS_SHORTCUT_REQ) - && getNotification().getNotificationStyle() == Notification.MessagingStyle.class - && TextUtils.isEmpty(conversationId)) { + if (TextUtils.isEmpty(conversationId) + && (Settings.Global.getInt(context.getContentResolver(), + Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 0) == 0) + && getNotification().getNotificationStyle() == Notification.MessagingStyle.class) { conversationId = getId() + getTag() + PLACEHOLDER_CONVERSATION_ID; } return conversationId; diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index faa5cf591408..cd20b357e2f1 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -42,8 +42,6 @@ public class FeatureFlagUtils { public static final String DYNAMIC_SYSTEM = "settings_dynamic_system"; public static final String SETTINGS_WIFITRACKER2 = "settings_wifitracker2"; public static final String SETTINGS_FUSE_FLAG = "settings_fuse"; - public static final String NOTIF_CONVO_BYPASS_SHORTCUT_REQ = - "settings_notif_convo_bypass_shortcut_req"; /** @hide */ public static final String SETTINGS_DO_NOT_RESTORE_PRESERVED = "settings_do_not_restore_preserved"; @@ -66,7 +64,6 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put(SETTINGS_WIFITRACKER2, "true"); DEFAULT_FLAGS.put("settings_controller_loading_enhancement", "false"); DEFAULT_FLAGS.put("settings_conditionals", "false"); - DEFAULT_FLAGS.put(NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "true"); // This flags guards a feature introduced in R and will be removed in the next release // (b/148367230). DEFAULT_FLAGS.put(SETTINGS_DO_NOT_RESTORE_PRESERVED, "true"); diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index b0981730c1eb..b677ccd9a618 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -1037,7 +1037,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall mTmpTransaction.setWindowCrop(mSurfaceControl, mSurfaceWidth, mSurfaceHeight); } - } else if ((layoutSizeChanged || positionChanged) && + } else if ((layoutSizeChanged || positionChanged || visibleChanged) && viewRoot.useBLAST()) { viewRoot.setUseBLASTSyncTransaction(); } diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index dc87453bd867..6a109253a27c 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -1275,7 +1275,14 @@ public final class AccessibilityManager { /** * Register the provided {@link RemoteAction} with the given actionId - * + * <p> + * To perform established system actions, an accessibility service uses the GLOBAL_ACTION + * constants in {@link android.accessibilityservice.AccessibilityService}. To provide a + * customized implementation for one of these actions, the id of the registered system action + * must match that of the corresponding GLOBAL_ACTION constant. For example, to register a + * Back action, {@code actionId} must be + * {@link android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_BACK} + * </p> * @param action The remote action to be registered with the given actionId as system action. * @param actionId The id uniquely identify the system action. * @hide diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java index 08b32930971a..fb962103990c 100644 --- a/core/java/android/widget/Toast.java +++ b/core/java/android/widget/Toast.java @@ -43,7 +43,7 @@ import android.os.ServiceManager; import android.util.Log; import android.view.View; import android.view.WindowManager; -import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.IAccessibilityManager; import com.android.internal.annotations.GuardedBy; @@ -610,10 +610,10 @@ public class Toast { */ TN(Context context, String packageName, Binder token, List<Callback> callbacks, @Nullable Looper looper) { - WindowManager windowManager = context.getSystemService(WindowManager.class); - AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(context); - mPresenter = new ToastPresenter(context, windowManager, accessibilityManager, - getService(), packageName); + IAccessibilityManager accessibilityManager = IAccessibilityManager.Stub.asInterface( + ServiceManager.getService(Context.ACCESSIBILITY_SERVICE)); + mPresenter = new ToastPresenter(context, accessibilityManager, getService(), + packageName); mParams = mPresenter.getLayoutParams(); mPackageName = packageName; mToken = token; diff --git a/core/java/android/widget/ToastPresenter.java b/core/java/android/widget/ToastPresenter.java index e9d4aa668891..2679c69be4f6 100644 --- a/core/java/android/widget/ToastPresenter.java +++ b/core/java/android/widget/ToastPresenter.java @@ -27,6 +27,7 @@ import android.content.res.Resources; import android.graphics.PixelFormat; import android.os.IBinder; import android.os.RemoteException; +import android.os.UserHandle; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; @@ -34,8 +35,10 @@ import android.view.View; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.IAccessibilityManager; import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; /** @@ -49,12 +52,14 @@ public class ToastPresenter { private static final long SHORT_DURATION_TIMEOUT = 4000; private static final long LONG_DURATION_TIMEOUT = 7000; + @VisibleForTesting + public static final int TEXT_TOAST_LAYOUT = R.layout.transient_notification; + /** * Returns the default text toast view for message {@code text}. */ public static View getTextToastView(Context context, CharSequence text) { - View view = LayoutInflater.from(context).inflate( - R.layout.transient_notification, null); + View view = LayoutInflater.from(context).inflate(TEXT_TOAST_LAYOUT, null); TextView textView = view.findViewById(com.android.internal.R.id.message); textView.setText(text); return view; @@ -70,15 +75,23 @@ public class ToastPresenter { @Nullable private View mView; @Nullable private IBinder mToken; - public ToastPresenter(Context context, WindowManager windowManager, - AccessibilityManager accessibilityManager, + public ToastPresenter(Context context, IAccessibilityManager accessibilityManager, INotificationManager notificationManager, String packageName) { mContext = context; mResources = context.getResources(); - mWindowManager = windowManager; - mAccessibilityManager = accessibilityManager; + mWindowManager = context.getSystemService(WindowManager.class); mNotificationManager = notificationManager; mPackageName = packageName; + + // We obtain AccessibilityManager manually via its constructor instead of using method + // AccessibilityManager.getInstance() for 2 reasons: + // 1. We want to be able to inject IAccessibilityManager in tests to verify behavior. + // 2. getInstance() caches the instance for the process even if we pass a different + // context to it. This is problematic for multi-user because callers can pass a context + // created via Context.createContextAsUser(). + mAccessibilityManager = new AccessibilityManager(context, accessibilityManager, + UserHandle.getCallingUserId()); + mParams = createLayoutParams(); } diff --git a/core/java/android/window/TaskOrganizerTaskEmbedder.java b/core/java/android/window/TaskOrganizerTaskEmbedder.java index 39a0101bbf59..2091c9398e95 100644 --- a/core/java/android/window/TaskOrganizerTaskEmbedder.java +++ b/core/java/android/window/TaskOrganizerTaskEmbedder.java @@ -254,9 +254,7 @@ public class TaskOrganizerTaskEmbedder extends TaskEmbedder { mTaskToken = taskInfo.token; mTaskLeash = mTaskToken.getLeash(); mTransaction.reparent(mTaskLeash, mSurfaceControl) - .show(mTaskLeash) - .show(mSurfaceControl) - .apply(); + .show(mSurfaceControl).apply(); if (mPendingNotifyBoundsChanged) { // TODO: Either defer show or hide and synchronize show with the resize notifyBoundsChanged(); diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java index 553721d75fd6..be43e82c3f88 100644 --- a/core/java/com/android/internal/app/AlertController.java +++ b/core/java/com/android/internal/app/AlertController.java @@ -286,6 +286,7 @@ public class AlertController { if (mTitleView != null) { mTitleView.setText(title); } + mWindow.setTitle(title); } /** diff --git a/core/java/com/android/internal/app/ChooserActivityLogger.java b/core/java/com/android/internal/app/ChooserActivityLogger.java index dc482443040a..088973cde3e9 100644 --- a/core/java/com/android/internal/app/ChooserActivityLogger.java +++ b/core/java/com/android/internal/app/ChooserActivityLogger.java @@ -191,6 +191,9 @@ public interface ChooserActivityLogger { * ChooserActivity. */ default int typeFromIntentString(String intent) { + if (intent == null) { + return FrameworkStatsLog.SHARESHEET_STARTED__INTENT_TYPE__INTENT_DEFAULT; + } switch (intent) { case Intent.ACTION_VIEW: return FrameworkStatsLog.SHARESHEET_STARTED__INTENT_TYPE__INTENT_ACTION_VIEW; diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index 5b79b184b6a4..38f5f3279c8e 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -204,8 +204,8 @@ oneway interface IStatusBar /** * Displays a text toast. */ - void showToast(String packageName, IBinder token, CharSequence text, IBinder windowToken, - int duration, @nullable ITransientNotificationCallback callback); + void showToast(int uid, String packageName, IBinder token, CharSequence text, + IBinder windowToken, int duration, @nullable ITransientNotificationCallback callback); /** * Cancels toast with token {@code token} in {@code packageName}. diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java index 523c7493420b..5a979ac97c54 100644 --- a/core/java/com/android/internal/widget/ConversationLayout.java +++ b/core/java/com/android/internal/widget/ConversationLayout.java @@ -121,6 +121,7 @@ public class ConversationLayout extends FrameLayout private CachingIconView mConversationIconBadgeBg; private Icon mLargeIcon; private View mExpandButtonContainer; + private View mExpandButtonInnerContainer; private ViewGroup mExpandButtonAndContentContainer; private NotificationExpandButton mExpandButton; private MessagingLinearLayout mImageMessageContainer; @@ -242,6 +243,7 @@ public class ConversationLayout extends FrameLayout mConversationHeader = findViewById(R.id.conversation_header); mContentContainer = findViewById(R.id.notification_action_list_margin_target); mExpandButtonAndContentContainer = findViewById(R.id.expand_button_and_content_container); + mExpandButtonInnerContainer = findViewById(R.id.expand_button_inner_container); mExpandButton = findViewById(R.id.expand_button); mExpandButtonExpandedTopMargin = getResources().getDimensionPixelSize( R.dimen.conversation_expand_button_top_margin_expanded); @@ -1169,7 +1171,7 @@ public class ConversationLayout extends FrameLayout layoutParams.topMargin = topMargin; mExpandButton.setLayoutParams(layoutParams); - mExpandButtonContainer.setContentDescription(mContext.getText(contentDescriptionId)); + mExpandButtonInnerContainer.setContentDescription(mContext.getText(contentDescriptionId)); } private void updateContentEndPaddings() { @@ -1213,7 +1215,7 @@ public class ConversationLayout extends FrameLayout mExpandable = expandable; if (expandable) { mExpandButtonContainer.setVisibility(VISIBLE); - mExpandButtonContainer.setOnClickListener(onClickListener); + mExpandButtonInnerContainer.setOnClickListener(onClickListener); } else { // TODO: handle content paddings to end of layout mExpandButtonContainer.setVisibility(GONE); diff --git a/core/res/res/layout/notification_template_material_conversation.xml b/core/res/res/layout/notification_template_material_conversation.xml index b9ca29276cf0..e986b1886abf 100644 --- a/core/res/res/layout/notification_template_material_conversation.xml +++ b/core/res/res/layout/notification_template_material_conversation.xml @@ -245,6 +245,7 @@ android:id="@+id/notification_messaging" android:layout_width="match_parent" android:layout_height="wrap_content" + android:minHeight="@dimen/notification_text_size" android:spacing="@dimen/notification_messaging_spacing" android:clipToPadding="false" android:clipChildren="false" @@ -263,53 +264,65 @@ <include layout="@layout/notification_material_action_list" /> </com.android.internal.widget.RemeasuringLinearLayout> - <!--This is dynamically placed between here and at the end of the layout--> - <LinearLayout + <!--This is dynamically placed between here and at the end of the layout. It starts here since + only FrameLayout layout params have gravity--> + <FrameLayout android:id="@+id/expand_button_container" android:layout_width="wrap_content" - android:layout_height="@dimen/conversation_expand_button_size" + android:layout_height="match_parent" android:layout_gravity="end|top" - android:paddingStart="16dp" - android:orientation="horizontal" - android:paddingEnd="@dimen/notification_content_margin_end" - android:clipToPadding="false" android:clipChildren="false" - > - <!-- Images --> - <com.android.internal.widget.MessagingLinearLayout - android:id="@+id/conversation_image_message_container" - android:forceHasOverlappingRendering="false" - android:layout_width="40dp" - android:layout_height="40dp" - android:layout_marginEnd="11dp" - android:spacing="0dp" - android:layout_gravity="center" + android:clipToPadding="false"> + <!--This layout makes sure that we can nicely center the expand content in the + collapsed layout while the parent makes sure that we're never laid out bigger + than the messaging content.--> + <LinearLayout + android:id="@+id/expand_button_inner_container" + android:layout_width="wrap_content" + android:layout_height="@dimen/conversation_expand_button_size" + android:paddingStart="16dp" + android:orientation="horizontal" + android:layout_gravity="end|top" + android:paddingEnd="@dimen/notification_content_margin_end" android:clipToPadding="false" android:clipChildren="false" - /> - <!-- Unread Count --> - <TextView - android:id="@+id/conversation_unread_count" - android:layout_width="33sp" - android:layout_height="wrap_content" - android:layout_marginEnd="11dp" - android:layout_gravity="center" - android:gravity="center" - android:padding="2dp" - android:visibility="gone" - android:textAppearance="@style/TextAppearance.DeviceDefault.Notification" - android:textColor="#FFFFFF" - android:textSize="12sp" - android:background="@drawable/conversation_unread_bg" - /> - <com.android.internal.widget.NotificationExpandButton - android:id="@+id/expand_button" - android:layout_width="@dimen/notification_header_expand_icon_size" - android:layout_height="@dimen/notification_header_expand_icon_size" - android:layout_gravity="center" - android:drawable="@drawable/ic_expand_notification" - android:clickable="false" - android:importantForAccessibility="no" - /> - </LinearLayout> + > + <!-- Images --> + <com.android.internal.widget.MessagingLinearLayout + android:id="@+id/conversation_image_message_container" + android:forceHasOverlappingRendering="false" + android:layout_width="40dp" + android:layout_height="40dp" + android:layout_marginEnd="11dp" + android:spacing="0dp" + android:layout_gravity="center" + android:clipToPadding="false" + android:clipChildren="false" + /> + <!-- Unread Count --> + <TextView + android:id="@+id/conversation_unread_count" + android:layout_width="33sp" + android:layout_height="wrap_content" + android:layout_marginEnd="11dp" + android:layout_gravity="center" + android:gravity="center" + android:padding="2dp" + android:visibility="gone" + android:textAppearance="@style/TextAppearance.DeviceDefault.Notification" + android:textColor="#FFFFFF" + android:textSize="12sp" + android:background="@drawable/conversation_unread_bg" + /> + <com.android.internal.widget.NotificationExpandButton + android:id="@+id/expand_button" + android:layout_width="@dimen/notification_header_expand_icon_size" + android:layout_height="@dimen/notification_header_expand_icon_size" + android:layout_gravity="center" + android:drawable="@drawable/ic_expand_notification" + android:clickable="false" + android:importantForAccessibility="no" + /> + </LinearLayout> + </FrameLayout> </com.android.internal.widget.ConversationLayout> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 11dda41d0b57..ef1e8b74b05f 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3866,6 +3866,7 @@ <java-symbol type="id" name="conversation_icon_badge_ring" /> <java-symbol type="id" name="conversation_icon_badge_bg" /> <java-symbol type="id" name="expand_button_container" /> + <java-symbol type="id" name="expand_button_inner_container" /> <java-symbol type="id" name="messaging_group_content_container" /> <java-symbol type="id" name="expand_button_and_content_container" /> <java-symbol type="id" name="conversation_header" /> diff --git a/core/xsd/vts/Android.bp b/core/xsd/vts/Android.bp index a2a2168c8377..ca655f18149c 100644 --- a/core/xsd/vts/Android.bp +++ b/core/xsd/vts/Android.bp @@ -36,7 +36,7 @@ cc_test { ], test_suites: [ "general-tests", - "vts-core" + "vts" ], test_config: "vts_permission_validate_test.xml", } diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 155eb1cfbd46..c11762bcdb40 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -44,46 +44,56 @@ import java.util.Set; /** * @hide */ +@TestApi public class AudioSystem { private static final boolean DEBUG_VOLUME = false; private static final String TAG = "AudioSystem"; + + // private constructor to prevent instantiating AudioSystem + private AudioSystem() { + throw new UnsupportedOperationException("Trying to instantiate AudioSystem"); + } + /* These values must be kept in sync with system/audio.h */ /* * If these are modified, please also update Settings.System.VOLUME_SETTINGS * and attrs.xml and AudioManager.java. */ - /** Used to identify the default audio stream volume */ + /** @hide Used to identify the default audio stream volume */ + @TestApi public static final int STREAM_DEFAULT = -1; - /** Used to identify the volume of audio streams for phone calls */ + /** @hide Used to identify the volume of audio streams for phone calls */ public static final int STREAM_VOICE_CALL = 0; - /** Used to identify the volume of audio streams for system sounds */ + /** @hide Used to identify the volume of audio streams for system sounds */ public static final int STREAM_SYSTEM = 1; - /** Used to identify the volume of audio streams for the phone ring and message alerts */ + /** @hide Used to identify the volume of audio streams for the phone ring and message alerts */ public static final int STREAM_RING = 2; - /** Used to identify the volume of audio streams for music playback */ + /** @hide Used to identify the volume of audio streams for music playback */ public static final int STREAM_MUSIC = 3; - /** Used to identify the volume of audio streams for alarms */ + /** @hide Used to identify the volume of audio streams for alarms */ public static final int STREAM_ALARM = 4; - /** Used to identify the volume of audio streams for notifications */ + /** @hide Used to identify the volume of audio streams for notifications */ public static final int STREAM_NOTIFICATION = 5; - /** Used to identify the volume of audio streams for phone calls when connected on bluetooth */ + /** @hide + * Used to identify the volume of audio streams for phone calls when connected on bluetooth */ public static final int STREAM_BLUETOOTH_SCO = 6; - /** Used to identify the volume of audio streams for enforced system sounds in certain + /** @hide Used to identify the volume of audio streams for enforced system sounds in certain * countries (e.g camera in Japan) */ @UnsupportedAppUsage public static final int STREAM_SYSTEM_ENFORCED = 7; - /** Used to identify the volume of audio streams for DTMF tones */ + /** @hide Used to identify the volume of audio streams for DTMF tones */ public static final int STREAM_DTMF = 8; - /** Used to identify the volume of audio streams exclusively transmitted through the + /** @hide Used to identify the volume of audio streams exclusively transmitted through the * speaker (TTS) of the device */ public static final int STREAM_TTS = 9; - /** Used to identify the volume of audio streams for accessibility prompts */ + /** @hide Used to identify the volume of audio streams for accessibility prompts */ public static final int STREAM_ACCESSIBILITY = 10; - /** Used to identify the volume of audio streams for virtual assistant */ + /** @hide Used to identify the volume of audio streams for virtual assistant */ public static final int STREAM_ASSISTANT = 11; /** + * @hide * @deprecated Use {@link #numStreamTypes() instead} */ public static final int NUM_STREAMS = 5; @@ -96,9 +106,16 @@ public class AudioSystem // Expose only the getter method publicly so we can change it in the future private static final int NUM_STREAM_TYPES = 12; + + /** + * @hide + * @return total number of stream types + */ @UnsupportedAppUsage + @TestApi public static final int getNumStreamTypes() { return NUM_STREAM_TYPES; } + /** @hide */ public static final String[] STREAM_NAMES = new String[] { "STREAM_VOICE_CALL", "STREAM_SYSTEM", @@ -114,7 +131,8 @@ public class AudioSystem "STREAM_ASSISTANT" }; - /* + /** + * @hide * Sets the microphone mute on or off. * * @param on set <var>true</var> to mute the microphone; @@ -124,7 +142,8 @@ public class AudioSystem @UnsupportedAppUsage public static native int muteMicrophone(boolean on); - /* + /** + * @hide * Checks whether the microphone mute is on or off. * * @return true if microphone is muted, false if it's not @@ -133,15 +152,24 @@ public class AudioSystem public static native boolean isMicrophoneMuted(); /* modes for setPhoneState, must match AudioSystem.h audio_mode */ + /** @hide */ public static final int MODE_INVALID = -2; + /** @hide */ public static final int MODE_CURRENT = -1; + /** @hide */ public static final int MODE_NORMAL = 0; + /** @hide */ public static final int MODE_RINGTONE = 1; + /** @hide */ public static final int MODE_IN_CALL = 2; + /** @hide */ public static final int MODE_IN_COMMUNICATION = 3; + /** @hide */ public static final int MODE_CALL_SCREENING = 4; + /** @hide */ public static final int NUM_MODES = 5; + /** @hide */ public static String modeToString(int mode) { switch (mode) { case MODE_CURRENT: return "MODE_CURRENT"; @@ -156,15 +184,23 @@ public class AudioSystem } /* Formats for A2DP codecs, must match system/audio-base.h audio_format_t */ + /** @hide */ public static final int AUDIO_FORMAT_INVALID = 0xFFFFFFFF; + /** @hide */ public static final int AUDIO_FORMAT_DEFAULT = 0; + /** @hide */ public static final int AUDIO_FORMAT_AAC = 0x04000000; + /** @hide */ public static final int AUDIO_FORMAT_SBC = 0x1F000000; + /** @hide */ public static final int AUDIO_FORMAT_APTX = 0x20000000; + /** @hide */ public static final int AUDIO_FORMAT_APTX_HD = 0x21000000; + /** @hide */ public static final int AUDIO_FORMAT_LDAC = 0x23000000; /** + * @hide * Convert audio format enum values to Bluetooth codec values */ public static int audioFormatToBluetoothSourceCodec(int audioFormat) { @@ -179,25 +215,27 @@ public class AudioSystem } /* Routing bits for the former setRouting/getRouting API */ - /** @deprecated */ + /** @hide @deprecated */ @Deprecated public static final int ROUTE_EARPIECE = (1 << 0); - /** @deprecated */ + /** @hide @deprecated */ @Deprecated public static final int ROUTE_SPEAKER = (1 << 1); - /** @deprecated use {@link #ROUTE_BLUETOOTH_SCO} */ + /** @hide @deprecated use {@link #ROUTE_BLUETOOTH_SCO} */ @Deprecated public static final int ROUTE_BLUETOOTH = (1 << 2); - /** @deprecated */ + /** @hide @deprecated */ @Deprecated public static final int ROUTE_BLUETOOTH_SCO = (1 << 2); - /** @deprecated */ + /** @hide @deprecated */ @Deprecated public static final int ROUTE_HEADSET = (1 << 3); - /** @deprecated */ + /** @hide @deprecated */ @Deprecated public static final int ROUTE_BLUETOOTH_A2DP = (1 << 4); - /** @deprecated */ + /** @hide @deprecated */ @Deprecated public static final int ROUTE_ALL = 0xFFFFFFFF; // Keep in sync with system/media/audio/include/system/audio.h + /** @hide */ public static final int AUDIO_SESSION_ALLOCATE = 0; - /* + /** + * @hide * Checks whether the specified stream type is active. * * return true if any track playing on this stream is active. @@ -205,7 +243,8 @@ public class AudioSystem @UnsupportedAppUsage public static native boolean isStreamActive(int stream, int inPastMs); - /* + /** + * @hide * Checks whether the specified stream type is active on a remotely connected device. The notion * of what constitutes a remote device is enforced by the audio policy manager of the platform. * @@ -213,7 +252,8 @@ public class AudioSystem */ public static native boolean isStreamActiveRemotely(int stream, int inPastMs); - /* + /** + * @hide * Checks whether the specified audio source is active. * * return true if any recorder using this source is currently recording @@ -221,23 +261,27 @@ public class AudioSystem @UnsupportedAppUsage public static native boolean isSourceActive(int source); - /* + /** + * @hide * Returns a new unused audio session ID */ public static native int newAudioSessionId(); - /* + /** + * @hide * Returns a new unused audio player ID */ public static native int newAudioPlayerId(); /** + * @hide * Returns a new unused audio recorder ID */ public static native int newAudioRecorderId(); - /* + /** + * @hide * Sets a group generic audio configuration parameters. The use of these parameters * are platform dependent, see libaudio * @@ -247,7 +291,8 @@ public class AudioSystem @UnsupportedAppUsage public static native int setParameters(String keyValuePairs); - /* + /** + * @hide * Gets a group generic audio configuration parameters. The use of these parameters * are platform dependent, see libaudio * @@ -259,16 +304,16 @@ public class AudioSystem public static native String getParameters(String keys); // These match the enum AudioError in frameworks/base/core/jni/android_media_AudioSystem.cpp - /* Command sucessful or Media server restarted. see ErrorCallback */ + /** @hide Command successful or Media server restarted. see ErrorCallback */ public static final int AUDIO_STATUS_OK = 0; - /* Command failed or unspecified audio error. see ErrorCallback */ + /** @hide Command failed or unspecified audio error. see ErrorCallback */ public static final int AUDIO_STATUS_ERROR = 1; - /* Media server died. see ErrorCallback */ + /** @hide Media server died. see ErrorCallback */ public static final int AUDIO_STATUS_SERVER_DIED = 100; - private static ErrorCallback mErrorCallback; + private static ErrorCallback sErrorCallback; - /* + /** @hide * Handles the audio error callback. */ public interface ErrorCallback @@ -283,7 +328,8 @@ public class AudioSystem void onError(int error); }; - /* + /** + * @hide * Registers a callback to be invoked when an error occurs. * @param cb the callback to run */ @@ -291,7 +337,7 @@ public class AudioSystem public static void setErrorCallback(ErrorCallback cb) { synchronized (AudioSystem.class) { - mErrorCallback = cb; + sErrorCallback = cb; if (cb != null) { cb.onError(checkAudioFlinger()); } @@ -303,8 +349,8 @@ public class AudioSystem { ErrorCallback errorCallback = null; synchronized (AudioSystem.class) { - if (mErrorCallback != null) { - errorCallback = mErrorCallback; + if (sErrorCallback != null) { + errorCallback = sErrorCallback; } } if (errorCallback != null) { @@ -313,6 +359,7 @@ public class AudioSystem } /** + * @hide * Handles events from the audio policy manager about dynamic audio policies * @see android.media.audiopolicy.AudioPolicy */ @@ -326,6 +373,7 @@ public class AudioSystem private static DynamicPolicyCallback sDynPolicyCallback; + /** @hide */ public static void setDynamicPolicyCallback(DynamicPolicyCallback cb) { synchronized (AudioSystem.class) { @@ -355,6 +403,7 @@ public class AudioSystem } /** + * @hide * Handles events from the audio policy manager about recording events * @see android.media.AudioManager.AudioRecordingCallback */ @@ -386,6 +435,7 @@ public class AudioSystem private static AudioRecordingCallback sRecordingCallback; + /** @hide */ public static void setRecordingCallback(AudioRecordingCallback cb) { synchronized (AudioSystem.class) { sRecordingCallback = cb; @@ -434,13 +484,21 @@ public class AudioSystem * Error codes used by public APIs (AudioTrack, AudioRecord, AudioManager ...) * Must be kept in sync with frameworks/base/core/jni/android_media_AudioErrors.h */ + /** @hide */ public static final int SUCCESS = 0; + /** @hide */ public static final int ERROR = -1; + /** @hide */ public static final int BAD_VALUE = -2; + /** @hide */ public static final int INVALID_OPERATION = -3; + /** @hide */ public static final int PERMISSION_DENIED = -4; + /** @hide */ public static final int NO_INIT = -5; + /** @hide */ public static final int DEAD_OBJECT = -6; + /** @hide */ public static final int WOULD_BLOCK = -7; /** @hide */ @@ -458,6 +516,7 @@ public class AudioSystem public @interface AudioSystemError {} /** + * @hide * Convert an int error value to its String value for readability. * Accepted error values are the java AudioSystem errors, matching android_media_AudioErrors.h, * which map onto the native status_t type. @@ -494,74 +553,113 @@ public class AudioSystem // // audio device definitions: must be kept in sync with values in system/core/audio.h // - + /** @hide */ public static final int DEVICE_NONE = 0x0; // reserved bits + /** @hide */ public static final int DEVICE_BIT_IN = 0x80000000; + /** @hide */ public static final int DEVICE_BIT_DEFAULT = 0x40000000; // output devices, be sure to update AudioManager.java also + /** @hide */ @UnsupportedAppUsage public static final int DEVICE_OUT_EARPIECE = 0x1; + /** @hide */ @UnsupportedAppUsage public static final int DEVICE_OUT_SPEAKER = 0x2; + /** @hide */ @UnsupportedAppUsage public static final int DEVICE_OUT_WIRED_HEADSET = 0x4; + /** @hide */ @UnsupportedAppUsage public static final int DEVICE_OUT_WIRED_HEADPHONE = 0x8; + /** @hide */ @UnsupportedAppUsage public static final int DEVICE_OUT_BLUETOOTH_SCO = 0x10; + /** @hide */ @UnsupportedAppUsage public static final int DEVICE_OUT_BLUETOOTH_SCO_HEADSET = 0x20; + /** @hide */ @UnsupportedAppUsage public static final int DEVICE_OUT_BLUETOOTH_SCO_CARKIT = 0x40; + /** @hide */ @UnsupportedAppUsage public static final int DEVICE_OUT_BLUETOOTH_A2DP = 0x80; + /** @hide */ @UnsupportedAppUsage public static final int DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100; + /** @hide */ @UnsupportedAppUsage public static final int DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200; + /** @hide */ @UnsupportedAppUsage public static final int DEVICE_OUT_AUX_DIGITAL = 0x400; + /** @hide */ public static final int DEVICE_OUT_HDMI = DEVICE_OUT_AUX_DIGITAL; + /** @hide */ @UnsupportedAppUsage public static final int DEVICE_OUT_ANLG_DOCK_HEADSET = 0x800; + /** @hide */ @UnsupportedAppUsage public static final int DEVICE_OUT_DGTL_DOCK_HEADSET = 0x1000; + /** @hide */ @UnsupportedAppUsage public static final int DEVICE_OUT_USB_ACCESSORY = 0x2000; + /** @hide */ @UnsupportedAppUsage public static final int DEVICE_OUT_USB_DEVICE = 0x4000; + /** @hide */ @UnsupportedAppUsage public static final int DEVICE_OUT_REMOTE_SUBMIX = 0x8000; + /** @hide */ @UnsupportedAppUsage public static final int DEVICE_OUT_TELEPHONY_TX = 0x10000; + /** @hide */ public static final int DEVICE_OUT_LINE = 0x20000; + /** @hide */ public static final int DEVICE_OUT_HDMI_ARC = 0x40000; + /** @hide */ public static final int DEVICE_OUT_SPDIF = 0x80000; + /** @hide */ @UnsupportedAppUsage public static final int DEVICE_OUT_FM = 0x100000; + /** @hide */ public static final int DEVICE_OUT_AUX_LINE = 0x200000; + /** @hide */ public static final int DEVICE_OUT_SPEAKER_SAFE = 0x400000; + /** @hide */ public static final int DEVICE_OUT_IP = 0x800000; + /** @hide */ public static final int DEVICE_OUT_BUS = 0x1000000; + /** @hide */ public static final int DEVICE_OUT_PROXY = 0x2000000; + /** @hide */ public static final int DEVICE_OUT_USB_HEADSET = 0x4000000; + /** @hide */ public static final int DEVICE_OUT_HEARING_AID = 0x8000000; + /** @hide */ public static final int DEVICE_OUT_DEFAULT = DEVICE_BIT_DEFAULT; // Deprecated in R because multiple device types are no longer accessed as a bit mask. // Removing this will get lint warning about changing hidden apis. + /** @hide */ @UnsupportedAppUsage public static final int DEVICE_OUT_ALL_USB = (DEVICE_OUT_USB_ACCESSORY | DEVICE_OUT_USB_DEVICE | DEVICE_OUT_USB_HEADSET); + /** @hide */ public static final Set<Integer> DEVICE_OUT_ALL_SET; + /** @hide */ public static final Set<Integer> DEVICE_OUT_ALL_A2DP_SET; + /** @hide */ public static final Set<Integer> DEVICE_OUT_ALL_SCO_SET; + /** @hide */ public static final Set<Integer> DEVICE_OUT_ALL_USB_SET; + /** @hide */ public static final Set<Integer> DEVICE_OUT_ALL_HDMI_SYSTEM_AUDIO_SET; + /** @hide */ public static final Set<Integer> DEVICE_ALL_HDMI_SYSTEM_AUDIO_AND_SPEAKER_SET; static { DEVICE_OUT_ALL_SET = new HashSet<>(); @@ -621,53 +719,85 @@ public class AudioSystem } // input devices + /** @hide */ @UnsupportedAppUsage public static final int DEVICE_IN_COMMUNICATION = DEVICE_BIT_IN | 0x1; + /** @hide */ @UnsupportedAppUsage public static final int DEVICE_IN_AMBIENT = DEVICE_BIT_IN | 0x2; + /** @hide */ @UnsupportedAppUsage public static final int DEVICE_IN_BUILTIN_MIC = DEVICE_BIT_IN | 0x4; + /** @hide */ @UnsupportedAppUsage public static final int DEVICE_IN_BLUETOOTH_SCO_HEADSET = DEVICE_BIT_IN | 0x8; + /** @hide */ @UnsupportedAppUsage public static final int DEVICE_IN_WIRED_HEADSET = DEVICE_BIT_IN | 0x10; + /** @hide */ @UnsupportedAppUsage public static final int DEVICE_IN_AUX_DIGITAL = DEVICE_BIT_IN | 0x20; + /** @hide */ public static final int DEVICE_IN_HDMI = DEVICE_IN_AUX_DIGITAL; + /** @hide */ @UnsupportedAppUsage public static final int DEVICE_IN_VOICE_CALL = DEVICE_BIT_IN | 0x40; + /** @hide */ public static final int DEVICE_IN_TELEPHONY_RX = DEVICE_IN_VOICE_CALL; + /** @hide */ @UnsupportedAppUsage public static final int DEVICE_IN_BACK_MIC = DEVICE_BIT_IN | 0x80; + /** @hide */ @UnsupportedAppUsage public static final int DEVICE_IN_REMOTE_SUBMIX = DEVICE_BIT_IN | 0x100; + /** @hide */ @UnsupportedAppUsage public static final int DEVICE_IN_ANLG_DOCK_HEADSET = DEVICE_BIT_IN | 0x200; + /** @hide */ @UnsupportedAppUsage public static final int DEVICE_IN_DGTL_DOCK_HEADSET = DEVICE_BIT_IN | 0x400; + /** @hide */ @UnsupportedAppUsage public static final int DEVICE_IN_USB_ACCESSORY = DEVICE_BIT_IN | 0x800; + /** @hide */ @UnsupportedAppUsage public static final int DEVICE_IN_USB_DEVICE = DEVICE_BIT_IN | 0x1000; + /** @hide */ public static final int DEVICE_IN_FM_TUNER = DEVICE_BIT_IN | 0x2000; + /** @hide */ public static final int DEVICE_IN_TV_TUNER = DEVICE_BIT_IN | 0x4000; + /** @hide */ public static final int DEVICE_IN_LINE = DEVICE_BIT_IN | 0x8000; + /** @hide */ public static final int DEVICE_IN_SPDIF = DEVICE_BIT_IN | 0x10000; + /** @hide */ @UnsupportedAppUsage public static final int DEVICE_IN_BLUETOOTH_A2DP = DEVICE_BIT_IN | 0x20000; + /** @hide */ public static final int DEVICE_IN_LOOPBACK = DEVICE_BIT_IN | 0x40000; + /** @hide */ public static final int DEVICE_IN_IP = DEVICE_BIT_IN | 0x80000; + /** @hide */ public static final int DEVICE_IN_BUS = DEVICE_BIT_IN | 0x100000; + /** @hide */ public static final int DEVICE_IN_PROXY = DEVICE_BIT_IN | 0x1000000; + /** @hide */ public static final int DEVICE_IN_USB_HEADSET = DEVICE_BIT_IN | 0x2000000; + /** @hide */ public static final int DEVICE_IN_BLUETOOTH_BLE = DEVICE_BIT_IN | 0x4000000; + /** @hide */ public static final int DEVICE_IN_HDMI_ARC = DEVICE_BIT_IN | 0x8000000; + /** @hide */ public static final int DEVICE_IN_ECHO_REFERENCE = DEVICE_BIT_IN | 0x10000000; + /** @hide */ @UnsupportedAppUsage public static final int DEVICE_IN_DEFAULT = DEVICE_BIT_IN | DEVICE_BIT_DEFAULT; + /** @hide */ public static final Set<Integer> DEVICE_IN_ALL_SET; + /** @hide */ public static final Set<Integer> DEVICE_IN_ALL_SCO_SET; + /** @hide */ public static final Set<Integer> DEVICE_IN_ALL_USB_SET; static { DEVICE_IN_ALL_SET = new HashSet<>(); @@ -708,15 +838,19 @@ public class AudioSystem DEVICE_IN_ALL_USB_SET.add(DEVICE_IN_USB_HEADSET); } + /** @hide */ public static final String LEGACY_REMOTE_SUBMIX_ADDRESS = "0"; // device states, must match AudioSystem::device_connection_state + /** @hide */ @UnsupportedAppUsage public static final int DEVICE_STATE_UNAVAILABLE = 0; + /** @hide */ @UnsupportedAppUsage public static final int DEVICE_STATE_AVAILABLE = 1; private static final int NUM_DEVICE_STATES = 1; + /** @hide */ public static String deviceStateToString(int state) { switch (state) { case DEVICE_STATE_UNAVAILABLE: return "DEVICE_STATE_UNAVAILABLE"; @@ -725,63 +859,65 @@ public class AudioSystem } } - public static final String DEVICE_OUT_EARPIECE_NAME = "earpiece"; - public static final String DEVICE_OUT_SPEAKER_NAME = "speaker"; - public static final String DEVICE_OUT_WIRED_HEADSET_NAME = "headset"; - public static final String DEVICE_OUT_WIRED_HEADPHONE_NAME = "headphone"; - public static final String DEVICE_OUT_BLUETOOTH_SCO_NAME = "bt_sco"; - public static final String DEVICE_OUT_BLUETOOTH_SCO_HEADSET_NAME = "bt_sco_hs"; - public static final String DEVICE_OUT_BLUETOOTH_SCO_CARKIT_NAME = "bt_sco_carkit"; - public static final String DEVICE_OUT_BLUETOOTH_A2DP_NAME = "bt_a2dp"; + /** @hide */ public static final String DEVICE_OUT_EARPIECE_NAME = "earpiece"; + /** @hide */ public static final String DEVICE_OUT_SPEAKER_NAME = "speaker"; + /** @hide */ public static final String DEVICE_OUT_WIRED_HEADSET_NAME = "headset"; + /** @hide */ public static final String DEVICE_OUT_WIRED_HEADPHONE_NAME = "headphone"; + /** @hide */ public static final String DEVICE_OUT_BLUETOOTH_SCO_NAME = "bt_sco"; + /** @hide */ public static final String DEVICE_OUT_BLUETOOTH_SCO_HEADSET_NAME = "bt_sco_hs"; + /** @hide */ public static final String DEVICE_OUT_BLUETOOTH_SCO_CARKIT_NAME = "bt_sco_carkit"; + /** @hide */ public static final String DEVICE_OUT_BLUETOOTH_A2DP_NAME = "bt_a2dp"; + /** @hide */ public static final String DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES_NAME = "bt_a2dp_hp"; - public static final String DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER_NAME = "bt_a2dp_spk"; - public static final String DEVICE_OUT_AUX_DIGITAL_NAME = "aux_digital"; - public static final String DEVICE_OUT_HDMI_NAME = "hdmi"; - public static final String DEVICE_OUT_ANLG_DOCK_HEADSET_NAME = "analog_dock"; - public static final String DEVICE_OUT_DGTL_DOCK_HEADSET_NAME = "digital_dock"; - public static final String DEVICE_OUT_USB_ACCESSORY_NAME = "usb_accessory"; - public static final String DEVICE_OUT_USB_DEVICE_NAME = "usb_device"; - public static final String DEVICE_OUT_REMOTE_SUBMIX_NAME = "remote_submix"; - public static final String DEVICE_OUT_TELEPHONY_TX_NAME = "telephony_tx"; - public static final String DEVICE_OUT_LINE_NAME = "line"; - public static final String DEVICE_OUT_HDMI_ARC_NAME = "hmdi_arc"; - public static final String DEVICE_OUT_SPDIF_NAME = "spdif"; - public static final String DEVICE_OUT_FM_NAME = "fm_transmitter"; - public static final String DEVICE_OUT_AUX_LINE_NAME = "aux_line"; - public static final String DEVICE_OUT_SPEAKER_SAFE_NAME = "speaker_safe"; - public static final String DEVICE_OUT_IP_NAME = "ip"; - public static final String DEVICE_OUT_BUS_NAME = "bus"; - public static final String DEVICE_OUT_PROXY_NAME = "proxy"; - public static final String DEVICE_OUT_USB_HEADSET_NAME = "usb_headset"; - public static final String DEVICE_OUT_HEARING_AID_NAME = "hearing_aid_out"; - - public static final String DEVICE_IN_COMMUNICATION_NAME = "communication"; - public static final String DEVICE_IN_AMBIENT_NAME = "ambient"; - public static final String DEVICE_IN_BUILTIN_MIC_NAME = "mic"; - public static final String DEVICE_IN_BLUETOOTH_SCO_HEADSET_NAME = "bt_sco_hs"; - public static final String DEVICE_IN_WIRED_HEADSET_NAME = "headset"; - public static final String DEVICE_IN_AUX_DIGITAL_NAME = "aux_digital"; - public static final String DEVICE_IN_TELEPHONY_RX_NAME = "telephony_rx"; - public static final String DEVICE_IN_BACK_MIC_NAME = "back_mic"; - public static final String DEVICE_IN_REMOTE_SUBMIX_NAME = "remote_submix"; - public static final String DEVICE_IN_ANLG_DOCK_HEADSET_NAME = "analog_dock"; - public static final String DEVICE_IN_DGTL_DOCK_HEADSET_NAME = "digital_dock"; - public static final String DEVICE_IN_USB_ACCESSORY_NAME = "usb_accessory"; - public static final String DEVICE_IN_USB_DEVICE_NAME = "usb_device"; - public static final String DEVICE_IN_FM_TUNER_NAME = "fm_tuner"; - public static final String DEVICE_IN_TV_TUNER_NAME = "tv_tuner"; - public static final String DEVICE_IN_LINE_NAME = "line"; - public static final String DEVICE_IN_SPDIF_NAME = "spdif"; - public static final String DEVICE_IN_BLUETOOTH_A2DP_NAME = "bt_a2dp"; - public static final String DEVICE_IN_LOOPBACK_NAME = "loopback"; - public static final String DEVICE_IN_IP_NAME = "ip"; - public static final String DEVICE_IN_BUS_NAME = "bus"; - public static final String DEVICE_IN_PROXY_NAME = "proxy"; - public static final String DEVICE_IN_USB_HEADSET_NAME = "usb_headset"; - public static final String DEVICE_IN_BLUETOOTH_BLE_NAME = "bt_ble"; - public static final String DEVICE_IN_ECHO_REFERENCE_NAME = "echo_reference"; - public static final String DEVICE_IN_HDMI_ARC_NAME = "hdmi_arc"; + /** @hide */ public static final String DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER_NAME = "bt_a2dp_spk"; + /** @hide */ public static final String DEVICE_OUT_AUX_DIGITAL_NAME = "aux_digital"; + /** @hide */ public static final String DEVICE_OUT_HDMI_NAME = "hdmi"; + /** @hide */ public static final String DEVICE_OUT_ANLG_DOCK_HEADSET_NAME = "analog_dock"; + /** @hide */ public static final String DEVICE_OUT_DGTL_DOCK_HEADSET_NAME = "digital_dock"; + /** @hide */ public static final String DEVICE_OUT_USB_ACCESSORY_NAME = "usb_accessory"; + /** @hide */ public static final String DEVICE_OUT_USB_DEVICE_NAME = "usb_device"; + /** @hide */ public static final String DEVICE_OUT_REMOTE_SUBMIX_NAME = "remote_submix"; + /** @hide */ public static final String DEVICE_OUT_TELEPHONY_TX_NAME = "telephony_tx"; + /** @hide */ public static final String DEVICE_OUT_LINE_NAME = "line"; + /** @hide */ public static final String DEVICE_OUT_HDMI_ARC_NAME = "hmdi_arc"; + /** @hide */ public static final String DEVICE_OUT_SPDIF_NAME = "spdif"; + /** @hide */ public static final String DEVICE_OUT_FM_NAME = "fm_transmitter"; + /** @hide */ public static final String DEVICE_OUT_AUX_LINE_NAME = "aux_line"; + /** @hide */ public static final String DEVICE_OUT_SPEAKER_SAFE_NAME = "speaker_safe"; + /** @hide */ public static final String DEVICE_OUT_IP_NAME = "ip"; + /** @hide */ public static final String DEVICE_OUT_BUS_NAME = "bus"; + /** @hide */ public static final String DEVICE_OUT_PROXY_NAME = "proxy"; + /** @hide */ public static final String DEVICE_OUT_USB_HEADSET_NAME = "usb_headset"; + /** @hide */ public static final String DEVICE_OUT_HEARING_AID_NAME = "hearing_aid_out"; + + /** @hide */ public static final String DEVICE_IN_COMMUNICATION_NAME = "communication"; + /** @hide */ public static final String DEVICE_IN_AMBIENT_NAME = "ambient"; + /** @hide */ public static final String DEVICE_IN_BUILTIN_MIC_NAME = "mic"; + /** @hide */ public static final String DEVICE_IN_BLUETOOTH_SCO_HEADSET_NAME = "bt_sco_hs"; + /** @hide */ public static final String DEVICE_IN_WIRED_HEADSET_NAME = "headset"; + /** @hide */ public static final String DEVICE_IN_AUX_DIGITAL_NAME = "aux_digital"; + /** @hide */ public static final String DEVICE_IN_TELEPHONY_RX_NAME = "telephony_rx"; + /** @hide */ public static final String DEVICE_IN_BACK_MIC_NAME = "back_mic"; + /** @hide */ public static final String DEVICE_IN_REMOTE_SUBMIX_NAME = "remote_submix"; + /** @hide */ public static final String DEVICE_IN_ANLG_DOCK_HEADSET_NAME = "analog_dock"; + /** @hide */ public static final String DEVICE_IN_DGTL_DOCK_HEADSET_NAME = "digital_dock"; + /** @hide */ public static final String DEVICE_IN_USB_ACCESSORY_NAME = "usb_accessory"; + /** @hide */ public static final String DEVICE_IN_USB_DEVICE_NAME = "usb_device"; + /** @hide */ public static final String DEVICE_IN_FM_TUNER_NAME = "fm_tuner"; + /** @hide */ public static final String DEVICE_IN_TV_TUNER_NAME = "tv_tuner"; + /** @hide */ public static final String DEVICE_IN_LINE_NAME = "line"; + /** @hide */ public static final String DEVICE_IN_SPDIF_NAME = "spdif"; + /** @hide */ public static final String DEVICE_IN_BLUETOOTH_A2DP_NAME = "bt_a2dp"; + /** @hide */ public static final String DEVICE_IN_LOOPBACK_NAME = "loopback"; + /** @hide */ public static final String DEVICE_IN_IP_NAME = "ip"; + /** @hide */ public static final String DEVICE_IN_BUS_NAME = "bus"; + /** @hide */ public static final String DEVICE_IN_PROXY_NAME = "proxy"; + /** @hide */ public static final String DEVICE_IN_USB_HEADSET_NAME = "usb_headset"; + /** @hide */ public static final String DEVICE_IN_BLUETOOTH_BLE_NAME = "bt_ble"; + /** @hide */ public static final String DEVICE_IN_ECHO_REFERENCE_NAME = "echo_reference"; + /** @hide */ public static final String DEVICE_IN_HDMI_ARC_NAME = "hdmi_arc"; + /** @hide */ @UnsupportedAppUsage public static String getOutputDeviceName(int device) { @@ -848,6 +984,7 @@ public class AudioSystem } } + /** @hide */ public static String getInputDeviceName(int device) { switch(device) { @@ -910,6 +1047,7 @@ public class AudioSystem } /** + * @hide * Returns a human readable name for a given device type * @param device a native device type, NOT an AudioDeviceInfo type * @return a string describing the device type @@ -922,35 +1060,31 @@ public class AudioSystem } // phone state, match audio_mode??? - public static final int PHONE_STATE_OFFCALL = 0; - public static final int PHONE_STATE_RINGING = 1; - public static final int PHONE_STATE_INCALL = 2; + /** @hide */ public static final int PHONE_STATE_OFFCALL = 0; + /** @hide */ public static final int PHONE_STATE_RINGING = 1; + /** @hide */ public static final int PHONE_STATE_INCALL = 2; // device categories config for setForceUse, must match audio_policy_forced_cfg_t - @UnsupportedAppUsage - public static final int FORCE_NONE = 0; - public static final int FORCE_SPEAKER = 1; - public static final int FORCE_HEADPHONES = 2; - public static final int FORCE_BT_SCO = 3; - public static final int FORCE_BT_A2DP = 4; - public static final int FORCE_WIRED_ACCESSORY = 5; - @UnsupportedAppUsage - public static final int FORCE_BT_CAR_DOCK = 6; - @UnsupportedAppUsage - public static final int FORCE_BT_DESK_DOCK = 7; - @UnsupportedAppUsage - public static final int FORCE_ANALOG_DOCK = 8; - @UnsupportedAppUsage - public static final int FORCE_DIGITAL_DOCK = 9; - public static final int FORCE_NO_BT_A2DP = 10; - public static final int FORCE_SYSTEM_ENFORCED = 11; - public static final int FORCE_HDMI_SYSTEM_AUDIO_ENFORCED = 12; - public static final int FORCE_ENCODED_SURROUND_NEVER = 13; - public static final int FORCE_ENCODED_SURROUND_ALWAYS = 14; - public static final int FORCE_ENCODED_SURROUND_MANUAL = 15; - public static final int NUM_FORCE_CONFIG = 16; - public static final int FORCE_DEFAULT = FORCE_NONE; + /** @hide */ @UnsupportedAppUsage public static final int FORCE_NONE = 0; + /** @hide */ public static final int FORCE_SPEAKER = 1; + /** @hide */ public static final int FORCE_HEADPHONES = 2; + /** @hide */ public static final int FORCE_BT_SCO = 3; + /** @hide */ public static final int FORCE_BT_A2DP = 4; + /** @hide */ public static final int FORCE_WIRED_ACCESSORY = 5; + /** @hide */ @UnsupportedAppUsage public static final int FORCE_BT_CAR_DOCK = 6; + /** @hide */ @UnsupportedAppUsage public static final int FORCE_BT_DESK_DOCK = 7; + /** @hide */ @UnsupportedAppUsage public static final int FORCE_ANALOG_DOCK = 8; + /** @hide */ @UnsupportedAppUsage public static final int FORCE_DIGITAL_DOCK = 9; + /** @hide */ public static final int FORCE_NO_BT_A2DP = 10; + /** @hide */ public static final int FORCE_SYSTEM_ENFORCED = 11; + /** @hide */ public static final int FORCE_HDMI_SYSTEM_AUDIO_ENFORCED = 12; + /** @hide */ public static final int FORCE_ENCODED_SURROUND_NEVER = 13; + /** @hide */ public static final int FORCE_ENCODED_SURROUND_ALWAYS = 14; + /** @hide */ public static final int FORCE_ENCODED_SURROUND_MANUAL = 15; + /** @hide */ public static final int NUM_FORCE_CONFIG = 16; + /** @hide */ public static final int FORCE_DEFAULT = FORCE_NONE; + /** @hide */ public static String forceUseConfigToString(int config) { switch (config) { case FORCE_NONE: return "FORCE_NONE"; @@ -974,16 +1108,17 @@ public class AudioSystem } // usage for setForceUse, must match audio_policy_force_use_t - public static final int FOR_COMMUNICATION = 0; - public static final int FOR_MEDIA = 1; - public static final int FOR_RECORD = 2; - public static final int FOR_DOCK = 3; - public static final int FOR_SYSTEM = 4; - public static final int FOR_HDMI_SYSTEM_AUDIO = 5; - public static final int FOR_ENCODED_SURROUND = 6; - public static final int FOR_VIBRATE_RINGING = 7; + /** @hide */ public static final int FOR_COMMUNICATION = 0; + /** @hide */ public static final int FOR_MEDIA = 1; + /** @hide */ public static final int FOR_RECORD = 2; + /** @hide */ public static final int FOR_DOCK = 3; + /** @hide */ public static final int FOR_SYSTEM = 4; + /** @hide */ public static final int FOR_HDMI_SYSTEM_AUDIO = 5; + /** @hide */ public static final int FOR_ENCODED_SURROUND = 6; + /** @hide */ public static final int FOR_VIBRATE_RINGING = 7; private static final int NUM_FORCE_USE = 8; + /** @hide */ public static String forceUseUsageToString(int usage) { switch (usage) { case FOR_COMMUNICATION: return "FOR_COMMUNICATION"; @@ -998,7 +1133,7 @@ public class AudioSystem } } - /** Wrapper for native methods called from AudioService */ + /** @hide Wrapper for native methods called from AudioService */ public static int setStreamVolumeIndexAS(int stream, int index, int device) { if (DEBUG_VOLUME) { Log.i(TAG, "setStreamVolumeIndex: " + STREAM_NAMES[stream] @@ -1008,10 +1143,11 @@ public class AudioSystem } // usage for AudioRecord.startRecordingSync(), must match AudioSystem::sync_event_t - public static final int SYNC_EVENT_NONE = 0; - public static final int SYNC_EVENT_PRESENTATION_COMPLETE = 1; + /** @hide */ public static final int SYNC_EVENT_NONE = 0; + /** @hide */ public static final int SYNC_EVENT_PRESENTATION_COMPLETE = 1; /** + * @hide * @return command completion status, one of {@link #AUDIO_STATUS_OK}, * {@link #AUDIO_STATUS_ERROR} or {@link #AUDIO_STATUS_SERVER_DIED} */ @@ -1019,12 +1155,15 @@ public class AudioSystem public static native int setDeviceConnectionState(int device, int state, String device_address, String device_name, int codecFormat); + /** @hide */ @UnsupportedAppUsage public static native int getDeviceConnectionState(int device, String device_address); + /** @hide */ public static native int handleDeviceConfigChange(int device, String device_address, String device_name, int codecFormat); + /** @hide */ @UnsupportedAppUsage public static int setPhoneState(int state) { Log.w(TAG, "Do not use this method! Use AudioManager.setMode() instead."); @@ -1038,14 +1177,18 @@ public class AudioSystem * @return command completion status. */ public static native int setPhoneState(int state, int uid); + /** @hide */ @UnsupportedAppUsage public static native int setForceUse(int usage, int config); + /** @hide */ @UnsupportedAppUsage public static native int getForceUse(int usage); + /** @hide */ @UnsupportedAppUsage public static native int initStreamVolume(int stream, int indexMin, int indexMax); @UnsupportedAppUsage private static native int setStreamVolumeIndex(int stream, int index, int device); + /** @hide */ public static native int getStreamVolumeIndex(int stream, int device); /** * @hide @@ -1082,16 +1225,22 @@ public class AudioSystem */ public static native int getMaxVolumeIndexForAttributes(@NonNull AudioAttributes attributes); + /** @hide */ public static native int setMasterVolume(float value); + /** @hide */ public static native float getMasterVolume(); + /** @hide */ @UnsupportedAppUsage public static native int setMasterMute(boolean mute); + /** @hide */ @UnsupportedAppUsage public static native boolean getMasterMute(); + /** @hide */ @UnsupportedAppUsage public static native int getDevicesForStream(int stream); /** + * @hide * Do not use directly, see {@link AudioManager#getDevicesForAttributes(AudioAttributes)} * Get the audio devices that would be used for the routing of the given audio attributes. * @param attributes the {@link AudioAttributes} for which the routing is being queried @@ -1136,31 +1285,43 @@ public class AudioSystem /** @hide returns master balance value in range -1.f -> 1.f, where 0.f is dead center. */ @TestApi public static native float getMasterBalance(); - /** @hide changes the audio balance of the device. */ + /** @hide Changes the audio balance of the device. */ @TestApi public static native int setMasterBalance(float balance); // helpers for android.media.AudioManager.getProperty(), see description there for meaning + /** @hide */ @UnsupportedAppUsage(trackingBug = 134049522) public static native int getPrimaryOutputSamplingRate(); + /** @hide */ @UnsupportedAppUsage(trackingBug = 134049522) public static native int getPrimaryOutputFrameCount(); + /** @hide */ @UnsupportedAppUsage public static native int getOutputLatency(int stream); + /** @hide */ public static native int setLowRamDevice(boolean isLowRamDevice, long totalMemory); + /** @hide */ @UnsupportedAppUsage public static native int checkAudioFlinger(); + /** @hide */ public static native int listAudioPorts(ArrayList<AudioPort> ports, int[] generation); + /** @hide */ public static native int createAudioPatch(AudioPatch[] patch, AudioPortConfig[] sources, AudioPortConfig[] sinks); + /** @hide */ public static native int releaseAudioPatch(AudioPatch patch); + /** @hide */ public static native int listAudioPatches(ArrayList<AudioPatch> patches, int[] generation); + /** @hide */ public static native int setAudioPortConfig(AudioPortConfig config); + /** @hide */ public static native int startAudioSource(AudioPortConfig config, AudioAttributes audioAttributes); + /** @hide */ public static native int stopAudioSource(int handle); // declare this instance as having a dynamic policy callback handler @@ -1169,36 +1330,42 @@ public class AudioSystem private static native final void native_register_recording_callback(); // must be kept in sync with value in include/system/audio.h - public static final int AUDIO_HW_SYNC_INVALID = 0; + /** @hide */ public static final int AUDIO_HW_SYNC_INVALID = 0; + /** @hide */ public static native int getAudioHwSyncForSession(int sessionId); + /** @hide */ public static native int registerPolicyMixes(ArrayList<AudioMix> mixes, boolean register); - /** see AudioPolicy.setUidDeviceAffinities() */ + /** @hide see AudioPolicy.setUidDeviceAffinities() */ public static native int setUidDeviceAffinities(int uid, @NonNull int[] types, @NonNull String[] addresses); - /** see AudioPolicy.removeUidDeviceAffinities() */ + /** @hide see AudioPolicy.removeUidDeviceAffinities() */ public static native int removeUidDeviceAffinities(int uid); - /** see AudioPolicy.setUserIdDeviceAffinities() */ + /** @hide see AudioPolicy.setUserIdDeviceAffinities() */ public static native int setUserIdDeviceAffinities(int userId, @NonNull int[] types, @NonNull String[] addresses); - /** see AudioPolicy.removeUserIdDeviceAffinities() */ + /** @hide see AudioPolicy.removeUserIdDeviceAffinities() */ public static native int removeUserIdDeviceAffinities(int userId); + /** @hide */ public static native int systemReady(); + /** @hide */ public static native float getStreamVolumeDB(int stream, int index, int device); /** + * @hide * Communicate supported system usages to audio policy service. */ public static native int setSupportedSystemUsages(int[] systemUsages); /** + * @hide * @see AudioManager#setAllowedCapturePolicy() */ public static native int setAllowedCapturePolicy(int uid, int flags); @@ -1212,45 +1379,57 @@ public class AudioSystem private static native boolean native_is_offload_supported(int encoding, int sampleRate, int channelMask, int channelIndexMask, int streamType); + /** @hide */ public static native int getMicrophones(ArrayList<MicrophoneInfo> microphonesInfo); + /** @hide */ public static native int getSurroundFormats(Map<Integer, Boolean> surroundFormats, boolean reported); /** + * @hide * Returns a list of audio formats (codec) supported on the A2DP offload path. */ public static native int getHwOffloadEncodingFormatsSupportedForA2DP( ArrayList<Integer> formatList); + /** @hide */ public static native int setSurroundFormatEnabled(int audioFormat, boolean enabled); /** + * @hide * Communicate UID of active assistant to audio policy service. */ public static native int setAssistantUid(int uid); + /** + * @hide * Communicate UIDs of active accessibility services to audio policy service. */ public static native int setA11yServicesUids(int[] uids); + /** + * @hide * Communicate UID of current InputMethodService to audio policy service. */ public static native int setCurrentImeUid(int uid); /** + * @hide * @see AudioManager#isHapticPlaybackSupported() */ public static native boolean isHapticPlaybackSupported(); /** + * @hide * Send audio HAL server process pids to native audioserver process for use * when generating audio HAL servers tombstones */ public static native int setAudioHalPids(int[] pids); /** + * @hide * @see AudioManager#isCallScreeningModeSupported() */ public static native boolean isCallScreeningModeSupported(); @@ -1258,6 +1437,7 @@ public class AudioSystem // use case routing by product strategy /** + * @hide * Sets the preferred device to use for a given audio strategy in the audio policy engine * @param strategy the id of the strategy to configure * @param device the device type and address to route to when available @@ -1270,6 +1450,7 @@ public class AudioSystem device.getAddress()); } /** + * @hide * Set device routing per product strategy. * @param strategy the id of the strategy to configure * @param deviceType the native device type, NOT AudioDeviceInfo types @@ -1280,6 +1461,7 @@ public class AudioSystem int strategy, int deviceType, String deviceAddress); /** + * @hide * Remove preferred routing for the strategy * @param strategy the id of the strategy to configure * @return {@link #SUCCESS} if successfully removed @@ -1287,6 +1469,7 @@ public class AudioSystem public static native int removePreferredDeviceForStrategy(int strategy); /** + * @hide * Query previously set preferred device for a strategy * @param strategy the id of the strategy to query for * @param device an array of size 1 that will contain the preferred device, or null if @@ -1300,6 +1483,7 @@ public class AudioSystem // Items shared with audio service /** + * @hide * The delay before playing a sound. This small period exists so the user * can press another key (non-volume keys, too) to have it NOT be audible. * <p> @@ -1308,6 +1492,7 @@ public class AudioSystem public static final int PLAY_SOUND_DELAY = 300; /** + * @hide * Constant to identify a focus stack entry that is used to hold the focus while the phone * is ringing or during a call. Used by com.android.internal.telephony.CallManager when * entering and exiting calls. @@ -1315,6 +1500,7 @@ public class AudioSystem public final static String IN_VOICE_COMM_FOCUS_ID = "AudioFocus_For_Phone_Ring_And_Calls"; /** + * @hide * @see AudioManager#setVibrateSetting(int, int) */ public static int getValueForVibrateSetting(int existingValue, int vibrateType, @@ -1330,10 +1516,12 @@ public class AudioSystem return existingValue; } + /** @hide */ public static int getDefaultStreamVolume(int streamType) { return DEFAULT_STREAM_VOLUME[streamType]; } + /** @hide */ public static int[] DEFAULT_STREAM_VOLUME = new int[] { 4, // STREAM_VOICE_CALL 7, // STREAM_SYSTEM @@ -1349,20 +1537,22 @@ public class AudioSystem 5, // STREAM_ASSISTANT }; + /** @hide */ public static String streamToString(int stream) { if (stream >= 0 && stream < STREAM_NAMES.length) return STREAM_NAMES[stream]; if (stream == AudioManager.USE_DEFAULT_STREAM_TYPE) return "USE_DEFAULT_STREAM_TYPE"; return "UNKNOWN_STREAM_" + stream; } - /** The platform has no specific capabilities */ + /** @hide The platform has no specific capabilities */ public static final int PLATFORM_DEFAULT = 0; - /** The platform is voice call capable (a phone) */ + /** @hide The platform is voice call capable (a phone) */ public static final int PLATFORM_VOICE = 1; - /** The platform is a television or a set-top box */ + /** @hide The platform is a television or a set-top box */ public static final int PLATFORM_TELEVISION = 2; /** + * @hide * Return the platform type that this is running on. One of: * <ul> * <li>{@link #PLATFORM_VOICE}</li> @@ -1392,6 +1582,7 @@ public class AudioSystem } /** + * @hide * Return a set of audio device types from a bit mask audio device type, which may * represent multiple audio device types. * FIXME: Remove this when getting ride of bit mask usage of audio device types. @@ -1409,6 +1600,7 @@ public class AudioSystem } /** + * @hide * Return the intersection of two audio device types collections. */ public static Set<Integer> intersectionAudioDeviceTypes( @@ -1419,12 +1611,14 @@ public class AudioSystem } /** + * @hide * Return true if the audio device types collection only contains the given device type. */ public static boolean isSingleAudioDeviceType(@NonNull Set<Integer> types, int type) { return types.size() == 1 && types.contains(type); } + /** @hide */ public static final int DEFAULT_MUTE_STREAMS_AFFECTED = (1 << STREAM_MUSIC) | (1 << STREAM_RING) | @@ -1434,6 +1628,7 @@ public class AudioSystem (1 << STREAM_BLUETOOTH_SCO); /** + * @hide * Event posted by AudioTrack and AudioRecord JNI (JNIDeviceCallback) when routing changes. * Keep in sync with core/jni/android_media_DeviceCallback.h. */ diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java index dbba4eb320ba..48aed349c39a 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -45,6 +45,7 @@ import android.media.tv.tuner.frontend.ScanCallback; import android.media.tv.tunerresourcemanager.ResourceClientProfile; import android.media.tv.tunerresourcemanager.TunerDemuxRequest; import android.media.tv.tunerresourcemanager.TunerDescramblerRequest; +import android.media.tv.tunerresourcemanager.TunerFrontendInfo; import android.media.tv.tunerresourcemanager.TunerFrontendRequest; import android.media.tv.tunerresourcemanager.TunerLnbRequest; import android.media.tv.tunerresourcemanager.TunerResourceManager; @@ -256,6 +257,36 @@ public class Tuner implements AutoCloseable { mTunerResourceManager.registerClientProfile( profile, new HandlerExecutor(mHandler), mResourceListener, clientId); mClientId = clientId[0]; + + setFrontendInfoList(); + setLnbIds(); + } + + private void setFrontendInfoList() { + List<Integer> ids = nativeGetFrontendIds(); + if (ids == null) { + return; + } + TunerFrontendInfo[] infos = new TunerFrontendInfo[ids.size()]; + for (int i = 0; i < ids.size(); i++) { + int id = ids.get(i); + FrontendInfo frontendInfo = nativeGetFrontendInfo(id); + if (frontendInfo == null) { + continue; + } + TunerFrontendInfo tunerFrontendInfo = new TunerFrontendInfo( + id, frontendInfo.getType(), frontendInfo.getExclusiveGroupId()); + infos[i] = tunerFrontendInfo; + } + mTunerResourceManager.setFrontendInfoList(infos); + } + + private void setLnbIds() { + int[] ids = nativeGetLnbIds(); + if (ids == null) { + return; + } + mTunerResourceManager.setLnbInfoList(ids); } /** @@ -358,7 +389,7 @@ public class Tuner implements AutoCloseable { private native Filter nativeOpenFilter(int type, int subType, long bufferSize); private native TimeFilter nativeOpenTimeFilter(); - private native List<Integer> nativeGetLnbIds(); + private native int[] nativeGetLnbIds(); private native Lnb nativeOpenLnbByHandle(int handle); private native Lnb nativeOpenLnbByName(String name); diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java index 63a71e272e53..2c8899cfca78 100644 --- a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java +++ b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java @@ -390,7 +390,7 @@ public class TunerResourceManager { * <li>If no Lnb system can be granted, the API would return false. * <ul> * - * <p><strong>Note:</strong> {@link #setLnbInfos(int[])} must be called before this request. + * <p><strong>Note:</strong> {@link #setLnbInfoList(int[])} must be called before this request. * * @param request {@link TunerLnbRequest} information of the current request. * @param lnbId a one-element array to return the granted Lnb id. @@ -479,7 +479,7 @@ public class TunerResourceManager { * * <p>Client must call this whenever it releases an Lnb. * - * <p><strong>Note:</strong> {@link #setLnbInfos(int[])} must be called before this release. + * <p><strong>Note:</strong> {@link #setLnbInfoList(int[])} must be called before this release. * * @param lnbId the id of the released Tuner Lnb. */ diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index 5820be6db8ea..ac7fe5d0403d 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -1079,29 +1079,26 @@ jobject JTuner::getFrontendInfo(int id) { maxSymbolRate, acquireRange, exclusiveGroupId, statusCaps, jcaps); } -jobject JTuner::getLnbIds() { +jintArray JTuner::getLnbIds() { ALOGD("JTuner::getLnbIds()"); - mTuner->getLnbIds([&](Result, const hidl_vec<FrontendId>& lnbIds) { - mLnbIds = lnbIds; + Result res; + hidl_vec<LnbId> lnbIds; + mTuner->getLnbIds([&](Result r, const hidl_vec<LnbId>& ids) { + lnbIds = ids; + res = r; }); - if (mLnbIds.size() == 0) { + if (res != Result::SUCCESS || mLnbIds.size() == 0) { ALOGW("Lnb isn't available"); return NULL; } + mLnbIds = lnbIds; JNIEnv *env = AndroidRuntime::getJNIEnv(); - jclass arrayListClazz = env->FindClass("java/util/ArrayList"); - jmethodID arrayListAdd = env->GetMethodID(arrayListClazz, "add", "(Ljava/lang/Object;)Z"); - jobject obj = env->NewObject(arrayListClazz, env->GetMethodID(arrayListClazz, "<init>", "()V")); - jclass integerClazz = env->FindClass("java/lang/Integer"); - jmethodID intInit = env->GetMethodID(integerClazz, "<init>", "(I)V"); + jintArray ids = env->NewIntArray(mLnbIds.size()); + env->SetIntArrayRegion(ids, 0, mLnbIds.size(), reinterpret_cast<jint*>(&mLnbIds[0])); - for (int i=0; i < mLnbIds.size(); i++) { - jobject idObj = env->NewObject(integerClazz, intInit, mLnbIds[i]); - env->CallBooleanMethod(obj, arrayListAdd, idObj); - } - return obj; + return ids; } jobject JTuner::openLnbById(int id) { @@ -2405,7 +2402,7 @@ static jobject android_media_tv_Tuner_get_frontend_info(JNIEnv *env, jobject thi return tuner->getFrontendInfo(id); } -static jobject android_media_tv_Tuner_get_lnb_ids(JNIEnv *env, jobject thiz) { +static jintArray android_media_tv_Tuner_get_lnb_ids(JNIEnv *env, jobject thiz) { sp<JTuner> tuner = getTuner(env, thiz); return tuner->getLnbIds(); } @@ -3415,8 +3412,7 @@ static const JNINativeMethod gTunerMethods[] = { (void *)android_media_tv_Tuner_open_filter }, { "nativeOpenTimeFilter", "()Landroid/media/tv/tuner/filter/TimeFilter;", (void *)android_media_tv_Tuner_open_time_filter }, - { "nativeGetLnbIds", "()Ljava/util/List;", - (void *)android_media_tv_Tuner_get_lnb_ids }, + { "nativeGetLnbIds", "()[I", (void *)android_media_tv_Tuner_get_lnb_ids }, { "nativeOpenLnbByHandle", "(I)Landroid/media/tv/tuner/Lnb;", (void *)android_media_tv_Tuner_open_lnb_by_handle }, { "nativeOpenLnbByName", "(Ljava/lang/String;)Landroid/media/tv/tuner/Lnb;", diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h index 40fd644c62a0..73fc38dbdec8 100644 --- a/media/jni/android_media_tv_Tuner.h +++ b/media/jni/android_media_tv_Tuner.h @@ -179,7 +179,7 @@ struct JTuner : public RefBase { int stopScan(); int setLnb(int id); int setLna(bool enable); - jobject getLnbIds(); + jintArray getLnbIds(); jobject openLnbById(int id); jobject openLnbByName(jstring name); jobject openFilter(DemuxFilterType type, int bufferSize); diff --git a/packages/CarSystemUI/src/com/android/systemui/CarComponentBinder.java b/packages/CarSystemUI/src/com/android/systemui/CarComponentBinder.java index f11eff851aa3..d84b2f958feb 100644 --- a/packages/CarSystemUI/src/com/android/systemui/CarComponentBinder.java +++ b/packages/CarSystemUI/src/com/android/systemui/CarComponentBinder.java @@ -28,7 +28,6 @@ import dagger.Module; @Module(includes = { DefaultActivityBinder.class, DefaultBroadcastReceiverBinder.class, - DefaultServiceBinder.class, - CarSystemUIBinder.class}) + DefaultServiceBinder.class}) public class CarComponentBinder { } diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java index 5547fee0159c..c275536e4d92 100644 --- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java +++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java @@ -65,7 +65,7 @@ import dagger.Module; import dagger.Provides; @Module(includes = {DividerModule.class}) -abstract class CarSystemUIModule { +public abstract class CarSystemUIModule { @Singleton @Provides diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java index 7d544c9a9bd7..0e923f7164bb 100644 --- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java +++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java @@ -40,6 +40,6 @@ import dagger.Component; CarSystemUIModule.class, CarSystemUIBinder.class }) -interface CarSystemUIRootComponent extends SystemUIRootComponent { +public interface CarSystemUIRootComponent extends SystemUIRootComponent { } diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 9112602981e5..07f4a9a799ad 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -928,11 +928,15 @@ <!-- UI debug setting: show all ANRs summary [CHAR LIMIT=100] --> <string name="show_all_anrs_summary">Display App Not Responding dialog for background apps</string> - <!-- UI debug setting: show all ANRs? [CHAR LIMIT=25] --> + <!-- UI debug setting: show missing channel toasts? [CHAR LIMIT=25] --> <string name="show_notification_channel_warnings">Show notification channel warnings</string> - <!-- UI debug setting: show all ANRs summary [CHAR LIMIT=50] --> + <!-- UI debug setting: show missing channel toasts summary [CHAR LIMIT=50] --> <string name="show_notification_channel_warnings_summary">Displays on-screen warning when an app posts a notification without a valid channel</string> + <!-- UI debug setting: enforce shortcut requirements for conversation space [CHAR LIMIT=25] --> + <string name="enforce_shortcuts_for_conversations">Enforce shortcuts for conversation notifications</string> + <!-- UI debug setting: enforce shortcut requirements for conversation space summary [CHAR LIMIT=50] --> + <string name="enforce_shortcuts_for_conversations_summary">Require notifications to be backed by a long-lived sharing shortcut in order to appear in the conversation section</string> <!-- UI debug setting: force allow apps on external storage [CHAR LIMIT=50] --> <string name="force_allow_on_external">Force allow apps on external</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiSavedConfigUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiSavedConfigUtils.java index 19e38081fcad..65c7786235bf 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiSavedConfigUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiSavedConfigUtils.java @@ -65,5 +65,17 @@ public class WifiSavedConfigUtils { } return savedConfigs; } + + /** + * Returns the count of the saved configurations on the device, including both Wi-Fi networks + * and Passpoint profiles. + * + * @param context The application context + * @param wifiManager An instance of {@link WifiManager} + * @return count of saved Wi-Fi networks + */ + public static int getAllConfigsCount(Context context, WifiManager wifiManager) { + return getAllConfigs(context, wifiManager).size(); + } } diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 86ba8bba31ee..01a2b6952f2a 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -418,6 +418,7 @@ public class SettingsBackupTest { Settings.Global.RECOMMENDED_NETWORK_EVALUATOR_CACHE_EXPIRY_MS, Settings.Global.READ_EXTERNAL_STORAGE_ENFORCED_DEFAULT, Settings.Global.REQUIRE_PASSWORD_TO_DECRYPT, + Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, Settings.Global.SAFE_BOOT_DISALLOWED, Settings.Global.SELINUX_STATUS, Settings.Global.SELINUX_UPDATE_CONTENT_URL, diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java index 68bd4071de05..c2015ed41b00 100644 --- a/packages/Shell/src/com/android/shell/BugreportProgressService.java +++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java @@ -363,6 +363,7 @@ public class BugreportProgressService extends Service { public void onError(@BugreportErrorCode int errorCode) { synchronized (mLock) { stopProgressLocked(mInfo.id); + mInfo.deleteEmptyFiles(); } Log.e(TAG, "Bugreport API callback onError() errorCode = " + errorCode); return; @@ -1980,6 +1981,22 @@ public class BugreportProgressService extends Service { } /** + * Deletes empty files for a given bugreport. + */ + private void deleteEmptyFiles() { + if (bugreportFile.length() == 0) { + Log.i(TAG, "Deleting empty bugreport file: " + bugreportFile); + bugreportFile.delete(); + } + for (File file : screenshotFiles) { + if (file.length() == 0) { + Log.i(TAG, "Deleting empty screenshot file: " + file); + file.delete(); + } + } + } + + /** * Rename all screenshots files so that they contain the new {@code name} instead of the * {@code initialName} if user has changed it. */ diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 791b83277571..c6f03271f931 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -255,6 +255,9 @@ <!-- Query all packages on device on R+ --> <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" /> + <!-- Permission to register process observer --> + <uses-permission android:name="android.permission.SET_ACTIVITY_WATCHER"/> + <protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" /> <protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" /> <protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" /> diff --git a/packages/SystemUI/res/values/arrays_tv.xml b/packages/SystemUI/res/values/arrays_tv.xml index 95716c834483..3343a84e7a9c 100644 --- a/packages/SystemUI/res/values/arrays_tv.xml +++ b/packages/SystemUI/res/values/arrays_tv.xml @@ -35,7 +35,5 @@ </string-array> <string-array name="audio_recording_disclosure_exempt_apps" translatable="false"> - <item>com.google.android.katniss</item> - <item>com.google.android.apps.mediashell</item> </string-array> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java index ea1abf99a0f3..a0b49384d49f 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java @@ -323,6 +323,9 @@ public class ExpandedAnimationController /** Plays a dismiss animation on the dragged out bubble. */ public void dismissDraggedOutBubble(View bubble, Runnable after) { + if (bubble == null) { + return; + } animationForChild(bubble) .withStiffness(SpringForce.STIFFNESS_HIGH) .scaleX(1.1f) diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java index dba43430b490..d219a9e65a3c 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java @@ -319,7 +319,6 @@ public class PipAnimationController { getSurfaceTransactionHelper() .crop(tx, leash, getDestinationBounds()) .round(tx, leash, shouldApplyCornerRadius()); - tx.show(leash); tx.apply(); } }; @@ -360,7 +359,6 @@ public class PipAnimationController { getSurfaceTransactionHelper() .alpha(tx, leash, 1f) .round(tx, leash, shouldApplyCornerRadius()); - tx.show(leash); tx.apply(); } diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java index 8cff20ac31f7..d9872d7dcf17 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java @@ -319,23 +319,29 @@ public class PipTaskOrganizer extends TaskOrganizer { * TODO(b/152809058): consolidate the display info handling logic in SysUI */ @SuppressWarnings("unchecked") - public void mayUpdateCurrentAnimationOnRotationChange() { + public void onMovementBoundsChanged(boolean fromImeAdjustment, boolean fromShelfAdjustment) { final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController.getCurrentAnimator(); - if (animator != null && animator.isRunning() - && animator.getTransitionDirection() == TRANSITION_DIRECTION_TO_PIP) { - final Rect currentDestinationBounds = animator.getDestinationBounds(); - if (mPipBoundsHandler.getDisplayBounds().contains(currentDestinationBounds)) { - return; - } - final Rect newDestinationBounds = mPipBoundsHandler.getDestinationBounds( - getAspectRatioOrDefault(mTaskInfo.pictureInPictureParams), - null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo)); - if (animator.getAnimationType() == ANIM_TYPE_BOUNDS) { - animator.updateEndValue(newDestinationBounds); - } - animator.setDestinationBounds(newDestinationBounds); + if (animator == null || !animator.isRunning() + || animator.getTransitionDirection() != TRANSITION_DIRECTION_TO_PIP) { + return; + } + + final Rect currentDestinationBounds = animator.getDestinationBounds(); + if (!fromImeAdjustment && !fromShelfAdjustment + && mPipBoundsHandler.getDisplayBounds().contains(currentDestinationBounds)) { + // no need to update the destination bounds, bail early + return; + } + + final Rect newDestinationBounds = mPipBoundsHandler.getDestinationBounds( + getAspectRatioOrDefault(mTaskInfo.pictureInPictureParams), + null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo)); + if (newDestinationBounds.equals(currentDestinationBounds)) return; + if (animator.getAnimationType() == ANIM_TYPE_BOUNDS) { + animator.updateEndValue(newDestinationBounds); } + animator.setDestinationBounds(newDestinationBounds); } /** diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java index 99d6df517224..918c45b52d61 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java @@ -362,7 +362,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio mTouchHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds, animatingBounds, fromImeAdjustment, fromShelfAdjustment, mTmpDisplayInfo.rotation); - mPipTaskOrganizer.mayUpdateCurrentAnimationOnRotationChange(); + mPipTaskOrganizer.onMovementBoundsChanged(fromImeAdjustment, fromShelfAdjustment); } public void dump(PrintWriter pw) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index 94afde786e78..24195156d8cf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -322,10 +322,10 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< default void suppressAmbientDisplay(boolean suppress) { } /** - * @see IStatusBar#showToast(String, IBinder, CharSequence, IBinder, int, + * @see IStatusBar#showToast(int, String, IBinder, CharSequence, IBinder, int, * ITransientNotificationCallback) */ - default void showToast(String packageName, IBinder token, CharSequence text, + default void showToast(int uid, String packageName, IBinder token, CharSequence text, IBinder windowToken, int duration, @Nullable ITransientNotificationCallback callback) { } @@ -798,7 +798,7 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< } @Override - public void showToast(String packageName, IBinder token, CharSequence text, + public void showToast(int uid, String packageName, IBinder token, CharSequence text, IBinder windowToken, int duration, @Nullable ITransientNotificationCallback callback) { synchronized (mLock) { SomeArgs args = SomeArgs.obtain(); @@ -807,7 +807,8 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< args.arg3 = text; args.arg4 = windowToken; args.arg5 = callback; - args.argi1 = duration; + args.argi1 = uid; + args.argi2 = duration; mHandler.obtainMessage(MSG_SHOW_TOAST, args).sendToTarget(); } } @@ -1276,9 +1277,10 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< IBinder windowToken = (IBinder) args.arg4; ITransientNotificationCallback callback = (ITransientNotificationCallback) args.arg5; - int duration = args.argi1; + int uid = args.argi1; + int duration = args.argi2; for (Callbacks callbacks : mCallbacks) { - callbacks.showToast(packageName, token, text, windowToken, duration, + callbacks.showToast(uid, packageName, token, text, windowToken, duration, callback); } break; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt index 88cca43fd1a9..5879c15c2493 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt @@ -83,9 +83,9 @@ class PeopleNotificationIdentifierImpl @Inject constructor( private val Ranking.personTypeInfo get() = when { + !isConversation -> TYPE_NON_PERSON channel?.isImportantConversation == true -> TYPE_IMPORTANT_PERSON - isConversation -> TYPE_PERSON - else -> TYPE_NON_PERSON + else -> TYPE_PERSON } private fun extractPersonTypeInfo(sbn: StatusBarNotification) = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java index 4462c7218a76..3105155f28e8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -625,7 +625,9 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback // mOrientedHandle is initialized lazily mOrientationHandle.setVisibility(View.GONE); } - mNavigationBarView.setVisibility(View.VISIBLE); + if (mNavigationBarView != null) { + mNavigationBarView.setVisibility(View.VISIBLE); + } } private int deltaRotation(int oldRotation, int newRotation) { @@ -635,7 +637,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback } private void updatedFixedRotation() { - mFixedRotationEnabled = Settings.Global.getInt(getContext().getContentResolver(), + mFixedRotationEnabled = Settings.Global.getInt(mContentResolver, FIXED_ROTATION_TRANSFORM_SETTING_NAME, 0) != 0; if (!canShowSecondaryHandle()) { resetSecondaryHandle(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java index 07985ab5a43c..02ae1f8afcdf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java @@ -27,6 +27,7 @@ import android.os.UserHandle; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.SystemUI; import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.tv.micdisclosure.AudioRecordingDisclosureBar; import javax.inject.Inject; import javax.inject.Singleton; @@ -66,7 +67,8 @@ public class TvStatusBar extends SystemUI implements CommandQueue.Callbacks { // If the system process isn't there we're doomed anyway. } - new AudioRecordingDisclosureBar(mContext).start(); + // Creating AudioRecordingDisclosureBar and just letting it run + new AudioRecordingDisclosureBar(mContext); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioActivityObserver.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioActivityObserver.java new file mode 100644 index 000000000000..87b3956060f3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioActivityObserver.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.tv.micdisclosure; + +import android.content.Context; + +import java.util.Set; + +/** + * A base class for implementing observers for different kinds of activities related to audio + * recording. These observers are to be initialized by {@link AudioRecordingDisclosureBar} and to + * report back to it. + */ +abstract class AudioActivityObserver { + + interface OnAudioActivityStateChangeListener { + void onAudioActivityStateChange(boolean active, String packageName); + } + + final Context mContext; + + final OnAudioActivityStateChangeListener mListener; + + AudioActivityObserver(Context context, OnAudioActivityStateChangeListener listener) { + mContext = context; + mListener = listener; + } + + abstract Set<String> getActivePackages(); +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java index e70e30a5ab57..914868301d72 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.tv; +package com.android.systemui.statusbar.tv.micdisclosure; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; @@ -25,7 +25,6 @@ import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.annotation.IntDef; import android.annotation.UiThread; -import android.app.AppOpsManager; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; @@ -40,6 +39,7 @@ import android.view.WindowManager; import android.widget.TextView; import com.android.systemui.R; +import com.android.systemui.statusbar.tv.TvStatusBar; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -54,9 +54,10 @@ import java.util.Set; * * @see TvStatusBar */ -class AudioRecordingDisclosureBar { - private static final String TAG = "AudioRecordingDisclosureBar"; - private static final boolean DEBUG = false; +public class AudioRecordingDisclosureBar implements + AudioActivityObserver.OnAudioActivityStateChangeListener { + private static final String TAG = "AudioRecordingDisclosure"; + static final boolean DEBUG = false; // This title is used to test the microphone disclosure indicator in // CtsSystemUiHostTestCases:TvMicrophoneCaptureIndicatorTest @@ -98,10 +99,12 @@ class AudioRecordingDisclosureBar { private TextView mTextView; @State private int mState = STATE_NOT_SHOWN; + /** - * Set of the applications that currently are conducting audio recording. + * Array of the observers that monitor different aspects of the system, such as AppOps and + * microphone foreground services */ - private final Set<String> mActiveAudioRecordingPackages = new ArraySet<>(); + private final AudioActivityObserver[] mAudioActivityObservers; /** * Set of applications that we've notified the user about since the indicator came up. Meaning * that if an application is in this list then at some point since the indicator came up, it @@ -119,29 +122,52 @@ class AudioRecordingDisclosureBar { * one of the two aforementioned states. */ private final Queue<String> mPendingNotificationPackages = new LinkedList<>(); + /** + * Set of applications for which we make an exception and do not show the indicator. This gets + * populated once - in {@link #AudioRecordingDisclosureBar(Context)}. + */ + private final Set<String> mExemptPackages; - AudioRecordingDisclosureBar(Context context) { + public AudioRecordingDisclosureBar(Context context) { mContext = context; - } - void start() { - // Register AppOpsManager callback - final AppOpsManager appOpsManager = (AppOpsManager) mContext.getSystemService( - Context.APP_OPS_SERVICE); - appOpsManager.startWatchingActive( - new String[]{AppOpsManager.OPSTR_RECORD_AUDIO}, - mContext.getMainExecutor(), - new OnActiveRecordingListener()); + mExemptPackages = new ArraySet<>( + Arrays.asList(mContext.getResources().getStringArray( + R.array.audio_recording_disclosure_exempt_apps))); + + mAudioActivityObservers = new AudioActivityObserver[]{ + new RecordAudioAppOpObserver(mContext, this), + new MicrophoneForegroundServicesObserver(mContext, this), + }; } @UiThread - private void onStartedRecording(String packageName) { - if (!mActiveAudioRecordingPackages.add(packageName)) { - // This app is already known to perform recording + @Override + public void onAudioActivityStateChange(boolean active, String packageName) { + if (DEBUG) { + Log.d(TAG, + "onAudioActivityStateChange, packageName=" + packageName + ", active=" + + active); + } + + if (mExemptPackages.contains(packageName)) { + if (DEBUG) Log.d(TAG, " - exempt package: ignoring"); return; } + + if (active) { + showIndicatorForPackageIfNeeded(packageName); + } else { + hideIndicatorIfNeeded(); + } + } + + @UiThread + private void showIndicatorForPackageIfNeeded(String packageName) { + if (DEBUG) Log.d(TAG, "showIndicatorForPackageIfNeeded, packageName=" + packageName); if (!mSessionNotifiedPackages.add(packageName)) { // We've already notified user about this app, no need to do it again. + if (DEBUG) Log.d(TAG, " - already notified"); return; } @@ -167,23 +193,33 @@ class AudioRecordingDisclosureBar { } @UiThread - private void onDoneRecording(String packageName) { - if (!mActiveAudioRecordingPackages.remove(packageName)) { - // Was not marked as an active recorder, do nothing - return; - } - + private void hideIndicatorIfNeeded() { + if (DEBUG) Log.d(TAG, "hideIndicatorIfNeeded"); // If not MINIMIZED, will check whether the indicator should be hidden when the indicator - // comes to the STATE_MINIMIZED eventually. If is in the STATE_MINIMIZED, but there are - // other active recorders - simply ignore. - if (mState == STATE_MINIMIZED && mActiveAudioRecordingPackages.isEmpty()) { - mSessionNotifiedPackages.clear(); - hide(); + // comes to the STATE_MINIMIZED eventually. + if (mState != STATE_MINIMIZED) return; + + // If is in the STATE_MINIMIZED, but there are other active recorders - simply ignore. + for (int index = mAudioActivityObservers.length - 1; index >= 0; index--) { + for (String activePackage : mAudioActivityObservers[index].getActivePackages()) { + if (mExemptPackages.contains(activePackage)) continue; + if (DEBUG) Log.d(TAG, " - there are still ongoing activities"); + return; + } } + + // Clear the state and hide the indicator. + mSessionNotifiedPackages.clear(); + hide(); } @UiThread private void show(String packageName) { + final String label = getApplicationLabel(packageName); + if (DEBUG) { + Log.d(TAG, "Showing indicator for " + packageName + " (" + label + ")..."); + } + // Inflate the indicator view mIndicatorView = LayoutInflater.from(mContext).inflate( R.layout.tv_audio_recording_indicator, @@ -196,7 +232,6 @@ class AudioRecordingDisclosureBar { mBgRight = mIndicatorView.findViewById(R.id.bg_right); // Set up the notification text - final String label = getApplicationLabel(packageName); mTextView.setText(mContext.getString(R.string.app_accessed_mic, label)); // Initially change the visibility to INVISIBLE, wait until and receives the size and @@ -260,6 +295,9 @@ class AudioRecordingDisclosureBar { @UiThread private void expand(String packageName) { final String label = getApplicationLabel(packageName); + if (DEBUG) { + Log.d(TAG, "Expanding for " + packageName + " (" + label + ")..."); + } mTextView.setText(mContext.getString(R.string.app_accessed_mic, label)); final AnimatorSet set = new AnimatorSet(); @@ -283,6 +321,7 @@ class AudioRecordingDisclosureBar { @UiThread private void minimize() { + if (DEBUG) Log.d(TAG, "Minimizing..."); final int targetOffset = mTextsContainers.getWidth(); final AnimatorSet set = new AnimatorSet(); set.playTogether( @@ -305,6 +344,7 @@ class AudioRecordingDisclosureBar { @UiThread private void hide() { + if (DEBUG) Log.d(TAG, "Hiding..."); final int targetOffset = mIndicatorView.getWidth() - (int) mIconTextsContainer.getTranslationX(); final AnimatorSet set = new AnimatorSet(); @@ -326,6 +366,7 @@ class AudioRecordingDisclosureBar { @UiThread private void onExpanded() { + if (DEBUG) Log.d(TAG, "Expanded"); mState = STATE_SHOWN; mIndicatorView.postDelayed(this::minimize, MAXIMIZED_DURATION); @@ -333,20 +374,21 @@ class AudioRecordingDisclosureBar { @UiThread private void onMinimized() { + if (DEBUG) Log.d(TAG, "Minimized"); mState = STATE_MINIMIZED; if (!mPendingNotificationPackages.isEmpty()) { // There is a new application that started recording, tell the user about it. expand(mPendingNotificationPackages.poll()); - } else if (mActiveAudioRecordingPackages.isEmpty()) { - // Nobody is recording anymore, clear state and remove the indicator. - mSessionNotifiedPackages.clear(); - hide(); + } else { + hideIndicatorIfNeeded(); } } @UiThread private void onHidden() { + if (DEBUG) Log.d(TAG, "Hidden"); + final WindowManager windowManager = (WindowManager) mContext.getSystemService( Context.WINDOW_SERVICE); windowManager.removeView(mIndicatorView); @@ -392,35 +434,4 @@ class AudioRecordingDisclosureBar { } return pm.getApplicationLabel(appInfo).toString(); } - - private class OnActiveRecordingListener implements AppOpsManager.OnOpActiveChangedListener { - private final Set<String> mExemptApps; - - private OnActiveRecordingListener() { - mExemptApps = new ArraySet<>(Arrays.asList(mContext.getResources().getStringArray( - R.array.audio_recording_disclosure_exempt_apps))); - } - - @Override - public void onOpActiveChanged(String op, int uid, String packageName, boolean active) { - if (DEBUG) { - Log.d(TAG, - "OP_RECORD_AUDIO active change, active=" + active + ", app=" - + packageName); - } - - if (mExemptApps.contains(packageName)) { - if (DEBUG) { - Log.d(TAG, "\t- exempt app"); - } - return; - } - - if (active) { - onStartedRecording(packageName); - } else { - onDoneRecording(packageName); - } - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/MicrophoneForegroundServicesObserver.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/MicrophoneForegroundServicesObserver.java new file mode 100644 index 000000000000..1ede88a26020 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/MicrophoneForegroundServicesObserver.java @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.tv.micdisclosure; + +import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE; + +import static com.android.systemui.statusbar.tv.micdisclosure.AudioRecordingDisclosureBar.DEBUG; + +import android.annotation.UiThread; +import android.app.ActivityManager; +import android.app.IActivityManager; +import android.app.IProcessObserver; +import android.content.Context; +import android.os.RemoteException; +import android.util.ArrayMap; +import android.util.Log; +import android.util.SparseArray; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * The purpose of these class is to detect packages that are running foreground services of type + * 'microphone' and to report back to {@link AudioRecordingDisclosureBar}. + */ +class MicrophoneForegroundServicesObserver extends AudioActivityObserver { + private static final String TAG = "MicrophoneForegroundServicesObserver"; + private static final boolean ENABLED = true; + + private final IActivityManager mActivityManager; + /** + * A dictionary that maps PIDs to the package names. We only keep track of the PIDs that are + * "active" (those that are running FGS with FOREGROUND_SERVICE_TYPE_MICROPHONE flag). + */ + private final SparseArray<String[]> mPidToPackages = new SparseArray<>(); + /** + * A dictionary that maps "active" packages to the number of the "active" processes associated + * with those packages. We really only need this in case when one application is running in + * multiple processes, so that we don't lose track of the package when one of its "active" + * processes ceases, while others remain "active". + */ + private final Map<String, Integer> mPackageToProcessCount = new ArrayMap<>(); + + MicrophoneForegroundServicesObserver(Context context, + OnAudioActivityStateChangeListener listener) { + super(context, listener); + + mActivityManager = ActivityManager.getService(); + try { + mActivityManager.registerProcessObserver(mProcessObserver); + } catch (RemoteException e) { + Log.e(TAG, "Couldn't register process observer", e); + } + } + + @Override + Set<String> getActivePackages() { + return ENABLED ? mPackageToProcessCount.keySet() : Collections.emptySet(); + } + + @UiThread + private void onProcessForegroundServicesChanged(int pid, boolean hasMicFgs) { + final String[] changedPackages; + if (hasMicFgs) { + if (mPidToPackages.contains(pid)) { + // We are already tracking this pid - ignore. + changedPackages = null; + } else { + changedPackages = getPackageNames(pid); + mPidToPackages.append(pid, changedPackages); + } + } else { + changedPackages = mPidToPackages.removeReturnOld(pid); + } + + if (changedPackages == null) { + return; + } + + for (int index = changedPackages.length - 1; index >= 0; index--) { + final String packageName = changedPackages[index]; + int processCount = mPackageToProcessCount.getOrDefault(packageName, 0); + final boolean shouldNotify; + if (hasMicFgs) { + processCount++; + shouldNotify = processCount == 1; + } else { + processCount--; + shouldNotify = processCount == 0; + } + if (processCount > 0) { + mPackageToProcessCount.put(packageName, processCount); + } else { + mPackageToProcessCount.remove(packageName); + } + if (shouldNotify) notifyPackageStateChanged(packageName, hasMicFgs); + } + } + + @UiThread + private void onProcessDied(int pid) { + final String[] packages = mPidToPackages.removeReturnOld(pid); + if (packages == null) { + // This PID was not active - ignore. + return; + } + + for (int index = packages.length - 1; index >= 0; index--) { + final String packageName = packages[index]; + int processCount = mPackageToProcessCount.getOrDefault(packageName, 0); + if (processCount <= 0) { + Log.e(TAG, "Bookkeeping error, process count for " + packageName + " is " + + processCount); + continue; + } + processCount--; + if (processCount > 0) { + mPackageToProcessCount.put(packageName, processCount); + } else { + mPackageToProcessCount.remove(packageName); + notifyPackageStateChanged(packageName, false); + } + } + } + + @UiThread + private void notifyPackageStateChanged(String packageName, boolean active) { + if (active) { + if (DEBUG) Log.d(TAG, "New microphone fgs detected, package=" + packageName); + } else { + if (DEBUG) Log.d(TAG, "Microphone fgs is gone, package=" + packageName); + } + + if (ENABLED) mListener.onAudioActivityStateChange(active, packageName); + } + + @UiThread + private String[] getPackageNames(int pid) { + final List<ActivityManager.RunningAppProcessInfo> runningApps; + try { + runningApps = mActivityManager.getRunningAppProcesses(); + } catch (RemoteException e) { + Log.d(TAG, "Couldn't get package name for pid=" + pid); + return null; + } + if (runningApps == null) { + Log.wtf(TAG, "No running apps reported"); + } + for (ActivityManager.RunningAppProcessInfo app : runningApps) { + if (app.pid == pid) { + return app.pkgList; + } + } + return null; + } + + private final IProcessObserver mProcessObserver = new IProcessObserver.Stub() { + @Override + public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {} + + @Override + public void onForegroundServicesChanged(int pid, int uid, int serviceTypes) { + mContext.getMainExecutor().execute(() -> onProcessForegroundServicesChanged(pid, + (serviceTypes & FOREGROUND_SERVICE_TYPE_MICROPHONE) != 0)); + } + + @Override + public void onProcessDied(int pid, int uid) { + mContext.getMainExecutor().execute( + () -> MicrophoneForegroundServicesObserver.this.onProcessDied(pid)); + } + }; +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/RecordAudioAppOpObserver.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/RecordAudioAppOpObserver.java new file mode 100644 index 000000000000..b5b1c2b3018a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/RecordAudioAppOpObserver.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.tv.micdisclosure; + +import static com.android.systemui.statusbar.tv.micdisclosure.AudioRecordingDisclosureBar.DEBUG; + +import android.annotation.UiThread; +import android.app.AppOpsManager; +import android.content.Context; +import android.util.ArraySet; +import android.util.Log; + +import java.util.Set; + +/** + * The purpose of these class is to detect packages that are conducting audio recording (according + * to {@link AppOpsManager}) and report this to {@link AudioRecordingDisclosureBar}. + */ +class RecordAudioAppOpObserver extends AudioActivityObserver implements + AppOpsManager.OnOpActiveChangedListener { + private static final String TAG = "RecordAudioAppOpObserver"; + + /** + * Set of the applications that currently are conducting audio recording according to {@link + * AppOpsManager}. + */ + private final Set<String> mActiveAudioRecordingPackages = new ArraySet<>(); + + RecordAudioAppOpObserver(Context context, OnAudioActivityStateChangeListener listener) { + super(context, listener); + + // Register AppOpsManager callback + final AppOpsManager appOpsManager = (AppOpsManager) mContext.getSystemService( + Context.APP_OPS_SERVICE); + appOpsManager.startWatchingActive( + new String[]{AppOpsManager.OPSTR_RECORD_AUDIO}, + mContext.getMainExecutor(), + this); + } + + @UiThread + @Override + Set<String> getActivePackages() { + return mActiveAudioRecordingPackages; + } + + @UiThread + @Override + public void onOpActiveChanged(String op, int uid, String packageName, boolean active) { + if (DEBUG) { + Log.d(TAG, + "OP_RECORD_AUDIO active change, active=" + active + ", package=" + + packageName); + } + + if (active) { + if (mActiveAudioRecordingPackages.add(packageName)) { + mListener.onAudioActivityStateChange(true, packageName); + } + } else { + if (mActiveAudioRecordingPackages.remove(packageName)) { + mListener.onAudioActivityStateChange(false, packageName); + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java index 9ccb9bf5b62b..9b465ae15478 100644 --- a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java +++ b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java @@ -24,10 +24,10 @@ import android.content.Context; import android.content.res.Resources; import android.os.IBinder; import android.os.ServiceManager; +import android.os.UserHandle; import android.util.Log; import android.view.View; -import android.view.WindowManager; -import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.IAccessibilityManager; import android.widget.ToastPresenter; import com.android.internal.R; @@ -48,9 +48,8 @@ public class ToastUI extends SystemUI implements CommandQueue.Callbacks { private static final String TAG = "ToastUI"; private final CommandQueue mCommandQueue; - private final WindowManager mWindowManager; private final INotificationManager mNotificationManager; - private final AccessibilityManager mAccessibilityManager; + private final IAccessibilityManager mAccessibilityManager; private final int mGravity; private final int mY; @Nullable private ToastPresenter mPresenter; @@ -59,18 +58,17 @@ public class ToastUI extends SystemUI implements CommandQueue.Callbacks { @Inject public ToastUI(Context context, CommandQueue commandQueue) { this(context, commandQueue, - (WindowManager) context.getSystemService(Context.WINDOW_SERVICE), INotificationManager.Stub.asInterface( ServiceManager.getService(Context.NOTIFICATION_SERVICE)), - AccessibilityManager.getInstance(context)); + IAccessibilityManager.Stub.asInterface( + ServiceManager.getService(Context.ACCESSIBILITY_SERVICE))); } @VisibleForTesting - ToastUI(Context context, CommandQueue commandQueue, WindowManager windowManager, - INotificationManager notificationManager, AccessibilityManager accessibilityManager) { + ToastUI(Context context, CommandQueue commandQueue, INotificationManager notificationManager, + @Nullable IAccessibilityManager accessibilityManager) { super(context); mCommandQueue = commandQueue; - mWindowManager = windowManager; mNotificationManager = notificationManager; mAccessibilityManager = accessibilityManager; Resources resources = mContext.getResources(); @@ -85,15 +83,16 @@ public class ToastUI extends SystemUI implements CommandQueue.Callbacks { @Override @MainThread - public void showToast(String packageName, IBinder token, CharSequence text, + public void showToast(int uid, String packageName, IBinder token, CharSequence text, IBinder windowToken, int duration, @Nullable ITransientNotificationCallback callback) { if (mPresenter != null) { hideCurrentToast(); } - View view = ToastPresenter.getTextToastView(mContext, text); + Context context = mContext.createContextAsUser(UserHandle.getUserHandleForUid(uid), 0); + View view = ToastPresenter.getTextToastView(context, text); mCallback = callback; - mPresenter = new ToastPresenter(mContext, mWindowManager, mAccessibilityManager, - mNotificationManager, packageName); + mPresenter = new ToastPresenter(context, mAccessibilityManager, mNotificationManager, + packageName); mPresenter.show(view, token, windowToken, duration, mGravity, 0, mY, 0, 0, mCallback); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java index 56a748497d4e..0d66340a3917 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java @@ -51,6 +51,7 @@ public class PipAnimationControllerTest extends SysuiTestCase { private PipAnimationController mPipAnimationController; + @Mock private SurfaceControl mLeash; @Mock @@ -60,10 +61,6 @@ public class PipAnimationControllerTest extends SysuiTestCase { public void setUp() throws Exception { mPipAnimationController = new PipAnimationController( mContext, new PipSurfaceTransactionHelper(mContext)); - mLeash = new SurfaceControl.Builder() - .setContainerLayer() - .setName("FakeLeash") - .build(); MockitoAnnotations.initMocks(this); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java index 65fbe79b5e9f..0a10ab2fbf02 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java @@ -16,29 +16,43 @@ package com.android.systemui.toast; +import static android.view.accessibility.AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED; +import static android.widget.ToastPresenter.TEXT_TOAST_LAYOUT; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.INotificationManager; import android.app.ITransientNotificationCallback; +import android.content.Context; import android.os.Binder; +import android.os.Parcel; +import android.os.Parcelable; import android.testing.AndroidTestingRunner; +import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.IAccessibilityManager; import android.widget.FrameLayout; import android.widget.TextView; import android.widget.Toast; +import android.widget.ToastPresenter; import androidx.test.filters.SmallTest; import com.android.internal.R; +import com.android.internal.util.IntPair; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.CommandQueue; @@ -49,32 +63,53 @@ import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.stubbing.Answer; @SmallTest @RunWith(AndroidTestingRunner.class) public class ToastUITest extends SysuiTestCase { + private static final int ANDROID_UID = 1000; + private static final int SYSTEMUI_UID = 10140; + + private static final int UID_1 = 10255; private static final String PACKAGE_NAME_1 = "com.example1.test"; private static final Binder TOKEN_1 = new Binder(); private static final Binder WINDOW_TOKEN_1 = new Binder(); + + private static final int UID_2 = 10256; private static final String PACKAGE_NAME_2 = "com.example2.test"; private static final Binder TOKEN_2 = new Binder(); private static final Binder WINDOW_TOKEN_2 = new Binder(); + private static final String TEXT = "Hello World"; private static final int MESSAGE_RES_ID = R.id.message; + private Context mContextSpy; + private ToastUI mToastUI; + @Mock private LayoutInflater mLayoutInflater; @Mock private CommandQueue mCommandQueue; @Mock private WindowManager mWindowManager; @Mock private INotificationManager mNotificationManager; - @Mock private AccessibilityManager mAccessibilityManager; + @Mock private IAccessibilityManager mAccessibilityManager; @Mock private ITransientNotificationCallback mCallback; @Captor private ArgumentCaptor<View> mViewCaptor; @Captor private ArgumentCaptor<ViewGroup.LayoutParams> mParamsCaptor; - private ToastUI mToastUI; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mToastUI = new ToastUI(mContext, mCommandQueue, mWindowManager, mNotificationManager, + + // This is because inflate will result in WindowManager (WM) calls, which will fail since we + // are mocking it, so we mock LayoutInflater with the view obtained before mocking WM. + View view = ToastPresenter.getTextToastView(mContext, TEXT); + when(mLayoutInflater.inflate(eq(TEXT_TOAST_LAYOUT), any())).thenReturn(view); + mContext.addMockSystemService(LayoutInflater.class, mLayoutInflater); + + mContext.addMockSystemService(WindowManager.class, mWindowManager); + mContextSpy = spy(mContext); + doReturn(mContextSpy).when(mContextSpy).createContextAsUser(any(), anyInt()); + + mToastUI = new ToastUI(mContextSpy, mCommandQueue, mNotificationManager, mAccessibilityManager); } @@ -87,7 +122,8 @@ public class ToastUITest extends SysuiTestCase { @Test public void testShowToast_addsCorrectViewToWindowManager() throws Exception { - mToastUI.showToast(PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, null); + mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, + null); verify(mWindowManager).addView(mViewCaptor.capture(), any()); View view = mViewCaptor.getValue(); @@ -96,13 +132,14 @@ public class ToastUITest extends SysuiTestCase { @Test public void testShowToast_addsViewWithCorrectLayoutParamsToWindowManager() throws Exception { - mToastUI.showToast(PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, null); + mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, + null); verify(mWindowManager).addView(any(), mParamsCaptor.capture()); ViewGroup.LayoutParams params = mParamsCaptor.getValue(); assertThat(params).isInstanceOf(WindowManager.LayoutParams.class); WindowManager.LayoutParams windowParams = (WindowManager.LayoutParams) params; - assertThat(windowParams.packageName).isEqualTo(mContext.getPackageName()); + assertThat(windowParams.packageName).isEqualTo(mContextSpy.getPackageName()); assertThat(windowParams.getTitle()).isEqualTo("Toast"); assertThat(windowParams.token).isEqualTo(WINDOW_TOKEN_1); assertThat(windowParams.privateFlags @@ -111,7 +148,8 @@ public class ToastUITest extends SysuiTestCase { @Test public void testShowToast_forAndroidPackage_addsAllUserFlag() throws Exception { - mToastUI.showToast("android", TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, null); + mToastUI.showToast(ANDROID_UID, "android", TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, + null); verify(mWindowManager).addView(any(), mParamsCaptor.capture()); ViewGroup.LayoutParams params = mParamsCaptor.getValue(); @@ -123,8 +161,8 @@ public class ToastUITest extends SysuiTestCase { @Test public void testShowToast_forSystemUiPackage_addsAllUserFlag() throws Exception { - mToastUI.showToast("com.android.systemui", TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, - null); + mToastUI.showToast(SYSTEMUI_UID, "com.android.systemui", TOKEN_1, TEXT, WINDOW_TOKEN_1, + Toast.LENGTH_LONG, null); verify(mWindowManager).addView(any(), mParamsCaptor.capture()); ViewGroup.LayoutParams params = mParamsCaptor.getValue(); @@ -136,7 +174,7 @@ public class ToastUITest extends SysuiTestCase { @Test public void testShowToast_callsCallback() throws Exception { - mToastUI.showToast(PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, + mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, mCallback); verify(mCallback).onToastShown(); @@ -144,14 +182,24 @@ public class ToastUITest extends SysuiTestCase { @Test public void testShowToast_sendsAccessibilityEvent() throws Exception { - when(mAccessibilityManager.isEnabled()).thenReturn(true); - - mToastUI.showToast(PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, null); + // Enable accessibility + when(mAccessibilityManager.addClient(any(), anyInt())).thenReturn( + IntPair.of(STATE_FLAG_ACCESSIBILITY_ENABLED, AccessibilityEvent.TYPES_ALL_MASK)); + // AccessibilityManager recycles the event that goes over the wire after making the binder + // call to the service. Since we are mocking the service, that call is local, so if we use + // ArgumentCaptor or ArgumentMatcher it will retain a reference to the recycled event, which + // will already have its state reset by the time we verify its contents. So, instead, we + // serialize it at call-time and later on deserialize it to verity its contents. + Parcel eventParcel = Parcel.obtain(); + doAnswer(writeArgumentToParcel(0, eventParcel)).when( + mAccessibilityManager).sendAccessibilityEvent(any(), anyInt()); + + mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, + null); - ArgumentCaptor<AccessibilityEvent> eventCaptor = ArgumentCaptor.forClass( - AccessibilityEvent.class); - verify(mAccessibilityManager).sendAccessibilityEvent(eventCaptor.capture()); - AccessibilityEvent event = eventCaptor.getValue(); + eventParcel.setDataPosition(0); + assertThat(eventParcel.dataSize()).isGreaterThan(0); + AccessibilityEvent event = AccessibilityEvent.CREATOR.createFromParcel(eventParcel); assertThat(event.getEventType()).isEqualTo( AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); assertThat(event.getClassName()).isEqualTo(Toast.class.getName()); @@ -160,7 +208,7 @@ public class ToastUITest extends SysuiTestCase { @Test public void testHideToast_removesView() throws Exception { - mToastUI.showToast(PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, + mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, mCallback); View view = verifyWmAddViewAndAttachToParent(); @@ -171,7 +219,7 @@ public class ToastUITest extends SysuiTestCase { @Test public void testHideToast_finishesToken() throws Exception { - mToastUI.showToast(PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, + mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, mCallback); mToastUI.hideToast(PACKAGE_NAME_1, TOKEN_1); @@ -181,7 +229,7 @@ public class ToastUITest extends SysuiTestCase { @Test public void testHideToast_callsCallback() throws Exception { - mToastUI.showToast(PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, + mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, mCallback); mToastUI.hideToast(PACKAGE_NAME_1, TOKEN_1); @@ -191,7 +239,7 @@ public class ToastUITest extends SysuiTestCase { @Test public void testHideToast_whenNotCurrentToastToken_doesNotHideToast() throws Exception { - mToastUI.showToast(PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, + mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, mCallback); mToastUI.hideToast(PACKAGE_NAME_1, TOKEN_2); @@ -201,7 +249,7 @@ public class ToastUITest extends SysuiTestCase { @Test public void testHideToast_whenNotCurrentToastPackage_doesNotHideToast() throws Exception { - mToastUI.showToast(PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, + mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, mCallback); mToastUI.hideToast(PACKAGE_NAME_2, TOKEN_1); @@ -211,11 +259,12 @@ public class ToastUITest extends SysuiTestCase { @Test public void testShowToast_afterShowToast_hidesCurrentToast() throws Exception { - mToastUI.showToast(PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, + mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, mCallback); View view = verifyWmAddViewAndAttachToParent(); - mToastUI.showToast(PACKAGE_NAME_2, TOKEN_2, TEXT, WINDOW_TOKEN_2, Toast.LENGTH_LONG, null); + mToastUI.showToast(UID_2, PACKAGE_NAME_2, TOKEN_2, TEXT, WINDOW_TOKEN_2, Toast.LENGTH_LONG, + null); verify(mWindowManager).removeViewImmediate(view); verify(mNotificationManager).finishToken(PACKAGE_NAME_1, TOKEN_1); @@ -227,8 +276,15 @@ public class ToastUITest extends SysuiTestCase { verify(mWindowManager).addView(viewCaptor.capture(), any()); View view = viewCaptor.getValue(); // Simulate attaching to view hierarchy - ViewGroup parent = new FrameLayout(mContext); + ViewGroup parent = new FrameLayout(mContextSpy); parent.addView(view); return view; } + + private Answer<Void> writeArgumentToParcel(int i, Parcel dest) { + return inv -> { + inv.<Parcelable>getArgument(i).writeToParcel(dest, 0); + return null; + }; + } } diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java index c5478d2e1a14..5b6fe91b9c1a 100644 --- a/packages/Tethering/src/android/net/ip/IpServer.java +++ b/packages/Tethering/src/android/net/ip/IpServer.java @@ -33,6 +33,7 @@ import android.net.LinkAddress; import android.net.LinkProperties; import android.net.MacAddress; import android.net.RouteInfo; +import android.net.TetherOffloadRuleParcel; import android.net.TetheredClient; import android.net.TetheringManager; import android.net.TetheringRequestParcel; @@ -40,7 +41,7 @@ import android.net.dhcp.DhcpLeaseParcelable; import android.net.dhcp.DhcpServerCallbacks; import android.net.dhcp.DhcpServingParamsParcel; import android.net.dhcp.DhcpServingParamsParcelExt; -import android.net.dhcp.IDhcpLeaseCallbacks; +import android.net.dhcp.IDhcpEventCallbacks; import android.net.dhcp.IDhcpServer; import android.net.ip.IpNeighborMonitor.NeighborEvent; import android.net.ip.RouterAdvertisementDaemon.RaParams; @@ -279,6 +280,19 @@ public class IpServer extends StateMachine { return new Ipv6ForwardingRule(newUpstreamIfindex, downstreamIfindex, address, srcMac, dstMac); } + + // Don't manipulate TetherOffloadRuleParcel directly because implementing onNewUpstream() + // would be error-prone due to generated stable AIDL classes not having a copy constructor. + public TetherOffloadRuleParcel toTetherOffloadRuleParcel() { + final TetherOffloadRuleParcel parcel = new TetherOffloadRuleParcel(); + parcel.inputInterfaceIndex = upstreamIfindex; + parcel.outputInterfaceIndex = downstreamIfindex; + parcel.destination = address.getAddress(); + parcel.prefixLength = 128; + parcel.srcL2Address = srcMac.toByteArray(); + parcel.dstL2Address = dstMac.toByteArray(); + return parcel; + } } private final LinkedHashMap<Inet6Address, Ipv6ForwardingRule> mIpv6ForwardingRules = new LinkedHashMap<>(); @@ -448,7 +462,7 @@ public class IpServer extends StateMachine { } } - private class DhcpLeaseCallback extends IDhcpLeaseCallbacks.Stub { + private class DhcpLeaseCallback extends IDhcpEventCallbacks.Stub { @Override public void onLeasesChanged(List<DhcpLeaseParcelable> leaseParcelables) { final ArrayList<TetheredClient> leases = new ArrayList<>(); @@ -482,6 +496,11 @@ public class IpServer extends StateMachine { } @Override + public void onNewPrefixRequest(IpPrefix currentPrefix) { + //TODO: add specific implementation. + } + + @Override public int getInterfaceVersion() { return this.VERSION; } @@ -815,9 +834,7 @@ public class IpServer extends StateMachine { private void addIpv6ForwardingRule(Ipv6ForwardingRule rule) { try { - mNetd.tetherRuleAddDownstreamIpv6(mInterfaceParams.index, rule.upstreamIfindex, - rule.address.getAddress(), mInterfaceParams.macAddr.toByteArray(), - rule.dstMac.toByteArray()); + mNetd.tetherOffloadRuleAdd(rule.toTetherOffloadRuleParcel()); mIpv6ForwardingRules.put(rule.address, rule); } catch (RemoteException | ServiceSpecificException e) { mLog.e("Could not add IPv6 downstream rule: ", e); @@ -826,7 +843,7 @@ public class IpServer extends StateMachine { private void removeIpv6ForwardingRule(Ipv6ForwardingRule rule, boolean removeFromMap) { try { - mNetd.tetherRuleRemoveDownstreamIpv6(rule.upstreamIfindex, rule.address.getAddress()); + mNetd.tetherOffloadRuleRemove(rule.toTetherOffloadRuleParcel()); if (removeFromMap) { mIpv6ForwardingRules.remove(rule.address); } diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java b/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java index bd60594f27bc..639cf65d7936 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java @@ -20,6 +20,7 @@ import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE; import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK; import static android.net.TetheringConstants.EXTRA_RUN_PROVISION; import static android.net.TetheringManager.TETHERING_BLUETOOTH; +import static android.net.TetheringManager.TETHERING_ETHERNET; import static android.net.TetheringManager.TETHERING_INVALID; import static android.net.TetheringManager.TETHERING_USB; import static android.net.TetheringManager.TETHERING_WIFI; @@ -537,6 +538,7 @@ public class EntitlementManager { private static boolean isValidDownstreamType(int type) { switch (type) { case TETHERING_BLUETOOTH: + case TETHERING_ETHERNET: case TETHERING_USB: case TETHERING_WIFI: return true; @@ -650,6 +652,11 @@ public class EntitlementManager { private void handleRequestLatestTetheringEntitlementValue(int downstream, ResultReceiver receiver, boolean showEntitlementUi) { + if (!isValidDownstreamType(downstream)) { + receiver.send(TETHER_ERROR_ENTITLEMENT_UNKNOWN, null); + return; + } + final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); if (!isTetherProvisioningRequired(config)) { receiver.send(TETHER_ERROR_NO_ERROR, null); diff --git a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java index 843a4f19c3b8..dbd68ef77cb7 100644 --- a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java +++ b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java @@ -18,6 +18,7 @@ package android.net; import static android.Manifest.permission.MANAGE_TEST_NETWORKS; import static android.Manifest.permission.NETWORK_SETTINGS; +import static android.net.TetheringManager.TETHERING_ETHERNET; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -57,14 +58,17 @@ import org.junit.runner.RunWith; import java.io.FileDescriptor; import java.net.Inet4Address; +import java.net.InterfaceAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.nio.ByteBuffer; import java.util.Collection; import java.util.List; import java.util.Random; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; @RunWith(AndroidJUnit4.class) @MediumTest @@ -109,7 +113,7 @@ public class EthernetTetheringTest { } private void cleanUp() throws Exception { - mTm.stopTethering(TetheringManager.TETHERING_ETHERNET); + mTm.stopTethering(TETHERING_ETHERNET); if (mTetheringEventCallback != null) { mTetheringEventCallback.awaitInterfaceUntethered(); mTetheringEventCallback.unregister(); @@ -150,10 +154,7 @@ public class EthernetTetheringTest { Log.d(TAG, "Including test interfaces"); mEm.setIncludeTestInterfaces(true); - Log.d(TAG, "Requesting tethered interface"); - mTetheredInterfaceRequester.requestInterface(); - - final String iface = mTetheredInterfaceRequester.awaitRequestedInterface(); + final String iface = mTetheredInterfaceRequester.getInterface(); assertEquals("TetheredInterfaceCallback for unexpected interface", mTestIface.getInterfaceName(), iface); @@ -165,14 +166,13 @@ public class EthernetTetheringTest { // This test requires manipulating packets. Skip if there is a physical Ethernet connected. assumeFalse(mEm.isAvailable()); - Log.d(TAG, "Requesting tethered interface"); - mTetheredInterfaceRequester.requestInterface(); + CompletableFuture<String> futureIface = mTetheredInterfaceRequester.requestInterface(); mEm.setIncludeTestInterfaces(true); mTestIface = createTestInterface(); - final String iface = mTetheredInterfaceRequester.awaitRequestedInterface(); + final String iface = futureIface.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); assertEquals("TetheredInterfaceCallback for unexpected interface", mTestIface.getInterfaceName(), iface); @@ -180,12 +180,54 @@ public class EthernetTetheringTest { } @Test + public void testStaticIpv4() throws Exception { + assumeFalse(mEm.isAvailable()); + + mEm.setIncludeTestInterfaces(true); + + mTestIface = createTestInterface(); + + final String iface = mTetheredInterfaceRequester.getInterface(); + assertEquals("TetheredInterfaceCallback for unexpected interface", + mTestIface.getInterfaceName(), iface); + + assertInvalidStaticIpv4Request(iface, null, null); + assertInvalidStaticIpv4Request(iface, "2001:db8::1/64", "2001:db8:2::/64"); + assertInvalidStaticIpv4Request(iface, "192.0.2.2/28", "2001:db8:2::/28"); + assertInvalidStaticIpv4Request(iface, "2001:db8:2::/28", "192.0.2.2/28"); + assertInvalidStaticIpv4Request(iface, "192.0.2.2/28", null); + assertInvalidStaticIpv4Request(iface, null, "192.0.2.2/28"); + assertInvalidStaticIpv4Request(iface, "192.0.2.3/27", "192.0.2.2/28"); + + final String localAddr = "192.0.2.3/28"; + final String clientAddr = "192.0.2.2/28"; + mTetheringEventCallback = enableEthernetTethering(iface, + requestWithStaticIpv4(localAddr, clientAddr)); + + mTetheringEventCallback.awaitInterfaceTethered(); + assertInterfaceHasIpAddress(iface, clientAddr); + + byte[] client1 = MacAddress.fromString("1:2:3:4:5:6").toByteArray(); + byte[] client2 = MacAddress.fromString("a:b:c:d:e:f").toByteArray(); + + FileDescriptor fd = mTestIface.getFileDescriptor().getFileDescriptor(); + mTapPacketReader = makePacketReader(fd, getMTU(mTestIface)); + DhcpResults dhcpResults = runDhcp(fd, client1); + assertEquals(new LinkAddress(clientAddr), dhcpResults.ipAddress); + + try { + runDhcp(fd, client2); + fail("Only one client should get an IP address"); + } catch (TimeoutException expected) { } + + } + + @Test public void testPhysicalEthernet() throws Exception { assumeTrue(mEm.isAvailable()); // Get an interface to use. - mTetheredInterfaceRequester.requestInterface(); - String iface = mTetheredInterfaceRequester.awaitRequestedInterface(); + final String iface = mTetheredInterfaceRequester.getInterface(); // Enable Ethernet tethering and check that it starts. mTetheringEventCallback = enableEthernetTethering(iface); @@ -275,7 +317,8 @@ public class EthernetTetheringTest { } } - private MyTetheringEventCallback enableEthernetTethering(String iface) throws Exception { + private MyTetheringEventCallback enableEthernetTethering(String iface, + TetheringRequest request) throws Exception { MyTetheringEventCallback callback = new MyTetheringEventCallback(mTm, iface); mTm.registerTetheringEventCallback(mHandler::post, callback); @@ -286,34 +329,37 @@ public class EthernetTetheringTest { } }; Log.d(TAG, "Starting Ethernet tethering"); - mTm.startTethering( - new TetheringRequest.Builder(TetheringManager.TETHERING_ETHERNET).build(), - mHandler::post /* executor */, startTetheringCallback); + mTm.startTethering(request, mHandler::post /* executor */, startTetheringCallback); callback.awaitInterfaceTethered(); return callback; } + private MyTetheringEventCallback enableEthernetTethering(String iface) throws Exception { + return enableEthernetTethering(iface, + new TetheringRequest.Builder(TETHERING_ETHERNET).build()); + } + private int getMTU(TestNetworkInterface iface) throws SocketException { NetworkInterface nif = NetworkInterface.getByName(iface.getInterfaceName()); assertNotNull("Can't get NetworkInterface object for " + iface.getInterfaceName(), nif); return nif.getMTU(); } - private void checkVirtualEthernet(TestNetworkInterface iface, int mtu) throws Exception { - FileDescriptor fd = iface.getFileDescriptor().getFileDescriptor(); - mTapPacketReader = new TapPacketReader(mHandler, fd, mtu); - mHandler.post(() -> mTapPacketReader.start()); + private TapPacketReader makePacketReader(FileDescriptor fd, int mtu) { + final TapPacketReader reader = new TapPacketReader(mHandler, fd, mtu); + mHandler.post(() -> reader.start()); HandlerUtilsKt.waitForIdle(mHandler, TIMEOUT_MS); + return reader; + } + private void checkVirtualEthernet(TestNetworkInterface iface, int mtu) throws Exception { + FileDescriptor fd = iface.getFileDescriptor().getFileDescriptor(); + mTapPacketReader = makePacketReader(fd, mtu); mTetheringEventCallback = enableEthernetTethering(iface.getInterfaceName()); checkTetheredClientCallbacks(fd); } - private void checkTetheredClientCallbacks(FileDescriptor fd) throws Exception { - // Create a fake client. - byte[] clientMacAddr = new byte[6]; - new Random().nextBytes(clientMacAddr); - + private DhcpResults runDhcp(FileDescriptor fd, byte[] clientMacAddr) throws Exception { // We have to retransmit DHCP requests because IpServer declares itself to be ready before // its DhcpServer is actually started. TODO: fix this race and remove this loop. DhcpPacket offerPacket = null; @@ -323,13 +369,25 @@ public class EthernetTetheringTest { offerPacket = getNextDhcpPacket(); if (offerPacket instanceof DhcpOfferPacket) break; } - assertTrue("No DHCPOFFER received on interface within timeout", - offerPacket instanceof DhcpOfferPacket); + if (!(offerPacket instanceof DhcpOfferPacket)) { + throw new TimeoutException("No DHCPOFFER received on interface within timeout"); + } sendDhcpRequest(fd, offerPacket, clientMacAddr); DhcpPacket ackPacket = getNextDhcpPacket(); - assertTrue("No DHCPACK received on interface within timeout", - ackPacket instanceof DhcpAckPacket); + if (!(ackPacket instanceof DhcpAckPacket)) { + throw new TimeoutException("No DHCPACK received on interface within timeout"); + } + + return ackPacket.toDhcpResults(); + } + + private void checkTetheredClientCallbacks(FileDescriptor fd) throws Exception { + // Create a fake client. + byte[] clientMacAddr = new byte[6]; + new Random().nextBytes(clientMacAddr); + + DhcpResults dhcpResults = runDhcp(fd, clientMacAddr); final Collection<TetheredClient> clients = mTetheringEventCallback.awaitClientConnected(); assertEquals(1, clients.size()); @@ -337,7 +395,7 @@ public class EthernetTetheringTest { // Check the MAC address. assertEquals(MacAddress.fromBytes(clientMacAddr), client.getMacAddress()); - assertEquals(TetheringManager.TETHERING_ETHERNET, client.getTetheringType()); + assertEquals(TETHERING_ETHERNET, client.getTetheringType()); // Check the hostname. assertEquals(1, client.getAddresses().size()); @@ -345,7 +403,6 @@ public class EthernetTetheringTest { assertEquals(DHCP_HOSTNAME, info.getHostname()); // Check the address is the one that was handed out in the DHCP ACK. - DhcpResults dhcpResults = offerPacket.toDhcpResults(); assertLinkAddressMatches(dhcpResults.ipAddress, info.getAddress()); // Check that the lifetime is correct +/- 10s. @@ -373,8 +430,8 @@ public class EthernetTetheringTest { private final Handler mHandler; private final EthernetManager mEm; - private volatile TetheredInterfaceRequest mRequest; - private volatile String mIface; + private TetheredInterfaceRequest mRequest; + private final CompletableFuture<String> mFuture = new CompletableFuture<>(); TetheredInterfaceRequester(Handler handler, EthernetManager em) { mHandler = handler; @@ -384,25 +441,28 @@ public class EthernetTetheringTest { @Override public void onAvailable(String iface) { Log.d(TAG, "Ethernet interface available: " + iface); - mIface = iface; - mInterfaceAvailableLatch.countDown(); + mFuture.complete(iface); } + @Override - public void onUnavailable() {} + public void onUnavailable() { + mFuture.completeExceptionally(new IllegalStateException("onUnavailable received")); + } - public void requestInterface() { + public CompletableFuture<String> requestInterface() { assertNull("BUG: more than one tethered interface request", mRequest); + Log.d(TAG, "Requesting tethered interface"); mRequest = mEm.requestTetheredInterface(mHandler::post, this); + return mFuture; } - public String awaitRequestedInterface() throws InterruptedException { - assertTrue("No tethered interface available after " + TIMEOUT_MS + "ms", - mInterfaceAvailableLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); - return mIface; + public String getInterface() throws Exception { + return requestInterface().get(TIMEOUT_MS, TimeUnit.MILLISECONDS); } public void release() { if (mRequest != null) { + mFuture.obtrudeException(new IllegalStateException("Request already released")); mRequest.release(); mRequest = null; } @@ -442,6 +502,34 @@ public class EthernetTetheringTest { assertEquals("LinkAddress scope does not match", l1.getScope(), l2.getScope()); } + private TetheringRequest requestWithStaticIpv4(String local, String client) { + LinkAddress localAddr = local == null ? null : new LinkAddress(local); + LinkAddress clientAddr = client == null ? null : new LinkAddress(client); + return new TetheringRequest.Builder(TETHERING_ETHERNET) + .setStaticIpv4Addresses(localAddr, clientAddr).build(); + } + + private void assertInvalidStaticIpv4Request(String iface, String local, String client) + throws Exception { + try { + enableEthernetTethering(iface, requestWithStaticIpv4(local, client)); + fail("Unexpectedly accepted invalid IPv4 configuration: " + local + ", " + client); + } catch (IllegalArgumentException | NullPointerException expected) { } + } + + private void assertInterfaceHasIpAddress(String iface, String expected) throws Exception { + LinkAddress expectedAddr = new LinkAddress(expected); + NetworkInterface nif = NetworkInterface.getByName(iface); + for (InterfaceAddress ia : nif.getInterfaceAddresses()) { + final LinkAddress addr = new LinkAddress(ia.getAddress(), ia.getNetworkPrefixLength()); + if (expectedAddr.equals(addr)) { + return; + } + } + fail("Expected " + iface + " to have IP address " + expected + ", found " + + nif.getInterfaceAddresses()); + } + private TestNetworkInterface createTestInterface() throws Exception { TestNetworkManager tnm = mContext.getSystemService(TestNetworkManager.class); TestNetworkInterface iface = tnm.createTapInterface(); diff --git a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java index 3106e0e5e1c6..fdfdae837d51 100644 --- a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java +++ b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java @@ -43,7 +43,6 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doAnswer; @@ -66,6 +65,7 @@ import android.net.LinkAddress; import android.net.LinkProperties; import android.net.MacAddress; import android.net.RouteInfo; +import android.net.TetherOffloadRuleParcel; import android.net.dhcp.DhcpServingParamsParcel; import android.net.dhcp.IDhcpServer; import android.net.dhcp.IDhcpServerCallbacks; @@ -85,6 +85,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatcher; import org.mockito.Captor; import org.mockito.InOrder; import org.mockito.Mock; @@ -92,6 +93,7 @@ import org.mockito.MockitoAnnotations; import java.net.Inet4Address; import java.net.InetAddress; +import java.util.Arrays; @RunWith(AndroidJUnit4.class) @SmallTest @@ -514,6 +516,65 @@ public class IpServerTest { mLooper.dispatchAll(); } + /** + * Custom ArgumentMatcher for TetherOffloadRuleParcel. This is needed because generated stable + * AIDL classes don't have equals(), so we cannot just use eq(). A custom assert, such as: + * + * private void checkFooCalled(StableParcelable p, ...) { + * ArgumentCaptor<FooParam> captor = ArgumentCaptor.forClass(FooParam.class); + * verify(mMock).foo(captor.capture()); + * Foo foo = captor.getValue(); + * assertFooMatchesExpectations(foo); + * } + * + * almost works, but not quite. This is because if the code under test calls foo() twice, the + * first call to checkFooCalled() matches both the calls, putting both calls into the captor, + * and then fails with TooManyActualInvocations. It also makes it harder to use other mockito + * features such as never(), inOrder(), etc. + * + * This approach isn't great because if the match fails, the error message is unhelpful + * (actual: "android.net.TetherOffloadRuleParcel@8c827b0" or some such), but at least it does + * work. + * + * See ConnectivityServiceTest#assertRoutesAdded for an alternative approach which solves the + * TooManyActualInvocations problem described above by forcing the caller of the custom assert + * method to specify all expected invocations in one call. This is useful when the stable + * parcelable class being asserted on has a corresponding Java object (eg., RouteInfo and + * RouteInfoParcelable), and the caller can just pass in a list of them. It not useful here + * because there is no such object. + */ + private static class TetherOffloadRuleParcelMatcher implements + ArgumentMatcher<TetherOffloadRuleParcel> { + public final int upstreamIfindex; + public final InetAddress dst; + public final MacAddress dstMac; + + TetherOffloadRuleParcelMatcher(int upstreamIfindex, InetAddress dst, MacAddress dstMac) { + this.upstreamIfindex = upstreamIfindex; + this.dst = dst; + this.dstMac = dstMac; + } + + public boolean matches(TetherOffloadRuleParcel parcel) { + return upstreamIfindex == parcel.inputInterfaceIndex + && (TEST_IFACE_PARAMS.index == parcel.outputInterfaceIndex) + && Arrays.equals(dst.getAddress(), parcel.destination) + && (128 == parcel.prefixLength) + && Arrays.equals(TEST_IFACE_PARAMS.macAddr.toByteArray(), parcel.srcL2Address) + && Arrays.equals(dstMac.toByteArray(), parcel.dstL2Address); + } + + public String toString() { + return String.format("TetherOffloadRuleParcelMatcher(%d, %s, %s", + upstreamIfindex, dst.getHostAddress(), dstMac); + } + } + + private TetherOffloadRuleParcel matches( + int upstreamIfindex, InetAddress dst, MacAddress dstMac) { + return argThat(new TetherOffloadRuleParcelMatcher(upstreamIfindex, dst, dstMac)); + } + @Test public void addRemoveipv6ForwardingRules() throws Exception { initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */); @@ -537,13 +598,11 @@ public class IpServerTest { // Events on this interface are received and sent to netd. recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA); - verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX), - eq(neighA.getAddress()), eq(myMac.toByteArray()), eq(macA.toByteArray())); + verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA)); reset(mNetd); recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB); - verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX), - eq(neighB.getAddress()), eq(myMac.toByteArray()), eq(macB.toByteArray())); + verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighB, macB)); reset(mNetd); // Link-local and multicast neighbors are ignored. @@ -554,12 +613,12 @@ public class IpServerTest { // A neighbor that is no longer valid causes the rule to be removed. recvNewNeigh(myIfindex, neighA, NUD_FAILED, macA); - verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighA.getAddress())); + verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macA)); reset(mNetd); // A neighbor that is deleted causes the rule to be removed. recvDelNeigh(myIfindex, neighB, NUD_STALE, macB); - verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighB.getAddress())); + verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB)); reset(mNetd); // Upstream changes result in deleting and re-adding the rules. @@ -571,22 +630,16 @@ public class IpServerTest { LinkProperties lp = new LinkProperties(); lp.setInterfaceName(UPSTREAM_IFACE2); dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp); - inOrder.verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX2), - eq(neighA.getAddress()), eq(myMac.toByteArray()), eq(macA.toByteArray())); - inOrder.verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), - eq(neighA.getAddress())); - inOrder.verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX2), - eq(neighB.getAddress()), eq(myMac.toByteArray()), eq(macB.toByteArray())); - inOrder.verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), - eq(neighB.getAddress())); + inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX2, neighA, macA)); + inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macA)); + inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX2, neighB, macB)); + inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB)); reset(mNetd); // When the upstream is lost, rules are removed. dispatchTetherConnectionChanged(null, null); - verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX2), - eq(neighA.getAddress())); - verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX2), - eq(neighB.getAddress())); + verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX2, neighA, macA)); + verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX2, neighB, macB)); reset(mNetd); // If the upstream is IPv4-only, no rules are added. @@ -599,31 +652,27 @@ public class IpServerTest { lp.setInterfaceName(UPSTREAM_IFACE); dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp); recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB); - verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX), - eq(neighB.getAddress()), eq(myMac.toByteArray()), eq(macB.toByteArray())); - verify(mNetd, never()).tetherRuleAddDownstreamIpv6(anyInt(), anyInt(), - eq(neighA.getAddress()), any(), any()); + verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighB, macB)); + verify(mNetd, never()).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA)); // If upstream IPv6 connectivity is lost, rules are removed. reset(mNetd); dispatchTetherConnectionChanged(UPSTREAM_IFACE, null); - verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighB.getAddress())); + verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB)); // When the interface goes down, rules are removed. lp.setInterfaceName(UPSTREAM_IFACE); dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp); recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA); recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB); - verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX), - eq(neighA.getAddress()), eq(myMac.toByteArray()), eq(macA.toByteArray())); - verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX), - eq(neighB.getAddress()), eq(myMac.toByteArray()), eq(macB.toByteArray())); + verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA)); + verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighB, macB)); reset(mNetd); mIpServer.stop(); mLooper.dispatchAll(); - verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighA.getAddress())); - verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighB.getAddress())); + verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macA)); + verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB)); reset(mNetd); } diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java index 0a7850b68014..b3a30abca6f1 100644 --- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java +++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java @@ -17,8 +17,10 @@ package com.android.server.connectivity.tethering; import static android.net.TetheringManager.TETHERING_BLUETOOTH; +import static android.net.TetheringManager.TETHERING_ETHERNET; import static android.net.TetheringManager.TETHERING_USB; import static android.net.TetheringManager.TETHERING_WIFI; +import static android.net.TetheringManager.TETHERING_WIFI_P2P; import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN; import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; import static android.net.TetheringManager.TETHER_ERROR_PROVISIONING_FAILED; @@ -353,6 +355,20 @@ public final class EntitlementManagerTest { callbackTimeoutHelper(mCallbacklatch); assertEquals(0, mEnMgr.uiProvisionCount); mEnMgr.reset(); + // 8. Test get value for invalid downstream type. + mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; + receiver = new ResultReceiver(null) { + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + assertEquals(TETHER_ERROR_ENTITLEMENT_UNKNOWN, resultCode); + mCallbacklatch.countDown(); + } + }; + mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI_P2P, receiver, true); + mLooper.dispatchAll(); + callbackTimeoutHelper(mCallbacklatch); + assertEquals(0, mEnMgr.uiProvisionCount); + mEnMgr.reset(); } void callbackTimeoutHelper(final CountDownLatch latch) throws Exception { @@ -471,6 +487,22 @@ public final class EntitlementManagerTest { mLooper.dispatchAll(); assertEquals(0, mEnMgr.uiProvisionCount); assertEquals(3, mEnMgr.silentProvisionCount); + assertFalse(mEnMgr.isCellularUpstreamPermitted()); + mEnMgr.reset(); + // 7. start ui provisioning, upstream is mobile, downstream is ethernet + mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; + mEnMgr.startProvisioningIfNeeded(TETHERING_ETHERNET, true); + mLooper.dispatchAll(); + assertEquals(1, mEnMgr.uiProvisionCount); + assertEquals(0, mEnMgr.silentProvisionCount); + assertTrue(mEnMgr.isCellularUpstreamPermitted()); + mEnMgr.reset(); + // 8. downstream is invalid + mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; + mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI_P2P, true); + mLooper.dispatchAll(); + assertEquals(0, mEnMgr.uiProvisionCount); + assertEquals(0, mEnMgr.silentProvisionCount); mEnMgr.reset(); } diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinatorTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinatorTest.java new file mode 100644 index 000000000000..912124357ca8 --- /dev/null +++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinatorTest.java @@ -0,0 +1,156 @@ +/* + * 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.connectivity.tethering; + +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.net.RouteInfo.RTN_UNICAST; +import static android.net.ip.IpServer.STATE_LOCAL_ONLY; +import static android.net.ip.IpServer.STATE_TETHERED; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import android.net.InetAddresses; +import android.net.IpPrefix; +import android.net.LinkAddress; +import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.RouteInfo; +import android.net.ip.IpServer; +import android.net.util.SharedLog; + +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 org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.List; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class IPv6TetheringCoordinatorTest { + private static final String TEST_DNS_SERVER = "2001:4860:4860::8888"; + private static final String TEST_INTERFACE = "test_rmnet0"; + private static final String TEST_IPV6_ADDRESS = "2001:db8::1/64"; + private static final String TEST_IPV4_ADDRESS = "192.168.100.1/24"; + + private IPv6TetheringCoordinator mIPv6TetheringCoordinator; + private ArrayList<IpServer> mNotifyList; + + @Mock private SharedLog mSharedLog; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + when(mSharedLog.forSubComponent(anyString())).thenReturn(mSharedLog); + mNotifyList = new ArrayList<IpServer>(); + mIPv6TetheringCoordinator = new IPv6TetheringCoordinator(mNotifyList, mSharedLog); + } + + private UpstreamNetworkState createDualStackUpstream(final int transportType) { + final Network network = mock(Network.class); + final NetworkCapabilities netCap = + new NetworkCapabilities.Builder().addTransportType(transportType).build(); + final InetAddress dns = InetAddresses.parseNumericAddress(TEST_DNS_SERVER); + final LinkProperties linkProp = new LinkProperties(); + linkProp.setInterfaceName(TEST_INTERFACE); + linkProp.addLinkAddress(new LinkAddress(TEST_IPV6_ADDRESS)); + linkProp.addLinkAddress(new LinkAddress(TEST_IPV4_ADDRESS)); + linkProp.addRoute(new RouteInfo(new IpPrefix("::/0"), null, TEST_INTERFACE, RTN_UNICAST)); + linkProp.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"), null, TEST_INTERFACE, + RTN_UNICAST)); + linkProp.addDnsServer(dns); + return new UpstreamNetworkState(linkProp, netCap, network); + } + + private void assertOnlyOneV6AddressAndNoV4(LinkProperties lp) { + assertEquals(lp.getInterfaceName(), TEST_INTERFACE); + assertFalse(lp.hasIpv4Address()); + final List<LinkAddress> addresses = lp.getLinkAddresses(); + assertEquals(addresses.size(), 1); + final LinkAddress v6Address = addresses.get(0); + assertEquals(v6Address, new LinkAddress(TEST_IPV6_ADDRESS)); + } + + @Test + public void testUpdateIpv6Upstream() throws Exception { + // 1. Add first IpServer. + final IpServer firstServer = mock(IpServer.class); + mNotifyList.add(firstServer); + mIPv6TetheringCoordinator.addActiveDownstream(firstServer, STATE_TETHERED); + verify(firstServer).sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null); + verifyNoMoreInteractions(firstServer); + + // 2. Add second IpServer and it would not have ipv6 tethering. + final IpServer secondServer = mock(IpServer.class); + mNotifyList.add(secondServer); + mIPv6TetheringCoordinator.addActiveDownstream(secondServer, STATE_LOCAL_ONLY); + verifyNoMoreInteractions(secondServer); + reset(firstServer, secondServer); + + // 3. No upstream. + mIPv6TetheringCoordinator.updateUpstreamNetworkState(null); + verify(secondServer).sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null); + reset(firstServer, secondServer); + + // 4. Update ipv6 mobile upstream. + final UpstreamNetworkState mobileUpstream = createDualStackUpstream(TRANSPORT_CELLULAR); + final ArgumentCaptor<LinkProperties> lp = ArgumentCaptor.forClass(LinkProperties.class); + mIPv6TetheringCoordinator.updateUpstreamNetworkState(mobileUpstream); + verify(firstServer).sendMessage(eq(IpServer.CMD_IPV6_TETHER_UPDATE), eq(0), eq(0), + lp.capture()); + final LinkProperties v6OnlyLink = lp.getValue(); + assertOnlyOneV6AddressAndNoV4(v6OnlyLink); + verifyNoMoreInteractions(firstServer); + verifyNoMoreInteractions(secondServer); + reset(firstServer, secondServer); + + // 5. Remove first IpServer. + mNotifyList.remove(firstServer); + mIPv6TetheringCoordinator.removeActiveDownstream(firstServer); + verify(firstServer).sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null); + verify(secondServer).sendMessage(eq(IpServer.CMD_IPV6_TETHER_UPDATE), eq(0), eq(0), + lp.capture()); + final LinkProperties localOnlyLink = lp.getValue(); + assertNotNull(localOnlyLink); + assertNotEquals(localOnlyLink, v6OnlyLink); + reset(firstServer, secondServer); + + // 6. Remove second IpServer. + mNotifyList.remove(secondServer); + mIPv6TetheringCoordinator.removeActiveDownstream(secondServer); + verifyNoMoreInteractions(firstServer); + verify(secondServer).sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null); + } +} diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 1b180e3357d7..2100c1a8be44 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -197,7 +197,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private final MainHandler mMainHandler; - private final SystemActionPerformer mSystemActionPerformer; + // Lazily initialized - access through getSystemActionPerfomer() + private SystemActionPerformer mSystemActionPerformer; private MagnificationController mMagnificationController; @@ -295,8 +296,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class); mPackageManager = mContext.getPackageManager(); mSecurityPolicy = new AccessibilitySecurityPolicy(mContext, this); - mSystemActionPerformer = - new SystemActionPerformer(mContext, mWindowManagerService, null, this); mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler, mWindowManagerService, this, mSecurityPolicy, this); mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler); @@ -667,7 +666,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mSecurityPolicy.enforceCallerIsRecentsOrHasPermission( Manifest.permission.MANAGE_ACCESSIBILITY, FUNCTION_REGISTER_SYSTEM_ACTION); - mSystemActionPerformer.registerSystemAction(actionId, action); + getSystemActionPerformer().registerSystemAction(actionId, action); } /** @@ -680,7 +679,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mSecurityPolicy.enforceCallerIsRecentsOrHasPermission( Manifest.permission.MANAGE_ACCESSIBILITY, FUNCTION_UNREGISTER_SYSTEM_ACTION); - mSystemActionPerformer.unregisterSystemAction(actionId); + getSystemActionPerformer().unregisterSystemAction(actionId); + } + + private SystemActionPerformer getSystemActionPerformer() { + if (mSystemActionPerformer == null) { + mSystemActionPerformer = + new SystemActionPerformer(mContext, mWindowManagerService, null, this); + } + return mSystemActionPerformer; } @Override @@ -792,7 +799,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub synchronized (mLock) { mUiAutomationManager.registerUiTestAutomationServiceLocked(owner, serviceClient, mContext, accessibilityServiceInfo, sIdCounter++, mMainHandler, - mSecurityPolicy, this, mWindowManagerService, mSystemActionPerformer, + mSecurityPolicy, this, mWindowManagerService, getSystemActionPerformer(), mA11yWindowManager, flags); onUserStateChangedLocked(getCurrentUserStateLocked()); } @@ -1503,7 +1510,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (service == null) { service = new AccessibilityServiceConnection(userState, mContext, componentName, installedService, sIdCounter++, mMainHandler, mLock, mSecurityPolicy, - this, mWindowManagerService, mSystemActionPerformer, + this, mWindowManagerService, getSystemActionPerformer(), mA11yWindowManager, mActivityTaskManagerService); } else if (userState.mBoundServices.contains(service)) { continue; @@ -2741,7 +2748,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub userState, mContext, COMPONENT_NAME, info, sIdCounter++, mMainHandler, mLock, mSecurityPolicy, AccessibilityManagerService.this, mWindowManagerService, - mSystemActionPerformer, mA11yWindowManager, mActivityTaskManagerService) { + getSystemActionPerformer(), mA11yWindowManager, mActivityTaskManagerService) { @Override public boolean supportsFlagForNotImportantViews(AccessibilityServiceInfo info) { return true; diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 2eaa766ad32d..8a1de1f0763e 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -3153,7 +3153,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - nai.clatd.setNat64Prefix(prefix); + nai.clatd.setNat64PrefixFromDns(prefix); handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties)); } @@ -5865,9 +5865,9 @@ public class ConnectivityService extends IConnectivityManager.Stub @NonNull LinkProperties oldLp) { int netId = networkAgent.network.netId; - // The NetworkAgentInfo does not know whether clatd is running on its network or not, or - // whether there is a NAT64 prefix. Before we do anything else, make sure its LinkProperties - // are accurate. + // The NetworkAgent does not know whether clatd is running on its network or not, or whether + // a NAT64 prefix was discovered by the DNS resolver. Before we do anything else, make sure + // the LinkProperties for the network are accurate. networkAgent.clatd.fixupLinkProperties(oldLp, newLp); updateInterfaces(newLp, oldLp, netId, networkAgent.networkCapabilities, diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java index d9e7c3851906..e77458cc955a 100644 --- a/services/core/java/com/android/server/PackageWatchdog.java +++ b/services/core/java/com/android/server/PackageWatchdog.java @@ -315,28 +315,31 @@ public class PackageWatchdog { // causing any elapsed time to be deducted from all existing packages before we add new // packages. This maintains the invariant that the elapsed time for ALL (new and existing) // packages is the same. - syncState("observing new packages"); + mLongTaskHandler.post(() -> { + syncState("observing new packages"); - synchronized (mLock) { - ObserverInternal oldObserver = mAllObservers.get(observer.getName()); - if (oldObserver == null) { - Slog.d(TAG, observer.getName() + " started monitoring health " - + "of packages " + packageNames); - mAllObservers.put(observer.getName(), - new ObserverInternal(observer.getName(), packages)); - } else { - Slog.d(TAG, observer.getName() + " added the following " - + "packages to monitor " + packageNames); - oldObserver.updatePackagesLocked(packages); + synchronized (mLock) { + ObserverInternal oldObserver = mAllObservers.get(observer.getName()); + if (oldObserver == null) { + Slog.d(TAG, observer.getName() + " started monitoring health " + + "of packages " + packageNames); + mAllObservers.put(observer.getName(), + new ObserverInternal(observer.getName(), packages)); + } else { + Slog.d(TAG, observer.getName() + " added the following " + + "packages to monitor " + packageNames); + oldObserver.updatePackagesLocked(packages); + } } - } - // Register observer in case not already registered - registerHealthObserver(observer); + // Register observer in case not already registered + registerHealthObserver(observer); + + // Sync after we add the new packages to the observers. We may have received packges + // requiring an earlier schedule than we are currently scheduled for. + syncState("updated observers"); + }); - // Sync after we add the new packages to the observers. We may have received packges - // requiring an earlier schedule than we are currently scheduled for. - syncState("updated observers"); } /** diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 7523710dde9f..21760cdf02eb 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -1440,7 +1440,7 @@ public final class ActiveServices { active.mPackageName = r.packageName; active.mUid = r.appInfo.uid; active.mShownWhileScreenOn = mScreenOn; - if (r.app != null) { + if (r.app != null && r.app.uidRecord != null) { active.mAppOnTop = active.mShownWhileTop = r.app.uidRecord.getCurProcState() <= ActivityManager.PROCESS_STATE_TOP; diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java index 3ecf87c6860f..ad73b6491697 100644 --- a/services/core/java/com/android/server/biometrics/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/face/FaceService.java @@ -382,8 +382,10 @@ public class FaceService extends BiometricServiceBase { checkPermission(MANAGE_BIOMETRIC); updateActiveGroup(userId, opPackageName); - mNotificationManager.cancelAsUser(NOTIFICATION_TAG, NOTIFICATION_ID, - UserHandle.CURRENT); + mHandler.post(() -> { + mNotificationManager.cancelAsUser(NOTIFICATION_TAG, NOTIFICATION_ID, + UserHandle.CURRENT); + }); final boolean restricted = isRestricted(); final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper, diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java index 82465f8a093e..741cb5b41ea3 100644 --- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java +++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java @@ -81,7 +81,7 @@ public class Nat464Xlat extends BaseNetworkObserver { RUNNING, // start() called, and the stacked iface is known to be up. } - private IpPrefix mNat64Prefix; + private IpPrefix mNat64PrefixFromDns; private String mBaseIface; private String mIface; private Inet6Address mIPv6Address; @@ -100,7 +100,7 @@ public class Nat464Xlat extends BaseNetworkObserver { * currently connected and where the NetworkAgent has not disabled 464xlat. It is the signal to * enable NAT64 prefix discovery. * - * @param network the NetworkAgentInfo corresponding to the network. + * @param nai the NetworkAgentInfo corresponding to the network. * @return true if the network requires clat, false otherwise. */ @VisibleForTesting @@ -180,7 +180,7 @@ public class Nat464Xlat extends BaseNetworkObserver { String addrStr = null; try { - addrStr = mNetd.clatdStart(baseIface, mNat64Prefix.toString()); + addrStr = mNetd.clatdStart(baseIface, getNat64Prefix().toString()); } catch (RemoteException | ServiceSpecificException e) { Slog.e(TAG, "Error starting clatd on " + baseIface + ": " + e); } @@ -213,12 +213,10 @@ public class Nat464Xlat extends BaseNetworkObserver { } mIface = null; mBaseIface = null; - mState = State.IDLE; if (requiresClat(mNetwork)) { mState = State.DISCOVERING; } else { stopPrefixDiscovery(); - mState = State.IDLE; } } @@ -285,6 +283,7 @@ public class Nat464Xlat extends BaseNetworkObserver { private void stopPrefixDiscovery() { try { mDnsResolver.stopPrefix64Discovery(getNetId()); + mState = State.IDLE; } catch (RemoteException | ServiceSpecificException e) { Slog.e(TAG, "Error stopping prefix discovery on netId " + getNetId() + ": " + e); } @@ -294,32 +293,52 @@ public class Nat464Xlat extends BaseNetworkObserver { * Starts/stops NAT64 prefix discovery and clatd as necessary. */ public void update() { - // TODO: turn this class into a proper StateMachine. // http://b/126113090 - if (requiresClat(mNetwork)) { - if (!isPrefixDiscoveryStarted()) { - startPrefixDiscovery(); - } else if (shouldStartClat(mNetwork)) { - // NAT64 prefix detected. Start clatd. - // TODO: support the NAT64 prefix changing after it's been discovered. There is no - // need to support this at the moment because it cannot happen without changes to - // the Dns64Configuration code in netd. - start(); - } else { - // NAT64 prefix removed. Stop clatd and go back into DISCOVERING state. - stop(); - } - } else { - // Network no longer requires clat. Stop clat and prefix discovery. - if (isStarted()) { - stop(); - } else if (isPrefixDiscoveryStarted()) { - leaveStartedState(); - } + // TODO: turn this class into a proper StateMachine. http://b/126113090 + switch (mState) { + case IDLE: + if (requiresClat(mNetwork)) { + // Network is detected to be IPv6-only. + // TODO: consider going to STARTING directly if the NAT64 prefix is already + // known. This would however result in clatd running without prefix discovery + // running, which might be a surprising combination. + startPrefixDiscovery(); // Enters DISCOVERING state. + return; + } + break; + + case DISCOVERING: + if (shouldStartClat(mNetwork)) { + // NAT64 prefix detected. Start clatd. + start(); // Enters STARTING state. + return; + } + if (!requiresClat(mNetwork)) { + // IPv4 address added. Go back to IDLE state. + stopPrefixDiscovery(); + return; + } + break; + + case STARTING: + case RUNNING: + // NAT64 prefix removed, or IPv4 address added. + // Stop clatd and go back into DISCOVERING or idle. + if (!shouldStartClat(mNetwork)) { + stop(); + } + break; + // TODO: support the NAT64 prefix changing after it's been discovered. There is + // no need to support this at the moment because it cannot happen without + // changes to the Dns64Configuration code in netd. } } - public void setNat64Prefix(IpPrefix nat64Prefix) { - mNat64Prefix = nat64Prefix; + private IpPrefix getNat64Prefix() { + return mNat64PrefixFromDns; + } + + public void setNat64PrefixFromDns(IpPrefix prefix) { + mNat64PrefixFromDns = prefix; } /** @@ -328,7 +347,7 @@ public class Nat464Xlat extends BaseNetworkObserver { * has no idea that 464xlat is running on top of it. */ public void fixupLinkProperties(@NonNull LinkProperties oldLp, @NonNull LinkProperties lp) { - lp.setNat64Prefix(mNat64Prefix); + lp.setNat64Prefix(getNat64Prefix()); if (!isRunning()) { return; diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java index 61b18eedfc6e..962f337a8b3f 100644 --- a/services/core/java/com/android/server/content/ContentService.java +++ b/services/core/java/com/android/server/content/ContentService.java @@ -80,6 +80,7 @@ import com.android.server.pm.permission.PermissionManagerServiceInternal; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -398,78 +399,93 @@ public final class ContentService extends IContentService.Stub { */ @Override public void notifyChange(Uri[] uris, IContentObserver observer, - boolean observerWantsSelfNotifications, int flags, int userHandle, + boolean observerWantsSelfNotifications, int flags, int userId, int targetSdkVersion, String callingPackage) { - final ObserverCollector collector = new ObserverCollector(); - for (Uri uri : uris) { - notifyChange(uri, observer, observerWantsSelfNotifications, flags, userHandle, - targetSdkVersion, callingPackage, collector); - } - final long token = clearCallingIdentity(); - try { - collector.dispatch(); - } finally { - Binder.restoreCallingIdentity(token); - } - } - - public void notifyChange(Uri uri, IContentObserver observer, - boolean observerWantsSelfNotifications, int flags, int userHandle, - int targetSdkVersion, String callingPackage, ObserverCollector collector) { - if (DEBUG) Slog.d(TAG, "Notifying update of " + uri + " for user " + userHandle - + " from observer " + observer + ", flags " + Integer.toHexString(flags)); - - if (uri == null) { - throw new NullPointerException("Uri must not be null"); + if (DEBUG) { + Slog.d(TAG, "Notifying update of " + Arrays.toString(uris) + " for user " + userId + + ", observer " + observer + ", flags " + Integer.toHexString(flags)); } final int callingUid = Binder.getCallingUid(); final int callingPid = Binder.getCallingPid(); - final int callingUserHandle = UserHandle.getCallingUserId(); + final int callingUserId = UserHandle.getCallingUserId(); + + // Set of notification events that we need to dispatch + final ObserverCollector collector = new ObserverCollector(); - userHandle = handleIncomingUser(uri, callingPid, callingUid, - Intent.FLAG_GRANT_WRITE_URI_PERMISSION, true, userHandle); + // Set of content provider authorities that we've validated the caller + // has access to, mapped to the package name hosting that provider + final ArrayMap<Pair<String, Integer>, String> validatedProviders = new ArrayMap<>(); - final String msg = LocalServices.getService(ActivityManagerInternal.class) - .checkContentProviderAccess(uri.getAuthority(), userHandle); - if (msg != null) { - if (targetSdkVersion >= Build.VERSION_CODES.O) { - throw new SecurityException(msg); - } else { - if (msg.startsWith("Failed to find provider")) { - // Sigh, we need to quietly let apps targeting older API - // levels notify on non-existent providers. - } else { - Log.w(TAG, "Ignoring notify for " + uri + " from " + callingUid + ": " + msg); - return; + for (Uri uri : uris) { + // Validate that calling app has access to this provider + final int resolvedUserId = handleIncomingUser(uri, callingPid, callingUid, + Intent.FLAG_GRANT_WRITE_URI_PERMISSION, true, userId); + final Pair<String, Integer> provider = Pair.create(uri.getAuthority(), resolvedUserId); + if (!validatedProviders.containsKey(provider)) { + final String msg = LocalServices.getService(ActivityManagerInternal.class) + .checkContentProviderAccess(uri.getAuthority(), resolvedUserId); + if (msg != null) { + if (targetSdkVersion >= Build.VERSION_CODES.O) { + throw new SecurityException(msg); + } else { + if (msg.startsWith("Failed to find provider")) { + // Sigh, we need to quietly let apps targeting older API + // levels notify on non-existent providers. + } else { + Log.w(TAG, "Ignoring notify for " + uri + " from " + + callingUid + ": " + msg); + continue; + } + } } + + // Remember that we've validated this access + final String packageName = getProviderPackageName(uri, resolvedUserId); + validatedProviders.put(provider, packageName); } - } - // This makes it so that future permission checks will be in the context of this - // process rather than the caller's process. We will restore this before returning. - long identityToken = clearCallingIdentity(); - try { + // No concerns raised above, so caller has access; let's collect the + // notifications that should be dispatched synchronized (mRootNode) { - mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications, - flags, userHandle, collector); + final int segmentCount = ObserverNode.countUriSegments(uri); + mRootNode.collectObserversLocked(uri, segmentCount, 0, observer, + observerWantsSelfNotifications, flags, resolvedUserId, collector); } - if ((flags&ContentResolver.NOTIFY_SYNC_TO_NETWORK) != 0) { - SyncManager syncManager = getSyncManager(); - if (syncManager != null) { - syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle, - callingUid, - uri.getAuthority(), getSyncExemptionForCaller(callingUid), - callingUid, callingPid, callingPackage); + } + + final long token = clearCallingIdentity(); + try { + // Actually dispatch all the notifications we collected + collector.dispatch(); + + for (int i = 0; i < validatedProviders.size(); i++) { + final String authority = validatedProviders.keyAt(i).first; + final int resolvedUserId = validatedProviders.keyAt(i).second; + final String packageName = validatedProviders.valueAt(i); + + // Kick off sync adapters for any authorities we touched + if ((flags & ContentResolver.NOTIFY_SYNC_TO_NETWORK) != 0) { + SyncManager syncManager = getSyncManager(); + if (syncManager != null) { + syncManager.scheduleLocalSync(null /* all accounts */, callingUserId, + callingUid, + authority, getSyncExemptionForCaller(callingUid), + callingUid, callingPid, callingPackage); + } } - } - synchronized (mCache) { - final String providerPackageName = getProviderPackageName(uri, userHandle); - invalidateCacheLocked(userHandle, providerPackageName, uri); + // Invalidate caches for any authorities we touched + synchronized (mCache) { + for (Uri uri : uris) { + if (Objects.equals(uri.getAuthority(), authority)) { + invalidateCacheLocked(resolvedUserId, packageName, uri); + } + } + } } } finally { - restoreCallingIdentity(identityToken); + Binder.restoreCallingIdentity(token); } } @@ -1533,7 +1549,7 @@ public final class ContentService extends IContentService.Stub { } } - private String getUriSegment(Uri uri, int index) { + public static String getUriSegment(Uri uri, int index) { if (uri != null) { if (index == 0) { return uri.getAuthority(); @@ -1545,7 +1561,7 @@ public final class ContentService extends IContentService.Stub { } } - private int countUriSegments(Uri uri) { + public static int countUriSegments(Uri uri) { if (uri == null) { return 0; } @@ -1669,14 +1685,21 @@ public final class ContentService extends IContentService.Stub { } } + @VisibleForTesting + public void collectObserversLocked(Uri uri, int index, + IContentObserver observer, boolean observerWantsSelfNotifications, int flags, + int targetUserHandle, ObserverCollector collector) { + collectObserversLocked(uri, countUriSegments(uri), index, observer, + observerWantsSelfNotifications, flags, targetUserHandle, collector); + } + /** * targetUserHandle is either a hard user handle or is USER_ALL */ - public void collectObserversLocked(Uri uri, int index, IContentObserver observer, - boolean observerWantsSelfNotifications, int flags, - int targetUserHandle, ObserverCollector collector) { + public void collectObserversLocked(Uri uri, int segmentCount, int index, + IContentObserver observer, boolean observerWantsSelfNotifications, int flags, + int targetUserHandle, ObserverCollector collector) { String segment = null; - int segmentCount = countUriSegments(uri); if (index >= segmentCount) { // This is the leaf node, notify all observers if (DEBUG) Slog.d(TAG, "Collecting leaf observers @ #" + index + ", node " + mName); @@ -1696,7 +1719,7 @@ public final class ContentService extends IContentService.Stub { ObserverNode node = mChildren.get(i); if (segment == null || node.mName.equals(segment)) { // We found the child, - node.collectObserversLocked(uri, index + 1, observer, + node.collectObserversLocked(uri, segmentCount, index + 1, observer, observerWantsSelfNotifications, flags, targetUserHandle, collector); if (segment != null) { break; diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index ed3b9f1fc265..f9fc82bf05b1 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -214,7 +214,6 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; -import android.util.FeatureFlagUtils; import android.util.IntArray; import android.util.Log; import android.util.Pair; @@ -2745,15 +2744,15 @@ public class NotificationManagerService extends SystemService { return userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM : userId; } - private ToastRecord getToastRecord(int pid, String packageName, IBinder token, + private ToastRecord getToastRecord(int uid, int pid, String packageName, IBinder token, @Nullable CharSequence text, @Nullable ITransientNotification callback, int duration, Binder windowToken, int displayId, @Nullable ITransientNotificationCallback textCallback) { if (callback == null) { - return new TextToastRecord(this, mStatusBar, pid, packageName, token, text, duration, - windowToken, displayId, textCallback); + return new TextToastRecord(this, mStatusBar, uid, pid, packageName, token, text, + duration, windowToken, displayId, textCallback); } else { - return new CustomToastRecord(this, pid, packageName, token, callback, duration, + return new CustomToastRecord(this, uid, pid, packageName, token, callback, duration, windowToken, displayId); } } @@ -2878,8 +2877,8 @@ public class NotificationManagerService extends SystemService { Binder windowToken = new Binder(); mWindowManagerInternal.addWindowToken(windowToken, TYPE_TOAST, displayId); - record = getToastRecord(callingPid, pkg, token, text, callback, duration, - windowToken, displayId, textCallback); + record = getToastRecord(callingUid, callingPid, pkg, token, text, callback, + duration, windowToken, displayId, textCallback); mToastQueue.add(record); index = mToastQueue.size() - 1; keepProcessAliveForToastIfNeededLocked(callingPid); @@ -5617,18 +5616,16 @@ public class NotificationManagerService extends SystemService { mUsageStats.registerEnqueuedByApp(pkg); + final StatusBarNotification n = new StatusBarNotification( + pkg, opPkg, id, tag, notificationUid, callingPid, notification, + user, null, System.currentTimeMillis()); + // setup local book-keeping String channelId = notification.getChannelId(); if (mIsTelevision && (new Notification.TvExtender(notification)).getChannelId() != null) { channelId = (new Notification.TvExtender(notification)).getChannelId(); } - String shortcutId = notification.getShortcutId(); - if (FeatureFlagUtils.isEnabled(getContext(), - FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ) - && shortcutId == null - && notification.getNotificationStyle() == Notification.MessagingStyle.class) { - shortcutId = id + tag + NotificationChannel.PLACEHOLDER_CONVERSATION_ID; - } + String shortcutId = n.getShortcutId(getContext()); final NotificationChannel channel = mPreferencesHelper.getConversationNotificationChannel( pkg, notificationUid, channelId, shortcutId, true /* parent ok */, false /* includeDeleted */); @@ -5656,9 +5653,6 @@ public class NotificationManagerService extends SystemService { return; } - final StatusBarNotification n = new StatusBarNotification( - pkg, opPkg, id, tag, notificationUid, callingPid, notification, - user, null, System.currentTimeMillis()); final NotificationRecord r = new NotificationRecord(getContext(), n, channel); r.setIsAppImportanceLocked(mPreferencesHelper.getIsAppImportanceLocked(pkg, callingUid)); r.setPostSilently(postSilently); diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index 54a0f9f46892..192df4139b37 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -1383,9 +1383,8 @@ public final class NotificationRecord { || !Notification.MessagingStyle.class.equals(notification.getNotificationStyle())) { return false; } - if (mShortcutInfo == null - && !FeatureFlagUtils.isEnabled( - mContext, FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ)) { + if (mShortcutInfo == null && Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 0) == 1) { return false; } if (mIsNotConversationOverride) { diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index dbb246e9fbe8..8154988a4917 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -47,7 +47,6 @@ import android.service.notification.RankingHelperProto; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; -import android.util.FeatureFlagUtils; import android.util.Pair; import android.util.Slog; import android.util.SparseBooleanArray; @@ -181,8 +180,8 @@ public class PreferencesHelper implements RankingConfig { updateBadgingEnabled(); updateBubblesEnabled(); syncChannelsBypassingDnd(mContext.getUserId()); - mAllowInvalidShortcuts = FeatureFlagUtils.isEnabled(mContext, - FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ); + mAllowInvalidShortcuts = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 0) == 0; } public void readXml(XmlPullParser parser, boolean forRestore, int userId) diff --git a/services/core/java/com/android/server/notification/toast/CustomToastRecord.java b/services/core/java/com/android/server/notification/toast/CustomToastRecord.java index aca6f4853597..2b91a00f9da5 100644 --- a/services/core/java/com/android/server/notification/toast/CustomToastRecord.java +++ b/services/core/java/com/android/server/notification/toast/CustomToastRecord.java @@ -23,6 +23,7 @@ import android.app.ITransientNotification; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; +import android.os.UserHandle; import android.util.Slog; import com.android.server.notification.NotificationManagerService; @@ -35,11 +36,10 @@ public class CustomToastRecord extends ToastRecord { public final ITransientNotification callback; - public CustomToastRecord( - NotificationManagerService notificationManager, int pid, String packageName, - IBinder token, ITransientNotification callback, int duration, Binder windowToken, - int displayId) { - super(notificationManager, pid, packageName, token, duration, windowToken, displayId); + public CustomToastRecord(NotificationManagerService notificationManager, int uid, int pid, + String packageName, IBinder token, ITransientNotification callback, int duration, + Binder windowToken, int displayId) { + super(notificationManager, uid, pid, packageName, token, duration, windowToken, displayId); this.callback = checkNotNull(callback); } @@ -74,8 +74,8 @@ public class CustomToastRecord extends ToastRecord { public String toString() { return "CustomToastRecord{" + Integer.toHexString(System.identityHashCode(this)) + + " " + pid + ":" + pkg + "/" + UserHandle.formatUid(uid) + " token=" + token - + " packageName=" + pkg + " callback=" + callback + " duration=" + getDuration() + "}"; diff --git a/services/core/java/com/android/server/notification/toast/TextToastRecord.java b/services/core/java/com/android/server/notification/toast/TextToastRecord.java index 3c231b445f62..544520edc7fc 100644 --- a/services/core/java/com/android/server/notification/toast/TextToastRecord.java +++ b/services/core/java/com/android/server/notification/toast/TextToastRecord.java @@ -23,6 +23,7 @@ import android.annotation.Nullable; import android.app.ITransientNotificationCallback; import android.os.Binder; import android.os.IBinder; +import android.os.UserHandle; import android.util.Slog; import com.android.server.notification.NotificationManagerService; @@ -41,10 +42,10 @@ public class TextToastRecord extends ToastRecord { private final ITransientNotificationCallback mCallback; public TextToastRecord(NotificationManagerService notificationManager, - @Nullable StatusBarManagerInternal statusBarManager, int pid, String packageName, - IBinder token, CharSequence text, int duration, Binder windowToken, int displayId, - @Nullable ITransientNotificationCallback callback) { - super(notificationManager, pid, packageName, token, duration, windowToken, displayId); + @Nullable StatusBarManagerInternal statusBarManager, int uid, int pid, + String packageName, IBinder token, CharSequence text, int duration, Binder windowToken, + int displayId, @Nullable ITransientNotificationCallback callback) { + super(notificationManager, uid, pid, packageName, token, duration, windowToken, displayId); mStatusBar = statusBarManager; mCallback = callback; this.text = checkNotNull(text); @@ -59,7 +60,7 @@ public class TextToastRecord extends ToastRecord { Slog.w(TAG, "StatusBar not available to show text toast for package " + pkg); return false; } - mStatusBar.showToast(pkg, token, text, windowToken, getDuration(), mCallback); + mStatusBar.showToast(uid, pkg, token, text, windowToken, getDuration(), mCallback); return true; } @@ -75,8 +76,8 @@ public class TextToastRecord extends ToastRecord { public String toString() { return "TextToastRecord{" + Integer.toHexString(System.identityHashCode(this)) + + " " + pid + ":" + pkg + "/" + UserHandle.formatUid(uid) + " token=" + token - + " packageName=" + pkg + " text=" + text + " duration=" + getDuration() + "}"; diff --git a/services/core/java/com/android/server/notification/toast/ToastRecord.java b/services/core/java/com/android/server/notification/toast/ToastRecord.java index ef75a6f5dd7b..7915f7013227 100644 --- a/services/core/java/com/android/server/notification/toast/ToastRecord.java +++ b/services/core/java/com/android/server/notification/toast/ToastRecord.java @@ -28,6 +28,7 @@ import java.io.PrintWriter; * Represents a toast, a transient notification. */ public abstract class ToastRecord { + public final int uid; public final int pid; public final String pkg; public final IBinder token; @@ -36,11 +37,10 @@ public abstract class ToastRecord { protected final NotificationManagerService mNotificationManager; private int mDuration; - protected ToastRecord( - NotificationManagerService notificationManager, - int pid, String pkg, IBinder token, int duration, - Binder windowToken, int displayId) { + protected ToastRecord(NotificationManagerService notificationManager, int uid, int pid, + String pkg, IBinder token, int duration, Binder windowToken, int displayId) { this.mNotificationManager = notificationManager; + this.uid = uid; this.pid = pid; this.pkg = pkg; this.token = token; diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java index b9d656181b55..1e4dc7bfe778 100644 --- a/services/core/java/com/android/server/pm/ApexManager.java +++ b/services/core/java/com/android/server/pm/ApexManager.java @@ -30,7 +30,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; -import android.content.pm.PackageParser; +import android.content.pm.PackageParser.PackageParserException; import android.content.pm.parsing.PackageInfoWithoutStateUtils; import android.os.Binder; import android.os.Environment; @@ -137,7 +137,8 @@ public abstract class ApexManager { /** * Called by package manager service to scan apex package files when device boots up. * - * @param packageParser The package parser which supports caches. + * @param packageParser The package parser to support apex package parsing and caching parsed + * results. * @param executorService An executor to support parallel package parsing. */ abstract void scanApexPackagesTraced(@NonNull PackageParser2 packageParser, @@ -505,7 +506,13 @@ public abstract class ApexManager { } factoryPackagesSet.add(packageInfo.packageName); } - } else if (throwable instanceof PackageParser.PackageParserException) { + } else if (throwable instanceof PackageParserException) { + final PackageParserException e = (PackageParserException) throwable; + // Skip parsing non-coreApp apex file if system is in minimal boot state. + if (e.error == PackageManager.INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED) { + Slog.w(TAG, "Scan apex failed, not a coreApp:" + ai.modulePath); + continue; + } throw new IllegalStateException("Unable to parse: " + ai.modulePath, throwable); } else { throw new IllegalStateException("Unexpected exception occurred while parsing " @@ -522,7 +529,8 @@ public abstract class ApexManager { "APEX packages have not been scanned"); boolean matchActive = (flags & MATCH_ACTIVE_PACKAGE) != 0; boolean matchFactory = (flags & MATCH_FACTORY_PACKAGE) != 0; - for (PackageInfo packageInfo: mAllPackagesCache) { + for (int i = 0, size = mAllPackagesCache.size(); i < size; i++) { + final PackageInfo packageInfo = mAllPackagesCache.get(i); if (!packageInfo.packageName.equals(packageName)) { continue; } @@ -581,7 +589,8 @@ public abstract class ApexManager { if (!isApexSupported()) return false; Preconditions.checkState(mAllPackagesCache != null, "APEX packages have not been scanned"); - for (PackageInfo packageInfo : mAllPackagesCache) { + for (int i = 0, size = mAllPackagesCache.size(); i < size; i++) { + final PackageInfo packageInfo = mAllPackagesCache.get(i); if (packageInfo.packageName.equals(packageName)) { return true; } @@ -839,7 +848,8 @@ public abstract class ApexManager { IndentingPrintWriter ipw) { ipw.println(); ipw.increaseIndent(); - for (PackageInfo pi : packagesCache) { + for (int i = 0, size = packagesCache.size(); i < size; i++) { + final PackageInfo pi = packagesCache.get(i); if (packageName != null && !packageName.equals(pi.packageName)) { continue; } diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java index 40876754eae8..28c8642d3e60 100644 --- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java +++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java @@ -294,6 +294,12 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { .getAllCrossProfilePackages().contains(packageName)); } + private boolean isCrossProfilePackageWhitelistedByDefault(String packageName) { + return mInjector.withCleanCallingIdentity(() -> + mInjector.getDevicePolicyManagerInternal() + .getDefaultCrossProfilePackages().contains(packageName)); + } + private List<UserHandle> getTargetUserProfilesUnchecked( String packageName, @UserIdInt int userId) { return mInjector.withCleanCallingIdentity(() -> { @@ -528,6 +534,9 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { @Override public boolean canConfigureInteractAcrossProfiles(String packageName) { + if (!canUserAttemptToConfigureInteractAcrossProfiles(packageName)) { + return false; + } if (!hasOtherProfileWithPackageInstalled(packageName, mInjector.getCallingUserId())) { return false; } @@ -546,7 +555,35 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { return false; } return hasRequestedAppOpPermission( - AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), packageName); + AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), packageName) + && !isPlatformSignedAppWithNonUserConfigurablePermission(packageName, profileIds); + } + + private boolean isPlatformSignedAppWithNonUserConfigurablePermission( + String packageName, int[] profileIds) { + return !isCrossProfilePackageWhitelistedByDefault(packageName) + && isPlatformSignedAppWithAutomaticProfilesPermission(packageName, profileIds); + } + + /** + * Only platform-signed apps can be granted INTERACT_ACROSS_PROFILES automatically without user + * consent. + * + * Returns true if the app is automatically granted the permission in at least one profile. + */ + private boolean isPlatformSignedAppWithAutomaticProfilesPermission( + String packageName, int[] profileIds) { + for (int userId : profileIds) { + final int uid = mInjector.getPackageManagerInternal().getPackageUidInternal( + packageName, /* flags= */ 0, userId); + if (uid == -1) { + continue; + } + if (isPermissionGranted(Manifest.permission.INTERACT_ACROSS_PROFILES, uid)) { + return true; + } + } + return false; } private boolean hasOtherProfileWithPackageInstalled(String packageName, @UserIdInt int userId) { diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 8ff7ea9d61dd..57908f3a4dac 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -82,6 +82,7 @@ import com.android.internal.util.ImageUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.server.IoThread; import com.android.server.LocalServices; +import com.android.server.pm.parsing.PackageParser2; import com.android.server.pm.permission.PermissionManagerServiceInternal; import libcore.io.IoUtils; @@ -105,6 +106,7 @@ import java.util.List; import java.util.Objects; import java.util.Random; import java.util.function.IntPredicate; +import java.util.function.Supplier; /** The service responsible for installing packages. */ public class PackageInstallerService extends IPackageInstaller.Stub implements @@ -194,7 +196,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } }; - public PackageInstallerService(Context context, PackageManagerService pm) { + public PackageInstallerService(Context context, PackageManagerService pm, + Supplier<PackageParser2> apexParserSupplier) { mContext = context; mPm = pm; mPermissionManager = LocalServices.getService(PermissionManagerServiceInternal.class); @@ -213,7 +216,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements mSessionsDir.mkdirs(); mApexManager = ApexManager.getInstance(); - mStagingManager = new StagingManager(this, context); + mStagingManager = new StagingManager(this, context, apexParserSupplier); } boolean okToSendBroadcasts() { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index ab3b2be46988..bc3a0352484b 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -417,6 +417,7 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Predicate; +import java.util.function.Supplier; /** * Keep track of all those APKs everywhere. @@ -3571,7 +3572,11 @@ public class PackageManagerService extends IPackageManager.Stub } } - mInstallerService = new PackageInstallerService(mContext, this); + // Prepare a supplier of package parser for the staging manager to parse apex file + // during the staging installation. + final Supplier<PackageParser2> apexParserSupplier = () -> new PackageParser2( + mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir, mPackageParserCallback); + mInstallerService = new PackageInstallerService(mContext, this, apexParserSupplier); final Pair<ComponentName, String> instantAppResolverComponent = getInstantAppResolverLPr(); if (instantAppResolverComponent != null) { diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index e8696f60b59a..f6e4e1f26bd5 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -4667,6 +4667,10 @@ public final class Settings { pw.print(prefix); pw.print(" privateFlags="); printFlags(pw, privateFlags, PRIVATE_FLAG_DUMP_SPEC); pw.println(); } + if (pkg.hasPreserveLegacyExternalStorage()) { + pw.print(prefix); pw.print(" hasPreserveLegacyExternalStorage=true"); + pw.println(); + } pw.print(prefix); pw.print(" forceQueryable="); pw.print(ps.pkg.isForceQueryable()); if (ps.forceQueryableOverride) { pw.print(" (override=true)"); diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 60292b45c0ee..8f6bd026a9bd 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -35,11 +35,11 @@ import android.content.pm.PackageInstaller; import android.content.pm.PackageInstaller.SessionInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; -import android.content.pm.PackageParser; import android.content.pm.PackageParser.PackageParserException; import android.content.pm.PackageParser.SigningDetails; import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion; import android.content.pm.ParceledListSlice; +import android.content.pm.parsing.PackageInfoWithoutStateUtils; import android.content.rollback.IRollbackManager; import android.content.rollback.RollbackInfo; import android.content.rollback.RollbackManager; @@ -68,8 +68,10 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageHelper; import com.android.internal.os.BackgroundThread; import com.android.server.LocalServices; +import com.android.server.pm.parsing.PackageParser2; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.parsing.pkg.AndroidPackageUtils; +import com.android.server.pm.parsing.pkg.ParsedPackage; import com.android.server.rollback.WatchdogRollbackLogger; import java.io.File; @@ -80,6 +82,7 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Predicate; +import java.util.function.Supplier; /** * This class handles staged install sessions, i.e. install sessions that require packages to @@ -94,6 +97,7 @@ public class StagingManager { private final PowerManager mPowerManager; private final Context mContext; private final PreRebootVerificationHandler mPreRebootVerificationHandler; + private final Supplier<PackageParser2> mPackageParserSupplier; @GuardedBy("mStagedSessions") private final SparseArray<PackageInstallerSession> mStagedSessions = new SparseArray<>(); @@ -105,9 +109,11 @@ public class StagingManager { private final List<String> mFailedPackageNames = new ArrayList<>(); private String mNativeFailureReason; - StagingManager(PackageInstallerService pi, Context context) { + StagingManager(PackageInstallerService pi, Context context, + Supplier<PackageParser2> packageParserSupplier) { mPi = pi; mContext = context; + mPackageParserSupplier = packageParserSupplier; mApexManager = ApexManager.getInstance(); mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); @@ -223,17 +229,21 @@ public class StagingManager { final List<String> apexPackageNames = new ArrayList<>(); for (ApexInfo apexInfo : apexInfoList.apexInfos) { final PackageInfo packageInfo; - int flags = PackageManager.GET_META_DATA; - PackageParser.Package pkg; - try { + final int flags = PackageManager.GET_META_DATA; + try (PackageParser2 packageParser = mPackageParserSupplier.get()) { File apexFile = new File(apexInfo.modulePath); - PackageParser pp = new PackageParser(); - pkg = pp.parsePackage(apexFile, flags, false); + final ParsedPackage parsedPackage = packageParser.parsePackage( + apexFile, flags, false); + packageInfo = PackageInfoWithoutStateUtils.generate(parsedPackage, apexInfo, flags); + if (packageInfo == null) { + throw new PackageManagerException( + SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, + "Unable to generate package info: " + apexInfo.modulePath); + } } catch (PackageParserException e) { throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, "Failed to parse APEX package " + apexInfo.modulePath, e); } - packageInfo = PackageParser.generatePackageInfo(pkg, apexInfo, flags); final PackageInfo activePackage = mApexManager.getPackageInfo(packageInfo.packageName, ApexManager.MATCH_ACTIVE_PACKAGE); if (activePackage == null) { @@ -254,8 +264,9 @@ public class StagingManager { private int retrieveRollbackIdForCommitSession(int sessionId) throws PackageManagerException { RollbackManager rm = mContext.getSystemService(RollbackManager.class); - List<RollbackInfo> rollbacks = rm.getRecentlyCommittedRollbacks(); - for (RollbackInfo rollback : rollbacks) { + final List<RollbackInfo> rollbacks = rm.getRecentlyCommittedRollbacks(); + for (int i = 0, size = rollbacks.size(); i < size; i++) { + final RollbackInfo rollback = rollbacks.get(i); if (rollback.getCommittedSessionId() == sessionId) { return rollback.getRollbackId(); } @@ -388,7 +399,8 @@ public class StagingManager { } } } - for (PackageInstallerSession childSession : childrenSessions) { + for (int i = 0, size = childrenSessions.size(); i < size; i++) { + final PackageInstallerSession childSession = childrenSessions.get(i); if (sessionContainsApex(childSession)) { apexSessions.add(childSession); } @@ -402,15 +414,15 @@ public class StagingManager { IRollbackManager rm = IRollbackManager.Stub.asInterface( ServiceManager.getService(Context.ROLLBACK_SERVICE)); - for (PackageInstallerSession apexSession : apexSessions) { - String packageName = apexSession.getPackageName(); + for (int i = 0, sessionsSize = apexSessions.size(); i < sessionsSize; i++) { + final String packageName = apexSessions.get(i).getPackageName(); // Perform any snapshots or restores for the APEX itself snapshotAndRestoreApexUserData(packageName, allUsers, rm); // Process the apks inside the APEX - List<String> apksInApex = mApexManager.getApksInApex(packageName); - for (String apk: apksInApex) { - snapshotAndRestoreApkInApexUserData(apk, allUsers, rm); + final List<String> apksInApex = mApexManager.getApksInApex(packageName); + for (int j = 0, apksSize = apksInApex.size(); j < apksSize; j++) { + snapshotAndRestoreApkInApexUserData(apksInApex.get(j), allUsers, rm); } } } @@ -637,7 +649,8 @@ public class StagingManager { 0 /* UserHandle.SYSTEM */); PackageInstallerSession apkSession = mPi.getSession(apkSessionId); apkSession.open(); - for (String apkFilePath : apkFilePaths) { + for (int i = 0, size = apkFilePaths.size(); i < size; i++) { + final String apkFilePath = apkFilePaths.get(i); File apkFile = new File(apkFilePath); ParcelFileDescriptor pfd = ParcelFileDescriptor.open(apkFile, ParcelFileDescriptor.MODE_READ_ONLY); @@ -705,9 +718,9 @@ public class StagingManager { "Unable to prepare multi-package session for staged session"); } - for (PackageInstallerSession sessionToClone : childSessions) { - PackageInstallerSession apkChildSession = - createAndWriteApkSession(sessionToClone, preReboot); + for (int i = 0, size = childSessions.size(); i < size; i++) { + final PackageInstallerSession apkChildSession = createAndWriteApkSession( + childSessions.get(i), preReboot); try { apkParentSession.addChildSessionId(apkChildSession.sessionId); } catch (IllegalStateException e) { @@ -1206,10 +1219,9 @@ public class StagingManager { // multi-package sessions, find all the child sessions that contain an APEX. if (hasApex) { try { - final List<PackageInfo> apexPackages = - submitSessionToApexService(session); - for (PackageInfo apexPackage : apexPackages) { - validateApexSignature(apexPackage); + final List<PackageInfo> apexPackages = submitSessionToApexService(session); + for (int i = 0, size = apexPackages.size(); i < size; i++) { + validateApexSignature(apexPackages.get(i)); } } catch (PackageManagerException e) { session.setStagedSessionFailed(e.error, e.getMessage()); diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index b41bc5c2dc51..cd6c5bfaeb79 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -513,10 +513,7 @@ public final class PowerManagerService extends SystemService // The screen brightness setting override from the window manager // to allow the current foreground activity to override the brightness. - // Use -1 to disable. - private int mScreenBrightnessOverrideFromWindowManager = -1; - - private float mScreenBrightnessOverrideFromWindowManagerFloat = + private float mScreenBrightnessOverrideFromWindowManager = PowerManager.BRIGHTNESS_INVALID_FLOAT; // The window manager has determined the user to be inactive via other means. @@ -2846,7 +2843,7 @@ public final class PowerManagerService extends SystemService screenBrightnessOverride = mScreenBrightnessSettingDefault; } else if (isValidBrightness(mScreenBrightnessOverrideFromWindowManager)) { autoBrightness = false; - screenBrightnessOverride = mScreenBrightnessOverrideFromWindowManagerFloat; + screenBrightnessOverride = mScreenBrightnessOverrideFromWindowManager; } else { autoBrightness = (mScreenBrightnessModeSetting == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); @@ -2927,8 +2924,8 @@ public final class PowerManagerService extends SystemService return !mIsVrModeEnabled && mScreenBrightnessBoostInProgress; } - private static boolean isValidBrightness(int value) { - return value >= 0 && value <= 255; + private static boolean isValidBrightness(float value) { + return value >= PowerManager.BRIGHTNESS_MIN && value <= PowerManager.BRIGHTNESS_MAX; } @VisibleForTesting @@ -3583,13 +3580,11 @@ public final class PowerManagerService extends SystemService } } - // TODO(brightnessfloat): change to float - private void setScreenBrightnessOverrideFromWindowManagerInternal(int brightness) { + private void setScreenBrightnessOverrideFromWindowManagerInternal(float brightness) { synchronized (mLock) { - if (mScreenBrightnessOverrideFromWindowManager != brightness) { + if (!BrightnessSynchronizer.floatEquals(mScreenBrightnessOverrideFromWindowManager, + brightness)) { mScreenBrightnessOverrideFromWindowManager = brightness; - mScreenBrightnessOverrideFromWindowManagerFloat = - BrightnessSynchronizer.brightnessIntToFloat(mContext, brightness); mDirty |= DIRTY_SETTINGS; updatePowerStateLocked(); } @@ -3890,8 +3885,8 @@ public final class PowerManagerService extends SystemService + isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked() + ")"); pw.println(" mStayOnWhilePluggedInSetting=" + mStayOnWhilePluggedInSetting); pw.println(" mScreenBrightnessModeSetting=" + mScreenBrightnessModeSetting); - pw.println(" mScreenBrightnessOverrideFromWindowManagerFloat=" - + mScreenBrightnessOverrideFromWindowManagerFloat); + pw.println(" mScreenBrightnessOverrideFromWindowManager=" + + mScreenBrightnessOverrideFromWindowManager); pw.println(" mUserActivityTimeoutOverrideFromWindowManager=" + mUserActivityTimeoutOverrideFromWindowManager); pw.println(" mUserInactiveOverrideFromWindowManager=" @@ -4231,7 +4226,7 @@ public final class PowerManagerService extends SystemService proto.write( PowerServiceSettingsAndConfigurationDumpProto .SCREEN_BRIGHTNESS_OVERRIDE_FROM_WINDOW_MANAGER, - mScreenBrightnessOverrideFromWindowManagerFloat); + mScreenBrightnessOverrideFromWindowManager); proto.write( PowerServiceSettingsAndConfigurationDumpProto .USER_ACTIVITY_TIMEOUT_OVERRIDE_FROM_WINDOW_MANAGER_MS, @@ -5430,10 +5425,10 @@ public final class PowerManagerService extends SystemService @VisibleForTesting final class LocalService extends PowerManagerInternal { @Override - public void setScreenBrightnessOverrideFromWindowManager(int screenBrightness) { - if (screenBrightness < PowerManager.BRIGHTNESS_DEFAULT - || screenBrightness > PowerManager.BRIGHTNESS_ON) { - screenBrightness = PowerManager.BRIGHTNESS_DEFAULT; + public void setScreenBrightnessOverrideFromWindowManager(float screenBrightness) { + if (screenBrightness < PowerManager.BRIGHTNESS_MIN + || screenBrightness > PowerManager.BRIGHTNESS_MAX) { + screenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; } setScreenBrightnessOverrideFromWindowManagerInternal(screenBrightness); } diff --git a/services/core/java/com/android/server/security/FileIntegrityService.java b/services/core/java/com/android/server/security/FileIntegrityService.java index 841aca5a8d6f..225bd82e9a21 100644 --- a/services/core/java/com/android/server/security/FileIntegrityService.java +++ b/services/core/java/com/android/server/security/FileIntegrityService.java @@ -24,6 +24,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.os.Binder; import android.os.Build; +import android.os.Environment; import android.os.IBinder; import android.os.SystemProperties; import android.os.UserHandle; @@ -37,6 +38,7 @@ import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.nio.file.Path; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; @@ -131,12 +133,17 @@ public class FileIntegrityService extends SystemService { // duplicate the same loading logic here. // Load certificates trusted by the device manufacturer. - loadCertificatesFromDirectory("/product/etc/security/fsverity"); + // NB: Directories need to be synced with system/security/fsverity_init/fsverity_init.cpp. + final String relativeDir = "etc/security/fsverity"; + loadCertificatesFromDirectory(Environment.getRootDirectory().toPath() + .resolve(relativeDir)); + loadCertificatesFromDirectory(Environment.getProductDirectory().toPath() + .resolve(relativeDir)); } - private void loadCertificatesFromDirectory(String path) { + private void loadCertificatesFromDirectory(Path path) { try { - File[] files = new File(path).listFiles(); + File[] files = path.toFile().listFiles(); if (files == null) { return; } diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java index d88dccb9afeb..26497d8b0290 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java @@ -131,7 +131,7 @@ public interface StatusBarManagerInternal { * @see com.android.internal.statusbar.IStatusBar#showToast(String, IBinder, CharSequence, * IBinder, int, ITransientNotificationCallback) */ - void showToast(String packageName, IBinder token, CharSequence text, + void showToast(int uid, String packageName, IBinder token, CharSequence text, IBinder windowToken, int duration, @Nullable ITransientNotificationCallback textCallback); diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 78ef68c09988..d7a0c9871b48 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -502,12 +502,12 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D } @Override - public void showToast(String packageName, IBinder token, CharSequence text, + public void showToast(int uid, String packageName, IBinder token, CharSequence text, IBinder windowToken, int duration, @Nullable ITransientNotificationCallback callback) { if (mBar != null) { try { - mBar.showToast(packageName, token, text, windowToken, duration, callback); + mBar.showToast(uid, packageName, token, text, windowToken, duration, callback); } catch (RemoteException ex) { } } } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index e1ef76f128cd..80b8b5854966 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -178,7 +178,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> private Object mLastWindowFreezeSource = null; private Session mHoldScreen = null; - private float mScreenBrightness = -1; + private float mScreenBrightnessOverride = PowerManager.BRIGHTNESS_INVALID_FLOAT; private long mUserActivityTimeout = -1; private boolean mUpdateRotation = false; // Following variables are for debugging screen wakelock only. @@ -826,7 +826,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } mHoldScreen = null; - mScreenBrightness = -1; + mScreenBrightnessOverride = PowerManager.BRIGHTNESS_INVALID_FLOAT; mUserActivityTimeout = -1; mObscureApplicationContentOnSecondaryDisplays = false; mSustainedPerformanceModeCurrent = false; @@ -936,12 +936,14 @@ class RootWindowContainer extends WindowContainer<DisplayContent> mWmService.setHoldScreenLocked(mHoldScreen); if (!mWmService.mDisplayFrozen) { - final int brightness = mScreenBrightness < 0 || mScreenBrightness > 1.0f - ? -1 : toBrightnessOverride(mScreenBrightness); - + final float brightnessOverride = mScreenBrightnessOverride < PowerManager.BRIGHTNESS_MIN + || mScreenBrightnessOverride > PowerManager.BRIGHTNESS_MAX + ? PowerManager.BRIGHTNESS_INVALID_FLOAT : mScreenBrightnessOverride; + int brightnessFloatAsIntBits = Float.floatToIntBits(brightnessOverride); // Post these on a handler such that we don't call into power manager service while // holding the window manager lock to avoid lock contention with power manager lock. - mHandler.obtainMessage(SET_SCREEN_BRIGHTNESS_OVERRIDE, brightness, 0).sendToTarget(); + mHandler.obtainMessage(SET_SCREEN_BRIGHTNESS_OVERRIDE, brightnessFloatAsIntBits, + 0).sendToTarget(); mHandler.obtainMessage(SET_USER_ACTIVITY_TIMEOUT, mUserActivityTimeout).sendToTarget(); } @@ -1125,8 +1127,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent> + "has FLAG_KEEP_SCREEN_ON!!! called by%s", w, Debug.getCallers(10)); } - if (!syswin && w.mAttrs.screenBrightness >= 0 && mScreenBrightness < 0) { - mScreenBrightness = w.mAttrs.screenBrightness; + if (!syswin && w.mAttrs.screenBrightness >= 0 + && Float.isNaN(mScreenBrightnessOverride)) { + mScreenBrightnessOverride = w.mAttrs.screenBrightness; } final int type = attrs.type; @@ -1190,10 +1193,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> return doRequest; } - private static int toBrightnessOverride(float value) { - return (int)(value * PowerManager.BRIGHTNESS_ON); - } - private final class MyHandler extends Handler { public MyHandler(Looper looper) { @@ -1205,7 +1204,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> switch (msg.what) { case SET_SCREEN_BRIGHTNESS_OVERRIDE: mWmService.mPowerManagerInternal.setScreenBrightnessOverrideFromWindowManager( - msg.arg1); + Float.intBitsToFloat(msg.arg1)); break; case SET_USER_ACTIVITY_TIMEOUT: mWmService.mPowerManagerInternal. diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index c891c1166a06..cb897db9a2d0 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -2448,7 +2448,8 @@ class Task extends WindowContainer<WindowContainer> { Rect updateOverrideConfigurationFromLaunchBounds() { // If the task is controlled by another organized task, do not set override // configurations and let its parent (organized task) to control it; - final Rect bounds = isOrganized() && !isRootTask() ? null : getLaunchBounds(); + final Task rootTask = getRootTask(); + final Rect bounds = rootTask != this && rootTask.isOrganized() ? null : getLaunchBounds(); setBounds(bounds); if (bounds != null && !bounds.isEmpty()) { // TODO: Review if we actually want to do this - we are setting the launch bounds @@ -4131,17 +4132,6 @@ class Task extends WindowContainer<WindowContainer> { // Let the old organizer know it has lost control. sendTaskVanished(); mTaskOrganizer = organizer; - - // If the task is not yet visible when it is added to the task organizer, then we should - // hide it to allow the task organizer to show it when it is properly reparented. We skip - // this for tasks created by the organizer because they can synchronously update the leash - // before new children are added to the task. - if (!mCreatedByOrganizer && organizer != null - && (!getHasBeenVisible() || !hasVisibleChildren())) { - getPendingTransaction().hide(getSurfaceControl()); - commitPendingTransaction(); - } - sendTaskAppeared(); onTaskOrganizerChanged(); return true; diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index 75f2e1f0a8ba..2bbf8dbb274c 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -25,7 +25,6 @@ import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_CONFI import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_WINDOW_CONFIGS; import android.annotation.Nullable; -import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; import android.app.WindowConfiguration; import android.content.Intent; @@ -39,7 +38,6 @@ import android.window.ITaskOrganizer; import android.window.ITaskOrganizerController; import android.window.WindowContainerToken; -import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import java.io.PrintWriter; @@ -48,7 +46,6 @@ import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.WeakHashMap; -import java.util.function.Consumer; /** * Stores the TaskOrganizers associated with a given windowing mode and @@ -84,95 +81,17 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } } } - } - - /** - * A wrapper class around ITaskOrganizer to ensure that the calls are made in the right - * lifecycle order since we may be updating the visibility of task surface controls in a pending - * transaction before they are presented to the task org. - */ - private class TaskOrganizerCallbacks { - final WindowManagerService mService; - final ITaskOrganizer mTaskOrganizer; - final Consumer<Runnable> mDeferTaskOrgCallbacksConsumer; - - TaskOrganizerCallbacks(WindowManagerService wm, ITaskOrganizer taskOrg, - Consumer<Runnable> deferTaskOrgCallbacksConsumer) { - mService = wm; - mDeferTaskOrgCallbacksConsumer = deferTaskOrgCallbacksConsumer; - mTaskOrganizer = taskOrg; - } - - IBinder getBinder() { - return mTaskOrganizer.asBinder(); - } - - void onTaskAppeared(Task task) { - final RunningTaskInfo taskInfo = task.getTaskInfo(); - mDeferTaskOrgCallbacksConsumer.accept(() -> { - try { - mTaskOrganizer.onTaskAppeared(taskInfo); - } catch (RemoteException e) { - Slog.e(TAG, "Exception sending onTaskAppeared callback", e); - } - }); - } - - - void onTaskVanished(Task task) { - final RunningTaskInfo taskInfo = task.getTaskInfo(); - mDeferTaskOrgCallbacksConsumer.accept(() -> { - try { - mTaskOrganizer.onTaskVanished(taskInfo); - } catch (RemoteException e) { - Slog.e(TAG, "Exception sending onTaskVanished callback", e); - } - }); - } - - void onTaskInfoChanged(Task task, ActivityManager.RunningTaskInfo taskInfo) { - mDeferTaskOrgCallbacksConsumer.accept(() -> { - if (!task.isOrganized()) { - // This is safe to ignore if the task is no longer organized - return; - } - try { - mTaskOrganizer.onTaskInfoChanged(taskInfo); - } catch (RemoteException e) { - Slog.e(TAG, "Exception sending onTaskInfoChanged callback", e); - } - }); - } - - void onBackPressedOnTaskRoot(Task task) { - mDeferTaskOrgCallbacksConsumer.accept(() -> { - if (!task.isOrganized()) { - // This is safe to ignore if the task is no longer organized - return; - } - try { - mTaskOrganizer.onBackPressedOnTaskRoot(task.getTaskInfo()); - } catch (Exception e) { - Slog.e(TAG, "Exception sending onBackPressedOnTaskRoot callback", e); - } - }); - } - } + }; private class TaskOrganizerState { - private final TaskOrganizerCallbacks mOrganizer; + private final ITaskOrganizer mOrganizer; private final DeathRecipient mDeathRecipient; private final ArrayList<Task> mOrganizedTasks = new ArrayList<>(); private final int mUid; private boolean mInterceptBackPressedOnTaskRoot; TaskOrganizerState(ITaskOrganizer organizer, int uid) { - final Consumer<Runnable> deferTaskOrgCallbacksConsumer = - mDeferTaskOrgCallbacksConsumer != null - ? mDeferTaskOrgCallbacksConsumer - : mService.mWindowManager.mAnimator::addAfterPrepareSurfacesRunnable; - mOrganizer = new TaskOrganizerCallbacks(mService.mWindowManager, organizer, - deferTaskOrgCallbacksConsumer); + mOrganizer = organizer; mDeathRecipient = new DeathRecipient(organizer); try { organizer.asBinder().linkToDeath(mDeathRecipient, 0); @@ -188,18 +107,26 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { void addTask(Task t) { mOrganizedTasks.add(t); - mOrganizer.onTaskAppeared(t); + try { + mOrganizer.onTaskAppeared(t.getTaskInfo()); + } catch (Exception e) { + Slog.e(TAG, "Exception sending taskAppeared callback" + e); + } } void removeTask(Task t) { + try { + mOrganizer.onTaskVanished(t.getTaskInfo()); + } catch (Exception e) { + Slog.e(TAG, "Exception sending taskVanished callback" + e); + } mOrganizedTasks.remove(t); - mOrganizer.onTaskVanished(t); } void dispose() { releaseTasks(); for (int i = mTaskOrganizersForWindowingMode.size() - 1; i >= 0; --i) { - mTaskOrganizersForWindowingMode.valueAt(i).remove(mOrganizer.getBinder()); + mTaskOrganizersForWindowingMode.valueAt(i).remove(mOrganizer.asBinder()); } } @@ -212,7 +139,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } void unlinkDeath() { - mOrganizer.getBinder().unlinkToDeath(mDeathRecipient, 0); + mOrganizer.asBinder().unlinkToDeath(mDeathRecipient, 0); } } @@ -222,10 +149,9 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { private final WeakHashMap<Task, RunningTaskInfo> mLastSentTaskInfos = new WeakHashMap<>(); private final ArrayList<Task> mPendingTaskInfoChanges = new ArrayList<>(); - private final ActivityTaskManagerService mService; + final ActivityTaskManagerService mService; - private RunningTaskInfo mTmpTaskInfo; - private Consumer<Runnable> mDeferTaskOrgCallbacksConsumer; + RunningTaskInfo mTmpTaskInfo; TaskOrganizerController(ActivityTaskManagerService atm) { mService = atm; @@ -237,15 +163,6 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } /** - * Specifies the consumer to run to defer the task org callbacks. Can be overridden while - * testing to allow the callbacks to be sent synchronously. - */ - @VisibleForTesting - public void setDeferTaskOrgCallbacksConsumer(Consumer<Runnable> consumer) { - mDeferTaskOrgCallbacksConsumer = consumer; - } - - /** * Register a TaskOrganizer to manage tasks as they enter the given windowing mode. * If there was already a TaskOrganizer for this windowing mode it will be evicted * but will continue to organize it's existing tasks. @@ -336,7 +253,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { if (state == null) { return null; } - return state.mOrganizer.mTaskOrganizer; + return state.mOrganizer; } void onTaskAppeared(ITaskOrganizer organizer, Task task) { @@ -441,10 +358,11 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { // change. mTmpTaskInfo = null; - if (task.isOrganized()) { - final TaskOrganizerState state = mTaskOrganizerStates.get( - task.mTaskOrganizer.asBinder()); - state.mOrganizer.onTaskInfoChanged(task, newInfo); + if (task.mTaskOrganizer != null) { + try { + task.mTaskOrganizer.onTaskInfoChanged(newInfo); + } catch (RemoteException e) { + } } } @@ -603,7 +521,11 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { return false; } - state.mOrganizer.onBackPressedOnTaskRoot(task); + try { + state.mOrganizer.onBackPressedOnTaskRoot(task.getTaskInfo()); + } catch (Exception e) { + Slog.e(TAG, "Exception sending interceptBackPressedOnTaskRoot callback" + e); + } return true; } @@ -620,7 +542,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { final TaskOrganizerState state = mTaskOrganizerStates.get(taskOrgs.get(j)); final ArrayList<Task> tasks = state.mOrganizedTasks; pw.print(innerPrefix + " "); - pw.println(state.mOrganizer.mTaskOrganizer + " uid=" + state.mUid + ":"); + pw.println(state.mOrganizer + " uid=" + state.mUid + ":"); for (int k = 0; k < tasks.size(); k++) { pw.println(innerPrefix + " " + tasks.get(k)); } diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 37a0417fcf75..569b8f61c4f4 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -2207,11 +2207,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< private Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter, boolean isVoiceInteraction) { - if (isOrganized()) { - // Defer to the task organizer to run animations - return null; - } - final DisplayContent displayContent = getDisplayContent(); final DisplayInfo displayInfo = displayContent.getDisplayInfo(); final int width = displayInfo.appWidth; diff --git a/services/core/xsd/vts/Android.bp b/services/core/xsd/vts/Android.bp index 636d11069731..a942108667a5 100644 --- a/services/core/xsd/vts/Android.bp +++ b/services/core/xsd/vts/Android.bp @@ -36,7 +36,7 @@ cc_test { ], test_suites: [ "general-tests", - "vts-core" + "vts" ], test_config: "vts_defaultPermissions_validate_test.xml", } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index d1c47d9feed7..67e83bad154f 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -12459,6 +12459,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return DevicePolicyManagerService.this.getAllCrossProfilePackages(); } + @Override + public List<String> getDefaultCrossProfilePackages() { + return DevicePolicyManagerService.this.getDefaultCrossProfilePackages(); + } + /** * Sends the {@code intent} to the packages with cross profile capabilities. * diff --git a/services/incremental/Android.bp b/services/incremental/Android.bp index 02bb0bc3e49c..b13d33054e19 100644 --- a/services/incremental/Android.bp +++ b/services/incremental/Android.bp @@ -50,7 +50,6 @@ cc_defaults { "libbinder", "libcrypto", "libcutils", - "libdataloader", "libincfs", "liblog", "libz", diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp index 0da167303ccd..d36eae89c1ff 100644 --- a/services/incremental/IncrementalService.cpp +++ b/services/incremental/IncrementalService.cpp @@ -17,7 +17,6 @@ #define LOG_TAG "IncrementalService" #include "IncrementalService.h" -#include "IncrementalServiceValidation.h" #include <android-base/file.h> #include <android-base/logging.h> @@ -582,25 +581,29 @@ int IncrementalService::setStorageParams(StorageId storageId, bool enableReadLog return -EINVAL; } - ifs->dataLoaderFilesystemParams.readLogsEnabled = enableReadLogs; if (enableReadLogs) { - // We never unregister the callbacks, but given a restricted number of data loaders and even fewer asking for read log access, should be ok. - registerAppOpsCallback(ifs->dataLoaderParams.packageName); + if (auto status = + mAppOpsManager->checkPermission(kDataUsageStats, kOpUsage, + ifs->dataLoaderParams.packageName.c_str()); + !status.isOk()) { + LOG(ERROR) << "checkPermission failed: " << status.toString8(); + return fromBinderStatus(status); + } } - return applyStorageParams(*ifs); -} + if (auto status = applyStorageParams(*ifs, enableReadLogs); !status.isOk()) { + LOG(ERROR) << "applyStorageParams failed: " << status.toString8(); + return fromBinderStatus(status); + } -int IncrementalService::applyStorageParams(IncFsMount& ifs) { - const bool enableReadLogs = ifs.dataLoaderFilesystemParams.readLogsEnabled; if (enableReadLogs) { - if (auto status = CheckPermissionForDataDelivery(kDataUsageStats, kOpUsage); - !status.isOk()) { - LOG(ERROR) << "CheckPermissionForDataDelivery failed: " << status.toString8(); - return fromBinderStatus(status); - } + registerAppOpsCallback(ifs->dataLoaderParams.packageName); } + return 0; +} + +binder::Status IncrementalService::applyStorageParams(IncFsMount& ifs, bool enableReadLogs) { using unique_fd = ::android::base::unique_fd; ::android::os::incremental::IncrementalFileSystemControlParcel control; control.cmd.reset(unique_fd(dup(ifs.control.cmd()))); @@ -611,13 +614,7 @@ int IncrementalService::applyStorageParams(IncFsMount& ifs) { } std::lock_guard l(mMountOperationLock); - const auto status = mVold->setIncFsMountOptions(control, enableReadLogs); - if (!status.isOk()) { - LOG(ERROR) << "Calling Vold::setIncFsMountOptions() failed: " << status.toString8(); - return fromBinderStatus(status); - } - - return 0; + return mVold->setIncFsMountOptions(control, enableReadLogs); } void IncrementalService::deleteStorage(StorageId storageId) { @@ -1280,39 +1277,54 @@ bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_ } void IncrementalService::registerAppOpsCallback(const std::string& packageName) { - if (packageName.empty()) { - return; - } - + sp<IAppOpsCallback> listener; { std::unique_lock lock{mCallbacksLock}; - if (!mCallbackRegistered.insert(packageName).second) { + auto& cb = mCallbackRegistered[packageName]; + if (cb) { return; } + cb = new AppOpsListener(*this, packageName); + listener = cb; } - /* TODO(b/152633648): restore callback after it's not crashing Binder anymore. - sp<AppOpsListener> listener = new AppOpsListener(*this, packageName); mAppOpsManager->startWatchingMode(AppOpsManager::OP_GET_USAGE_STATS, String16(packageName.c_str()), listener); - */ } -void IncrementalService::onAppOppChanged(const std::string& packageName) { +bool IncrementalService::unregisterAppOpsCallback(const std::string& packageName) { + sp<IAppOpsCallback> listener; + { + std::unique_lock lock{mCallbacksLock}; + auto found = mCallbackRegistered.find(packageName); + if (found == mCallbackRegistered.end()) { + return false; + } + listener = found->second; + mCallbackRegistered.erase(found); + } + + mAppOpsManager->stopWatchingMode(listener); + return true; +} + +void IncrementalService::onAppOpChanged(const std::string& packageName) { + if (!unregisterAppOpsCallback(packageName)) { + return; + } + std::vector<IfsMountPtr> affected; { std::lock_guard l(mLock); affected.reserve(mMounts.size()); for (auto&& [id, ifs] : mMounts) { - if (ifs->dataLoaderFilesystemParams.readLogsEnabled && ifs->dataLoaderParams.packageName == packageName) { + if (ifs->mountId == id && ifs->dataLoaderParams.packageName == packageName) { affected.push_back(ifs); } } } - /* TODO(b/152633648): restore callback after it's not crashing Kernel anymore. for (auto&& ifs : affected) { - applyStorageParams(*ifs); + applyStorageParams(*ifs, false); } - */ } binder::Status IncrementalService::IncrementalDataLoaderListener::onStatusChanged(MountId mountId, @@ -1378,8 +1390,8 @@ binder::Status IncrementalService::IncrementalDataLoaderListener::onStatusChange return binder::Status::ok(); } -void IncrementalService::AppOpsListener::opChanged(int32_t op, const String16&) { - incrementalService.onAppOppChanged(packageName); +void IncrementalService::AppOpsListener::opChanged(int32_t, const String16&) { + incrementalService.onAppOpChanged(packageName); } } // namespace android::incremental diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h index ff69633e185b..58002974e180 100644 --- a/services/incremental/IncrementalService.h +++ b/services/incremental/IncrementalService.h @@ -40,7 +40,6 @@ #include "ServiceWrappers.h" #include "android/content/pm/BnDataLoaderStatusListener.h" #include "incfs.h" -#include "dataloader_ndk.h" #include "path.h" using namespace android::os::incremental; @@ -182,7 +181,6 @@ private: StorageMap storages; BindMap bindPoints; DataLoaderParamsParcel dataLoaderParams; - DataLoaderFilesystemParams dataLoaderFilesystemParams; std::atomic<int> nextStorageDirNo{0}; std::atomic<int> dataLoaderStatus = -1; bool dataLoaderStartRequested = false; @@ -193,9 +191,7 @@ private: : root(std::move(root)), control(std::move(control)), mountId(mountId), - incrementalService(incrementalService) { - dataLoaderFilesystemParams.readLogsEnabled = false; - } + incrementalService(incrementalService) {} IncFsMount(IncFsMount&&) = delete; IncFsMount& operator=(IncFsMount&&) = delete; ~IncFsMount(); @@ -234,10 +230,11 @@ private: std::string normalizePathToStorage(const IfsMountPtr incfs, StorageId storage, std::string_view path); - int applyStorageParams(IncFsMount& ifs); + binder::Status applyStorageParams(IncFsMount& ifs, bool enableReadLogs); void registerAppOpsCallback(const std::string& packageName); - void onAppOppChanged(const std::string& packageName); + bool unregisterAppOpsCallback(const std::string& packageName); + void onAppOpChanged(const std::string& packageName); // Member variables std::unique_ptr<VoldServiceWrapper> const mVold; @@ -252,7 +249,7 @@ private: BindPathMap mBindsByPath; std::mutex mCallbacksLock; - std::set<std::string> mCallbackRegistered; + std::map<std::string, sp<AppOpsListener>> mCallbackRegistered; std::atomic_bool mSystemReady = false; StorageId mNextId = 0; diff --git a/services/incremental/IncrementalServiceValidation.h b/services/incremental/IncrementalServiceValidation.h index 24f9f7f94dfd..48894c6926c8 100644 --- a/services/incremental/IncrementalServiceValidation.h +++ b/services/incremental/IncrementalServiceValidation.h @@ -41,7 +41,8 @@ inline int fromBinderStatus(const binder::Status& status) { : -EIO; } -inline binder::Status CheckPermissionForDataDelivery(const char* permission, const char* operation) { +inline binder::Status CheckPermissionForDataDelivery(const char* permission, const char* operation, + const char* package) { using android::base::StringPrintf; int32_t pid; @@ -52,23 +53,23 @@ inline binder::Status CheckPermissionForDataDelivery(const char* permission, con StringPrintf("UID %d / PID %d lacks permission %s", uid, pid, permission)); } + String16 packageName{package}; + // Caller must also have op granted. PermissionController pc; - // Package is a required parameter. Need to obtain one. - Vector<String16> packages; - pc.getPackagesForUid(uid, packages); - if (packages.empty()) { + if (auto packageUid = pc.getPackageUid(packageName, 0); packageUid != uid) { return Exception(binder::Status::EX_SECURITY, - StringPrintf("UID %d / PID %d has no packages", uid, pid)); + StringPrintf("UID %d / PID %d does not own package %s", uid, pid, + package)); } - switch (auto result = pc.noteOp(String16(operation), uid, packages[0]); result) { + switch (auto result = pc.noteOp(String16(operation), uid, packageName); result) { case PermissionController::MODE_ALLOWED: case PermissionController::MODE_DEFAULT: return binder::Status::ok(); default: return Exception(binder::Status::EX_SECURITY, - StringPrintf("UID %d / PID %d lacks app-op %s, error %d", uid, pid, - operation, result)); + StringPrintf("UID %d / PID %d / package %s lacks app-op %s, error %d", + uid, pid, package, operation, result)); } } diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h index 449b457045c6..84bf1ffaf45c 100644 --- a/services/incremental/ServiceWrappers.h +++ b/services/incremental/ServiceWrappers.h @@ -16,6 +16,8 @@ #pragma once +#include "IncrementalServiceValidation.h" + #include <android-base/strings.h> #include <android-base/unique_fd.h> #include <android/content/pm/DataLoaderParamsParcel.h> @@ -85,7 +87,10 @@ public: class AppOpsManagerWrapper { public: virtual ~AppOpsManagerWrapper() = default; + virtual binder::Status checkPermission(const char* permission, const char* operation, + const char* package) const = 0; virtual void startWatchingMode(int32_t op, const String16& packageName, const sp<IAppOpsCallback>& callback) = 0; + virtual void stopWatchingMode(const sp<IAppOpsCallback>& callback) = 0; }; class ServiceManagerWrapper { @@ -105,17 +110,19 @@ public: ~RealVoldService() = default; binder::Status mountIncFs(const std::string& backingPath, const std::string& targetDir, int32_t flags, - IncrementalFileSystemControlParcel* _aidl_return) const override { + IncrementalFileSystemControlParcel* _aidl_return) const final { return mInterface->mountIncFs(backingPath, targetDir, flags, _aidl_return); } - binder::Status unmountIncFs(const std::string& dir) const override { + binder::Status unmountIncFs(const std::string& dir) const final { return mInterface->unmountIncFs(dir); } binder::Status bindMount(const std::string& sourceDir, - const std::string& targetDir) const override { + const std::string& targetDir) const final { return mInterface->bindMount(sourceDir, targetDir); } - binder::Status setIncFsMountOptions(const ::android::os::incremental::IncrementalFileSystemControlParcel& control, bool enableReadLogs) const override { + binder::Status setIncFsMountOptions( + const ::android::os::incremental::IncrementalFileSystemControlParcel& control, + bool enableReadLogs) const final { return mInterface->setIncFsMountOptions(control, enableReadLogs); } @@ -131,13 +138,13 @@ public: binder::Status initializeDataLoader(MountId mountId, const DataLoaderParamsParcel& params, const FileSystemControlParcel& control, const sp<IDataLoaderStatusListener>& listener, - bool* _aidl_return) const override { + bool* _aidl_return) const final { return mInterface->initializeDataLoader(mountId, params, control, listener, _aidl_return); } - binder::Status getDataLoader(MountId mountId, sp<IDataLoader>* _aidl_return) const override { + binder::Status getDataLoader(MountId mountId, sp<IDataLoader>* _aidl_return) const final { return mInterface->getDataLoader(mountId, _aidl_return); } - binder::Status destroyDataLoader(MountId mountId) const override { + binder::Status destroyDataLoader(MountId mountId) const final { return mInterface->destroyDataLoader(mountId); } @@ -148,9 +155,18 @@ private: class RealAppOpsManager : public AppOpsManagerWrapper { public: ~RealAppOpsManager() = default; - void startWatchingMode(int32_t op, const String16& packageName, const sp<IAppOpsCallback>& callback) override { + binder::Status checkPermission(const char* permission, const char* operation, + const char* package) const final { + return android::incremental::CheckPermissionForDataDelivery(permission, operation, package); + } + void startWatchingMode(int32_t op, const String16& packageName, + const sp<IAppOpsCallback>& callback) final { mAppOpsManager.startWatchingMode(op, packageName, callback); } + void stopWatchingMode(const sp<IAppOpsCallback>& callback) final { + mAppOpsManager.stopWatchingMode(callback); + } + private: android::AppOpsManager mAppOpsManager; }; @@ -174,36 +190,35 @@ class RealIncFs : public IncFsWrapper { public: RealIncFs() = default; ~RealIncFs() = default; - Control createControl(IncFsFd cmd, IncFsFd pendingReads, IncFsFd logs) const override { + Control createControl(IncFsFd cmd, IncFsFd pendingReads, IncFsFd logs) const final { return incfs::createControl(cmd, pendingReads, logs); } ErrorCode makeFile(const Control& control, std::string_view path, int mode, FileId id, - NewFileParams params) const override { + NewFileParams params) const final { return incfs::makeFile(control, path, mode, id, params); } - ErrorCode makeDir(const Control& control, std::string_view path, int mode) const override { + ErrorCode makeDir(const Control& control, std::string_view path, int mode) const final { return incfs::makeDir(control, path, mode); } - RawMetadata getMetadata(const Control& control, FileId fileid) const override { + RawMetadata getMetadata(const Control& control, FileId fileid) const final { return incfs::getMetadata(control, fileid); } - RawMetadata getMetadata(const Control& control, std::string_view path) const override { + RawMetadata getMetadata(const Control& control, std::string_view path) const final { return incfs::getMetadata(control, path); } - FileId getFileId(const Control& control, std::string_view path) const override { + FileId getFileId(const Control& control, std::string_view path) const final { return incfs::getFileId(control, path); } - ErrorCode link(const Control& control, std::string_view from, - std::string_view to) const override { + ErrorCode link(const Control& control, std::string_view from, std::string_view to) const final { return incfs::link(control, from, to); } - ErrorCode unlink(const Control& control, std::string_view path) const override { + ErrorCode unlink(const Control& control, std::string_view path) const final { return incfs::unlink(control, path); } - base::unique_fd openForSpecialOps(const Control& control, FileId id) const override { + base::unique_fd openForSpecialOps(const Control& control, FileId id) const final { return base::unique_fd{incfs::openForSpecialOps(control, id).release()}; } - ErrorCode writeBlocks(Span<const DataBlock> blocks) const override { + ErrorCode writeBlocks(Span<const DataBlock> blocks) const final { return incfs::writeBlocks(blocks); } }; diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp index 5553f688060a..0635ae169281 100644 --- a/services/incremental/test/IncrementalServiceTest.cpp +++ b/services/incremental/test/IncrementalServiceTest.cpp @@ -221,7 +221,28 @@ public: }; class MockAppOpsManager : public AppOpsManagerWrapper { +public: + MOCK_CONST_METHOD3(checkPermission, binder::Status(const char*, const char*, const char*)); MOCK_METHOD3(startWatchingMode, void(int32_t, const String16&, const sp<IAppOpsCallback>&)); + MOCK_METHOD1(stopWatchingMode, void(const sp<IAppOpsCallback>&)); + + void checkPermissionSuccess() { + ON_CALL(*this, checkPermission(_, _, _)).WillByDefault(Return(android::incremental::Ok())); + } + void checkPermissionFails() { + ON_CALL(*this, checkPermission(_, _, _)) + .WillByDefault( + Return(android::incremental::Exception(binder::Status::EX_SECURITY, {}))); + } + void initializeStartWatchingMode() { + ON_CALL(*this, startWatchingMode(_, _, _)) + .WillByDefault(Invoke(this, &MockAppOpsManager::storeCallback)); + } + void storeCallback(int32_t, const String16&, const sp<IAppOpsCallback>& cb) { + mStoredCallback = cb; + } + + sp<IAppOpsCallback> mStoredCallback; }; class MockServiceManager : public ServiceManagerWrapper { @@ -418,9 +439,15 @@ TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccess) { mVold->setIncFsMountOptionsSuccess(); mDataLoaderManager->initializeDataLoaderSuccess(); mDataLoaderManager->getDataLoaderSuccess(); + mAppOpsManager->checkPermissionSuccess(); EXPECT_CALL(*mDataLoaderManager, destroyDataLoader(_)); EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2); - EXPECT_CALL(*mVold, setIncFsMountOptions(_, _)); + // We are calling setIncFsMountOptions(true). + EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(1); + // After setIncFsMountOptions succeeded expecting to start watching. + EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1); + // Not expecting callback removal. + EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0); TemporaryDir tempDir; int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {}, @@ -429,6 +456,56 @@ TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccess) { ASSERT_GE(mIncrementalService->setStorageParams(storageId, true), 0); } +TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndPermissionChanged) { + mVold->mountIncFsSuccess(); + mIncFs->makeFileSuccess(); + mVold->bindMountSuccess(); + mVold->setIncFsMountOptionsSuccess(); + mDataLoaderManager->initializeDataLoaderSuccess(); + mDataLoaderManager->getDataLoaderSuccess(); + mAppOpsManager->checkPermissionSuccess(); + mAppOpsManager->initializeStartWatchingMode(); + EXPECT_CALL(*mDataLoaderManager, destroyDataLoader(_)); + EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2); + // We are calling setIncFsMountOptions(true). + EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(1); + // setIncFsMountOptions(false) is called on the callback. + EXPECT_CALL(*mVold, setIncFsMountOptions(_, false)).Times(1); + // After setIncFsMountOptions succeeded expecting to start watching. + EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1); + // After callback is called, disable read logs and remove callback. + EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(1); + TemporaryDir tempDir; + int storageId = + mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {}, + IncrementalService::CreateOptions::CreateNew); + ASSERT_GE(storageId, 0); + ASSERT_GE(mIncrementalService->setStorageParams(storageId, true), 0); + ASSERT_NE(nullptr, mAppOpsManager->mStoredCallback.get()); + mAppOpsManager->mStoredCallback->opChanged(0, {}); +} + +TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsCheckPermissionFails) { + mVold->mountIncFsSuccess(); + mIncFs->makeFileSuccess(); + mVold->bindMountSuccess(); + mDataLoaderManager->initializeDataLoaderSuccess(); + mDataLoaderManager->getDataLoaderSuccess(); + mAppOpsManager->checkPermissionFails(); + EXPECT_CALL(*mDataLoaderManager, destroyDataLoader(_)); + EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2); + // checkPermission fails, no calls to set opitions, start or stop WatchingMode. + EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(0); + EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(0); + EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0); + TemporaryDir tempDir; + int storageId = + mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {}, + IncrementalService::CreateOptions::CreateNew); + ASSERT_GE(storageId, 0); + ASSERT_LT(mIncrementalService->setStorageParams(storageId, true), 0); +} + TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsFails) { mVold->mountIncFsSuccess(); mIncFs->makeFileSuccess(); @@ -436,9 +513,14 @@ TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsFails) { mVold->setIncFsMountOptionsFails(); mDataLoaderManager->initializeDataLoaderSuccess(); mDataLoaderManager->getDataLoaderSuccess(); + mAppOpsManager->checkPermissionSuccess(); EXPECT_CALL(*mDataLoaderManager, destroyDataLoader(_)); EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2); - EXPECT_CALL(*mVold, setIncFsMountOptions(_, _)); + // We are calling setIncFsMountOptions. + EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(1); + // setIncFsMountOptions fails, no calls to start or stop WatchingMode. + EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(0); + EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0); TemporaryDir tempDir; int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {}, diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 2a914ecf4db6..e5ffbacb357b 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -519,10 +519,8 @@ public final class SystemServer { // Initialize native services. System.loadLibrary("android_servers"); - // Debug builds - allow heap profiling. - if (Build.IS_DEBUGGABLE) { - initZygoteChildHeapProfiling(); - } + // Allow heap / perf profiling. + initZygoteChildHeapProfiling(); // Debug builds - spawn a thread to monitor for fd leaks. if (Build.IS_DEBUGGABLE) { diff --git a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java index 138f9829c088..f8d197acf883 100644 --- a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java +++ b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java @@ -199,6 +199,12 @@ public class CrossProfileAppsServiceImplRoboTest { CROSS_PROFILE_APP_PACKAGE_NAME, PERSONAL_PROFILE_UID, PERSONAL_PROFILE_USER_ID); ShadowApplicationPackageManager.setPackageUidAsUser( CROSS_PROFILE_APP_PACKAGE_NAME, WORK_PROFILE_UID, WORK_PROFILE_USER_ID); + when(mPackageManagerInternal.getPackageUidInternal( + CROSS_PROFILE_APP_PACKAGE_NAME, /* flags= */ 0, PERSONAL_PROFILE_USER_ID)) + .thenReturn(PERSONAL_PROFILE_UID); + when(mPackageManagerInternal.getPackageUidInternal( + CROSS_PROFILE_APP_PACKAGE_NAME, /* flags= */ 0, WORK_PROFILE_USER_ID)) + .thenReturn(WORK_PROFILE_UID); } @Before @@ -456,6 +462,19 @@ public class CrossProfileAppsServiceImplRoboTest { } @Test + public void canUserAttemptToConfigureInteractAcrossProfiles_platformSignedAppWithAutomaticPermission_returnsFalse() { + mockCrossProfileAppNotWhitelistedByOem(); + shadowOf(mContext).grantPermissions( + Process.myPid(), + PERSONAL_PROFILE_UID, + Manifest.permission.INTERACT_ACROSS_PROFILES); + + assertThat(mCrossProfileAppsServiceImpl + .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME)) + .isFalse(); + } + + @Test public void canUserAttemptToConfigureInteractAcrossProfiles_returnsTrue() { assertThat(mCrossProfileAppsServiceImpl .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME)) @@ -528,6 +547,11 @@ public class CrossProfileAppsServiceImplRoboTest { .thenReturn(new ArrayList<>()); } + private void mockCrossProfileAppNotWhitelistedByOem() { + when(mDevicePolicyManagerInternal.getDefaultCrossProfilePackages()) + .thenReturn(new ArrayList<>()); + } + private boolean receivedManifestCanInteractAcrossProfilesChangedBroadcast() { final UserHandle userHandle = UserHandle.of(PERSONAL_PROFILE_USER_ID); if (!mSentUserBroadcasts.containsKey(userHandle)) { diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java index 5109de501157..b60e99363706 100644 --- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java @@ -17,7 +17,9 @@ package com.android.server.pm; -import static org.hamcrest.Matchers.arrayContaining; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.empty; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -48,7 +50,6 @@ import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.parsing.pkg.ParsedPackage; -import org.hamcrest.Matcher; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -58,7 +59,7 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import java.security.cert.CertificateException; -import java.util.Arrays; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; @@ -650,32 +651,32 @@ public class AppsFilterTest { final int hasProviderAppId = Process.FIRST_APPLICATION_UID + 1; final int queriesProviderAppId = Process.FIRST_APPLICATION_UID + 2; PackageSetting system = simulateAddPackage(appsFilter, pkg("some.system.pkg"), systemAppId); - PackageSetting seesNothing = simulateAddPackage(appsFilter, pkg("some.system.pkg"), + PackageSetting seesNothing = simulateAddPackage(appsFilter, pkg("com.some.package"), seesNothingAppId); PackageSetting hasProvider = simulateAddPackage(appsFilter, - pkgWithProvider("com.some.package", "com.some.authority"), hasProviderAppId); + pkgWithProvider("com.some.other.package", "com.some.authority"), hasProviderAppId); PackageSetting queriesProvider = simulateAddPackage(appsFilter, - pkgQueriesProvider("com.some.other.package", "com.some.authority"), + pkgQueriesProvider("com.yet.some.other.package", "com.some.authority"), queriesProviderAppId); final int[] systemFilter = appsFilter.getVisibilityWhitelist(system, new int[]{0}, mExisting).get(0); - assertThat(Arrays.asList(systemFilter), arrayContaining(systemAppId)); + assertThat(toList(systemFilter), empty()); final int[] seesNothingFilter = appsFilter.getVisibilityWhitelist(seesNothing, new int[]{0}, mExisting).get(0); - assertThat(Arrays.asList(seesNothingFilter), - arrayContaining(systemAppId, seesNothingAppId)); + assertThat(toList(seesNothingFilter), + contains(seesNothingAppId)); final int[] hasProviderFilter = appsFilter.getVisibilityWhitelist(hasProvider, new int[]{0}, mExisting).get(0); - assertThat(Arrays.asList(hasProviderFilter), - arrayContaining(systemAppId, hasProviderAppId, queriesProviderAppId)); + assertThat(toList(hasProviderFilter), + contains(hasProviderAppId, queriesProviderAppId)); int[] queriesProviderFilter = appsFilter.getVisibilityWhitelist(queriesProvider, new int[]{0}, mExisting).get(0); - assertThat(Arrays.asList(queriesProviderFilter), - arrayContaining(systemAppId, queriesProviderAppId)); + assertThat(toList(queriesProviderFilter), + contains(queriesProviderAppId)); // provider read appsFilter.grantImplicitAccess(hasProviderAppId, queriesProviderAppId); @@ -683,11 +684,16 @@ public class AppsFilterTest { // ensure implicit access is included in the filter queriesProviderFilter = appsFilter.getVisibilityWhitelist(queriesProvider, new int[]{0}, mExisting).get(0); - assertThat(Arrays.asList(queriesProviderFilter), - arrayContaining(systemAppId, hasProviderAppId, queriesProviderAppId)); + assertThat(toList(queriesProviderFilter), + contains(hasProviderAppId, queriesProviderAppId)); } - private void assertThat(List<int[]> asList, Matcher<Integer[]> arrayContainingInAnyOrder) { + private List<Integer> toList(int[] array) { + ArrayList<Integer> ret = new ArrayList<>(array.length); + for (int i = 0; i < array.length; i++) { + ret.add(i, array[i]); + } + return ret; } private interface WithSettingBuilder { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java index 47ad83147262..a23ade68b344 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java @@ -18,7 +18,6 @@ package com.android.server.notification; import static android.app.NotificationManager.IMPORTANCE_HIGH; import static android.app.NotificationManager.IMPORTANCE_LOW; -import static android.util.FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNull; @@ -77,8 +76,8 @@ public class NotificationChannelExtractorTest extends UiServiceTestCase { @Test public void testInvalidShortcutFlagEnabled_looksUpCorrectChannel() { - Settings.Global.putString( - mContext.getContentResolver(), NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "true"); + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 0); NotificationChannelExtractor extractor = new NotificationChannelExtractor(); extractor.setConfig(mConfig); @@ -97,7 +96,8 @@ public class NotificationChannelExtractorTest extends UiServiceTestCase { NotificationChannel updatedChannel = new NotificationChannel("a", "", IMPORTANCE_HIGH); when(mConfig.getConversationNotificationChannel( - any(), anyInt(), eq("a"), eq(r.getSbn().getShortcutId(mContext)), eq(true), eq(false))) + any(), anyInt(), eq("a"), eq(r.getSbn().getShortcutId(mContext)), + eq(true), eq(false))) .thenReturn(updatedChannel); assertNull(extractor.process(r)); @@ -106,8 +106,8 @@ public class NotificationChannelExtractorTest extends UiServiceTestCase { @Test public void testInvalidShortcutFlagDisabled_looksUpCorrectChannel() { - Settings.Global.putString( - mContext.getContentResolver(), NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "false"); + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 1); NotificationChannelExtractor extractor = new NotificationChannelExtractor(); extractor.setConfig(mConfig); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index f9596b53407f..15220e1ff54a 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -4791,7 +4791,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // enqueue toast -> no toasts enqueued ((INotificationManager) mService.mService).enqueueTextToast(testPackage, new Binder(), "Text", 2000, 0, null); - verify(mStatusBar).showToast(any(), any(), any(), any(), anyInt(), any()); + verify(mStatusBar).showToast(anyInt(), any(), any(), any(), any(), anyInt(), any()); } @Test diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java index 00b9273c1eb1..3139bfaaf1f5 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java @@ -63,7 +63,6 @@ import android.os.UserHandle; import android.provider.Settings; import android.service.notification.Adjustment; import android.service.notification.StatusBarNotification; -import android.util.FeatureFlagUtils; import android.widget.RemoteViews; import androidx.test.filters.SmallTest; @@ -124,8 +123,8 @@ public class NotificationRecordTest extends UiServiceTestCase { when(mMockContext.getResources()).thenReturn(getContext().getResources()); when(mMockContext.getPackageManager()).thenReturn(mPm); when(mMockContext.getContentResolver()).thenReturn(mContentResolver); - Settings.Global.putString(mContentResolver, - FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "false"); + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 1); ApplicationInfo appInfo = new ApplicationInfo(); appInfo.targetSdkVersion = Build.VERSION_CODES.O; when(mMockContext.getApplicationInfo()).thenReturn(appInfo); @@ -1138,8 +1137,8 @@ public class NotificationRecordTest extends UiServiceTestCase { @Test public void testIsConversation_bypassShortcutFlagEnabled() { - Settings.Global.putString(mContentResolver, - FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "true"); + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 0); StatusBarNotification sbn = getMessagingStyleNotification(); NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); record.setShortcutInfo(null); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index af605112307f..ed5ec6ac785b 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -22,7 +22,6 @@ import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_MAX; import static android.app.NotificationManager.IMPORTANCE_NONE; import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; -import static android.util.FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ; import static com.android.server.notification.PreferencesHelper.NOTIFICATION_CHANNEL_COUNT_LIMIT; @@ -2922,9 +2921,10 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test - public void testPlaceholderConversationId_flagOn() throws Exception { - Settings.Global.putString( - mContext.getContentResolver(), NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "true"); + public void testPlaceholderConversationId_shortcutNotRequired() throws Exception { + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 0); + mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger); final String xml = "<ranking version=\"1\">\n" @@ -2942,9 +2942,9 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test - public void testPlaceholderConversationId_flagOff() throws Exception { - Settings.Global.putString( - mContext.getContentResolver(), NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "false"); + public void testPlaceholderConversationId_shortcutRequired() throws Exception { + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 1); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger); final String xml = "<ranking version=\"1\">\n" @@ -2962,9 +2962,9 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test - public void testNormalConversationId_flagOff() throws Exception { - Settings.Global.putString( - mContext.getContentResolver(), NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "false"); + public void testNormalConversationId_shortcutRequired() throws Exception { + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 1); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger); final String xml = "<ranking version=\"1\">\n" @@ -2982,9 +2982,9 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test - public void testNoConversationId_flagOff() throws Exception { - Settings.Global.putString( - mContext.getContentResolver(), NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "false"); + public void testNoConversationId_shortcutRequired() throws Exception { + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 1); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger); final String xml = "<ranking version=\"1\">\n" diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java index 62d311ae7b36..2ce9c2b9ced0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java @@ -69,8 +69,6 @@ import android.window.WindowContainerTransaction; import androidx.test.filters.SmallTest; -import org.junit.After; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -119,13 +117,6 @@ public class TaskOrganizerTests extends WindowTestsBase { return createTaskStackOnDisplay(mDisplayContent); } - @Before - public void setUp() { - // We defer callbacks since we need to adjust task surface visibility, but for these tests, - // just run the callbacks synchronously - mWm.mAtmService.mTaskOrganizerController.setDeferTaskOrgCallbacksConsumer((r) -> r.run()); - } - @Test public void testAppearVanish() throws RemoteException { final ActivityStack stack = createStack(); diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java index 6c0f2b0cd278..edbdd4e94ac8 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java @@ -40,6 +40,9 @@ import android.hardware.soundtrigger.SoundTrigger.SoundModelEvent; import android.hardware.soundtrigger.SoundTriggerModule; import android.os.Binder; import android.os.DeadObjectException; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; import android.os.PowerManager; import android.os.PowerManager.ServiceType; import android.os.RemoteException; @@ -113,6 +116,13 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { private PowerSaveModeListener mPowerSaveModeListener; + // Handler to process call state changes will delay to allow time for the audio + // and sound trigger HALs to process the end of call notifications + // before we re enable pending recognition requests. + private final Handler mHandler; + private static final int MSG_CALL_STATE_CHANGED = 0; + private static final int CALL_INACTIVE_MSG_DELAY_MS = 1000; + SoundTriggerHelper(Context context) { ArrayList <ModuleProperties> modules = new ArrayList<>(); int status = SoundTrigger.listModules(modules); @@ -130,6 +140,31 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { // TODO: Figure out how to determine which module corresponds to the DSP hardware. mModuleProperties = modules.get(0); } + + Looper looper = Looper.myLooper(); + if (looper == null) { + looper = Looper.getMainLooper(); + } + if (looper != null) { + mHandler = new Handler(looper) { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_CALL_STATE_CHANGED: + synchronized (mLock) { + onCallStateChangedLocked( + TelephonyManager.CALL_STATE_OFFHOOK == msg.arg1); + } + break; + default: + Slog.e(TAG, "unknown message in handler:" + msg.what); + break; + } + } + }; + } else { + mHandler = null; + } } /** @@ -227,6 +262,37 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { return status; } + private int prepareForRecognition(ModelData modelData) { + if (mModule == null) { + mModule = SoundTrigger.attachModule(mModuleProperties.getId(), this, null); + if (mModule == null) { + Slog.w(TAG, "prepareForRecognition: cannot attach to sound trigger module"); + return STATUS_ERROR; + } + } + // Load the model if it is not loaded. + if (!modelData.isModelLoaded()) { + // Before we try and load this model, we should first make sure that any other + // models that don't have an active recognition/dead callback are unloaded. Since + // there is a finite limit on the number of models that the hardware may be able to + // have loaded, we want to make sure there's room for our model. + stopAndUnloadDeadModelsLocked(); + int[] handle = new int[] { 0 }; + int status = mModule.loadSoundModel(modelData.getSoundModel(), handle); + if (status != SoundTrigger.STATUS_OK) { + Slog.w(TAG, "prepareForRecognition: loadSoundModel failed with status: " + status); + return status; + } + modelData.setHandle(handle[0]); + modelData.setLoaded(); + if (DBG) { + Slog.d(TAG, "prepareForRecognition: Sound model loaded with handle:" + handle[0]); + } + } + return STATUS_OK; + } + + /** * Starts recognition for the given sound model. A single routine for both keyphrase and * generic sound models. @@ -248,12 +314,17 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { Slog.w(TAG, "Attempting startRecognition without the capability"); return STATUS_ERROR; } - if (mModule == null) { - mModule = SoundTrigger.attachModule(mModuleProperties.getId(), this, null); - if (mModule == null) { - Slog.w(TAG, "startRecognition cannot attach to sound trigger module"); - return STATUS_ERROR; + + IRecognitionStatusCallback oldCallback = modelData.getCallback(); + if (oldCallback != null && oldCallback.asBinder() != callback.asBinder()) { + Slog.w(TAG, "Canceling previous recognition for model id: " + + modelData.getModelId()); + try { + oldCallback.onError(STATUS_ERROR); + } catch (RemoteException e) { + Slog.w(TAG, "RemoteException in onDetectionStopped", e); } + modelData.clearCallback(); } // If the existing SoundModel is different (for the same UUID for Generic and same @@ -287,48 +358,25 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } } - IRecognitionStatusCallback oldCallback = modelData.getCallback(); - if (oldCallback != null && oldCallback.asBinder() != callback.asBinder()) { - Slog.w(TAG, "Canceling previous recognition for model id: " + - modelData.getModelId()); - try { - oldCallback.onError(STATUS_ERROR); - } catch (RemoteException e) { - Slog.w(TAG, "RemoteException in onDetectionStopped", e); - } - modelData.clearCallback(); - } - - // Load the model if it is not loaded. - if (!modelData.isModelLoaded()) { - // Before we try and load this model, we should first make sure that any other - // models that don't have an active recognition/dead callback are unloaded. Since - // there is a finite limit on the number of models that the hardware may be able to - // have loaded, we want to make sure there's room for our model. - stopAndUnloadDeadModelsLocked(); - int[] handle = new int[] { INVALID_VALUE }; - int status = mModule.loadSoundModel(soundModel, handle); - if (status != SoundTrigger.STATUS_OK) { - Slog.w(TAG, "loadSoundModel call failed with " + status); - return status; - } - if (handle[0] == INVALID_VALUE) { - Slog.w(TAG, "loadSoundModel call returned invalid sound model handle"); - return STATUS_ERROR; - } - modelData.setHandle(handle[0]); - modelData.setLoaded(); - Slog.d(TAG, "Sound model loaded with handle:" + handle[0]); - } modelData.setCallback(callback); modelData.setRequested(true); modelData.setRecognitionConfig(recognitionConfig); modelData.setSoundModel(soundModel); - int status = startRecognitionLocked(modelData, + if (!isRecognitionAllowed()) { + initializeTelephonyAndPowerStateListeners(); + return STATUS_OK; + } + + int status = prepareForRecognition(modelData); + if (status != STATUS_OK) { + Slog.w(TAG, "startRecognition failed to prepare model for recognition"); + return status; + } + status = startRecognitionLocked(modelData, false /* Don't notify for synchronous calls */); - // Initialize power save, call active state monitoring logic. + // Initialize power save, call active state monitoring logic. if (status == STATUS_OK) { initializeTelephonyAndPowerStateListeners(); } @@ -398,7 +446,8 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { if (DBG) { Slog.d(TAG, "stopRecognition for keyphraseId=" + keyphraseId + ", callback =" + callback.asBinder()); - Slog.d(TAG, "current callback=" + (modelData == null ? "null" : + Slog.d(TAG, "current callback=" + + ((modelData == null || modelData.getCallback() == null) ? "null" : modelData.getCallback().asBinder())); } int status = stopRecognition(modelData, callback); @@ -507,8 +556,8 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { synchronized (mLock) { MetricsLogger.count(mContext, "sth_unload_keyphrase_sound_model", 1); ModelData modelData = getKeyphraseModelDataLocked(keyphraseId); - if (mModule == null || modelData == null || modelData.getHandle() == INVALID_VALUE || - !modelData.isKeyphraseModel()) { + if (mModule == null || modelData == null || !modelData.isModelLoaded() + || !modelData.isKeyphraseModel()) { return STATUS_ERROR; } @@ -943,6 +992,10 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { return STATUS_OK; } if (start) { + int status = prepareForRecognition(model); + if (status != STATUS_OK) { + return status; + } return startRecognitionLocked(model, notify); } else { return stopRecognitionLocked(model, notify); @@ -992,8 +1045,15 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { @Override public void onCallStateChanged(int state, String arg1) { if (DBG) Slog.d(TAG, "onCallStateChanged: " + state); - synchronized (mLock) { - onCallStateChangedLocked(TelephonyManager.CALL_STATE_OFFHOOK == state); + + if (mHandler != null) { + synchronized (mLock) { + mHandler.removeMessages(MSG_CALL_STATE_CHANGED); + Message msg = mHandler.obtainMessage(MSG_CALL_STATE_CHANGED, state, 0); + mHandler.sendMessageDelayed( + msg, (TelephonyManager.CALL_STATE_OFFHOOK == state) ? 0 + : CALL_INACTIVE_MSG_DELAY_MS); + } } } } @@ -1216,9 +1276,8 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { // models. private int startRecognitionLocked(ModelData modelData, boolean notify) { IRecognitionStatusCallback callback = modelData.getCallback(); - int handle = modelData.getHandle(); RecognitionConfig config = modelData.getRecognitionConfig(); - if (callback == null || handle == INVALID_VALUE || config == null) { + if (callback == null || !modelData.isModelLoaded() || config == null) { // Nothing to do here. Slog.w(TAG, "startRecognition: Bad data passed in."); MetricsLogger.count(mContext, "sth_start_recognition_error", 1); @@ -1235,7 +1294,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { if (mModule == null) { return STATUS_ERROR; } - int status = mModule.startRecognition(handle, config); + int status = mModule.startRecognition(modelData.getHandle(), config); if (status != SoundTrigger.STATUS_OK) { Slog.w(TAG, "startRecognition failed with " + status); MetricsLogger.count(mContext, "sth_start_recognition_error", 1); @@ -1376,7 +1435,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { // Model handle is an integer used by the HAL as an identifier for sound // models. - private int mModelHandle = INVALID_VALUE; + private int mModelHandle; // The SoundModel instance, one of KeyphraseSoundModel or GenericSoundModel. private SoundModel mSoundModel = null; @@ -1436,7 +1495,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { synchronized void clearState() { mModelState = MODEL_NOTLOADED; - mModelHandle = INVALID_VALUE; mRecognitionConfig = null; mRequested = false; mCallback = null; diff --git a/tests/ApkVerityTest/Android.bp b/tests/ApkVerityTest/Android.bp index 248206817740..02c75edafcd5 100644 --- a/tests/ApkVerityTest/Android.bp +++ b/tests/ApkVerityTest/Android.bp @@ -16,7 +16,7 @@ java_test_host { name: "ApkVerityTest", srcs: ["src/**/*.java"], libs: ["tradefed", "compatibility-tradefed", "compatibility-host-util"], - test_suites: ["general-tests", "vts-core"], + test_suites: ["general-tests", "vts"], target_required: [ "block_device_writer_module", ], diff --git a/tests/ApkVerityTest/block_device_writer/Android.bp b/tests/ApkVerityTest/block_device_writer/Android.bp index 2760fe8df2d8..37fbc29470f6 100644 --- a/tests/ApkVerityTest/block_device_writer/Android.bp +++ b/tests/ApkVerityTest/block_device_writer/Android.bp @@ -48,6 +48,6 @@ cc_test { }, auto_gen_config: false, - test_suites: ["general-tests", "pts", "vts-core"], + test_suites: ["general-tests", "pts", "vts"], gtest: false, } diff --git a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java index 9b248878fe96..d0ebb5283f49 100644 --- a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java +++ b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java @@ -181,7 +181,7 @@ public class Nat464XlatTest { Nat464Xlat nat = makeNat464Xlat(); ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class); - nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX)); + nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX)); // Start clat. nat.start(); @@ -222,7 +222,7 @@ public class Nat464XlatTest { ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class); InOrder inOrder = inOrder(mNetd, mConnectivity); - nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX)); + nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX)); nat.start(); @@ -309,7 +309,7 @@ public class Nat464XlatTest { Nat464Xlat nat = makeNat464Xlat(); ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class); - nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX)); + nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX)); nat.start(); @@ -348,7 +348,7 @@ public class Nat464XlatTest { public void testStopBeforeClatdStarts() throws Exception { Nat464Xlat nat = makeNat464Xlat(); - nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX)); + nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX)); nat.start(); @@ -380,7 +380,7 @@ public class Nat464XlatTest { public void testStopAndClatdNeverStarts() throws Exception { Nat464Xlat nat = makeNat464Xlat(); - nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX)); + nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX)); nat.start(); |