diff options
129 files changed, 4189 insertions, 1289 deletions
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java index 08b1c2b9f548..abf78c67fa6f 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java @@ -56,6 +56,12 @@ import java.util.List; * instantiate this class directly; instead, retrieve it through * {@link android.content.Context#getSystemService * Context.getSystemService(Context.JOB_SCHEDULER_SERVICE)}. + * + * <p class="caution"><strong>Note:</strong> Beginning with API 30 + * ({@link android.os.Build.VERSION_CODES#R}), JobScheduler will throttle runaway applications. + * Calling {@link #schedule(JobInfo)} and other such methods with very high frequency is indicative + * of an app bug and so, to make sure the system doesn't get overwhelmed, JobScheduler will begin + * to throttle apps that show buggy behavior, regardless of target SDK version. */ @SystemService(Context.JOB_SCHEDULER_SERVICE) public abstract class JobScheduler { diff --git a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java index 4ffcf8ab6076..4b4fb9623ba0 100644 --- a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java +++ b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java @@ -80,27 +80,22 @@ public class PowerWhitelistManager { } /** - * Add the specified package to the power save whitelist. - * - * @return true if the package was successfully added to the whitelist + * Add the specified package to the permanent power save whitelist. */ @RequiresPermission(android.Manifest.permission.DEVICE_POWER) - public boolean addToWhitelist(@NonNull String packageName) { - return addToWhitelist(Collections.singletonList(packageName)) == 1; + public void addToWhitelist(@NonNull String packageName) { + addToWhitelist(Collections.singletonList(packageName)); } /** - * Add the specified packages to the power save whitelist. - * - * @return the number of packages that were successfully added to the whitelist + * Add the specified packages to the permanent power save whitelist. */ @RequiresPermission(android.Manifest.permission.DEVICE_POWER) - public int addToWhitelist(@NonNull List<String> packageNames) { + public void addToWhitelist(@NonNull List<String> packageNames) { try { - return mService.addPowerSaveWhitelistApps(packageNames); + mService.addPowerSaveWhitelistApps(packageNames); } catch (RemoteException e) { e.rethrowFromSystemServer(); - return 0; } } diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java index d2d942a4a7e5..dc72d6d9c4b3 100644 --- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java +++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java @@ -85,6 +85,7 @@ public interface AppStandbyInternal { /** * Checks if an app has been idle for a while and filters out apps that are excluded. * It returns false if the current system state allows all apps to be considered active. + * This happens if the device is plugged in or otherwise temporarily allowed to make exceptions. * Called by interface impls. */ boolean isAppIdleFiltered(String packageName, int appId, int userId, diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index b516279b58a5..e4c6b52f94bb 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -37,8 +37,6 @@ import android.app.job.JobSnapshot; import android.app.job.JobWorkItem; import android.app.usage.UsageStatsManager; import android.app.usage.UsageStatsManagerInternal; -import android.compat.annotation.ChangeId; -import android.compat.annotation.EnabledAfter; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; @@ -57,7 +55,6 @@ import android.net.Uri; import android.os.BatteryStats; import android.os.BatteryStatsInternal; import android.os.Binder; -import android.os.Build; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -90,7 +87,6 @@ import com.android.server.AppStateTracker; import com.android.server.DeviceIdleInternal; import com.android.server.FgThread; import com.android.server.LocalServices; -import com.android.server.compat.PlatformCompat; import com.android.server.job.JobSchedulerServiceDumpProto.ActiveJob; import com.android.server.job.JobSchedulerServiceDumpProto.PendingJob; import com.android.server.job.controllers.BackgroundJobsController; @@ -155,16 +151,6 @@ public class JobSchedulerService extends com.android.server.SystemService /** The maximum number of jobs that we allow an unprivileged app to schedule */ private static final int MAX_JOBS_PER_APP = 100; - /** - * {@link #schedule(JobInfo)}, {@link #scheduleAsPackage(JobInfo, String, int, String)}, and - * {@link #enqueue(JobInfo, JobWorkItem)} will throw a {@link IllegalStateException} if the app - * calls the APIs too frequently. - */ - @ChangeId - // This means the change will be enabled for target SDK larger than 29 (Q), meaning R and up. - @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q) - protected static final long CRASH_ON_EXCEEDED_LIMIT = 144363383L; - @VisibleForTesting public static Clock sSystemClock = Clock.systemUTC(); @@ -264,7 +250,6 @@ public class JobSchedulerService extends com.android.server.SystemService private final CountQuotaTracker mQuotaTracker; private static final String QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG = ".schedulePersisted()"; - private final PlatformCompat mPlatformCompat; /** * Queue of pending jobs. The JobServiceContext class will receive jobs from this list @@ -986,9 +971,7 @@ public class JobSchedulerService extends com.android.server.SystemService Slog.e(TAG, userId + "-" + pkg + " has called schedule() too many times"); mAppStandbyInternal.restrictApp( pkg, userId, UsageStatsManager.REASON_SUB_RESTRICT_BUGGY); - if (mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION - && mPlatformCompat.isChangeEnabledByPackageName( - CRASH_ON_EXCEEDED_LIMIT, pkg, userId)) { + if (mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION) { final boolean isDebuggable; synchronized (mLock) { if (!mDebuggableApps.containsKey(packageName)) { @@ -1370,8 +1353,6 @@ public class JobSchedulerService extends com.android.server.SystemService // Set up the app standby bucketing tracker mStandbyTracker = new StandbyTracker(); mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class); - mPlatformCompat = - (PlatformCompat) ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE); mQuotaTracker = new CountQuotaTracker(context, Categorizer.SINGLE_CATEGORIZER); mQuotaTracker.setCountLimit(Category.SINGLE_CATEGORY, mConstants.API_QUOTA_SCHEDULE_COUNT, diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index f1bfa0411978..e343478ec61f 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -48,6 +48,7 @@ import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET; +import static com.android.server.SystemService.PHASE_BOOT_COMPLETED; import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY; import android.annotation.NonNull; @@ -71,9 +72,8 @@ import android.content.pm.ParceledListSlice; import android.database.ContentObserver; import android.hardware.display.DisplayManager; import android.net.ConnectivityManager; -import android.net.Network; -import android.net.NetworkRequest; import android.net.NetworkScoreManager; +import android.os.BatteryManager; import android.os.BatteryStats; import android.os.Build; import android.os.Environment; @@ -285,6 +285,7 @@ public class AppStandbyController implements AppStandbyInternal { long mInitialForegroundServiceStartTimeoutMillis; private volatile boolean mAppIdleEnabled; + private boolean mIsCharging; private boolean mSystemServicesReady = false; // There was a system update, defaults need to be initialized after services are ready private boolean mPendingInitializeDefaults; @@ -360,6 +361,11 @@ public class AppStandbyController implements AppStandbyInternal { mHandler = new AppStandbyHandler(mInjector.getLooper()); mPackageManager = mContext.getPackageManager(); + DeviceStateReceiver deviceStateReceiver = new DeviceStateReceiver(); + IntentFilter deviceStates = new IntentFilter(BatteryManager.ACTION_CHARGING); + deviceStates.addAction(BatteryManager.ACTION_DISCHARGING); + mContext.registerReceiver(deviceStateReceiver, deviceStates); + synchronized (mAppIdleLock) { mAppIdleHistory = new AppIdleHistory(mInjector.getDataSystemDirectory(), mInjector.elapsedRealtime()); @@ -417,6 +423,8 @@ public class AppStandbyController implements AppStandbyInternal { if (mPendingOneTimeCheckIdleStates) { postOneTimeCheckIdleStates(); } + } else if (phase == PHASE_BOOT_COMPLETED) { + setChargingState(mInjector.isCharging()); } } @@ -515,6 +523,16 @@ public class AppStandbyController implements AppStandbyInternal { appUsage.bucketingReason, false); } + @VisibleForTesting + void setChargingState(boolean isCharging) { + synchronized (mAppIdleLock) { + if (mIsCharging != isCharging) { + if (DEBUG) Slog.d(TAG, "Setting mIsCharging to " + isCharging); + mIsCharging = isCharging; + } + } + } + @Override public void postCheckIdleStates(int userId) { mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_IDLE_STATES, userId, 0)); @@ -977,6 +995,11 @@ public class AppStandbyController implements AppStandbyInternal { if (isAppSpecial(packageName, appId, userId)) { return false; } else { + synchronized (mAppIdleLock) { + if (!mAppIdleEnabled || mIsCharging) { + return false; + } + } return isAppIdleUnfiltered(packageName, userId, elapsedRealtime); } } @@ -1543,6 +1566,8 @@ public class AppStandbyController implements AppStandbyInternal { pw.println(); pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled); + pw.print(" mIsCharging="); + pw.print(mIsCharging); pw.println(); pw.print("mScreenThresholds="); pw.println(Arrays.toString(mAppStandbyScreenThresholds)); pw.print("mElapsedThresholds="); pw.println(Arrays.toString(mAppStandbyElapsedThresholds)); @@ -1560,6 +1585,7 @@ public class AppStandbyController implements AppStandbyInternal { private final Looper mLooper; private IDeviceIdleController mDeviceIdleController; private IBatteryStats mBatteryStats; + private BatteryManager mBatteryManager; private PackageManagerInternal mPackageManagerInternal; private DisplayManager mDisplayManager; private PowerManager mPowerManager; @@ -1593,6 +1619,7 @@ public class AppStandbyController implements AppStandbyInternal { mDisplayManager = (DisplayManager) mContext.getSystemService( Context.DISPLAY_SERVICE); mPowerManager = mContext.getSystemService(PowerManager.class); + mBatteryManager = mContext.getSystemService(BatteryManager.class); final ActivityManager activityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); @@ -1630,6 +1657,10 @@ public class AppStandbyController implements AppStandbyInternal { return buildFlag && runtimeFlag; } + boolean isCharging() { + return mBatteryManager.isCharging(); + } + boolean isPowerSaveWhitelistExceptIdleApp(String packageName) throws RemoteException { return mDeviceIdleController.isPowerSaveWhitelistExceptIdleApp(packageName); } @@ -1766,15 +1797,19 @@ public class AppStandbyController implements AppStandbyInternal { } }; - private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder().build(); - - private final ConnectivityManager.NetworkCallback mNetworkCallback - = new ConnectivityManager.NetworkCallback() { + private class DeviceStateReceiver extends BroadcastReceiver { @Override - public void onAvailable(Network network) { - mConnectivityManager.unregisterNetworkCallback(this); + public void onReceive(Context context, Intent intent) { + switch (intent.getAction()) { + case BatteryManager.ACTION_CHARGING: + setChargingState(true); + break; + case BatteryManager.ACTION_DISCHARGING: + setChargingState(false); + break; + } } - }; + } private final DisplayManager.DisplayListener mDisplayListener = new DisplayManager.DisplayListener() { diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java index 3eed26b108f4..7d18578aff66 100644 --- a/apex/media/framework/java/android/media/MediaParser.java +++ b/apex/media/framework/java/android/media/MediaParser.java @@ -50,6 +50,7 @@ import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.upstream.TransferListener; import com.google.android.exoplayer2.util.ParsableByteArray; +import com.google.android.exoplayer2.video.ColorInfo; import java.io.EOFException; import java.io.IOException; @@ -810,19 +811,17 @@ public final class MediaParser { // Private static methods. private static MediaFormat toMediaFormat(Format format) { - MediaFormat result = new MediaFormat(); - if (format.bitrate != Format.NO_VALUE) { - result.setInteger(MediaFormat.KEY_BIT_RATE, format.bitrate); - } - if (format.channelCount != Format.NO_VALUE) { - result.setInteger(MediaFormat.KEY_CHANNEL_COUNT, format.channelCount); - } + setOptionalMediaFormatInt(result, MediaFormat.KEY_BIT_RATE, format.bitrate); + setOptionalMediaFormatInt(result, MediaFormat.KEY_CHANNEL_COUNT, format.channelCount); + + ColorInfo colorInfo = format.colorInfo; + if (colorInfo != null) { + setOptionalMediaFormatInt( + result, MediaFormat.KEY_COLOR_TRANSFER, colorInfo.colorTransfer); + setOptionalMediaFormatInt(result, MediaFormat.KEY_COLOR_RANGE, colorInfo.colorRange); + setOptionalMediaFormatInt(result, MediaFormat.KEY_COLOR_STANDARD, colorInfo.colorSpace); - if (format.colorInfo != null) { - result.setInteger(MediaFormat.KEY_COLOR_TRANSFER, format.colorInfo.colorTransfer); - result.setInteger(MediaFormat.KEY_COLOR_RANGE, format.colorInfo.colorRange); - result.setInteger(MediaFormat.KEY_COLOR_STANDARD, format.colorInfo.colorSpace); if (format.colorInfo.hdrStaticInfo != null) { result.setByteBuffer( MediaFormat.KEY_HDR_STATIC_INFO, @@ -830,63 +829,50 @@ public final class MediaParser { } } - if (format.sampleMimeType != null) { - result.setString(MediaFormat.KEY_MIME, format.sampleMimeType); - } - if (format.codecs != null) { - result.setString(MediaFormat.KEY_CODECS_STRING, format.codecs); - } + setOptionalMediaFormatString(result, MediaFormat.KEY_MIME, format.sampleMimeType); + setOptionalMediaFormatString(result, MediaFormat.KEY_CODECS_STRING, format.codecs); if (format.frameRate != Format.NO_VALUE) { result.setFloat(MediaFormat.KEY_FRAME_RATE, format.frameRate); } - if (format.width != Format.NO_VALUE) { - result.setInteger(MediaFormat.KEY_WIDTH, format.width); - } - if (format.height != Format.NO_VALUE) { - result.setInteger(MediaFormat.KEY_HEIGHT, format.height); - } + setOptionalMediaFormatInt(result, MediaFormat.KEY_WIDTH, format.width); + setOptionalMediaFormatInt(result, MediaFormat.KEY_HEIGHT, format.height); + List<byte[]> initData = format.initializationData; if (initData != null) { for (int i = 0; i < initData.size(); i++) { result.setByteBuffer("csd-" + i, ByteBuffer.wrap(initData.get(i))); } } - if (format.language != null) { - result.setString(MediaFormat.KEY_LANGUAGE, format.language); - } - if (format.maxInputSize != Format.NO_VALUE) { - result.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, format.maxInputSize); - } - if (format.pcmEncoding != Format.NO_VALUE) { - result.setInteger(MediaFormat.KEY_PCM_ENCODING, format.pcmEncoding); - } - if (format.rotationDegrees != Format.NO_VALUE) { - result.setInteger(MediaFormat.KEY_ROTATION, format.rotationDegrees); - } - if (format.sampleRate != Format.NO_VALUE) { - result.setInteger(MediaFormat.KEY_SAMPLE_RATE, format.sampleRate); - } + setOptionalMediaFormatString(result, MediaFormat.KEY_LANGUAGE, format.language); + setOptionalMediaFormatInt(result, MediaFormat.KEY_MAX_INPUT_SIZE, format.maxInputSize); + setOptionalMediaFormatInt(result, MediaFormat.KEY_PCM_ENCODING, format.pcmEncoding); + setOptionalMediaFormatInt(result, MediaFormat.KEY_ROTATION, format.rotationDegrees); + setOptionalMediaFormatInt(result, MediaFormat.KEY_SAMPLE_RATE, format.sampleRate); + int selectionFlags = format.selectionFlags; - if ((selectionFlags & C.SELECTION_FLAG_AUTOSELECT) != 0) { - result.setInteger(MediaFormat.KEY_IS_AUTOSELECT, 1); - } - if ((selectionFlags & C.SELECTION_FLAG_DEFAULT) != 0) { - result.setInteger(MediaFormat.KEY_IS_DEFAULT, 1); - } - if ((selectionFlags & C.SELECTION_FLAG_FORCED) != 0) { - result.setInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE, 1); - } - if (format.encoderDelay != Format.NO_VALUE) { - result.setInteger(MediaFormat.KEY_ENCODER_DELAY, format.encoderDelay); - } - if (format.encoderPadding != Format.NO_VALUE) { - result.setInteger(MediaFormat.KEY_ENCODER_PADDING, format.encoderPadding); + result.setInteger( + MediaFormat.KEY_IS_AUTOSELECT, selectionFlags & C.SELECTION_FLAG_AUTOSELECT); + result.setInteger(MediaFormat.KEY_IS_DEFAULT, selectionFlags & C.SELECTION_FLAG_DEFAULT); + result.setInteger( + MediaFormat.KEY_IS_FORCED_SUBTITLE, selectionFlags & C.SELECTION_FLAG_FORCED); + + setOptionalMediaFormatInt(result, MediaFormat.KEY_ENCODER_DELAY, format.encoderDelay); + setOptionalMediaFormatInt(result, MediaFormat.KEY_ENCODER_PADDING, format.encoderPadding); + + if (format.pixelWidthHeightRatio != Format.NO_VALUE && format.pixelWidthHeightRatio != 0) { + int parWidth = 1; + int parHeight = 1; + if (format.pixelWidthHeightRatio < 1.0f) { + parHeight = 1 << 30; + parWidth = (int) (format.pixelWidthHeightRatio * parHeight); + } else if (format.pixelWidthHeightRatio > 1.0f) { + parWidth = 1 << 30; + parHeight = (int) (parWidth / format.pixelWidthHeightRatio); + } + result.setInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_WIDTH, parWidth); + result.setInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_HEIGHT, parHeight); + result.setFloat("pixel-width-height-ratio-float", format.pixelWidthHeightRatio); } - // TODO: Implement float to fraction conversion. - // if (format.pixelWidthHeightRatio != Format.NO_VALUE) { - // result.setInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_WIDTH, ); - // result.setInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_HEIGHT, ); - // } // LACK OF SUPPORT FOR: // format.accessibilityChannel; @@ -899,6 +885,19 @@ public final class MediaParser { return result; } + private static void setOptionalMediaFormatInt(MediaFormat mediaFormat, String key, int value) { + if (value != Format.NO_VALUE) { + mediaFormat.setInteger(key, value); + } + } + + private static void setOptionalMediaFormatString( + MediaFormat mediaFormat, String key, @Nullable String value) { + if (value != null) { + mediaFormat.setString(key, value); + } + } + private static DrmInitData toFrameworkDrmInitData( com.google.android.exoplayer2.drm.DrmInitData drmInitData) { // TODO: Implement. diff --git a/apex/statsd/Android.bp b/apex/statsd/Android.bp index 1f9f18cd051a..c0f84a0ba070 100644 --- a/apex/statsd/Android.bp +++ b/apex/statsd/Android.bp @@ -20,6 +20,7 @@ apex { apex_defaults { native_shared_libs: [ + "libstatspull", "libstats_jni", ], // binaries: ["vold"], @@ -28,6 +29,7 @@ apex_defaults { "service-statsd", ], // prebuilts: ["my_prebuilt"], + compile_multilib: "both", name: "com.android.os.statsd-defaults", key: "com.android.os.statsd.key", certificate: ":com.android.os.statsd.certificate", diff --git a/apex/statsd/aidl/Android.bp b/apex/statsd/aidl/Android.bp index 7c93bc73e45d..4ccdd7e734db 100644 --- a/apex/statsd/aidl/Android.bp +++ b/apex/statsd/aidl/Android.bp @@ -38,6 +38,10 @@ aidl_interface { }, ndk: { enabled: true, + apex_available: [ + "com.android.os.statsd", + ], } - } + + }, } diff --git a/apex/statsd/framework/Android.bp b/apex/statsd/framework/Android.bp index 63a853a6005c..ab669d4ac45a 100644 --- a/apex/statsd/framework/Android.bp +++ b/apex/statsd/framework/Android.bp @@ -12,6 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + default_visibility: [ ":__pkg__" ] +} + genrule { name: "statslog-statsd-java-gen", tools: ["stats-log-api-gen"], @@ -25,6 +29,9 @@ java_library_static { srcs: [ ":statslog-statsd-java-gen", ], + visibility: [ + "//cts/hostsidetests/statsd/apps:__subpackages__", + ] } filegroup { @@ -34,6 +41,9 @@ filegroup { ":framework-statsd-aidl-sources", ":statslog-statsd-java-gen", ], + visibility: [ + "//frameworks/base", // For the "global" stubs. + ], } java_defaults { @@ -139,6 +149,10 @@ java_library { "framework-statsd-defaults", ], srcs: [ ":framework-statsd-stubs-srcs-publicapi" ], + visibility: [ + "//frameworks/base", // Framework + "//frameworks/base/apex/statsd", // statsd apex + ] } java_library { @@ -147,6 +161,10 @@ java_library { "framework-statsd-defaults", ], srcs: [ ":framework-statsd-stubs-srcs-systemapi" ], + visibility: [ + "//frameworks/base", // Framework + "//frameworks/base/apex/statsd", // statsd apex + ] } java_library { @@ -155,4 +173,9 @@ java_library { "framework-statsd-defaults", ], srcs: [ ":framework-statsd-stubs-srcs-module_libs_api" ], + visibility: [ + "//frameworks/base", // Framework + "//frameworks/base/apex/statsd", // statsd apex + "//frameworks/opt/net/wifi/service" // wifi service + ] } diff --git a/api/current.txt b/api/current.txt index ac52e589cbb2..4887c66660bb 100644 --- a/api/current.txt +++ b/api/current.txt @@ -12703,8 +12703,7 @@ package android.content.res { public class Resources { ctor @Deprecated public Resources(android.content.res.AssetManager, android.util.DisplayMetrics, android.content.res.Configuration); - method public void addLoader(@NonNull android.content.res.loader.ResourcesLoader); - method public void clearLoaders(); + method public void addLoaders(@NonNull android.content.res.loader.ResourcesLoader...); method public final void finishPreloading(); method public final void flushLayoutCache(); method @NonNull public android.content.res.XmlResourceParser getAnimation(@AnimRes @AnimatorRes int) throws android.content.res.Resources.NotFoundException; @@ -12731,7 +12730,6 @@ package android.content.res { method @NonNull public int[] getIntArray(@ArrayRes int) throws android.content.res.Resources.NotFoundException; method public int getInteger(@IntegerRes int) throws android.content.res.Resources.NotFoundException; method @NonNull public android.content.res.XmlResourceParser getLayout(@LayoutRes int) throws android.content.res.Resources.NotFoundException; - method @NonNull public java.util.List<android.content.res.loader.ResourcesLoader> getLoaders(); method @Deprecated public android.graphics.Movie getMovie(@RawRes int) throws android.content.res.Resources.NotFoundException; method @NonNull public String getQuantityString(@PluralsRes int, int, java.lang.Object...) throws android.content.res.Resources.NotFoundException; method @NonNull public String getQuantityString(@PluralsRes int, int) throws android.content.res.Resources.NotFoundException; @@ -12759,8 +12757,7 @@ package android.content.res { method public android.content.res.AssetFileDescriptor openRawResourceFd(@RawRes int) throws android.content.res.Resources.NotFoundException; method public void parseBundleExtra(String, android.util.AttributeSet, android.os.Bundle) throws org.xmlpull.v1.XmlPullParserException; method public void parseBundleExtras(android.content.res.XmlResourceParser, android.os.Bundle) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; - method public void removeLoader(@NonNull android.content.res.loader.ResourcesLoader); - method public void setLoaders(@NonNull java.util.List<android.content.res.loader.ResourcesLoader>); + method public void removeLoaders(@NonNull android.content.res.loader.ResourcesLoader...); method @Deprecated public void updateConfiguration(android.content.res.Configuration, android.util.DisplayMetrics); field @AnyRes public static final int ID_NULL = 0; // 0x0 } @@ -45514,7 +45511,7 @@ package android.telecom { field public static final int DIRECTION_INCOMING = 0; // 0x0 field public static final int DIRECTION_OUTGOING = 1; // 0x1 field public static final int DIRECTION_UNKNOWN = -1; // 0xffffffff - field public static final int PROPERTY_ASSISTED_DIALING_USED = 512; // 0x200 + field public static final int PROPERTY_ASSISTED_DIALING = 512; // 0x200 field public static final int PROPERTY_CONFERENCE = 1; // 0x1 field public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 4; // 0x4 field public static final int PROPERTY_ENTERPRISE_CALL = 32; // 0x20 @@ -45603,7 +45600,8 @@ package android.telecom { method public final java.util.List<android.telecom.Connection> getConferenceableConnections(); method public final int getConnectionCapabilities(); method public final int getConnectionProperties(); - method public final long getConnectionTime(); + method public final long getConnectionStartElapsedRealtimeMillis(); + method @IntRange(from=0) public final long getConnectionTime(); method public final java.util.List<android.telecom.Connection> getConnections(); method public final android.telecom.DisconnectCause getDisconnectCause(); method public final android.os.Bundle getExtras(); @@ -45633,8 +45631,9 @@ package android.telecom { method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>); method public final void setConnectionCapabilities(int); method public final void setConnectionProperties(int); - method public final void setConnectionStartElapsedRealTime(long); - method public final void setConnectionTime(long); + method @Deprecated public final void setConnectionStartElapsedRealTime(long); + method public final void setConnectionStartElapsedRealtimeMillis(long); + method public final void setConnectionTime(@IntRange(from=0) long); method public final void setDialing(); method public final void setDisconnected(android.telecom.DisconnectCause); method public final void setExtras(@Nullable android.os.Bundle); @@ -45794,7 +45793,7 @@ package android.telecom { field public static final String EXTRA_IS_RTT_AUDIO_PRESENT = "android.telecom.extra.IS_RTT_AUDIO_PRESENT"; field public static final String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER"; field public static final String EXTRA_SIP_INVITE = "android.telecom.extra.SIP_INVITE"; - field public static final int PROPERTY_ASSISTED_DIALING_USED = 512; // 0x200 + field public static final int PROPERTY_ASSISTED_DIALING = 512; // 0x200 field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20 field public static final int PROPERTY_HIGH_DEF_AUDIO = 4; // 0x4 field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10 @@ -47133,6 +47132,19 @@ package android.telephony { field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ClosedSubscriberGroupInfo> CREATOR; } + public final class DisplayInfo implements android.os.Parcelable { + method public int describeContents(); + method public int getNetworkType(); + method public int getOverrideNetworkType(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.DisplayInfo> CREATOR; + field public static final int OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO = 2; // 0x2 + field public static final int OVERRIDE_NETWORK_TYPE_LTE_CA = 1; // 0x1 + field public static final int OVERRIDE_NETWORK_TYPE_NONE = 0; // 0x0 + field public static final int OVERRIDE_NETWORK_TYPE_NR_NSA = 3; // 0x3 + field public static final int OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE = 4; // 0x4 + } + public class IccOpenLogicalChannelResponse implements android.os.Parcelable { method public int describeContents(); method public int getChannel(); @@ -47389,6 +47401,7 @@ package android.telephony { method public void onDataActivity(int); method public void onDataConnectionStateChanged(int); method public void onDataConnectionStateChanged(int, int); + method @RequiresPermission("android.permission.READ_PHONE_STATE") public void onDisplayInfoChanged(@NonNull android.telephony.DisplayInfo); method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo); method public void onMessageWaitingIndicatorChanged(boolean); method @RequiresPermission("android.permission.MODIFY_PHONE_STATE") public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState); @@ -47406,6 +47419,7 @@ package android.telephony { field public static final int LISTEN_CELL_LOCATION = 16; // 0x10 field public static final int LISTEN_DATA_ACTIVITY = 128; // 0x80 field public static final int LISTEN_DATA_CONNECTION_STATE = 64; // 0x40 + field public static final int LISTEN_DISPLAY_INFO_CHANGED = 1048576; // 0x100000 field public static final int LISTEN_EMERGENCY_NUMBER_LIST = 16777216; // 0x1000000 field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 134217728; // 0x8000000 field public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 4; // 0x4 diff --git a/api/system-current.txt b/api/system-current.txt index b07066df7b3c..fdf33ded1478 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1809,7 +1809,7 @@ package android.content { field public static final String TELEPHONY_REGISTRY_SERVICE = "telephony_registry"; field public static final String TETHERING_SERVICE = "tethering"; field public static final String VR_SERVICE = "vrmanager"; - field public static final String WIFI_COND_SERVICE = "wificond"; + field public static final String WIFI_NL80211_SERVICE = "wifinl80211"; field @Deprecated public static final String WIFI_RTT_SERVICE = "rttmanager"; field public static final String WIFI_SCANNING_SERVICE = "wifiscanner"; } @@ -8329,21 +8329,21 @@ package android.net.wifi.wificond { field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.wificond.RadioChainInfo> CREATOR; } - public class WifiCondManager { + public class WifiNl80211Manager { method public void abortScan(@NonNull String); method public void enableVerboseLogging(boolean); method @NonNull public int[] getChannelsMhzForBand(int); method @Nullable public android.net.wifi.wificond.DeviceWiphyCapabilities getDeviceWiphyCapabilities(@NonNull String); method @NonNull public java.util.List<android.net.wifi.wificond.NativeScanResult> getScanResults(@NonNull String, int); - method @Nullable public android.net.wifi.wificond.WifiCondManager.TxPacketCounters getTxPacketCounters(@NonNull String); - method @Nullable public static android.net.wifi.wificond.WifiCondManager.OemSecurityType parseOemSecurityTypeElement(int, int, @NonNull byte[]); - method public boolean registerApCallback(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiCondManager.SoftApCallback); - method public void sendMgmtFrame(@NonNull String, @NonNull byte[], int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiCondManager.SendMgmtFrameCallback); + method @Nullable public android.net.wifi.wificond.WifiNl80211Manager.TxPacketCounters getTxPacketCounters(@NonNull String); + method @Nullable public static android.net.wifi.wificond.WifiNl80211Manager.OemSecurityType parseOemSecurityTypeElement(int, int, @NonNull byte[]); + method public boolean registerApCallback(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiNl80211Manager.SoftApCallback); + method public void sendMgmtFrame(@NonNull String, @NonNull byte[], int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiNl80211Manager.SendMgmtFrameCallback); method public void setOnServiceDeadCallback(@NonNull Runnable); - method public boolean setupInterfaceForClientMode(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiCondManager.ScanEventCallback, @NonNull android.net.wifi.wificond.WifiCondManager.ScanEventCallback); + method public boolean setupInterfaceForClientMode(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiNl80211Manager.ScanEventCallback, @NonNull android.net.wifi.wificond.WifiNl80211Manager.ScanEventCallback); method public boolean setupInterfaceForSoftApMode(@NonNull String); - method @Nullable public android.net.wifi.wificond.WifiCondManager.SignalPollResult signalPoll(@NonNull String); - method public boolean startPnoScan(@NonNull String, @NonNull android.net.wifi.wificond.PnoSettings, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiCondManager.PnoScanRequestCallback); + method @Nullable public android.net.wifi.wificond.WifiNl80211Manager.SignalPollResult signalPoll(@NonNull String); + method public boolean startPnoScan(@NonNull String, @NonNull android.net.wifi.wificond.PnoSettings, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiNl80211Manager.PnoScanRequestCallback); method public boolean startScan(@NonNull String, int, @Nullable java.util.Set<java.lang.Integer>, @Nullable java.util.List<byte[]>); method public boolean stopPnoScan(@NonNull String); method public boolean tearDownClientInterface(@NonNull String); @@ -8358,43 +8358,43 @@ package android.net.wifi.wificond { field public static final int SEND_MGMT_FRAME_ERROR_UNKNOWN = 1; // 0x1 } - public static class WifiCondManager.OemSecurityType { - ctor public WifiCondManager.OemSecurityType(int, @NonNull java.util.List<java.lang.Integer>, @NonNull java.util.List<java.lang.Integer>, int); + public static class WifiNl80211Manager.OemSecurityType { + ctor public WifiNl80211Manager.OemSecurityType(int, @NonNull java.util.List<java.lang.Integer>, @NonNull java.util.List<java.lang.Integer>, int); field public final int groupCipher; field @NonNull public final java.util.List<java.lang.Integer> keyManagement; field @NonNull public final java.util.List<java.lang.Integer> pairwiseCipher; field public final int protocol; } - public static interface WifiCondManager.PnoScanRequestCallback { + public static interface WifiNl80211Manager.PnoScanRequestCallback { method public void onPnoRequestFailed(); method public void onPnoRequestSucceeded(); } - public static interface WifiCondManager.ScanEventCallback { + public static interface WifiNl80211Manager.ScanEventCallback { method public void onScanFailed(); method public void onScanResultReady(); } - public static interface WifiCondManager.SendMgmtFrameCallback { + public static interface WifiNl80211Manager.SendMgmtFrameCallback { method public void onAck(int); method public void onFailure(int); } - public static class WifiCondManager.SignalPollResult { + public static class WifiNl80211Manager.SignalPollResult { field public final int associationFrequencyMHz; field public final int currentRssiDbm; field public final int rxBitrateMbps; field public final int txBitrateMbps; } - public static interface WifiCondManager.SoftApCallback { + public static interface WifiNl80211Manager.SoftApCallback { method public void onConnectedClientsChanged(@NonNull android.net.wifi.wificond.NativeWifiClient, boolean); method public void onFailure(); method public void onSoftApChannelSwitched(int, int); } - public static class WifiCondManager.TxPacketCounters { + public static class WifiNl80211Manager.TxPacketCounters { field public final int txPacketFailed; field public final int txPacketSucceeded; } @@ -8839,8 +8839,8 @@ package android.os { } public class PowerWhitelistManager { - method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public boolean addToWhitelist(@NonNull String); - method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public int addToWhitelist(@NonNull java.util.List<java.lang.String>); + method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull String); + method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull java.util.List<java.lang.String>); method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void whitelistAppTemporarily(@NonNull String, long); method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long whitelistAppTemporarilyForEvent(@NonNull String, int, @NonNull String); field public static final int EVENT_MMS = 2; // 0x2 @@ -10854,27 +10854,26 @@ package android.telecom { public abstract class Conference extends android.telecom.Conferenceable { method @Deprecated public final android.telecom.AudioState getAudioState(); method @Deprecated public final long getConnectTimeMillis(); - method public final long getConnectionStartElapsedRealTime(); method public android.telecom.Connection getPrimaryConnection(); method @NonNull public final String getTelecomCallId(); method @Deprecated public void onAudioStateChanged(android.telecom.AudioState); - method public final void setAddress(@NonNull android.net.Uri, int); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setAddress(@NonNull android.net.Uri, int); method public final void setCallerDisplayName(@NonNull String, int); - method public void setConferenceState(boolean); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setConferenceState(boolean); method @Deprecated public final void setConnectTimeMillis(long); } public abstract class Connection extends android.telecom.Conferenceable { method @Deprecated public final android.telecom.AudioState getAudioState(); - method public final long getConnectElapsedTimeMillis(); - method public final long getConnectTimeMillis(); + method @IntRange(from=0) public final long getConnectTimeMillis(); + method public final long getConnectionStartElapsedRealtimeMillis(); method @Nullable public android.telecom.PhoneAccountHandle getPhoneAccountHandle(); method @Nullable public final String getTelecomCallId(); method @Deprecated public void onAudioStateChanged(android.telecom.AudioState); method public final void resetConnectionTime(); method public void setCallDirection(int); - method public final void setConnectTimeMillis(long); - method public final void setConnectionStartElapsedRealTime(long); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setConnectTimeMillis(@IntRange(from=0) long); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setConnectionStartElapsedRealtimeMillis(long); method public void setPhoneAccountHandle(@NonNull android.telecom.PhoneAccountHandle); method public void setTelecomCallId(@NonNull String); field public static final int CAPABILITY_CONFERENCE_HAS_NO_CHILDREN = 2097152; // 0x200000 @@ -11033,7 +11032,7 @@ package android.telecom { } public static class PhoneAccount.Builder { - method @NonNull public android.telecom.PhoneAccount.Builder setGroupId(@NonNull String); + method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telecom.PhoneAccount.Builder setGroupId(@NonNull String); } public class PhoneAccountSuggestionService extends android.app.Service { @@ -11108,7 +11107,7 @@ package android.telecom { method public int getCallState(); method public android.telecom.PhoneAccountHandle getConnectionManager(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCurrentTtyMode(); - method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDefaultDialerPackage(int); + method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDefaultDialerPackage(@NonNull android.os.UserHandle); method @Deprecated public android.content.ComponentName getDefaultPhoneApp(); method public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsForPackage(); method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsSupportingScheme(String); @@ -12572,6 +12571,7 @@ package android.telephony { method public void notifyDataActivityChanged(int, int); method public void notifyDataConnectionForSubscriber(int, int, int, @Nullable android.telephony.PreciseDataConnectionState); method public void notifyDisconnectCause(int, int, int, int); + method public void notifyDisplayInfoChanged(int, int, @NonNull android.telephony.DisplayInfo); method public void notifyEmergencyNumberList(int, int); method public void notifyImsDisconnectCause(int, @NonNull android.telephony.ims.ImsReasonInfo); method public void notifyMessageWaitingChanged(int, int, boolean); diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt index 0caee6bebbda..dfb0d7460ca4 100644 --- a/api/system-lint-baseline.txt +++ b/api/system-lint-baseline.txt @@ -234,7 +234,7 @@ OnNameExpected: android.content.ContentProvider#checkUriPermission(android.net.U If implemented by developer, should follow the on<Something> style; otherwise consider marking final -PairedRegistration: android.net.wifi.wificond.WifiCondManager#registerApCallback(String, java.util.concurrent.Executor, android.net.wifi.wificond.WifiCondManager.SoftApCallback): +PairedRegistration: android.net.wifi.wificond.WifiNl80211Manager#registerApCallback(String, java.util.concurrent.Executor, android.net.wifi.wificond.WifiNl80211Manager.SoftApCallback): diff --git a/api/test-current.txt b/api/test-current.txt index 84fd6f41ae5d..4c8bb0290cae 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -2451,8 +2451,8 @@ package android.os { } public class PowerWhitelistManager { - method @RequiresPermission("android.permission.DEVICE_POWER") public boolean addToWhitelist(@NonNull String); - method @RequiresPermission("android.permission.DEVICE_POWER") public int addToWhitelist(@NonNull java.util.List<java.lang.String>); + method @RequiresPermission("android.permission.DEVICE_POWER") public void addToWhitelist(@NonNull String); + method @RequiresPermission("android.permission.DEVICE_POWER") public void addToWhitelist(@NonNull java.util.List<java.lang.String>); method @RequiresPermission("android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST") public void whitelistAppTemporarily(@NonNull String, long); method @RequiresPermission("android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST") public long whitelistAppTemporarilyForEvent(@NonNull String, int, @NonNull String); field public static final int EVENT_MMS = 2; // 0x2 @@ -3441,23 +3441,22 @@ package android.telecom { } public abstract class Conference extends android.telecom.Conferenceable { - method public final long getConnectionStartElapsedRealTime(); method public android.telecom.Connection getPrimaryConnection(); method @NonNull public final String getTelecomCallId(); - method public final void setAddress(@NonNull android.net.Uri, int); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setAddress(@NonNull android.net.Uri, int); method public final void setCallerDisplayName(@NonNull String, int); - method public void setConferenceState(boolean); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setConferenceState(boolean); } public abstract class Connection extends android.telecom.Conferenceable { - method public final long getConnectElapsedTimeMillis(); - method public final long getConnectTimeMillis(); + method @IntRange(from=0) public final long getConnectTimeMillis(); + method public final long getConnectionStartElapsedRealtimeMillis(); method @Nullable public android.telecom.PhoneAccountHandle getPhoneAccountHandle(); method @Nullable public final String getTelecomCallId(); method public final void resetConnectionTime(); method public void setCallDirection(int); - method public final void setConnectTimeMillis(long); - method public final void setConnectionStartElapsedRealTime(long); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setConnectTimeMillis(@IntRange(from=0) long); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setConnectionStartElapsedRealtimeMillis(long); method public void setPhoneAccountHandle(@NonNull android.telecom.PhoneAccountHandle); method public void setTelecomCallId(@NonNull String); field public static final int CAPABILITY_CONFERENCE_HAS_NO_CHILDREN = 2097152; // 0x200000 @@ -3489,7 +3488,7 @@ package android.telecom { } public static class PhoneAccount.Builder { - method @NonNull public android.telecom.PhoneAccount.Builder setGroupId(@NonNull String); + method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telecom.PhoneAccount.Builder setGroupId(@NonNull String); } public class PhoneAccountSuggestionService extends android.app.Service { @@ -3503,7 +3502,7 @@ package android.telecom { public class TelecomManager { method @NonNull @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccounts(boolean); method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public int getCurrentTtyMode(); - method @Nullable @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getDefaultDialerPackage(int); + method @Nullable @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getDefaultDialerPackage(@NonNull android.os.UserHandle); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isInEmergencyCall(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUserSelectedOutgoingPhoneAccount(@Nullable android.telecom.PhoneAccountHandle); field public static final int TTY_MODE_FULL = 1; // 0x1 @@ -3721,6 +3720,7 @@ package android.telephony { method public void notifyDataActivityChanged(int, int); method public void notifyDataConnectionForSubscriber(int, int, int, @Nullable android.telephony.PreciseDataConnectionState); method public void notifyDisconnectCause(int, int, int, int); + method public void notifyDisplayInfoChanged(int, int, @NonNull android.telephony.DisplayInfo); method public void notifyEmergencyNumberList(int, int); method public void notifyImsDisconnectCause(int, @NonNull android.telephony.ims.ImsReasonInfo); method public void notifyMessageWaitingChanged(int, int, boolean); diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 03f97d80824d..23a4437910f7 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -381,7 +381,7 @@ message Atom { UserspaceRebootReported userspace_reboot_reported = 243 [(module) = "framework"]; NotificationReported notification_reported = 244 [(module) = "framework"]; NotificationPanelReported notification_panel_reported = 245; - NotificationChannelModified notification_panel_modified = 246; + NotificationChannelModified notification_channel_modified = 246; IntegrityCheckResultReported integrity_check_result_reported = 247 [(module) = "framework"]; IntegrityRulesPushed integrity_rules_pushed = 248 [(module) = "framework"]; CellBroadcastMessageReported cb_message_reported = diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 8b07418668ba..9b7306089dca 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -132,7 +132,7 @@ import android.net.lowpan.LowpanManager; import android.net.nsd.INsdManager; import android.net.nsd.NsdManager; import android.net.wifi.WifiFrameworkInitializer; -import android.net.wifi.wificond.WifiCondManager; +import android.net.wifi.wificond.WifiNl80211Manager; import android.nfc.NfcManager; import android.os.BatteryManager; import android.os.BatteryStats; @@ -761,11 +761,11 @@ public final class SystemServiceRegistry { return new EthernetManager(ctx.getOuterContext(), service); }}); - registerService(Context.WIFI_COND_SERVICE, WifiCondManager.class, - new CachedServiceFetcher<WifiCondManager>() { + registerService(Context.WIFI_NL80211_SERVICE, WifiNl80211Manager.class, + new CachedServiceFetcher<WifiNl80211Manager>() { @Override - public WifiCondManager createService(ContextImpl ctx) { - return new WifiCondManager(ctx.getOuterContext()); + public WifiNl80211Manager createService(ContextImpl ctx) { + return new WifiNl80211Manager(ctx.getOuterContext()); } }); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index dc11013dab10..4a5a23ab36f8 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -4718,17 +4718,42 @@ public class DevicePolicyManager { public static final int KEYGUARD_DISABLE_FEATURES_ALL = 0x7fffffff; /** - * Keyguard features that when set on a managed profile that doesn't have its own challenge will - * affect the profile's parent user. These can also be set on the managed profile's parent - * {@link DevicePolicyManager} instance. + * Keyguard features that when set on a non-organization-owned managed profile that doesn't + * have its own challenge will affect the profile's parent user. These can also be set on the + * managed profile's parent {@link DevicePolicyManager} instance to explicitly control the + * parent user. + * + * <p> + * Organization-owned managed profile supports disabling additional keyguard features on the + * parent user as defined in {@link #ORG_OWNED_PROFILE_KEYGUARD_FEATURES_PARENT_ONLY}. * * @hide */ - public static final int PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER = + public static final int NON_ORG_OWNED_PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER = DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS | DevicePolicyManager.KEYGUARD_DISABLE_BIOMETRICS; /** + * Keyguard features that when set by the profile owner of an organization-owned managed + * profile will affect the profile's parent user if set on the managed profile's parent + * {@link DevicePolicyManager} instance. + * + * @hide + */ + public static final int ORG_OWNED_PROFILE_KEYGUARD_FEATURES_PARENT_ONLY = + KEYGUARD_DISABLE_SECURE_CAMERA; + + /** + * Keyguard features that when set on a normal or organization-owned managed profile, have + * the potential to affect the profile's parent user. + * + * @hide + */ + public static final int PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER = + DevicePolicyManager.NON_ORG_OWNED_PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER + | DevicePolicyManager.ORG_OWNED_PROFILE_KEYGUARD_FEATURES_PARENT_ONLY; + + /** * @deprecated This method does not actually modify the storage encryption of the device. * It has never affected the encryption status of a device. * @@ -6115,11 +6140,20 @@ public class DevicePolicyManager { * <li>{@link #KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS} which affects notifications generated * by applications in the managed profile. * </ul> + * <p> + * From version {@link android.os.Build.VERSION_CODES#R} the profile owner of an + * organization-owned managed profile can set: + * <ul> + * <li>{@link #KEYGUARD_DISABLE_SECURE_CAMERA} which affects the parent user when called on the + * parent profile. + * </ul> * {@link #KEYGUARD_DISABLE_TRUST_AGENTS}, {@link #KEYGUARD_DISABLE_FINGERPRINT}, - * {@link #KEYGUARD_DISABLE_FACE} and {@link #KEYGUARD_DISABLE_IRIS} can also be - * set on the {@link DevicePolicyManager} instance returned by - * {@link #getParentProfileInstance(ComponentName)} in order to set restrictions on the parent - * profile. + * {@link #KEYGUARD_DISABLE_FACE}, {@link #KEYGUARD_DISABLE_IRIS} and + * {@link #KEYGUARD_DISABLE_SECURE_CAMERA} can also be set on the {@link DevicePolicyManager} + * instance returned by {@link #getParentProfileInstance(ComponentName)} in order to set + * restrictions on the parent profile. {@link #KEYGUARD_DISABLE_SECURE_CAMERA} can only be set + * on the parent profile instance if the calling device admin is the profile owner of an + * organization-owned managed profile. * <p> * Requests to disable other features on a managed profile will be ignored. * <p> diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java index ea66fd475153..db4f1de1f743 100644 --- a/core/java/android/app/role/RoleManager.java +++ b/core/java/android/app/role/RoleManager.java @@ -90,6 +90,7 @@ public final class RoleManager { * The name of the dialer role. * * @see Intent#ACTION_DIAL + * @see android.telecom.InCallService */ public static final String ROLE_DIALER = "android.app.role.DIALER"; diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java index 5668944dfd4e..2c701b48455c 100644 --- a/core/java/android/app/usage/UsageStatsManager.java +++ b/core/java/android/app/usage/UsageStatsManager.java @@ -599,7 +599,8 @@ public final class UsageStatsManager { /** * Returns whether the specified app is currently considered inactive. This will be true if the * app hasn't been used directly or indirectly for a period of time defined by the system. This - * could be of the order of several hours or days. + * could be of the order of several hours or days. Apps are not considered inactive when the + * device is charging. * @param packageName The package name of the app to query * @return whether the app is currently considered inactive */ diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 49f62f407806..cfba9f4cd10a 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -4064,16 +4064,16 @@ public abstract class Context { /** * Use with {@link #getSystemService(String)} to retrieve a - * {@link android.net.wifi.WifiCondManager} for handling management of the Wi-Fi control - * daemon. + * {@link android.net.wifi.wificond.WifiNl80211Manager} for handling management of the + * Wi-Fi nl802.11 daemon (wificond). * * @see #getSystemService(String) - * @see android.net.wifi.WifiCondManager + * @see android.net.wifi.wificond.WifiNl80211Manager * @hide */ @SystemApi @SuppressLint("ServiceName") - public static final String WIFI_COND_SERVICE = "wificond"; + public static final String WIFI_NL80211_SERVICE = "wifinl80211"; /** * Use with {@link #getSystemService(String)} to retrieve a {@link diff --git a/core/java/android/content/integrity/InstallerAllowedByManifestFormula.java b/core/java/android/content/integrity/InstallerAllowedByManifestFormula.java index 475f019e7b26..9d37299e2373 100644 --- a/core/java/android/content/integrity/InstallerAllowedByManifestFormula.java +++ b/core/java/android/content/integrity/InstallerAllowedByManifestFormula.java @@ -16,6 +16,10 @@ package android.content.integrity; +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + import java.util.Map; /** @@ -25,7 +29,29 @@ import java.util.Map; * * @hide */ -public class InstallerAllowedByManifestFormula extends IntegrityFormula { +public class InstallerAllowedByManifestFormula extends IntegrityFormula implements Parcelable { + + public static final String INSTALLER_CERTIFICATE_NOT_EVALUATED = ""; + + public InstallerAllowedByManifestFormula() { + } + + private InstallerAllowedByManifestFormula(Parcel in) { + } + + @NonNull + public static final Creator<InstallerAllowedByManifestFormula> CREATOR = + new Creator<InstallerAllowedByManifestFormula>() { + @Override + public InstallerAllowedByManifestFormula createFromParcel(Parcel in) { + return new InstallerAllowedByManifestFormula(in); + } + + @Override + public InstallerAllowedByManifestFormula[] newArray(int size) { + return new InstallerAllowedByManifestFormula[size]; + } + }; @Override public int getTag() { @@ -54,10 +80,30 @@ public class InstallerAllowedByManifestFormula extends IntegrityFormula { private static boolean installerInAllowedInstallersFromManifest( AppInstallMetadata appInstallMetadata, Map<String, String> allowedInstallersAndCertificates) { - return allowedInstallersAndCertificates.containsKey(appInstallMetadata.getInstallerName()) - && appInstallMetadata.getInstallerCertificates() - .contains( - allowedInstallersAndCertificates - .get(appInstallMetadata.getInstallerName())); + String installerPackage = appInstallMetadata.getInstallerName(); + + if (!allowedInstallersAndCertificates.containsKey(installerPackage)) { + return false; + } + + // If certificate is not specified in the manifest, we do not check it. + if (!allowedInstallersAndCertificates.get(installerPackage) + .equals(INSTALLER_CERTIFICATE_NOT_EVALUATED)) { + return appInstallMetadata.getInstallerCertificates() + .contains( + allowedInstallersAndCertificates + .get(appInstallMetadata.getInstallerName())); + } + + return true; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { } } diff --git a/core/java/android/content/integrity/IntegrityFormula.java b/core/java/android/content/integrity/IntegrityFormula.java index ac4c9071f755..c5e5c8a8daad 100644 --- a/core/java/android/content/integrity/IntegrityFormula.java +++ b/core/java/android/content/integrity/IntegrityFormula.java @@ -214,6 +214,8 @@ public abstract class IntegrityFormula { return LongAtomicFormula.CREATOR.createFromParcel(in); case BOOLEAN_ATOMIC_FORMULA_TAG: return BooleanAtomicFormula.CREATOR.createFromParcel(in); + case INSTALLER_ALLOWED_BY_MANIFEST_FORMULA_TAG: + return InstallerAllowedByManifestFormula.CREATOR.createFromParcel(in); default: throw new IllegalArgumentException("Unknown formula tag " + tag); } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index c78d30dd9133..6bee2dd65d76 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -38,7 +38,7 @@ import android.app.PackageInstallObserver; import android.app.admin.DevicePolicyManager; import android.app.usage.StorageStatsManager; import android.compat.annotation.ChangeId; -import android.compat.annotation.Disabled; +import android.compat.annotation.EnabledAfter; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; @@ -3581,7 +3581,7 @@ public abstract class PackageManager { * @hide */ @ChangeId - @Disabled + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q) public static final long FILTER_APPLICATION_QUERY = 135549675L; /** {@hide} */ diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 471e83c4c3eb..cb809da3b867 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -62,6 +62,7 @@ import android.view.DisplayAdjustments; import android.view.ViewDebug; import android.view.ViewHierarchyEncoder; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.internal.util.GrowingArrayUtils; @@ -112,7 +113,7 @@ public class Resources { static final String TAG = "Resources"; private static final Object sSync = new Object(); - private final Object mLock = new Object(); + private final Object mUpdateLock = new Object(); // Used by BridgeResources in layoutlib @UnsupportedAppUsage @@ -139,6 +140,7 @@ public class Resources { @UnsupportedAppUsage final ClassLoader mClassLoader; + @GuardedBy("mUpdateLock") private UpdateCallbacks mCallbacks = null; /** @@ -2375,6 +2377,7 @@ public class Resources { * * <p>Loaders are listed in increasing precedence order. A loader will override the resources * and assets of loaders listed before itself. + * @hide */ @NonNull public List<ResourcesLoader> getLoaders() { @@ -2382,87 +2385,81 @@ public class Resources { } /** - * Appends a loader to the end of the loader list. If the loader is already present in the - * loader list, the list will not be modified. - * - * @param loader the loader to add - */ - public void addLoader(@NonNull ResourcesLoader loader) { - synchronized (mLock) { - checkCallbacksRegistered(); - - final List<ResourcesLoader> loaders = new ArrayList<>( - mResourcesImpl.getAssets().getLoaders()); - if (loaders.contains(loader)) { - return; - } - - loaders.add(loader); - mCallbacks.onLoadersChanged(this, loaders); - loader.registerOnProvidersChangedCallback(this, mCallbacks); - } - } - - /** - * Removes a loader from the loaders. If the loader is not present in the loader list, the list + * Adds a loader to the list of loaders. If the loader is already present in the list, the list * will not be modified. * - * @param loader the loader to remove + * @param loaders the loaders to add */ - public void removeLoader(@NonNull ResourcesLoader loader) { - synchronized (mLock) { + public void addLoaders(@NonNull ResourcesLoader... loaders) { + synchronized (mUpdateLock) { checkCallbacksRegistered(); + final List<ResourcesLoader> newLoaders = + new ArrayList<>(mResourcesImpl.getAssets().getLoaders()); + final ArraySet<ResourcesLoader> loaderSet = new ArraySet<>(newLoaders); + + for (int i = 0; i < loaders.length; i++) { + final ResourcesLoader loader = loaders[i]; + if (!loaderSet.contains(loader)) { + newLoaders.add(loader); + } + } - final List<ResourcesLoader> loaders = new ArrayList<>( - mResourcesImpl.getAssets().getLoaders()); - if (!loaders.remove(loader)) { + if (loaderSet.size() == newLoaders.size()) { return; } - mCallbacks.onLoadersChanged(this, loaders); - loader.unregisterOnProvidersChangedCallback(this); + mCallbacks.onLoadersChanged(this, newLoaders); + for (int i = loaderSet.size(), n = newLoaders.size(); i < n; i++) { + newLoaders.get(i).registerOnProvidersChangedCallback(this, mCallbacks); + } } } /** - * Sets the list of loaders. + * Removes loaders from the list of loaders. If the loader is not present in the list, the list + * will not be modified. * - * @param loaders the new loaders + * @param loaders the loaders to remove */ - public void setLoaders(@NonNull List<ResourcesLoader> loaders) { - synchronized (mLock) { + public void removeLoaders(@NonNull ResourcesLoader... loaders) { + synchronized (mUpdateLock) { checkCallbacksRegistered(); - + final ArraySet<ResourcesLoader> removedLoaders = new ArraySet<>(loaders); + final List<ResourcesLoader> newLoaders = new ArrayList<>(); final List<ResourcesLoader> oldLoaders = mResourcesImpl.getAssets().getLoaders(); - int index = 0; - boolean modified = loaders.size() != oldLoaders.size(); - final ArraySet<ResourcesLoader> seenLoaders = new ArraySet<>(); - for (final ResourcesLoader loader : loaders) { - if (!seenLoaders.add(loader)) { - throw new IllegalArgumentException("Loader " + loader + " present twice"); - } - if (!modified && oldLoaders.get(index++) != loader) { - modified = true; + for (int i = 0, n = oldLoaders.size(); i < n; i++) { + final ResourcesLoader loader = oldLoaders.get(i); + if (!removedLoaders.contains(loader)) { + newLoaders.add(loader); } } - if (!modified) { + if (oldLoaders.size() == newLoaders.size()) { return; } - mCallbacks.onLoadersChanged(this, loaders); - for (int i = 0, n = oldLoaders.size(); i < n; i++) { - oldLoaders.get(i).unregisterOnProvidersChangedCallback(this); - } - for (ResourcesLoader newLoader : loaders) { - newLoader.registerOnProvidersChangedCallback(this, mCallbacks); + mCallbacks.onLoadersChanged(this, newLoaders); + for (int i = 0; i < loaders.length; i++) { + loaders[i].unregisterOnProvidersChangedCallback(this); } } } - /** Removes all {@link ResourcesLoader ResourcesLoader(s)}. */ + /** + * Removes all {@link ResourcesLoader ResourcesLoader(s)}. + * @hide + */ + @VisibleForTesting public void clearLoaders() { - setLoaders(Collections.emptyList()); + synchronized (mUpdateLock) { + checkCallbacksRegistered(); + final List<ResourcesLoader> newLoaders = Collections.emptyList(); + final List<ResourcesLoader> oldLoaders = mResourcesImpl.getAssets().getLoaders(); + mCallbacks.onLoadersChanged(this, newLoaders); + for (ResourcesLoader loader : oldLoaders) { + loader.unregisterOnProvidersChangedCallback(this); + } + } } } diff --git a/core/java/android/content/res/loader/ResourcesLoader.java b/core/java/android/content/res/loader/ResourcesLoader.java index 69daceeaffc2..58fec603a2d5 100644 --- a/core/java/android/content/res/loader/ResourcesLoader.java +++ b/core/java/android/content/res/loader/ResourcesLoader.java @@ -40,8 +40,8 @@ import java.util.List; * of {@link ResourcesProvider ResourcesProvider(s)} a loader contains propagates to all Resources * objects that use the loader. * - * <p>Loaders retrieved with {@link Resources#getLoaders()} are listed in increasing precedence - * order. A loader will override the resources and assets of loaders listed before itself. + * <p>Loaders must be added to Resources objects in increasing precedence order. A loader will + * override the resources and assets of loaders added before itself. * * <p>Providers retrieved with {@link #getProviders()} are listed in increasing precedence order. A * provider will override the resources and assets of providers listed before itself. diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java index ef28e6c6db2a..ac36188ee61a 100644 --- a/core/java/android/hardware/biometrics/BiometricPrompt.java +++ b/core/java/android/hardware/biometrics/BiometricPrompt.java @@ -75,6 +75,18 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan /** * @hide */ + public static final String KEY_DEVICE_CREDENTIAL_TITLE = "device_credential_title"; + /** + * @hide + */ + public static final String KEY_DEVICE_CREDENTIAL_SUBTITLE = "device_credential_subtitle"; + /** + * @hide + */ + public static final String KEY_DEVICE_CREDENTIAL_DESCRIPTION = "device_credential_description"; + /** + * @hide + */ public static final String KEY_NEGATIVE_TEXT = "negative_text"; /** * @hide @@ -221,6 +233,30 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan } /** + * Sets an optional title, subtitle, and/or description that will override other text when + * the user is authenticating with PIN/pattern/password. Currently for internal use only. + * @return This builder. + * @hide + */ + @RequiresPermission(USE_BIOMETRIC_INTERNAL) + @NonNull + public Builder setTextForDeviceCredential( + @Nullable CharSequence title, + @Nullable CharSequence subtitle, + @Nullable CharSequence description) { + if (title != null) { + mBundle.putCharSequence(KEY_DEVICE_CREDENTIAL_TITLE, title); + } + if (subtitle != null) { + mBundle.putCharSequence(KEY_DEVICE_CREDENTIAL_SUBTITLE, subtitle); + } + if (description != null) { + mBundle.putCharSequence(KEY_DEVICE_CREDENTIAL_DESCRIPTION, description); + } + return this; + } + + /** * Required: Sets the text, executor, and click listener for the negative button on the * prompt. This is typically a cancel button, but may be also used to show an alternative * method for authentication, such as a screen that asks for a backup password. diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java index 09ec6c35fcb9..d83715c692f7 100644 --- a/core/java/android/net/IpSecManager.java +++ b/core/java/android/net/IpSecManager.java @@ -51,7 +51,7 @@ import java.net.Socket; * * <p>Note that not all aspects of IPsec are permitted by this API. Applications may create * transport mode security associations and apply them to individual sockets. Applications looking - * to create a VPN should use {@link VpnService}. + * to create an IPsec VPN should use {@link VpnManager} and {@link Ikev2VpnProfile}. * * @see <a href="https://tools.ietf.org/html/rfc4301">RFC 4301, Security Architecture for the * Internet Protocol</a> diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java index e65bd9f20ec4..d2735008611c 100644 --- a/core/java/android/telephony/PhoneStateListener.java +++ b/core/java/android/telephony/PhoneStateListener.java @@ -301,6 +301,13 @@ public class PhoneStateListener { public static final int LISTEN_USER_MOBILE_DATA_STATE = 0x00080000; /** + * Listen for display info changed event. + * + * @see #onDisplayInfoChanged + */ + public static final int LISTEN_DISPLAY_INFO_CHANGED = 0x00100000; + + /** * Listen for changes to the phone capability. * * @see #onPhoneCapabilityChanged @@ -848,6 +855,21 @@ public class PhoneStateListener { } /** + * Callback invoked when the display info has changed on the registered subscription. + * <p> The {@link DisplayInfo} contains status information shown to the user based on + * carrier policy. + * + * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE} or that the calling + * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * @param displayInfo The display information. + */ + @RequiresPermission((android.Manifest.permission.READ_PHONE_STATE)) + public void onDisplayInfoChanged(@NonNull DisplayInfo displayInfo) { + // default implementation empty + } + + /** * Callback invoked when the current emergency number list has changed on the registered * subscription. * Note, the registration subId comes from {@link TelephonyManager} object which registers @@ -1226,6 +1248,15 @@ public class PhoneStateListener { () -> psl.onUserMobileDataStateChanged(enabled))); } + public void onDisplayInfoChanged(DisplayInfo displayInfo) { + PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); + if (psl == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute( + () -> psl.onDisplayInfoChanged(displayInfo))); + } + public void onOemHookRawEvent(byte[] rawData) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java index 4024db1e16c4..2c077bbdc772 100644 --- a/core/java/android/telephony/TelephonyRegistryManager.java +++ b/core/java/android/telephony/TelephonyRegistryManager.java @@ -589,6 +589,24 @@ public class TelephonyRegistryManager { } /** + * Notify display info changed. + * + * @param slotIndex The SIM slot index for which display info has changed. Can be + * derived from {@code subscriptionId} except when {@code subscriptionId} is invalid, such as + * when the device is in emergency-only mode. + * @param subscriptionId Subscription id for which display network info has changed. + * @param displayInfo The display info. + */ + public void notifyDisplayInfoChanged(int slotIndex, int subscriptionId, + @NonNull DisplayInfo displayInfo) { + try { + sRegistry.notifyDisplayInfoChanged(slotIndex, subscriptionId, displayInfo); + } catch (RemoteException ex) { + // system process is dead + } + } + + /** * Notify IMS call disconnect causes which contains {@link android.telephony.ims.ImsReasonInfo}. * * @param subId for which ims call disconnect. diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 25480683c8a8..9cd6050efc0b 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -882,6 +882,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } else { hideDirectly(types); } + if (mViewRoot.mView == null) { + return; + } mViewRoot.mView.dispatchWindowInsetsAnimationPrepare(animation); mViewRoot.mView.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() { @Override diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java index 06cb51927ba8..0c1edac6608c 100644 --- a/core/java/android/view/RenderNodeAnimator.java +++ b/core/java/android/view/RenderNodeAnimator.java @@ -19,7 +19,6 @@ package android.view; import android.animation.Animator; import android.animation.TimeInterpolator; import android.animation.ValueAnimator; -import android.compat.annotation.UnsupportedAppUsage; import android.graphics.CanvasProperty; import android.graphics.Paint; import android.graphics.RecordingCanvas; @@ -109,12 +108,10 @@ public class RenderNodeAnimator extends Animator { private long mStartDelay = 0; private long mStartTime; - @UnsupportedAppUsage public static int mapViewPropertyToRenderProperty(int viewProperty) { return sViewPropertyAnimatorMap.get(viewProperty); } - @UnsupportedAppUsage public RenderNodeAnimator(int property, float finalValue) { mRenderProperty = property; mFinalValue = finalValue; @@ -122,7 +119,6 @@ public class RenderNodeAnimator extends Animator { init(nCreateAnimator(property, finalValue)); } - @UnsupportedAppUsage public RenderNodeAnimator(CanvasProperty<Float> property, float finalValue) { init(nCreateCanvasPropertyFloatAnimator( property.getNativeContainer(), finalValue)); @@ -137,7 +133,6 @@ public class RenderNodeAnimator extends Animator { * {@link #PAINT_STROKE_WIDTH} * @param finalValue The target value for the property */ - @UnsupportedAppUsage public RenderNodeAnimator(CanvasProperty<Paint> property, int paintField, float finalValue) { init(nCreateCanvasPropertyPaintAnimator( property.getNativeContainer(), paintField, finalValue)); @@ -289,7 +284,6 @@ public class RenderNodeAnimator extends Animator { } /** @hide */ - @UnsupportedAppUsage public void setTarget(View view) { mViewTarget = view; setTarget(mViewTarget.mRenderNode); @@ -301,7 +295,6 @@ public class RenderNodeAnimator extends Animator { } /** @hide */ - @UnsupportedAppUsage public void setTarget(DisplayListCanvas canvas) { setTarget((RecordingCanvas) canvas); } @@ -316,7 +309,6 @@ public class RenderNodeAnimator extends Animator { mTarget.addAnimator(this); } - @UnsupportedAppUsage public void setStartValue(float startValue) { checkMutable(); nSetStartValue(mNativePtr.get(), startValue); @@ -337,7 +329,6 @@ public class RenderNodeAnimator extends Animator { return mUnscaledStartDelay; } - @UnsupportedAppUsage @Override public RenderNodeAnimator setDuration(long duration) { checkMutable(); @@ -502,7 +493,6 @@ public class RenderNodeAnimator extends Animator { } // Called by native - @UnsupportedAppUsage private static void callOnFinished(RenderNodeAnimator animator) { if (animator.mHandler != null) { animator.mHandler.post(animator::onFinished); diff --git a/core/java/android/webkit/UserPackage.java b/core/java/android/webkit/UserPackage.java index 8a1a0b5ef9b0..556b24c94b36 100644 --- a/core/java/android/webkit/UserPackage.java +++ b/core/java/android/webkit/UserPackage.java @@ -34,7 +34,7 @@ public class UserPackage { private final UserInfo mUserInfo; private final PackageInfo mPackageInfo; - public static final int MINIMUM_SUPPORTED_SDK = Build.VERSION_CODES.Q; + public static final int MINIMUM_SUPPORTED_SDK = Build.VERSION_CODES.R; public UserPackage(UserInfo user, PackageInfo packageInfo) { this.mUserInfo = user; diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java index 941af6ef1d7a..8790bbdcd8f7 100644 --- a/core/java/android/webkit/WebViewFactory.java +++ b/core/java/android/webkit/WebViewFactory.java @@ -47,7 +47,7 @@ public final class WebViewFactory { // visible for WebViewZygoteInit to look up the class by reflection and call preloadInZygote. /** @hide */ private static final String CHROMIUM_WEBVIEW_FACTORY = - "com.android.webview.chromium.WebViewChromiumFactoryProviderForQ"; + "com.android.webview.chromium.WebViewChromiumFactoryProviderForR"; private static final String CHROMIUM_WEBVIEW_FACTORY_METHOD = "create"; diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java index fb6f266e456f..a1c22e9994c8 100644 --- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java +++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java @@ -262,10 +262,8 @@ public class AccessibilityShortcutController { String toastMessage = String.format(toastMessageFormatString, serviceName); Toast warningToast = mFrameworkObjectProvider.makeToastFromText( mContext, toastMessage, Toast.LENGTH_LONG); - if (warningToast.getWindowParams() != null) { - warningToast.getWindowParams().privateFlags |= - WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; - } + warningToast.getWindowParams().privateFlags |= + WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; warningToast.show(); } diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl index 0f50596f935d..3d5dfbbb871a 100644 --- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl +++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl @@ -21,6 +21,7 @@ import android.telephony.CallAttributes; import android.telephony.CellIdentity; import android.telephony.CellInfo; import android.telephony.DataConnectionRealTimeInfo; +import android.telephony.DisplayInfo; import android.telephony.PhoneCapability; import android.telephony.PreciseCallState; import android.telephony.PreciseDataConnectionState; @@ -54,6 +55,7 @@ oneway interface IPhoneStateListener { void onOemHookRawEvent(in byte[] rawData); void onCarrierNetworkChange(in boolean active); void onUserMobileDataStateChanged(in boolean enabled); + void onDisplayInfoChanged(in DisplayInfo displayInfo); void onPhoneCapabilityChanged(in PhoneCapability capability); void onActiveDataSubIdChanged(in int subId); void onRadioPowerStateChanged(in int state); diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl index 47752c5b2d94..520ffc9584e2 100644 --- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl +++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl @@ -23,6 +23,7 @@ import android.telephony.BarringInfo; import android.telephony.CallQuality; import android.telephony.CellIdentity; import android.telephony.CellInfo; +import android.telephony.DisplayInfo; import android.telephony.ims.ImsReasonInfo; import android.telephony.PhoneCapability; import android.telephony.PhysicalChannelConfig; @@ -87,6 +88,7 @@ interface ITelephonyRegistry { void notifyOpportunisticSubscriptionInfoChanged(); void notifyCarrierNetworkChange(in boolean active); void notifyUserMobileDataStateChangedForPhoneId(in int phoneId, in int subId, in boolean state); + void notifyDisplayInfoChanged(int slotIndex, int subId, in DisplayInfo displayInfo); void notifyPhoneCapabilityChanged(in PhoneCapability capability); void notifyActiveDataSubIdChanged(int activeDataSubId); void notifyRadioPowerStateChanged(in int phoneId, in int subId, in int state); diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto index bf4cdee72b2d..03676dd5a1c6 100644 --- a/core/proto/android/os/incident.proto +++ b/core/proto/android/os/incident.proto @@ -428,7 +428,7 @@ message IncidentProto { (section).args = "dropbox --proto system_app_wtf" ]; - optional android.service.dropbox.DropBoxManagerServiceDumpProto dropbox_system_server_crashes = 3037 [ + optional android.service.dropbox.DropBoxManagerServiceDumpProto dropbox_system_server_crash = 3037 [ (section).type = SECTION_DUMPSYS, (section).args = "dropbox --proto system_server_crash" ]; diff --git a/core/proto/android/stats/mediametrics/mediametrics.proto b/core/proto/android/stats/mediametrics/mediametrics.proto index 34ed90a8c90c..e1af9622adb3 100644 --- a/core/proto/android/stats/mediametrics/mediametrics.proto +++ b/core/proto/android/stats/mediametrics/mediametrics.proto @@ -154,6 +154,8 @@ message CodecData { optional int64 latency_avg = 18; optional int64 latency_count = 19; optional int64 latency_unknown = 20; + optional int32 queue_input_buffer_error = 21; + optional int32 queue_secure_input_buffer_error = 22; } /** diff --git a/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/DirectoryAssetsProviderTest.kt b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/DirectoryAssetsProviderTest.kt index 9e94bdc8a081..afe9d7f19f0d 100644 --- a/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/DirectoryAssetsProviderTest.kt +++ b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/DirectoryAssetsProviderTest.kt @@ -44,7 +44,7 @@ class DirectoryAssetsProviderTest : ResourceLoaderTestBase() { testDir = context.filesDir.resolve("DirectoryAssetsProvider_${testName.methodName}") assetsProvider = DirectoryAssetsProvider(testDir) loader = ResourcesLoader() - resources.addLoader(loader) + resources.addLoaders(loader) } @After diff --git a/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderAssetsTest.kt b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderAssetsTest.kt index e3ba93d64b0f..da5092de0627 100644 --- a/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderAssetsTest.kt +++ b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderAssetsTest.kt @@ -119,7 +119,7 @@ class ResourceLoaderAssetsTest : ResourceLoaderTestBase() { val loader = ResourcesLoader() loader.providers = listOf(one, two) - resources.addLoader(loader) + resources.addLoaders(loader) assertOpenedAsset() inOrder(two.assetsProvider, one.assetsProvider).apply { @@ -149,7 +149,7 @@ class ResourceLoaderAssetsTest : ResourceLoaderTestBase() { val loader2 = ResourcesLoader() loader2.addProvider(two) - resources.loaders = listOf(loader1, loader2) + resources.addLoaders(loader1, loader2) assertOpenedAsset() inOrder(two.assetsProvider, one.assetsProvider).apply { @@ -170,7 +170,7 @@ class ResourceLoaderAssetsTest : ResourceLoaderTestBase() { val loader = ResourcesLoader() val one = ResourcesProvider.empty(assetsProvider1) val two = ResourcesProvider.empty(assetsProvider2) - resources.addLoader(loader) + resources.addLoaders(loader) loader.providers = listOf(one, two) assertOpenedAsset() @@ -186,7 +186,7 @@ class ResourceLoaderAssetsTest : ResourceLoaderTestBase() { val loader = ResourcesLoader() val one = ResourcesProvider.empty(assetsProvider1) val two = ResourcesProvider.empty(assetsProvider2) - resources.addLoader(loader) + resources.addLoaders(loader) loader.providers = listOf(one, two) assertOpenedAsset() @@ -202,7 +202,7 @@ class ResourceLoaderAssetsTest : ResourceLoaderTestBase() { val loader = ResourcesLoader() val one = ResourcesProvider.empty(assetsProvider1) val two = ResourcesProvider.empty(assetsProvider2) - resources.addLoader(loader) + resources.addLoaders(loader) loader.providers = listOf(one, two) assertOpenedAsset() diff --git a/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderValuesTest.kt b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderValuesTest.kt index 0cc56d721651..16eafcd451c2 100644 --- a/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderValuesTest.kt +++ b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderValuesTest.kt @@ -192,13 +192,13 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() { } @Test - fun addMultipleProviders() { + fun addProvidersRepeatedly() { val originalValue = getValue() val testOne = openOne() val testTwo = openTwo() val loader = ResourcesLoader() - resources.addLoader(loader) + resources.addLoaders(loader) loader.addProvider(testOne) assertEquals(valueOne, getValue()) @@ -213,25 +213,25 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() { } @Test - fun addMultipleLoaders() { + fun addLoadersRepeatedly() { val originalValue = getValue() val testOne = openOne() val testTwo = openTwo() val loader1 = ResourcesLoader() val loader2 = ResourcesLoader() - resources.addLoader(loader1) + resources.addLoaders(loader1) loader1.addProvider(testOne) assertEquals(valueOne, getValue()) - resources.addLoader(loader2) + resources.addLoaders(loader2) loader2.addProvider(testTwo) assertEquals(valueTwo, getValue()) - resources.removeLoader(loader1) + resources.removeLoaders(loader1) assertEquals(valueTwo, getValue()) - resources.removeLoader(loader2) + resources.removeLoaders(loader2) assertEquals(originalValue, getValue()) } @@ -242,7 +242,7 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() { val testTwo = openTwo() val loader = ResourcesLoader() - resources.addLoader(loader) + resources.addLoaders(loader) loader.providers = listOf(testOne, testTwo) assertEquals(valueTwo, getValue()) @@ -254,20 +254,20 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() { } @Test - fun setMultipleLoaders() { + fun addMultipleLoaders() { val originalValue = getValue() val loader1 = ResourcesLoader() loader1.addProvider(openOne()) val loader2 = ResourcesLoader() loader2.addProvider(openTwo()) - resources.loaders = listOf(loader1, loader2) + resources.addLoaders(loader1, loader2) assertEquals(valueTwo, getValue()) - resources.removeLoader(loader2) + resources.removeLoaders(loader2) assertEquals(valueOne, getValue()) - resources.loaders = Collections.emptyList() + resources.removeLoaders(loader1) assertEquals(originalValue, getValue()) } @@ -291,7 +291,7 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() { val testTwo = openTwo() val loader = ResourcesLoader() - resources.addLoader(loader) + resources.addLoaders(loader) loader.addProvider(testOne) loader.addProvider(testTwo) loader.addProvider(testOne) @@ -308,9 +308,9 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() { val loader2 = ResourcesLoader() loader2.addProvider(openTwo()) - resources.addLoader(loader1) - resources.addLoader(loader2) - resources.addLoader(loader1) + resources.addLoaders(loader1) + resources.addLoaders(loader2) + resources.addLoaders(loader1) assertEquals(2, resources.loaders.size) assertEquals(resources.loaders[0], loader1) @@ -323,7 +323,7 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() { val testTwo = openTwo() val loader = ResourcesLoader() - resources.addLoader(loader) + resources.addLoaders(loader) loader.addProvider(testOne) loader.addProvider(testTwo) @@ -341,12 +341,16 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() { val loader2 = ResourcesLoader() loader2.addProvider(openTwo()) - resources.loaders = listOf(loader1, loader2) - resources.removeLoader(loader1) - resources.removeLoader(loader1) + resources.addLoaders(loader1, loader2) + resources.removeLoaders(loader1) + resources.removeLoaders(loader1) assertEquals(1, resources.loaders.size) assertEquals(resources.loaders[0], loader2) + + resources.removeLoaders(loader2, loader2) + + assertEquals(0, resources.loaders.size) } @Test @@ -355,7 +359,7 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() { val testTwo = openTwo() val loader = ResourcesLoader() - resources.addLoader(loader) + resources.addLoaders(loader) loader.providers = listOf(testOne, testTwo) loader.providers = listOf(testOne, testTwo) @@ -365,14 +369,14 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() { } @Test - fun repeatedSetLoaders() { + fun repeatedAddMultipleLoaders() { val loader1 = ResourcesLoader() loader1.addProvider(openOne()) val loader2 = ResourcesLoader() loader2.addProvider(openTwo()) - resources.loaders = listOf(loader1, loader2) - resources.loaders = listOf(loader1, loader2) + resources.addLoaders(loader1, loader2) + resources.addLoaders(loader1, loader2) assertEquals(2, resources.loaders.size) assertEquals(resources.loaders[0], loader1) @@ -386,7 +390,7 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() { val testTwo = openTwo() val loader = ResourcesLoader() - resources.addLoader(loader) + resources.addLoaders(loader) loader.addProvider(testOne) loader.addProvider(testTwo) assertEquals(valueTwo, getValue()) @@ -414,20 +418,20 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() { val loader2 = ResourcesLoader() loader2.addProvider(testTwo) - resources.addLoader(loader1) - resources.addLoader(loader2) + resources.addLoaders(loader1) + resources.addLoaders(loader2) assertEquals(valueTwo, getValue()) - resources.removeLoader(loader1) + resources.removeLoaders(loader1) assertEquals(valueTwo, getValue()) - resources.addLoader(loader1) + resources.addLoaders(loader1) assertEquals(valueOne, getValue()) - resources.removeLoader(loader2) + resources.removeLoaders(loader2) assertEquals(valueOne, getValue()) - resources.removeLoader(loader1) + resources.removeLoaders(loader1) assertEquals(originalValue, getValue()) } @@ -444,10 +448,11 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() { val loader2 = ResourcesLoader() loader2.providers = listOf(testThree, testFour) - resources.loaders = listOf(loader1, loader2) + resources.addLoaders(loader1, loader2) assertEquals(valueFour, getValue()) - resources.loaders = listOf(loader2, loader1) + resources.removeLoaders(loader1) + resources.addLoaders(loader1) assertEquals(valueTwo, getValue()) loader1.removeProvider(testTwo) @@ -471,7 +476,7 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() { val loader2 = ResourcesLoader() loader2.addProvider(openTwo()) - resources.loaders = listOf(loader1) + resources.addLoaders(loader1) assertEquals(valueOne, getValue()) // The child context should include the loaders of the original context. @@ -479,12 +484,12 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() { assertEquals(valueOne, getValue(childContext)) // Changing the loaders of the child context should not affect the original context. - childContext.resources.loaders = listOf(loader1, loader2) + childContext.resources.addLoaders(loader2) assertEquals(valueOne, getValue()) assertEquals(valueTwo, getValue(childContext)) // Changing the loaders of the original context should not affect the child context. - resources.removeLoader(loader1) + resources.removeLoaders(loader1) assertEquals(originalValue, getValue()) assertEquals(valueTwo, getValue(childContext)) @@ -506,7 +511,7 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() { val testTwo = openTwo() val loader = ResourcesLoader() - resources.addLoader(loader) + resources.addLoaders(loader) loader.addProvider(testOne) assertEquals(valueOne, getValue()) @@ -527,7 +532,7 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() { assertEquals(originalValue, getValue()) assertEquals(originalValue, getValue(childContext2)) - childContext2.resources.addLoader(loader) + childContext2.resources.addLoaders(loader) assertEquals(originalValue, getValue()) assertEquals(valueTwo, getValue(childContext)) assertEquals(valueTwo, getValue(childContext2)) @@ -539,7 +544,7 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() { loader.addProvider(openOne()) val applicationContext = context.applicationContext - applicationContext.resources.addLoader(loader) + applicationContext.resources.addLoaders(loader) assertEquals(valueOne, getValue(applicationContext)) val activity = mTestActivityRule.launchActivity(Intent()) @@ -556,7 +561,7 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() { loader2.addProvider(openTwo()) val applicationContext = context.applicationContext - applicationContext.resources.addLoader(loader1) + applicationContext.resources.addLoaders(loader1) assertEquals(valueOne, getValue(applicationContext)) var token: IBinder? = null @@ -569,7 +574,7 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() { assertEquals(valueOne, getValue(applicationContext)) assertEquals(valueOne, getValue(activity)) - activity.resources.addLoader(loader2) + activity.resources.addLoaders(loader2) assertEquals(valueOne, getValue(applicationContext)) assertEquals(valueTwo, getValue(activity)) @@ -598,10 +603,11 @@ class ResourceLoaderValuesTest : ResourceLoaderTestBase() { loader2.addProvider(provider1) loader2.addProvider(openTwo()) - resources.loaders = listOf(loader1, loader2) + resources.addLoaders(loader1, loader2) assertEquals(valueTwo, getValue()) - resources.loaders = listOf(loader2, loader1) + resources.removeLoaders(loader1) + resources.addLoaders(loader1) assertEquals(valueOne, getValue()) assertEquals(2, resources.assets.apkAssets.count { apkAssets -> apkAssets.isForLoader }) diff --git a/core/tests/coretests/src/android/content/integrity/InstallerAllowedByManifestFormulaTest.java b/core/tests/coretests/src/android/content/integrity/InstallerAllowedByManifestFormulaTest.java index c897ace0e0b5..693d4cae1f1c 100644 --- a/core/tests/coretests/src/android/content/integrity/InstallerAllowedByManifestFormulaTest.java +++ b/core/tests/coretests/src/android/content/integrity/InstallerAllowedByManifestFormulaTest.java @@ -16,15 +16,20 @@ package android.content.integrity; +import static android.content.integrity.InstallerAllowedByManifestFormula.INSTALLER_CERTIFICATE_NOT_EVALUATED; + import static com.google.common.truth.Truth.assertThat; import com.google.common.collect.ImmutableMap; -import org.testng.annotations.Test; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; import java.util.Arrays; import java.util.Collections; +@RunWith(JUnit4.class) public class InstallerAllowedByManifestFormulaTest { private static final InstallerAllowedByManifestFormula @@ -70,7 +75,7 @@ public class InstallerAllowedByManifestFormulaTest { } @Test - public void testFormulaMatches_certificateNotInManifest() { + public void testFormulaMatches_certificateDoesNotMatchManifest() { AppInstallMetadata appInstallMetadata = getAppInstallMetadataBuilder() .setInstallerName("installer1") .setInstallerCertificates(Arrays.asList("installer_cert3", "random_cert")) @@ -92,6 +97,19 @@ public class InstallerAllowedByManifestFormulaTest { assertThat(FORMULA.matches(appInstallMetadata)).isTrue(); } + @Test + public void testFormulaMatches_certificateNotSpecifiedInManifest() { + AppInstallMetadata appInstallMetadata = getAppInstallMetadataBuilder() + .setInstallerName("installer1") + .setInstallerCertificates(Arrays.asList("installer_cert3", "random_cert")) + .setAllowedInstallersAndCert(ImmutableMap.of( + "installer1", INSTALLER_CERTIFICATE_NOT_EVALUATED, + "installer2", "installer_cert1" + )).build(); + + assertThat(FORMULA.matches(appInstallMetadata)).isTrue(); + } + /** Returns a builder with all fields filled with some dummy data. */ private AppInstallMetadata.Builder getAppInstallMetadataBuilder() { return new AppInstallMetadata.Builder() diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index 08c3f988e85e..44f9b7c6eb39 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -27,16 +27,34 @@ #pragma GCC diagnostic ignored "-Wunused-function" using ::android::hardware::Void; +using ::android::hardware::hidl_bitfield; using ::android::hardware::hidl_vec; using ::android::hardware::tv::tuner::V1_0::DataFormat; +using ::android::hardware::tv::tuner::V1_0::DemuxAlpFilterSettings; +using ::android::hardware::tv::tuner::V1_0::DemuxAlpFilterType; +using ::android::hardware::tv::tuner::V1_0::DemuxFilterAvSettings; +using ::android::hardware::tv::tuner::V1_0::DemuxFilterDownloadSettings; using ::android::hardware::tv::tuner::V1_0::DemuxFilterMainType; using ::android::hardware::tv::tuner::V1_0::DemuxFilterPesDataSettings; +using ::android::hardware::tv::tuner::V1_0::DemuxFilterRecordSettings; +using ::android::hardware::tv::tuner::V1_0::DemuxFilterSectionBits; +using ::android::hardware::tv::tuner::V1_0::DemuxFilterSectionSettings; using ::android::hardware::tv::tuner::V1_0::DemuxFilterSettings; +using ::android::hardware::tv::tuner::V1_0::DemuxIpFilterSettings; +using ::android::hardware::tv::tuner::V1_0::DemuxIpFilterType; +using ::android::hardware::tv::tuner::V1_0::DemuxMmtpFilterSettings; +using ::android::hardware::tv::tuner::V1_0::DemuxMmtpFilterType; using ::android::hardware::tv::tuner::V1_0::DemuxMmtpPid; using ::android::hardware::tv::tuner::V1_0::DemuxQueueNotifyBits; +using ::android::hardware::tv::tuner::V1_0::DemuxRecordScIndexType; +using ::android::hardware::tv::tuner::V1_0::DemuxScHevcIndex; +using ::android::hardware::tv::tuner::V1_0::DemuxScIndex; +using ::android::hardware::tv::tuner::V1_0::DemuxTlvFilterSettings; +using ::android::hardware::tv::tuner::V1_0::DemuxTlvFilterType; using ::android::hardware::tv::tuner::V1_0::DemuxTpid; using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterSettings; using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterType; +using ::android::hardware::tv::tuner::V1_0::DemuxTsIndex; using ::android::hardware::tv::tuner::V1_0::DvrSettings; using ::android::hardware::tv::tuner::V1_0::FrontendAnalogSettings; using ::android::hardware::tv::tuner::V1_0::FrontendAnalogSifStandard; @@ -1367,10 +1385,133 @@ static jobject android_media_tv_Tuner_open_time_filter(JNIEnv, jobject) { return NULL; } -static DemuxFilterSettings getFilterSettings( +static DemuxFilterSectionBits getFilterSectionBits(JNIEnv *env, const jobject& settings) { + jclass clazz = env->FindClass("android/media/tv/tuner/filter/SectionSettingsWithSectionBits"); + jbyteArray jfilterBytes = static_cast<jbyteArray>( + env->GetObjectField(settings, env->GetFieldID(clazz, "mFilter", "[B"))); + jsize size = env->GetArrayLength(jfilterBytes); + std::vector<uint8_t> filterBytes(size); + env->GetByteArrayRegion( + jfilterBytes, 0, size, reinterpret_cast<jbyte*>(&filterBytes[0])); + + jbyteArray jmask = static_cast<jbyteArray>( + env->GetObjectField(settings, env->GetFieldID(clazz, "mMask", "[B"))); + size = env->GetArrayLength(jmask); + std::vector<uint8_t> mask(size); + env->GetByteArrayRegion(jmask, 0, size, reinterpret_cast<jbyte*>(&mask[0])); + + jbyteArray jmode = static_cast<jbyteArray>( + env->GetObjectField(settings, env->GetFieldID(clazz, "mMode", "[B"))); + size = env->GetArrayLength(jmode); + std::vector<uint8_t> mode(size); + env->GetByteArrayRegion(jmode, 0, size, reinterpret_cast<jbyte*>(&mode[0])); + + DemuxFilterSectionBits filterSectionBits { + .filter = filterBytes, + .mask = mask, + .mode = mode, + }; + return filterSectionBits; +} + +static DemuxFilterSectionSettings::Condition::TableInfo getFilterTableInfo( + JNIEnv *env, const jobject& settings) { + jclass clazz = env->FindClass("android/media/tv/tuner/filter/SectionSettingsWithTableInfo"); + uint16_t tableId = static_cast<uint16_t>( + env->GetIntField(settings, env->GetFieldID(clazz, "mTableId", "I"))); + uint16_t version = static_cast<uint16_t>( + env->GetIntField(settings, env->GetFieldID(clazz, "mVersion", "I"))); + DemuxFilterSectionSettings::Condition::TableInfo tableInfo { + .tableId = tableId, + .version = version, + }; + return tableInfo; +} + +static DemuxFilterSectionSettings getFilterSectionSettings(JNIEnv *env, const jobject& settings) { + jclass clazz = env->FindClass("android/media/tv/tuner/filter/SectionSettings"); + bool isCheckCrc = static_cast<bool>( + env->GetBooleanField(settings, env->GetFieldID(clazz, "mCrcEnabled", "Z"))); + bool isRepeat = static_cast<bool>( + env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsRepeat", "Z"))); + bool isRaw = static_cast<bool>( + env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsRaw", "Z"))); + + DemuxFilterSectionSettings filterSectionSettings { + .isCheckCrc = isCheckCrc, + .isRepeat = isRepeat, + .isRaw = isRaw, + }; + if (env->IsInstanceOf( + settings, + env->FindClass("android/media/tv/tuner/filter/SectionSettingsWithSectionBits"))) { + filterSectionSettings.condition.sectionBits(getFilterSectionBits(env, settings)); + } else if (env->IsInstanceOf( + settings, + env->FindClass("android/media/tv/tuner/filter/SectionSettingsWithTableInfo"))) { + filterSectionSettings.condition.tableInfo(getFilterTableInfo(env, settings)); + } + return filterSectionSettings; +} + +static DemuxFilterAvSettings getFilterAvSettings(JNIEnv *env, const jobject& settings) { + jclass clazz = env->FindClass("android/media/tv/tuner/filter/AvSettings"); + bool isPassthrough = static_cast<bool>( + env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsPassthrough", "Z"))); + DemuxFilterAvSettings filterAvSettings { + .isPassthrough = isPassthrough, + }; + return filterAvSettings; +} + +static DemuxFilterPesDataSettings getFilterPesDataSettings(JNIEnv *env, const jobject& settings) { + jclass clazz = env->FindClass("android/media/tv/tuner/filter/PesSettings"); + uint16_t streamId = static_cast<uint16_t>( + env->GetIntField(settings, env->GetFieldID(clazz, "mStreamId", "I"))); + bool isRaw = static_cast<bool>( + env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsRaw", "Z"))); + DemuxFilterPesDataSettings filterPesDataSettings { + .streamId = streamId, + .isRaw = isRaw, + }; + return filterPesDataSettings; +} + +static DemuxFilterRecordSettings getFilterRecordSettings(JNIEnv *env, const jobject& settings) { + jclass clazz = env->FindClass("android/media/tv/tuner/filter/RecordSettings"); + hidl_bitfield<DemuxTsIndex> tsIndexMask = static_cast<hidl_bitfield<DemuxTsIndex>>( + env->GetIntField(settings, env->GetFieldID(clazz, "mTsIndexMask", "I"))); + DemuxRecordScIndexType scIndexType = static_cast<DemuxRecordScIndexType>( + env->GetIntField(settings, env->GetFieldID(clazz, "mScIndexType", "I"))); + jint scIndexMask = env->GetIntField(settings, env->GetFieldID(clazz, "mScIndexMask", "I")); + + DemuxFilterRecordSettings filterRecordSettings { + .tsIndexMask = tsIndexMask, + .scIndexType = scIndexType, + }; + if (scIndexType == DemuxRecordScIndexType::SC) { + filterRecordSettings.scIndexMask.sc(static_cast<hidl_bitfield<DemuxScIndex>>(scIndexMask)); + } else if (scIndexType == DemuxRecordScIndexType::SC_HEVC) { + filterRecordSettings.scIndexMask.scHevc( + static_cast<hidl_bitfield<DemuxScHevcIndex>>(scIndexMask)); + } + return filterRecordSettings; +} + +static DemuxFilterDownloadSettings getFilterDownloadSettings(JNIEnv *env, const jobject& settings) { + jclass clazz = env->FindClass("android/media/tv/tuner/filter/DownloadSettings"); + uint32_t downloadId = static_cast<uint32_t>( + env->GetIntField(settings, env->GetFieldID(clazz, "mDownloadId", "I"))); + + DemuxFilterDownloadSettings filterDownloadSettings { + .downloadId = downloadId, + }; + return filterDownloadSettings; +} + +static DemuxFilterSettings getFilterConfiguration( JNIEnv *env, int type, int subtype, jobject filterSettingsObj) { DemuxFilterSettings filterSettings; - // TODO: more setting types jobject settingsObj = env->GetObjectField( filterSettingsObj, @@ -1378,27 +1519,121 @@ static DemuxFilterSettings getFilterSettings( env->FindClass("android/media/tv/tuner/filter/FilterConfiguration"), "mSettings", "Landroid/media/tv/tuner/filter/Settings;")); - if (type == (int)DemuxFilterMainType::TS) { - // DemuxTsFilterSettings - jclass clazz = env->FindClass("android/media/tv/tuner/filter/TsFilterConfiguration"); - int tpid = env->GetIntField(filterSettingsObj, env->GetFieldID(clazz, "mTpid", "I")); - if (subtype == (int)DemuxTsFilterType::PES) { - // DemuxFilterPesDataSettings - jclass settingClazz = - env->FindClass("android/media/tv/tuner/filter/PesSettings"); - int streamId = env->GetIntField( - settingsObj, env->GetFieldID(settingClazz, "mStreamId", "I")); - bool isRaw = (bool)env->GetBooleanField( - settingsObj, env->GetFieldID(settingClazz, "mIsRaw", "Z")); - DemuxFilterPesDataSettings filterPesDataSettings { - .streamId = static_cast<uint16_t>(streamId), - .isRaw = isRaw, - }; + DemuxFilterMainType mainType = static_cast<DemuxFilterMainType>(type); + switch (mainType) { + case DemuxFilterMainType::TS: { + jclass clazz = env->FindClass("android/media/tv/tuner/filter/TsFilterConfiguration"); + int tpid = env->GetIntField(filterSettingsObj, env->GetFieldID(clazz, "mTpid", "I")); DemuxTsFilterSettings tsFilterSettings { - .tpid = static_cast<uint16_t>(tpid), + .tpid = static_cast<uint16_t>(tpid), }; - tsFilterSettings.filterSettings.pesData(filterPesDataSettings); + + DemuxTsFilterType tsType = static_cast<DemuxTsFilterType>(subtype); + switch (tsType) { + case DemuxTsFilterType::SECTION: + tsFilterSettings.filterSettings.section( + getFilterSectionSettings(env, settingsObj)); + break; + case DemuxTsFilterType::AUDIO: + case DemuxTsFilterType::VIDEO: + tsFilterSettings.filterSettings.av(getFilterAvSettings(env, settingsObj)); + break; + case DemuxTsFilterType::PES: + tsFilterSettings.filterSettings.pesData( + getFilterPesDataSettings(env, settingsObj)); + break; + case DemuxTsFilterType::RECORD: + tsFilterSettings.filterSettings.record( + getFilterRecordSettings(env, settingsObj)); + break; + default: + break; + } filterSettings.ts(tsFilterSettings); + break; + } + case DemuxFilterMainType::MMTP: { + DemuxMmtpFilterSettings mmtpFilterSettings; + DemuxMmtpFilterType mmtpType = static_cast<DemuxMmtpFilterType>(subtype); + switch (mmtpType) { + case DemuxMmtpFilterType::SECTION: + mmtpFilterSettings.filterSettings.section( + getFilterSectionSettings(env, settingsObj)); + break; + case DemuxMmtpFilterType::AUDIO: + case DemuxMmtpFilterType::VIDEO: + mmtpFilterSettings.filterSettings.av(getFilterAvSettings(env, settingsObj)); + break; + case DemuxMmtpFilterType::PES: + mmtpFilterSettings.filterSettings.pesData( + getFilterPesDataSettings(env, settingsObj)); + break; + case DemuxMmtpFilterType::RECORD: + mmtpFilterSettings.filterSettings.record( + getFilterRecordSettings(env, settingsObj)); + break; + case DemuxMmtpFilterType::DOWNLOAD: + mmtpFilterSettings.filterSettings.download( + getFilterDownloadSettings(env, settingsObj)); + break; + default: + break; + } + filterSettings.mmtp(mmtpFilterSettings); + break; + } + case DemuxFilterMainType::IP: { + DemuxIpFilterSettings ipFilterSettings; + DemuxIpFilterType ipType = static_cast<DemuxIpFilterType>(subtype); + switch (ipType) { + case DemuxIpFilterType::SECTION: + ipFilterSettings.filterSettings.section( + getFilterSectionSettings(env, settingsObj)); + break; + case DemuxIpFilterType::IP: + // TODO: handle passthrough + ipFilterSettings.filterSettings.bPassthrough(false); + break; + default: + break; + } + filterSettings.ip(ipFilterSettings); + break; + } + case DemuxFilterMainType::TLV: { + DemuxTlvFilterSettings tlvFilterSettings; + DemuxTlvFilterType tlvType = static_cast<DemuxTlvFilterType>(subtype); + switch (tlvType) { + case DemuxTlvFilterType::SECTION: + tlvFilterSettings.filterSettings.section( + getFilterSectionSettings(env, settingsObj)); + break; + case DemuxTlvFilterType::TLV: + // TODO: handle passthrough + tlvFilterSettings.filterSettings.bPassthrough(false); + break; + default: + break; + } + filterSettings.tlv(tlvFilterSettings); + break; + } + case DemuxFilterMainType::ALP: { + DemuxAlpFilterSettings alpFilterSettings; + DemuxAlpFilterType alpType = static_cast<DemuxAlpFilterType>(subtype); + switch (alpType) { + case DemuxAlpFilterType::SECTION: + alpFilterSettings.filterSettings.section( + getFilterSectionSettings(env, settingsObj)); + break; + default: + break; + } + filterSettings.alp(alpFilterSettings); + break; + } + default: { + break; } } return filterSettings; @@ -1439,7 +1674,7 @@ static int android_media_tv_Tuner_configure_filter( ALOGD("Failed to configure filter: filter not found"); return (int)Result::INVALID_STATE; } - DemuxFilterSettings filterSettings = getFilterSettings(env, type, subtype, settings); + DemuxFilterSettings filterSettings = getFilterConfiguration(env, type, subtype, settings); Result res = iFilterSp->configure(filterSettings); MQDescriptorSync<uint8_t> filterMQDesc; if (res == Result::SUCCESS && filterSp->mFilterMQ == NULL) { diff --git a/packages/PrintSpooler/res/values-ja/donottranslate.xml b/packages/PrintSpooler/res/values-ja/donottranslate.xml index d334ddd312cf..6a0f768336ae 100644 --- a/packages/PrintSpooler/res/values-ja/donottranslate.xml +++ b/packages/PrintSpooler/res/values-ja/donottranslate.xml @@ -16,7 +16,7 @@ <resources> - <string name="mediasize_default">JIS_B5</string> + <string name="mediasize_default">ISO_A4</string> <string name="mediasize_standard">@string/mediasize_standard_japan</string> </resources> diff --git a/packages/SystemUI/res/layout/bubble_overflow_activity.xml b/packages/SystemUI/res/layout/bubble_overflow_activity.xml index 95f205a1be34..481c4dbe3bf1 100644 --- a/packages/SystemUI/res/layout/bubble_overflow_activity.xml +++ b/packages/SystemUI/res/layout/bubble_overflow_activity.xml @@ -14,8 +14,45 @@ ~ limitations under the License --> -<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/bubble_overflow_recycler" - android:layout_gravity="center_horizontal" +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/bubble_overflow_container" android:layout_width="match_parent" - android:layout_height="wrap_content"/> + android:layout_height="match_parent" + android:orientation="vertical" + android:layout_gravity="center_horizontal"> + + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/bubble_overflow_recycler" + android:layout_gravity="center_horizontal" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + + <LinearLayout + android:id="@+id/bubble_overflow_empty_state" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:gravity="center"> + + <TextView + android:id="@+id/bubble_overflow_empty_title" + android:text="@string/bubble_overflow_empty_title" + android:fontFamily="@*android:string/config_bodyFontFamilyMedium" + android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2" + android:textColor="?android:attr/textColorSecondary" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center"/> + + <TextView + android:id="@+id/bubble_overflow_empty_subtitle" + android:fontFamily="@*android:string/config_bodyFontFamily" + android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2" + android:textColor="?android:attr/textColorSecondary" + android:text="@string/bubble_overflow_empty_subtitle" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center"/> + </LinearLayout> +</LinearLayout> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 6dd89d88d252..ef9e705d0a81 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1780,6 +1780,12 @@ <!-- [CHAR LIMIT=150] Notification Importance title: bubble level summary --> <string name="notification_channel_summary_bubble">Keeps your attention with a floating shortcut to this content.</string> + <!-- [CHAR LIMIT=NONE] Empty overflow title --> + <string name="bubble_overflow_empty_title">No recent bubbles</string> + + <!-- [CHAR LIMIT=NONE] Empty overflow subtitle --> + <string name="bubble_overflow_empty_subtitle">Recently dismissed bubbles will appear here for easy retrieval.</string> + <!-- Notification: Control panel: Label that displays when the app's notifications cannot be blocked. --> <string name="notification_unblockable_desc">These notifications can\'t be modified.</string> diff --git a/packages/SystemUI/src/com/android/systemui/SysUIToast.java b/packages/SystemUI/src/com/android/systemui/SysUIToast.java index 0f7f1bebae57..023b74b9c07e 100644 --- a/packages/SystemUI/src/com/android/systemui/SysUIToast.java +++ b/packages/SystemUI/src/com/android/systemui/SysUIToast.java @@ -19,7 +19,6 @@ import static android.widget.Toast.Duration; import android.annotation.StringRes; import android.content.Context; -import android.view.WindowManager; import android.widget.Toast; public class SysUIToast { @@ -29,10 +28,7 @@ public class SysUIToast { } public static Toast makeText(Context context, CharSequence text, @Duration int duration) { - Toast toast = Toast.makeText(context, text, duration); - toast.getWindowParams().privateFlags |= - WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; - return toast; + return Toast.makeText(context, text, duration); } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java index 68b05e358786..9de10406a822 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java @@ -16,6 +16,7 @@ package com.android.systemui.biometrics; +import android.annotation.NonNull; import android.content.Context; import android.graphics.drawable.Drawable; import android.hardware.biometrics.BiometricPrompt; @@ -33,6 +34,8 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; +import androidx.annotation.Nullable; + import com.android.internal.widget.LockPatternUtils; import com.android.systemui.Interpolators; import com.android.systemui.R; @@ -126,18 +129,18 @@ public abstract class AuthCredentialView extends LinearLayout { mHandler.postDelayed(mClearErrorRunnable, ERROR_DURATION_MS); } - private void setTextOrHide(TextView view, String string) { - if (TextUtils.isEmpty(string)) { + private void setTextOrHide(TextView view, CharSequence text) { + if (TextUtils.isEmpty(text)) { view.setVisibility(View.GONE); } else { - view.setText(string); + view.setText(text); } Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this); } - private void setText(TextView view, String string) { - view.setText(string); + private void setText(TextView view, CharSequence text) { + view.setText(text); } void setEffectiveUserId(int effectiveUserId) { @@ -173,11 +176,9 @@ public abstract class AuthCredentialView extends LinearLayout { protected void onAttachedToWindow() { super.onAttachedToWindow(); - setText(mTitleView, mBiometricPromptBundle.getString(BiometricPrompt.KEY_TITLE)); - setTextOrHide(mSubtitleView, - mBiometricPromptBundle.getString(BiometricPrompt.KEY_SUBTITLE)); - setTextOrHide(mDescriptionView, - mBiometricPromptBundle.getString(BiometricPrompt.KEY_DESCRIPTION)); + setText(mTitleView, getTitle(mBiometricPromptBundle)); + setTextOrHide(mSubtitleView, getSubtitle(mBiometricPromptBundle)); + setTextOrHide(mDescriptionView, getDescription(mBiometricPromptBundle)); final boolean isManagedProfile = Utils.isManagedProfile(mContext, mEffectiveUserId); final Drawable image; @@ -279,4 +280,28 @@ public abstract class AuthCredentialView extends LinearLayout { } } } + + @Nullable + private static CharSequence getTitle(@NonNull Bundle bundle) { + final CharSequence credentialTitle = + bundle.getCharSequence(BiometricPrompt.KEY_DEVICE_CREDENTIAL_TITLE); + return credentialTitle != null ? credentialTitle + : bundle.getCharSequence(BiometricPrompt.KEY_TITLE); + } + + @Nullable + private static CharSequence getSubtitle(@NonNull Bundle bundle) { + final CharSequence credentialSubtitle = + bundle.getCharSequence(BiometricPrompt.KEY_DEVICE_CREDENTIAL_SUBTITLE); + return credentialSubtitle != null ? credentialSubtitle + : bundle.getCharSequence(BiometricPrompt.KEY_SUBTITLE); + } + + @Nullable + private static CharSequence getDescription(@NonNull Bundle bundle) { + final CharSequence credentialDescription = + bundle.getCharSequence(BiometricPrompt.KEY_DEVICE_CREDENTIAL_DESCRIPTION); + return credentialDescription != null ? credentialDescription + : bundle.getCharSequence(BiometricPrompt.KEY_DESCRIPTION); + } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java index 45705b76f09c..1e39954ed414 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java @@ -226,6 +226,10 @@ class Bubble { mIconView.update(this); } + void setInflated(boolean inflated) { + mInflated = inflated; + } + /** * Set visibility of bubble in the expanded state. * diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index 05838abe184a..762e5f21cd63 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -749,7 +749,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi } void promoteBubbleFromOverflow(Bubble bubble) { - mBubbleData.promoteBubbleFromOverflow(bubble); + bubble.setInflateSynchronously(mInflateSynchronously); + mBubbleData.promoteBubbleFromOverflow(bubble, mStackView, mBubbleIconFactory); } /** diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java index 673121f92716..8a5aad875979 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java @@ -199,16 +199,21 @@ public class BubbleData { dispatchPendingChanges(); } - public void promoteBubbleFromOverflow(Bubble bubble) { + public void promoteBubbleFromOverflow(Bubble bubble, BubbleStackView stack, + BubbleIconFactory factory) { if (DEBUG_BUBBLE_DATA) { Log.d(TAG, "promoteBubbleFromOverflow: " + bubble); } - mOverflowBubbles.remove(bubble); - doAdd(bubble); - setSelectedBubbleInternal(bubble); + // Preserve new order for next repack, which sorts by last updated time. bubble.markUpdatedAt(mTimeSource.currentTimeMillis()); - trim(); + setSelectedBubbleInternal(bubble); + mOverflowBubbles.remove(bubble); + + bubble.inflate( + b -> notificationEntryUpdated(bubble, /* suppressFlyout */ + false, /* showInShade */ true), + mContext, stack, factory); dispatchPendingChanges(); } @@ -445,6 +450,10 @@ public class BubbleData { mOverflowBubbles.add(0, bubbleToRemove); if (mOverflowBubbles.size() == mMaxOverflowBubbles + 1) { // Remove oldest bubble. + if (DEBUG_BUBBLE_DATA) { + Log.d(TAG, "Overflow full. Remove bubble: " + mOverflowBubbles.get( + mOverflowBubbles.size() - 1)); + } mOverflowBubbles.remove(mOverflowBubbles.size() - 1); } } @@ -511,7 +520,7 @@ public class BubbleData { if (Objects.equals(bubble, mSelectedBubble)) { return; } - if (bubble != null && !mBubbles.contains(bubble)) { + if (bubble != null && !mBubbles.contains(bubble) && !mOverflowBubbles.contains(bubble)) { Log.e(TAG, "Cannot select bubble which doesn't exist!" + " (" + bubble + ") bubbles=" + mBubbles); return; diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java index 0d5261dcb7f3..fe191f40d31f 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java @@ -294,7 +294,8 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList ta.recycle(); mPointerDrawable.setTint(bgColor); - if (ScreenDecorationsUtils.supportsRoundedCornersOnWindows(mContext.getResources())) { + if (mActivityView != null && ScreenDecorationsUtils.supportsRoundedCornersOnWindows( + mContext.getResources())) { mActivityView.setCornerRadius(cornerRadius); } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java index 2d55a1ddf654..f3cfa834dfc1 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java @@ -26,7 +26,10 @@ import android.graphics.Color; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; +import android.view.View; import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.TextView; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -46,6 +49,7 @@ import javax.inject.Inject; public class BubbleOverflowActivity extends Activity { private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleOverflowActivity" : TAG_BUBBLES; + private LinearLayout mEmptyState; private BubbleController mBubbleController; private BubbleOverflowAdapter mAdapter; private RecyclerView mRecyclerView; @@ -64,6 +68,7 @@ public class BubbleOverflowActivity extends Activity { setBackgroundColor(); mMaxBubbles = getResources().getInteger(R.integer.bubbles_max_rendered); + mEmptyState = findViewById(R.id.bubble_overflow_empty_state); mRecyclerView = findViewById(R.id.bubble_overflow_recycler); mRecyclerView.setLayoutManager( new GridLayoutManager(getApplicationContext(), @@ -73,9 +78,9 @@ public class BubbleOverflowActivity extends Activity { mBubbleController::promoteBubbleFromOverflow); mRecyclerView.setAdapter(mAdapter); - updateData(mBubbleController.getOverflowBubbles()); + onDataChanged(mBubbleController.getOverflowBubbles()); mBubbleController.setOverflowCallback(() -> { - updateData(mBubbleController.getOverflowBubbles()); + onDataChanged(mBubbleController.getOverflowBubbles()); }); } @@ -87,7 +92,7 @@ public class BubbleOverflowActivity extends Activity { findViewById(android.R.id.content).setBackgroundColor(bgColor); } - void updateData(List<Bubble> bubbles) { + void onDataChanged(List<Bubble> bubbles) { mOverflowBubbles.clear(); if (bubbles.size() > mMaxBubbles) { mOverflowBubbles.addAll(bubbles.subList(mMaxBubbles, bubbles.size())); @@ -96,6 +101,12 @@ public class BubbleOverflowActivity extends Activity { } mAdapter.notifyDataSetChanged(); + if (mOverflowBubbles.isEmpty()) { + mEmptyState.setVisibility(View.VISIBLE); + } else { + mEmptyState.setVisibility(View.GONE); + } + if (DEBUG_OVERFLOW) { Log.d(TAG, "Updated overflow bubbles:\n" + BubbleDebugConfig.formatBubblesString( mOverflowBubbles, /*selected*/ null)); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index bce172b89187..cff78cfeaae0 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -528,6 +528,12 @@ public class BubbleStackView extends FrameLayout { mBubbleContainer.addView(mOverflowBtn, 0, new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); + setOverflowBtnTheme(); + mOverflowBtn.setVisibility(GONE); + } + + // TODO(b/149146374) Propagate theme change to bubbles in overflow. + private void setOverflowBtnTheme() { TypedArray ta = mContext.obtainStyledAttributes( new int[]{android.R.attr.colorBackgroundFloating}); int bgColor = ta.getColor(0, Color.WHITE /* default */); @@ -537,8 +543,6 @@ public class BubbleStackView extends FrameLayout { ColorDrawable bg = new ColorDrawable(bgColor); AdaptiveIconDrawable adaptiveIcon = new AdaptiveIconDrawable(bg, fg); mOverflowBtn.setImageDrawable(adaptiveIcon); - - mOverflowBtn.setVisibility(GONE); } void showExpandedViewContents(int displayId) { @@ -568,6 +572,9 @@ public class BubbleStackView extends FrameLayout { */ public void onThemeChanged() { setUpFlyout(); + if (BubbleExperimentConfig.allowBubbleOverflow(mContext)) { + setOverflowBtnTheme(); + } } /** Respond to the phone being rotated by repositioning the stack and hiding any flyouts. */ @@ -795,6 +802,7 @@ public class BubbleStackView extends FrameLayout { if (removedIndex >= 0) { mBubbleContainer.removeViewAt(removedIndex); bubble.cleanupExpandedState(); + bubble.setInflated(false); logBubbleEvent(bubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED); } else { Log.d(TAG, "was asked to remove Bubble, but didn't find the view! " + bubble); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java index 6d4b13ccf494..91d2de760696 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java @@ -638,9 +638,9 @@ public class NotifCollection implements Dumpable { private static boolean shouldDismissOnClearAll( NotificationEntry entry, @UserIdInt int userId) { - // TODO: (b/149396544) add FLAG_BUBBLE check here + in NoManService return userIdMatches(entry, userId) && entry.isClearable() + && !hasFlag(entry, Notification.FLAG_BUBBLE) && entry.getDismissState() != DISMISSED; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt index 4f27c0f04c3f..5b4a927bb8f0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt @@ -26,26 +26,23 @@ import android.content.Context import android.content.DialogInterface import android.graphics.Color import android.graphics.PixelFormat -import android.graphics.drawable.Drawable import android.graphics.drawable.ColorDrawable +import android.graphics.drawable.Drawable import android.util.Log import android.view.Gravity import android.view.View import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import android.view.Window -import android.view.WindowInsets.Type import android.view.WindowInsets.Type.statusBars import android.view.WindowManager import android.widget.TextView import com.android.internal.annotations.VisibleForTesting - import com.android.systemui.R - import javax.inject.Inject import javax.inject.Singleton -const val TAG = "ChannelDialogController" +private const val TAG = "ChannelDialogController" /** * ChannelEditorDialogController is the controller for the dialog half-shelf @@ -149,9 +146,9 @@ class ChannelEditorDialogController @Inject constructor( val channels = groupList .flatMap { group -> group.channels.asSequence().filterNot { channel -> - channel.isImportanceLockedByOEM - || channel.importance == IMPORTANCE_NONE - || channel.isImportanceLockedByCriticalDeviceFunction + channel.isImportanceLockedByOEM || + channel.importance == IMPORTANCE_NONE || + channel.isImportanceLockedByCriticalDeviceFunction } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java index e2513dac44d8..d744fc398d7a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java @@ -74,11 +74,15 @@ import javax.inject.Singleton; @Singleton public final class NotifBindPipeline { private final Map<NotificationEntry, BindEntry> mBindEntries = new ArrayMap<>(); + private final NotifBindPipelineLogger mLogger; private BindStage mStage; @Inject - NotifBindPipeline(CommonNotifCollection collection) { + NotifBindPipeline( + CommonNotifCollection collection, + NotifBindPipelineLogger logger) { collection.addCollectionListener(mCollectionListener); + mLogger = logger; } /** @@ -86,6 +90,8 @@ public final class NotifBindPipeline { */ public void setStage( BindStage stage) { + mLogger.logStageSet(stage.getClass().getName()); + mStage = stage; mStage.setBindRequestListener(this::onBindRequested); } @@ -96,6 +102,8 @@ public final class NotifBindPipeline { public void manageRow( @NonNull NotificationEntry entry, @NonNull ExpandableNotificationRow row) { + mLogger.logManagedRow(entry.getKey()); + final BindEntry bindEntry = getBindEntry(entry); bindEntry.row = row; if (bindEntry.invalidated) { @@ -130,6 +138,8 @@ public final class NotifBindPipeline { * callbacks when the run finishes. If a run is already in progress, it is restarted. */ private void startPipeline(NotificationEntry entry) { + mLogger.logStartPipeline(entry.getKey()); + if (mStage == null) { throw new IllegalStateException("No stage was ever set on the pipeline"); } @@ -147,10 +157,11 @@ public final class NotifBindPipeline { private void onPipelineComplete(NotificationEntry entry) { final BindEntry bindEntry = getBindEntry(entry); + final Set<BindCallback> callbacks = bindEntry.callbacks; - bindEntry.invalidated = false; + mLogger.logFinishedPipeline(entry.getKey(), callbacks.size()); - final Set<BindCallback> callbacks = bindEntry.callbacks; + bindEntry.invalidated = false; for (BindCallback cb : callbacks) { cb.onBindFinished(entry); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt new file mode 100644 index 000000000000..2717d7ad143b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt @@ -0,0 +1,61 @@ +/* + * 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.notification.row + +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel.INFO +import com.android.systemui.log.dagger.NotificationLog +import javax.inject.Inject + +class NotifBindPipelineLogger @Inject constructor( + @NotificationLog private val buffer: LogBuffer +) { + fun logStageSet(stageName: String) { + buffer.log(TAG, INFO, { + str1 = stageName + }, { + "Stage set: $str1" + }) + } + + fun logManagedRow(notifKey: String) { + buffer.log(TAG, INFO, { + str1 = notifKey + }, { + "Row set for notif: $str1" + }) + } + + fun logStartPipeline(notifKey: String) { + buffer.log(TAG, INFO, { + str1 = notifKey + }, { + "Start pipeline for notif: $str1" + }) + } + + fun logFinishedPipeline(notifKey: String, numCallbacks: Int) { + buffer.log(TAG, INFO, { + str1 = notifKey + int1 = numCallbacks + }, { + "Finished pipeline for notif $str1 with $int1 callbacks" + }) + } +} + +private const val TAG = "NotifBindPipeline"
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java index 5170d0b85b17..88ed0bb37d0d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java @@ -157,6 +157,15 @@ public final class RowContentBindParams { return mViewsNeedReinflation; } + @Override + public String toString() { + return String.format("RowContentBindParams[mContentViews=%x mDirtyContentViews=%x " + + "mUseLowPriority=%b mUseChildInGroup=%b mUseIncreasedHeight=%b " + + "mUseIncreasedHeadsUpHeight=%b mViewsNeedReinflation=%b]", + mContentViews, mDirtyContentViews, mUseLowPriority, mUseChildInGroup, + mUseIncreasedHeight, mUseIncreasedHeadsUpHeight, mViewsNeedReinflation); + } + /** * Content views that should be inflated by default for all notifications. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java index f78324596fb4..c632f3eb22a2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java @@ -38,13 +38,16 @@ import javax.inject.Singleton; public class RowContentBindStage extends BindStage<RowContentBindParams> { private final NotificationRowContentBinder mBinder; private final NotifInflationErrorManager mNotifInflationErrorManager; + private final RowContentBindStageLogger mLogger; @Inject RowContentBindStage( NotificationRowContentBinder binder, - NotifInflationErrorManager errorManager) { + NotifInflationErrorManager errorManager, + RowContentBindStageLogger logger) { mBinder = binder; mNotifInflationErrorManager = errorManager; + mLogger = logger; } @Override @@ -54,6 +57,8 @@ public class RowContentBindStage extends BindStage<RowContentBindParams> { @NonNull StageCallback callback) { RowContentBindParams params = getStageParams(entry); + mLogger.logStageParams(entry.getKey(), params.toString()); + // Resolve content to bind/unbind. @InflationFlag int inflationFlags = params.getContentViews(); @InflationFlag int invalidatedFlags = params.getDirtyContentViews(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStageLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStageLogger.kt new file mode 100644 index 000000000000..29cce3375c8a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStageLogger.kt @@ -0,0 +1,37 @@ +/* + * 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.notification.row + +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel.INFO +import com.android.systemui.log.dagger.NotificationLog +import javax.inject.Inject + +class RowContentBindStageLogger @Inject constructor( + @NotificationLog private val buffer: LogBuffer +) { + fun logStageParams(notifKey: String, stageParams: String) { + buffer.log(TAG, INFO, { + str1 = notifKey + str2 = stageParams + }, { + "Invalidated notif $str1 with params: \n$str2" + }) + } +} + +private const val TAG = "RowContentBindStage"
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index 70b43bfc0367..e1a20b6ac5d3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -365,8 +365,12 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit } catch (RemoteException ex) { // system process is dead if we're here. } + if (!isBubble) { if (parentToCancelFinal != null) { + // TODO: (b/145659174) remove - this cancels the parent if the notification clicked + // on will auto-cancel and is the only child in the group. This won't be + // necessary in the new pipeline due to group pruning in ShadeListBuilder. removeNotification(parentToCancelFinal); } if (shouldAutoCancel(sbn) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java index 408bba48d422..6408f7a38133 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java @@ -59,7 +59,7 @@ public class NotifBindPipelineTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); CommonNotifCollection collection = mock(CommonNotifCollection.class); - mBindPipeline = new NotifBindPipeline(collection); + mBindPipeline = new NotifBindPipeline(collection, mock(NotifBindPipelineLogger.class)); mBindPipeline.setStage(mStage); ArgumentCaptor<NotifCollectionListener> collectionListenerCaptor = diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index fd5512d62968..7a1bd052a336 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -111,11 +111,13 @@ public class NotificationTestHelper { mock(NotifRemoteViewCache.class), mock(NotificationRemoteInputManager.class)); contentBinder.setInflateSynchronously(true); - mBindStage = new RowContentBindStage(contentBinder, mock(NotifInflationErrorManager.class)); + mBindStage = new RowContentBindStage(contentBinder, + mock(NotifInflationErrorManager.class), + mock(RowContentBindStageLogger.class)); CommonNotifCollection collection = mock(CommonNotifCollection.class); - mBindPipeline = new NotifBindPipeline(collection); + mBindPipeline = new NotifBindPipeline(collection, mock(NotifBindPipelineLogger.class)); mBindPipeline.setStage(mBindStage); ArgumentCaptor<NotifCollectionListener> collectionListenerCaptor = diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java index d9fe6551ba1c..0f2482ce9c4e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java @@ -60,8 +60,10 @@ public class RowContentBindStageTest extends SysuiTestCase { public void setUp() { MockitoAnnotations.initMocks(this); - mRowContentBindStage = new RowContentBindStage(mBinder, - mock(NotifInflationErrorManager.class)); + mRowContentBindStage = new RowContentBindStage( + mBinder, + mock(NotifInflationErrorManager.class), + mock(RowContentBindStageLogger.class)); mRowContentBindStage.createStageParams(mEntry); } diff --git a/packages/services/PacProcessor/jni/Android.bp b/packages/services/PacProcessor/jni/Android.bp index ab21a76229ba..351e92c630d1 100644 --- a/packages/services/PacProcessor/jni/Android.bp +++ b/packages/services/PacProcessor/jni/Android.bp @@ -37,7 +37,8 @@ cc_library_shared { "-Wunused", "-Wunreachable-code", ], - sanitize: { - cfi: true, - }, + // Re-enable when b/145990493 is fixed + // sanitize: { + // cfi: true, + // }, } diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java index 4f49fb7578a1..0b3899d15993 100644 --- a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java +++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java @@ -16,6 +16,8 @@ package com.android.server.appprediction; +import static android.provider.DeviceConfig.NAMESPACE_SYSTEMUI; + import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppGlobals; @@ -30,12 +32,17 @@ import android.content.pm.ParceledListSlice; import android.content.pm.ServiceInfo; import android.os.RemoteCallbackList; import android.os.RemoteException; +import android.provider.DeviceConfig; import android.service.appprediction.AppPredictionService; +import android.service.appprediction.IPredictionService; import android.util.ArrayMap; import android.util.Slog; import com.android.internal.annotations.GuardedBy; +import com.android.internal.infra.AbstractRemoteService; +import com.android.server.LocalServices; import com.android.server.infra.AbstractPerUserSystemService; +import com.android.server.people.PeopleServiceInternal; import java.util.function.Consumer; @@ -47,6 +54,8 @@ public class AppPredictionPerUserService extends implements RemoteAppPredictionService.RemoteAppPredictionServiceCallbacks { private static final String TAG = AppPredictionPerUserService.class.getSimpleName(); + private static final String PREDICT_USING_PEOPLE_SERVICE_PREFIX = + "predict_using_people_service_"; @Nullable @GuardedBy("mLock") @@ -104,14 +113,16 @@ public class AppPredictionPerUserService extends @GuardedBy("mLock") public void onCreatePredictionSessionLocked(@NonNull AppPredictionContext context, @NonNull AppPredictionSessionId sessionId) { - final RemoteAppPredictionService service = getRemoteServiceLocked(); - if (service != null) { - service.onCreatePredictionSession(context, sessionId); - - if (!mSessionInfos.containsKey(sessionId)) { - mSessionInfos.put(sessionId, new AppPredictionSessionInfo(sessionId, context, - this::removeAppPredictionSessionInfo)); - } + if (!mSessionInfos.containsKey(sessionId)) { + mSessionInfos.put(sessionId, new AppPredictionSessionInfo(sessionId, context, + DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI, + PREDICT_USING_PEOPLE_SERVICE_PREFIX + context.getUiSurface(), false), + this::removeAppPredictionSessionInfo)); + } + final boolean serviceExists = resolveService(sessionId, s -> + s.onCreatePredictionSession(context, sessionId)); + if (!serviceExists) { + mSessionInfos.remove(sessionId); } } @@ -121,10 +132,7 @@ public class AppPredictionPerUserService extends @GuardedBy("mLock") public void notifyAppTargetEventLocked(@NonNull AppPredictionSessionId sessionId, @NonNull AppTargetEvent event) { - final RemoteAppPredictionService service = getRemoteServiceLocked(); - if (service != null) { - service.notifyAppTargetEvent(sessionId, event); - } + resolveService(sessionId, s -> s.notifyAppTargetEvent(sessionId, event)); } /** @@ -133,10 +141,8 @@ public class AppPredictionPerUserService extends @GuardedBy("mLock") public void notifyLaunchLocationShownLocked(@NonNull AppPredictionSessionId sessionId, @NonNull String launchLocation, @NonNull ParceledListSlice targetIds) { - final RemoteAppPredictionService service = getRemoteServiceLocked(); - if (service != null) { - service.notifyLaunchLocationShown(sessionId, launchLocation, targetIds); - } + resolveService(sessionId, s -> + s.notifyLaunchLocationShown(sessionId, launchLocation, targetIds)); } /** @@ -145,10 +151,7 @@ public class AppPredictionPerUserService extends @GuardedBy("mLock") public void sortAppTargetsLocked(@NonNull AppPredictionSessionId sessionId, @NonNull ParceledListSlice targets, @NonNull IPredictionCallback callback) { - final RemoteAppPredictionService service = getRemoteServiceLocked(); - if (service != null) { - service.sortAppTargets(sessionId, targets, callback); - } + resolveService(sessionId, s -> s.sortAppTargets(sessionId, targets, callback)); } /** @@ -157,14 +160,11 @@ public class AppPredictionPerUserService extends @GuardedBy("mLock") public void registerPredictionUpdatesLocked(@NonNull AppPredictionSessionId sessionId, @NonNull IPredictionCallback callback) { - final RemoteAppPredictionService service = getRemoteServiceLocked(); - if (service != null) { - service.registerPredictionUpdates(sessionId, callback); - - AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId); - if (sessionInfo != null) { - sessionInfo.addCallbackLocked(callback); - } + final boolean serviceExists = resolveService(sessionId, s -> + s.registerPredictionUpdates(sessionId, callback)); + final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId); + if (serviceExists && sessionInfo != null) { + sessionInfo.addCallbackLocked(callback); } } @@ -174,14 +174,11 @@ public class AppPredictionPerUserService extends @GuardedBy("mLock") public void unregisterPredictionUpdatesLocked(@NonNull AppPredictionSessionId sessionId, @NonNull IPredictionCallback callback) { - final RemoteAppPredictionService service = getRemoteServiceLocked(); - if (service != null) { - service.unregisterPredictionUpdates(sessionId, callback); - - AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId); - if (sessionInfo != null) { - sessionInfo.removeCallbackLocked(callback); - } + final boolean serviceExists = resolveService(sessionId, s -> + s.unregisterPredictionUpdates(sessionId, callback)); + final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId); + if (serviceExists && sessionInfo != null) { + sessionInfo.removeCallbackLocked(callback); } } @@ -190,10 +187,7 @@ public class AppPredictionPerUserService extends */ @GuardedBy("mLock") public void requestPredictionUpdateLocked(@NonNull AppPredictionSessionId sessionId) { - final RemoteAppPredictionService service = getRemoteServiceLocked(); - if (service != null) { - service.requestPredictionUpdate(sessionId); - } + resolveService(sessionId, s -> s.requestPredictionUpdate(sessionId)); } /** @@ -201,14 +195,11 @@ public class AppPredictionPerUserService extends */ @GuardedBy("mLock") public void onDestroyPredictionSessionLocked(@NonNull AppPredictionSessionId sessionId) { - final RemoteAppPredictionService service = getRemoteServiceLocked(); - if (service != null) { - service.onDestroyPredictionSession(sessionId); - - AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId); - if (sessionInfo != null) { - sessionInfo.destroy(); - } + final boolean serviceExists = resolveService(sessionId, s -> + s.onDestroyPredictionSession(sessionId)); + final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId); + if (serviceExists && sessionInfo != null) { + sessionInfo.destroy(); } } @@ -312,6 +303,33 @@ public class AppPredictionPerUserService extends @GuardedBy("mLock") @Nullable + protected boolean resolveService(@NonNull final AppPredictionSessionId sessionId, + @NonNull final AbstractRemoteService.AsyncRequest<IPredictionService> cb) { + final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId); + if (sessionInfo == null) return false; + if (sessionInfo.mUsesPeopleService) { + final IPredictionService service = + LocalServices.getService(PeopleServiceInternal.class); + if (service != null) { + try { + cb.run(service); + } catch (RemoteException e) { + // Shouldn't happen. + Slog.w(TAG, "Failed to invoke service:" + service, e); + } + } + return service != null; + } else { + final RemoteAppPredictionService service = getRemoteServiceLocked(); + if (service != null) { + service.scheduleOnResolvedService(cb); + } + return service != null; + } + } + + @GuardedBy("mLock") + @Nullable private RemoteAppPredictionService getRemoteServiceLocked() { if (mRemoteService == null) { final String serviceName = getComponentNameLocked(); @@ -334,8 +352,12 @@ public class AppPredictionPerUserService extends private static final class AppPredictionSessionInfo { private static final boolean DEBUG = false; // Do not submit with true + @NonNull private final AppPredictionSessionId mSessionId; + @NonNull private final AppPredictionContext mPredictionContext; + private final boolean mUsesPeopleService; + @NonNull private final Consumer<AppPredictionSessionId> mRemoveSessionInfoAction; private final RemoteCallbackList<IPredictionCallback> mCallbacks = @@ -352,13 +374,17 @@ public class AppPredictionPerUserService extends } }; - AppPredictionSessionInfo(AppPredictionSessionId id, AppPredictionContext predictionContext, - Consumer<AppPredictionSessionId> removeSessionInfoAction) { + AppPredictionSessionInfo( + @NonNull final AppPredictionSessionId id, + @NonNull final AppPredictionContext predictionContext, + final boolean usesPeopleService, + @NonNull final Consumer<AppPredictionSessionId> removeSessionInfoAction) { if (DEBUG) { Slog.d(TAG, "Creating AppPredictionSessionInfo for session Id=" + id); } mSessionId = id; mPredictionContext = predictionContext; + mUsesPeopleService = usesPeopleService; mRemoveSessionInfoAction = removeSessionInfoAction; } diff --git a/services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java b/services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java index 04e0e7f7102f..ceb1cafcebeb 100644 --- a/services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java +++ b/services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java @@ -16,13 +16,8 @@ package com.android.server.appprediction; import android.annotation.NonNull; -import android.app.prediction.AppPredictionContext; -import android.app.prediction.AppPredictionSessionId; -import android.app.prediction.AppTargetEvent; -import android.app.prediction.IPredictionCallback; import android.content.ComponentName; import android.content.Context; -import android.content.pm.ParceledListSlice; import android.os.IBinder; import android.service.appprediction.IPredictionService; import android.text.format.DateUtils; @@ -71,74 +66,17 @@ public class RemoteAppPredictionService extends } /** - * Notifies the service of a new prediction session. - */ - public void onCreatePredictionSession(@NonNull AppPredictionContext context, - @NonNull AppPredictionSessionId sessionId) { - scheduleAsyncRequest((s) -> s.onCreatePredictionSession(context, sessionId)); - } - - /** - * Records an app target event to the service. - */ - public void notifyAppTargetEvent(@NonNull AppPredictionSessionId sessionId, - @NonNull AppTargetEvent event) { - scheduleAsyncRequest((s) -> s.notifyAppTargetEvent(sessionId, event)); - } - - /** - * Records when a launch location is shown. - */ - public void notifyLaunchLocationShown(@NonNull AppPredictionSessionId sessionId, - @NonNull String launchLocation, @NonNull ParceledListSlice targetIds) { - scheduleAsyncRequest((s) - -> s.notifyLaunchLocationShown(sessionId, launchLocation, targetIds)); - } - - /** - * Requests the service to sort a list of apps or shortcuts. - */ - public void sortAppTargets(@NonNull AppPredictionSessionId sessionId, - @NonNull ParceledListSlice targets, @NonNull IPredictionCallback callback) { - scheduleAsyncRequest((s) -> s.sortAppTargets(sessionId, targets, callback)); - } - - - /** - * Registers a callback for continuous updates of predicted apps or shortcuts. - */ - public void registerPredictionUpdates(@NonNull AppPredictionSessionId sessionId, - @NonNull IPredictionCallback callback) { - scheduleAsyncRequest((s) -> s.registerPredictionUpdates(sessionId, callback)); - } - - /** - * Unregisters a callback for continuous updates of predicted apps or shortcuts. - */ - public void unregisterPredictionUpdates(@NonNull AppPredictionSessionId sessionId, - @NonNull IPredictionCallback callback) { - scheduleAsyncRequest((s) -> s.unregisterPredictionUpdates(sessionId, callback)); - } - - /** - * Requests a new set of predicted apps or shortcuts. - */ - public void requestPredictionUpdate(@NonNull AppPredictionSessionId sessionId) { - scheduleAsyncRequest((s) -> s.requestPredictionUpdate(sessionId)); - } - - /** - * Notifies the service of the end of an existing prediction session. + * Schedules a request to bind to the remote service. */ - public void onDestroyPredictionSession(@NonNull AppPredictionSessionId sessionId) { - scheduleAsyncRequest((s) -> s.onDestroyPredictionSession(sessionId)); + public void reconnect() { + super.scheduleBind(); } /** - * Schedules a request to bind to the remote service. + * Schedule async request on remote service. */ - public void reconnect() { - super.scheduleBind(); + public void scheduleOnResolvedService(@NonNull AsyncRequest<IPredictionService> request) { + scheduleAsyncRequest(request); } /** diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java index 31ea5faa05f1..9a33fc9548a0 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java @@ -998,6 +998,11 @@ public final class ContentCaptureManagerService extends sendErrorSignal(mClientAdapterReference, serviceAdapterReference, ContentCaptureManager.DATA_SHARE_ERROR_UNKNOWN); + } finally { + synchronized (parentService.mLock) { + parentService.mPackagesWithShareRequests + .remove(mDataShareRequest.getPackageName()); + } } }); diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java index c98762074b53..9540f4336464 100644 --- a/services/core/java/com/android/server/IpSecService.java +++ b/services/core/java/com/android/server/IpSecService.java @@ -1556,16 +1556,16 @@ public class IpSecService extends IIpSecService.Stub { } Objects.requireNonNull(callingPackage, "Null calling package cannot create IpSec tunnels"); - switch (getAppOpsManager().noteOp(TUNNEL_OP, Binder.getCallingUid(), callingPackage)) { - case AppOpsManager.MODE_DEFAULT: - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService"); - break; - case AppOpsManager.MODE_ALLOWED: - return; - default: - throw new SecurityException("Request to ignore AppOps for non-legacy API"); + + // OP_MANAGE_IPSEC_TUNNELS will return MODE_ERRORED by default, including for the system + // server. If the appop is not granted, require that the caller has the MANAGE_IPSEC_TUNNELS + // permission or is the System Server. + if (AppOpsManager.MODE_ALLOWED == getAppOpsManager().noteOpNoThrow( + TUNNEL_OP, Binder.getCallingUid(), callingPackage)) { + return; } + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService"); } private void createOrUpdateTransform( diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 75e310dd9202..b5b22f19d426 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -1300,13 +1300,6 @@ class StorageManagerService extends IStorageManager.Stub vol.state = newState; onVolumeStateChangedLocked(vol, oldState, newState); } - try { - if (vol.type == VolumeInfo.TYPE_PRIVATE && state == VolumeInfo.STATE_MOUNTED) { - mInstaller.onPrivateVolumeMounted(vol.getFsUuid()); - } - } catch (Installer.InstallerException e) { - Slog.i(TAG, "Failed when private volume mounted " + vol, e); - } } } @@ -3110,6 +3103,15 @@ class StorageManagerService extends IStorageManager.Stub try { mVold.prepareUserStorage(volumeUuid, userId, serialNumber, flags); + // After preparing user storage, we should check if we should mount data mirror again, + // and we do it for user 0 only as we only need to do once for all users. + if (volumeUuid != null) { + final StorageManager storage = mContext.getSystemService(StorageManager.class); + VolumeInfo info = storage.findVolumeByUuid(volumeUuid); + if (info != null && userId == 0 && info.type == VolumeInfo.TYPE_PRIVATE) { + mInstaller.tryMountDataMirror(volumeUuid); + } + } } catch (Exception e) { Slog.wtf(TAG, e); } diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 0e5a6bb8bd1c..f85fc284330c 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -61,6 +61,7 @@ import android.telephony.CellSignalStrengthTdscdma; import android.telephony.CellSignalStrengthWcdma; import android.telephony.DataFailCause; import android.telephony.DisconnectCause; +import android.telephony.DisplayInfo; import android.telephony.LocationAccessPolicy; import android.telephony.PhoneCapability; import android.telephony.PhoneStateListener; @@ -205,6 +206,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private boolean[] mUserMobileDataState; + private DisplayInfo[] mDisplayInfos; + private SignalStrength[] mSignalStrength; private boolean[] mMessageWaiting; @@ -284,7 +287,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { static final int ENFORCE_PHONE_STATE_PERMISSION_MASK = PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR | PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR - | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST; + | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST + | PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED; static final int ENFORCE_PRECISE_PHONE_STATE_PERMISSION_MASK = PhoneStateListener.LISTEN_PRECISE_CALL_STATE @@ -443,6 +447,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mCallAttributes = copyOf(mCallAttributes, mNumPhones); mOutgoingCallEmergencyNumber = copyOf(mOutgoingCallEmergencyNumber, mNumPhones); mOutgoingSmsEmergencyNumber = copyOf(mOutgoingSmsEmergencyNumber, mNumPhones); + mDisplayInfos = copyOf(mDisplayInfos, mNumPhones); // ds -> ss switch. if (mNumPhones < oldNumPhones) { @@ -482,6 +487,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mBackgroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE; mPreciseDataConnectionStates.add(new HashMap<Integer, PreciseDataConnectionState>()); mBarringInfo.add(i, new BarringInfo()); + mDisplayInfos[i] = null; } } @@ -540,6 +546,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mOutgoingCallEmergencyNumber = new EmergencyNumber[numPhones]; mOutgoingSmsEmergencyNumber = new EmergencyNumber[numPhones]; mBarringInfo = new ArrayList<>(); + mDisplayInfos = new DisplayInfo[numPhones]; for (int i = 0; i < numPhones; i++) { mCallState[i] = TelephonyManager.CALL_STATE_IDLE; mDataActivity[i] = TelephonyManager.DATA_ACTIVITY_NONE; @@ -568,6 +575,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mBackgroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE; mPreciseDataConnectionStates.add(new HashMap<Integer, PreciseDataConnectionState>()); mBarringInfo.add(i, new BarringInfo()); + mDisplayInfos[i] = null; } mAppOps = mContext.getSystemService(AppOpsManager.class); @@ -978,6 +986,15 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { remove(r.binder); } } + if ((events & PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED) != 0) { + try { + if (mDisplayInfos[phoneId] != null) { + r.callback.onDisplayInfoChanged(mDisplayInfos[phoneId]); + } + } catch (RemoteException ex) { + remove(r.binder); + } + } if ((events & PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST) != 0) { try { r.callback.onEmergencyNumberListChanged(mEmergencyNumberList); @@ -1501,6 +1518,45 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } + /** + * Notify display network info changed. + * + * @param phoneId Phone id + * @param subId Subscription id + * @param displayInfo Display network info + * + * @see PhoneStateListener#onDisplayInfoChanged(DisplayInfo) + */ + public void notifyDisplayInfoChanged(int phoneId, int subId, + @NonNull DisplayInfo displayInfo) { + if (!checkNotifyPermission("notifyDisplayInfoChanged()")) { + return; + } + if (VDBG) { + log("notifyDisplayInfoChanged: PhoneId=" + phoneId + + " subId=" + subId + " displayInfo=" + displayInfo); + } + synchronized (mRecords) { + if (validatePhoneId(phoneId)) { + if (mDisplayInfos[phoneId] != null) { + mDisplayInfos[phoneId] = displayInfo; + for (Record r : mRecords) { + if (r.matchPhoneStateListenerEvent( + PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED) + && idMatch(r.subId, subId, phoneId)) { + try { + r.callback.onDisplayInfoChanged(displayInfo); + } catch (RemoteException ex) { + mRemoveList.add(r.binder); + } + } + } + } + } + handleRemoveListLocked(); + } + } + public void notifyCallForwardingChanged(boolean cfi) { notifyCallForwardingChangedForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, cfi); } @@ -2730,6 +2786,20 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } + if ((events & PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED) != 0) { + try { + if (VDBG) { + log("checkPossibleMissNotify: onDisplayInfoChanged phoneId=" + + phoneId + " dpi=" + mDisplayInfos[phoneId]); + } + if (mDisplayInfos[phoneId] != null) { + r.callback.onDisplayInfoChanged(mDisplayInfos[phoneId]); + } + } catch (RemoteException ex) { + mRemoveList.add(r.binder); + } + } + if ((events & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) { try { if (VDBG) { diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 06561f57f495..3ffe1be89060 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -56,7 +56,6 @@ import static android.app.AppOpsManager.resolveFirstUnrestrictedUidState; import static android.content.Intent.ACTION_PACKAGE_REMOVED; import static android.content.Intent.EXTRA_REPLACING; import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS; -import static android.os.Process.STATSD_UID; import static com.android.server.appop.AppOpsService.ModeCallback.ALL_OPS; @@ -411,9 +410,9 @@ public class AppOpsService extends IAppOpsService.Stub { Slog.e(TAG, "Bad app ops settings", e); } TOP_STATE_SETTLE_TIME = mParser.getDurationMillis( - KEY_TOP_STATE_SETTLE_TIME, 30 * 1000L); + KEY_TOP_STATE_SETTLE_TIME, 5 * 1000L); FG_SERVICE_STATE_SETTLE_TIME = mParser.getDurationMillis( - KEY_FG_SERVICE_STATE_SETTLE_TIME, 10 * 1000L); + KEY_FG_SERVICE_STATE_SETTLE_TIME, 5 * 1000L); BG_STATE_SETTLE_TIME = mParser.getDurationMillis( KEY_BG_STATE_SETTLE_TIME, 1 * 1000L); } @@ -1890,9 +1889,9 @@ public class AppOpsService extends IAppOpsService.Stub { ActivityManagerInternal ami = LocalServices.getService(ActivityManagerInternal.class); boolean isCallerInstrumented = ami.isUidCurrentlyInstrumented(Binder.getCallingUid()); - boolean isCallerStatsCollector = Binder.getCallingUid() == STATSD_UID; + boolean isCallerSystem = Binder.getCallingPid() == Process.myPid(); - if (!isCallerStatsCollector && !isCallerInstrumented) { + if (!isCallerSystem && !isCallerInstrumented) { mHandler.post(() -> callback.sendResult(new Bundle())); return; } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 97a8b8764bcf..67d7530fa11e 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -128,6 +128,7 @@ import android.util.Log; import android.util.MathUtils; import android.util.PrintWriterPrinter; import android.util.Slog; +import android.util.SparseArray; import android.util.SparseIntArray; import android.view.KeyEvent; import android.view.accessibility.AccessibilityManager; @@ -247,6 +248,7 @@ public class AudioService extends IAudioService.Stub // AudioHandler messages private static final int MSG_SET_DEVICE_VOLUME = 0; private static final int MSG_PERSIST_VOLUME = 1; + private static final int MSG_PERSIST_VOLUME_GROUP = 2; private static final int MSG_PERSIST_RINGER_MODE = 3; private static final int MSG_AUDIO_SERVER_DIED = 4; private static final int MSG_PLAY_SOUND_EFFECT = 5; @@ -780,6 +782,10 @@ public class AudioService extends IAudioService.Stub mSettingsObserver = new SettingsObserver(); createStreamStates(); + // must be called after createStreamStates() as it uses MUSIC volume as default if no + // persistent data + initVolumeGroupStates(); + // mSafeUsbMediaVolumeIndex must be initialized after createStreamStates() because it // relies on audio policy having correct ranges for volume indexes. mSafeUsbMediaVolumeIndex = getSafeUsbMediaVolumeIndex(); @@ -1018,6 +1024,9 @@ public class AudioService extends IAudioService.Stub streamState.applyAllVolumes(); } + // Restore audio volume groups + restoreVolumeGroups(); + // Restore mono mode updateMasterMono(mContentResolver); @@ -2288,20 +2297,20 @@ public class AudioService extends IAudioService.Stub String callingPackage) { enforceModifyAudioRoutingPermission(); Objects.requireNonNull(attr, "attr must not be null"); - // @todo not hold the caller context, post message - int stream = AudioProductStrategy.getLegacyStreamTypeForStrategyWithAudioAttributes(attr); - final int device = getDeviceForStream(stream); - - int oldIndex = AudioSystem.getVolumeIndexForAttributes(attr, device); - - AudioSystem.setVolumeIndexForAttributes(attr, index, device); - final int volumeGroup = getVolumeGroupIdForAttributes(attr); - final AudioVolumeGroup avg = getAudioVolumeGroupById(volumeGroup); - if (avg == null) { + if (sVolumeGroupStates.indexOfKey(volumeGroup) < 0) { + Log.e(TAG, ": no volume group found for attributes " + attr.toString()); return; } - for (final int groupedStream : avg.getLegacyStreamTypes()) { + final VolumeGroupState vgs = sVolumeGroupStates.get(volumeGroup); + + sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_SET_GROUP_VOL, attr, vgs.name(), + index/*val1*/, flags/*val2*/, callingPackage)); + + vgs.setVolumeIndex(index, flags); + + // For legacy reason, propagate to all streams associated to this volume group + for (final int groupedStream : vgs.getLegacyStreamTypes()) { setStreamVolume(groupedStream, index, flags, callingPackage, callingPackage, Binder.getCallingUid()); } @@ -2323,10 +2332,12 @@ public class AudioService extends IAudioService.Stub public int getVolumeIndexForAttributes(@NonNull AudioAttributes attr) { enforceModifyAudioRoutingPermission(); Objects.requireNonNull(attr, "attr must not be null"); - int stream = AudioProductStrategy.getLegacyStreamTypeForStrategyWithAudioAttributes(attr); - final int device = getDeviceForStream(stream); - - return AudioSystem.getVolumeIndexForAttributes(attr, device); + final int volumeGroup = getVolumeGroupIdForAttributes(attr); + if (sVolumeGroupStates.indexOfKey(volumeGroup) < 0) { + throw new IllegalArgumentException("No volume group for attributes " + attr); + } + final VolumeGroupState vgs = sVolumeGroupStates.get(volumeGroup); + return vgs.getVolumeIndex(); } /** @see AudioManager#getMaxVolumeIndexForAttributes(attr) */ @@ -3754,6 +3765,8 @@ public class AudioService extends IAudioService.Stub enforceSafeMediaVolume(TAG); } } + + readVolumeGroupsSettings(); } /** @see AudioManager#setSpeakerphoneOn(boolean) */ @@ -4654,6 +4667,310 @@ public class AudioService extends IAudioService.Stub /////////////////////////////////////////////////////////////////////////// // Inner classes /////////////////////////////////////////////////////////////////////////// + /** + * Key is the AudioManager VolumeGroupId + * Value is the VolumeGroupState + */ + private static final SparseArray<VolumeGroupState> sVolumeGroupStates = new SparseArray<>(); + + private void initVolumeGroupStates() { + for (final AudioVolumeGroup avg : getAudioVolumeGroups()) { + try { + // if no valid attributes, this volume group is not controllable, throw exception + ensureValidAttributes(avg); + } catch (IllegalArgumentException e) { + // Volume Groups without attributes are not controllable through set/get volume + // using attributes. Do not append them. + Log.d(TAG, "volume group " + avg.name() + " for internal policy needs"); + continue; + } + sVolumeGroupStates.append(avg.getId(), new VolumeGroupState(avg)); + } + for (int i = 0; i < sVolumeGroupStates.size(); i++) { + final VolumeGroupState vgs = sVolumeGroupStates.valueAt(i); + vgs.applyAllVolumes(); + } + } + + private void ensureValidAttributes(AudioVolumeGroup avg) { + boolean hasAtLeastOneValidAudioAttributes = avg.getAudioAttributes().stream() + .anyMatch(aa -> !aa.equals(AudioProductStrategy.sDefaultAttributes)); + if (!hasAtLeastOneValidAudioAttributes) { + throw new IllegalArgumentException("Volume Group " + avg.name() + + " has no valid audio attributes"); + } + } + + private void readVolumeGroupsSettings() { + Log.v(TAG, "readVolumeGroupsSettings"); + for (int i = 0; i < sVolumeGroupStates.size(); i++) { + final VolumeGroupState vgs = sVolumeGroupStates.valueAt(i); + vgs.readSettings(); + vgs.applyAllVolumes(); + } + } + + // Called upon crash of AudioServer + private void restoreVolumeGroups() { + Log.v(TAG, "restoreVolumeGroups"); + for (int i = 0; i < sVolumeGroupStates.size(); i++) { + final VolumeGroupState vgs = sVolumeGroupStates.valueAt(i); + vgs.applyAllVolumes(); + } + } + + private void dumpVolumeGroups(PrintWriter pw) { + pw.println("\nVolume Groups (device: index)"); + for (int i = 0; i < sVolumeGroupStates.size(); i++) { + final VolumeGroupState vgs = sVolumeGroupStates.valueAt(i); + vgs.dump(pw); + pw.println(""); + } + } + + // NOTE: Locking order for synchronized objects related to volume management: + // 1 mSettingsLock + // 2 VolumeGroupState.class + private class VolumeGroupState { + private final AudioVolumeGroup mAudioVolumeGroup; + private final SparseIntArray mIndexMap = new SparseIntArray(8); + private int mIndexMin; + private int mIndexMax; + private int mLegacyStreamType = AudioSystem.STREAM_DEFAULT; + private int mPublicStreamType = AudioSystem.STREAM_MUSIC; + private AudioAttributes mAudioAttributes = AudioProductStrategy.sDefaultAttributes; + + // No API in AudioSystem to get a device from strategy or from attributes. + // Need a valid public stream type to use current API getDeviceForStream + private int getDeviceForVolume() { + return getDeviceForStream(mPublicStreamType); + } + + private VolumeGroupState(AudioVolumeGroup avg) { + mAudioVolumeGroup = avg; + Log.v(TAG, "VolumeGroupState for " + avg.toString()); + for (final AudioAttributes aa : avg.getAudioAttributes()) { + if (!aa.equals(AudioProductStrategy.sDefaultAttributes)) { + mAudioAttributes = aa; + break; + } + } + final int[] streamTypes = mAudioVolumeGroup.getLegacyStreamTypes(); + if (streamTypes.length != 0) { + // Uses already initialized MIN / MAX if a stream type is attached to group + mLegacyStreamType = streamTypes[0]; + for (final int streamType : streamTypes) { + if (streamType != AudioSystem.STREAM_DEFAULT + && streamType < AudioSystem.getNumStreamTypes()) { + mPublicStreamType = streamType; + break; + } + } + mIndexMin = MIN_STREAM_VOLUME[mPublicStreamType]; + mIndexMax = MAX_STREAM_VOLUME[mPublicStreamType]; + } else if (!avg.getAudioAttributes().isEmpty()) { + mIndexMin = AudioSystem.getMinVolumeIndexForAttributes(mAudioAttributes); + mIndexMax = AudioSystem.getMaxVolumeIndexForAttributes(mAudioAttributes); + } else { + Log.e(TAG, "volume group: " + mAudioVolumeGroup.name() + + " has neither valid attributes nor valid stream types assigned"); + return; + } + // Load volume indexes from data base + readSettings(); + } + + public @NonNull int[] getLegacyStreamTypes() { + return mAudioVolumeGroup.getLegacyStreamTypes(); + } + + public String name() { + return mAudioVolumeGroup.name(); + } + + public int getVolumeIndex() { + return getIndex(getDeviceForVolume()); + } + + public void setVolumeIndex(int index, int flags) { + if (mUseFixedVolume) { + return; + } + setVolumeIndex(index, getDeviceForVolume(), flags); + } + + private void setVolumeIndex(int index, int device, int flags) { + // Set the volume index + setVolumeIndexInt(index, device, flags); + + // Update local cache + mIndexMap.put(device, index); + + // update data base - post a persist volume group msg + sendMsg(mAudioHandler, + MSG_PERSIST_VOLUME_GROUP, + SENDMSG_QUEUE, + device, + 0, + this, + PERSIST_DELAY); + } + + private void setVolumeIndexInt(int index, int device, int flags) { + // Set the volume index + AudioSystem.setVolumeIndexForAttributes(mAudioAttributes, index, device); + } + + public int getIndex(int device) { + synchronized (VolumeGroupState.class) { + int index = mIndexMap.get(device, -1); + // there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT + return (index != -1) ? index : mIndexMap.get(AudioSystem.DEVICE_OUT_DEFAULT); + } + } + + public boolean hasIndexForDevice(int device) { + synchronized (VolumeGroupState.class) { + return (mIndexMap.get(device, -1) != -1); + } + } + + public int getMaxIndex() { + return mIndexMax; + } + + public int getMinIndex() { + return mIndexMin; + } + + public void applyAllVolumes() { + synchronized (VolumeGroupState.class) { + if (mLegacyStreamType != AudioSystem.STREAM_DEFAULT) { + // No-op to avoid regression with stream based volume management + return; + } + // apply device specific volumes first + int index; + for (int i = 0; i < mIndexMap.size(); i++) { + final int device = mIndexMap.keyAt(i); + if (device != AudioSystem.DEVICE_OUT_DEFAULT) { + index = mIndexMap.valueAt(i); + Log.v(TAG, "applyAllVolumes: restore index " + index + " for group " + + mAudioVolumeGroup.name() + " and device " + + AudioSystem.getOutputDeviceName(device)); + setVolumeIndexInt(index, device, 0 /*flags*/); + } + } + // apply default volume last: by convention , default device volume will be used + index = getIndex(AudioSystem.DEVICE_OUT_DEFAULT); + Log.v(TAG, "applyAllVolumes: restore default device index " + index + " for group " + + mAudioVolumeGroup.name()); + setVolumeIndexInt(index, AudioSystem.DEVICE_OUT_DEFAULT, 0 /*flags*/); + } + } + + private void persistVolumeGroup(int device) { + if (mUseFixedVolume) { + return; + } + Log.v(TAG, "persistVolumeGroup: storing index " + getIndex(device) + " for group " + + mAudioVolumeGroup.name() + " and device " + + AudioSystem.getOutputDeviceName(device)); + boolean success = Settings.System.putIntForUser(mContentResolver, + getSettingNameForDevice(device), + getIndex(device), + UserHandle.USER_CURRENT); + if (!success) { + Log.e(TAG, "persistVolumeGroup failed for group " + mAudioVolumeGroup.name()); + } + } + + public void readSettings() { + synchronized (VolumeGroupState.class) { + // First clear previously loaded (previous user?) settings + mIndexMap.clear(); + // force maximum volume on all streams if fixed volume property is set + if (mUseFixedVolume) { + mIndexMap.put(AudioSystem.DEVICE_OUT_DEFAULT, mIndexMax); + return; + } + for (int device : AudioSystem.DEVICE_OUT_ALL_SET) { + // retrieve current volume for device + // if no volume stored for current volume group and device, use default volume + // if default device, continue otherwise + int defaultIndex = (device == AudioSystem.DEVICE_OUT_DEFAULT) + ? AudioSystem.DEFAULT_STREAM_VOLUME[mPublicStreamType] : -1; + int index; + String name = getSettingNameForDevice(device); + index = Settings.System.getIntForUser( + mContentResolver, name, defaultIndex, UserHandle.USER_CURRENT); + if (index == -1) { + Log.e(TAG, "readSettings: No index stored for group " + + mAudioVolumeGroup.name() + ", device " + name); + continue; + } + Log.v(TAG, "readSettings: found stored index " + getValidIndex(index) + + " for group " + mAudioVolumeGroup.name() + ", device: " + name); + mIndexMap.put(device, getValidIndex(index)); + } + } + } + + private int getValidIndex(int index) { + if (index < mIndexMin) { + return mIndexMin; + } else if (mUseFixedVolume || index > mIndexMax) { + return mIndexMax; + } + return index; + } + + public @NonNull String getSettingNameForDevice(int device) { + final String suffix = AudioSystem.getOutputDeviceName(device); + if (suffix.isEmpty()) { + return mAudioVolumeGroup.name(); + } + return mAudioVolumeGroup.name() + "_" + AudioSystem.getOutputDeviceName(device); + } + + private void dump(PrintWriter pw) { + pw.println("- VOLUME GROUP " + mAudioVolumeGroup.name() + ":"); + pw.print(" Min: "); + pw.println(mIndexMin); + pw.print(" Max: "); + pw.println(mIndexMax); + pw.print(" Current: "); + for (int i = 0; i < mIndexMap.size(); i++) { + if (i > 0) { + pw.print(", "); + } + final int device = mIndexMap.keyAt(i); + pw.print(Integer.toHexString(device)); + final String deviceName = device == AudioSystem.DEVICE_OUT_DEFAULT ? "default" + : AudioSystem.getOutputDeviceName(device); + if (!deviceName.isEmpty()) { + pw.print(" ("); + pw.print(deviceName); + pw.print(")"); + } + pw.print(": "); + pw.print(mIndexMap.valueAt(i)); + } + pw.println(); + pw.print(" Devices: "); + int n = 0; + final int devices = getDeviceForVolume(); + for (int device : AudioSystem.DEVICE_OUT_ALL_SET) { + if ((devices & device) == device) { + if (n++ > 0) { + pw.print(", "); + } + pw.print(AudioSystem.getOutputDeviceName(device)); + } + } + } + } + // NOTE: Locking order for synchronized objects related to volume or ringer mode management: // 1 mScoclient OR mSafeMediaVolumeState @@ -5298,6 +5615,11 @@ public class AudioService extends IAudioService.Stub persistVolume((VolumeStreamState) msg.obj, msg.arg1); break; + case MSG_PERSIST_VOLUME_GROUP: + final VolumeGroupState vgs = (VolumeGroupState) msg.obj; + vgs.persistVolumeGroup(msg.arg1); + break; + case MSG_PERSIST_RINGER_MODE: // note that the value persisted is the current ringer mode, not the // value of ringer mode as of the time the request was made to persist @@ -6395,6 +6717,7 @@ public class AudioService extends IAudioService.Stub } mMediaFocusControl.dump(pw); dumpStreamStates(pw); + dumpVolumeGroups(pw); dumpRingerMode(pw); pw.println("\nAudio routes:"); pw.print(" mMainType=0x"); pw.println(Integer.toHexString( diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java index fcd8701f4ccf..add620e74df0 100644 --- a/services/core/java/com/android/server/audio/AudioServiceEvents.java +++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java @@ -16,6 +16,7 @@ package com.android.server.audio; +import android.media.AudioAttributes; import android.media.AudioManager; import android.media.AudioSystem; @@ -97,12 +98,15 @@ public class AudioServiceEvents { static final int VOL_ADJUST_VOL_UID = 5; static final int VOL_VOICE_ACTIVITY_HEARING_AID = 6; static final int VOL_MODE_CHANGE_HEARING_AID = 7; + static final int VOL_SET_GROUP_VOL = 8; final int mOp; final int mStream; final int mVal1; final int mVal2; final String mCaller; + final String mGroupName; + final AudioAttributes mAudioAttributes; /** used for VOL_ADJUST_VOL_UID, * VOL_ADJUST_SUGG_VOL, @@ -114,6 +118,8 @@ public class AudioServiceEvents { mVal1 = val1; mVal2 = val2; mCaller = caller; + mGroupName = null; + mAudioAttributes = null; } /** used for VOL_SET_HEARING_AID_VOL*/ @@ -124,6 +130,8 @@ public class AudioServiceEvents { // unused mStream = -1; mCaller = null; + mGroupName = null; + mAudioAttributes = null; } /** used for VOL_SET_AVRCP_VOL */ @@ -134,6 +142,8 @@ public class AudioServiceEvents { mVal2 = 0; mStream = -1; mCaller = null; + mGroupName = null; + mAudioAttributes = null; } /** used for VOL_VOICE_ACTIVITY_HEARING_AID */ @@ -144,6 +154,8 @@ public class AudioServiceEvents { mVal2 = voiceActive ? 1 : 0; // unused mCaller = null; + mGroupName = null; + mAudioAttributes = null; } /** used for VOL_MODE_CHANGE_HEARING_AID */ @@ -154,6 +166,19 @@ public class AudioServiceEvents { mVal2 = mode; // unused mCaller = null; + mGroupName = null; + mAudioAttributes = null; + } + + /** used for VOL_SET_GROUP_VOL */ + VolumeEvent(int op, AudioAttributes aa, String group, int index, int flags, String caller) { + mOp = op; + mStream = -1; + mVal1 = index; + mVal2 = flags; + mCaller = caller; + mGroupName = group; + mAudioAttributes = aa; } @Override @@ -208,6 +233,14 @@ public class AudioServiceEvents { .append(") causes setting HEARING_AID volume to idx:").append(mVal1) .append(" stream:").append(AudioSystem.streamToString(mStream)) .toString(); + case VOL_SET_GROUP_VOL: + return new StringBuilder("setVolumeIndexForAttributes(attr:") + .append(mAudioAttributes.toString()) + .append(" group: ").append(mGroupName) + .append(" index:").append(mVal1) + .append(" flags:0x").append(Integer.toHexString(mVal2)) + .append(") from ").append(mCaller) + .toString(); default: return new StringBuilder("FIXME invalid op:").append(mOp).toString(); } } diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java index c9c2c96a642a..204f072a72fc 100644 --- a/services/core/java/com/android/server/biometrics/AuthService.java +++ b/services/core/java/com/android/server/biometrics/AuthService.java @@ -153,7 +153,12 @@ public class AuthService extends SystemService { // Only allow internal clients to enable non-public options. if (bundle.getBoolean(BiometricPrompt.EXTRA_DISALLOW_BIOMETRICS_IF_POLICY_EXISTS) - || bundle.getBoolean(BiometricPrompt.KEY_USE_DEFAULT_TITLE, false)) { + || bundle.getBoolean(BiometricPrompt.KEY_USE_DEFAULT_TITLE, false) + || bundle.getCharSequence(BiometricPrompt.KEY_DEVICE_CREDENTIAL_TITLE) != null + || bundle.getCharSequence( + BiometricPrompt.KEY_DEVICE_CREDENTIAL_SUBTITLE) != null + || bundle.getCharSequence( + BiometricPrompt.KEY_DEVICE_CREDENTIAL_DESCRIPTION) != null) { checkInternalPermission(); } diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index cb88c4e8a739..1a68f1bff605 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -48,8 +48,12 @@ import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.net.ConnectivityManager; import android.net.INetworkManagementEventObserver; +import android.net.Ikev2VpnProfile; import android.net.IpPrefix; import android.net.IpSecManager; +import android.net.IpSecManager.IpSecTunnelInterface; +import android.net.IpSecManager.UdpEncapsulationSocket; +import android.net.IpSecTransform; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.LocalSocket; @@ -65,6 +69,12 @@ import android.net.RouteInfo; import android.net.UidRange; import android.net.VpnManager; import android.net.VpnService; +import android.net.ipsec.ike.ChildSessionCallback; +import android.net.ipsec.ike.ChildSessionConfiguration; +import android.net.ipsec.ike.ChildSessionParams; +import android.net.ipsec.ike.IkeSession; +import android.net.ipsec.ike.IkeSessionCallback; +import android.net.ipsec.ike.IkeSessionParams; import android.os.Binder; import android.os.Build.VERSION_CODES; import android.os.Bundle; @@ -113,6 +123,7 @@ import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -122,6 +133,9 @@ import java.util.Objects; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; /** @@ -176,14 +190,14 @@ public class Vpn { private final Context mContext; private final NetworkInfo mNetworkInfo; - private String mPackage; + @VisibleForTesting protected String mPackage; private int mOwnerUID; private boolean mIsPackageTargetingAtLeastQ; private String mInterface; private Connection mConnection; /** Tracks the runners for all VPN types managed by the platform (eg. LegacyVpn, PlatformVpn) */ - private VpnRunner mVpnRunner; + @VisibleForTesting protected VpnRunner mVpnRunner; private PendingIntent mStatusIntent; private volatile boolean mEnableTeardown = true; @@ -196,6 +210,7 @@ public class Vpn { @VisibleForTesting protected final NetworkCapabilities mNetworkCapabilities; private final SystemServices mSystemServices; + private final Ikev2SessionCreator mIkev2SessionCreator; /** * Whether to keep the connection active after rebooting, or upgrading or reinstalling. This @@ -238,17 +253,20 @@ public class Vpn { public Vpn(Looper looper, Context context, INetworkManagementService netService, @UserIdInt int userHandle) { - this(looper, context, netService, userHandle, new SystemServices(context)); + this(looper, context, netService, userHandle, + new SystemServices(context), new Ikev2SessionCreator()); } @VisibleForTesting protected Vpn(Looper looper, Context context, INetworkManagementService netService, - int userHandle, SystemServices systemServices) { + int userHandle, SystemServices systemServices, + Ikev2SessionCreator ikev2SessionCreator) { mContext = context; mNetd = netService; mUserHandle = userHandle; mLooper = looper; mSystemServices = systemServices; + mIkev2SessionCreator = ikev2SessionCreator; mPackage = VpnConfig.LEGACY_VPN; mOwnerUID = getAppUid(mPackage, mUserHandle); @@ -749,8 +767,9 @@ public class Vpn { private boolean isCurrentPreparedPackage(String packageName) { // We can't just check that packageName matches mPackage, because if the app was uninstalled - // and reinstalled it will no longer be prepared. Instead check the UID. - return getAppUid(packageName, mUserHandle) == mOwnerUID; + // and reinstalled it will no longer be prepared. Similarly if there is a shared UID, the + // calling package may not be the same as the prepared package. Check both UID and package. + return getAppUid(packageName, mUserHandle) == mOwnerUID && mPackage.equals(packageName); } /** Prepare the VPN for the given package. Does not perform permission checks. */ @@ -979,7 +998,11 @@ public class Vpn { } lp.setDomains(buffer.toString().trim()); - // TODO: Stop setting the MTU in jniCreate and set it here. + if (mConfig.mtu > 0) { + lp.setMtu(mConfig.mtu); + } + + // TODO: Stop setting the MTU in jniCreate return lp; } @@ -2004,30 +2027,369 @@ public class Vpn { protected abstract void exit(); } - private class IkeV2VpnRunner extends VpnRunner { - private static final String TAG = "IkeV2VpnRunner"; + interface IkeV2VpnRunnerCallback { + void onDefaultNetworkChanged(@NonNull Network network); - private final IpSecManager mIpSecManager; - private final VpnProfile mProfile; + void onChildOpened( + @NonNull Network network, @NonNull ChildSessionConfiguration childConfig); + + void onChildTransformCreated( + @NonNull Network network, @NonNull IpSecTransform transform, int direction); + + void onSessionLost(@NonNull Network network); + } + + /** + * Internal class managing IKEv2/IPsec VPN connectivity + * + * <p>The IKEv2 VPN will listen to, and run based on the lifecycle of Android's default Network. + * As a new default is selected, old IKE sessions will be torn down, and a new one will be + * started. + * + * <p>This class uses locking minimally - the Vpn instance lock is only ever held when fields of + * the outer class are modified. As such, care must be taken to ensure that no calls are added + * that might modify the outer class' state without acquiring a lock. + * + * <p>The overall structure of the Ikev2VpnRunner is as follows: + * + * <ol> + * <li>Upon startup, a NetworkRequest is registered with ConnectivityManager. This is called + * any time a new default network is selected + * <li>When a new default is connected, an IKE session is started on that Network. If there + * were any existing IKE sessions on other Networks, they are torn down before starting + * the new IKE session + * <li>Upon establishment, the onChildTransformCreated() callback is called twice, one for + * each direction, and finally onChildOpened() is called + * <li>Upon the onChildOpened() call, the VPN is fully set up. + * <li>Subsequent Network changes result in new onDefaultNetworkChanged() callbacks. See (2). + * </ol> + */ + class IkeV2VpnRunner extends VpnRunner implements IkeV2VpnRunnerCallback { + @NonNull private static final String TAG = "IkeV2VpnRunner"; + + @NonNull private final IpSecManager mIpSecManager; + @NonNull private final Ikev2VpnProfile mProfile; + @NonNull private final ConnectivityManager.NetworkCallback mNetworkCallback; + + /** + * Executor upon which ALL callbacks must be run. + * + * <p>This executor MUST be a single threaded executor, in order to ensure the consistency + * of the mutable Ikev2VpnRunner fields. The Ikev2VpnRunner is built mostly lock-free by + * virtue of everything being serialized on this executor. + */ + @NonNull private final ExecutorService mExecutor = Executors.newSingleThreadExecutor(); - IkeV2VpnRunner(VpnProfile profile) { + /** Signal to ensure shutdown is honored even if a new Network is connected. */ + private boolean mIsRunning = true; + + @Nullable private UdpEncapsulationSocket mEncapSocket; + @Nullable private IpSecTunnelInterface mTunnelIface; + @Nullable private IkeSession mSession; + @Nullable private Network mActiveNetwork; + + IkeV2VpnRunner(@NonNull Ikev2VpnProfile profile) { super(TAG); mProfile = profile; - - // TODO: move this to startVpnRunnerPrivileged() - mConfig = new VpnConfig(); - mIpSecManager = mContext.getSystemService(IpSecManager.class); + mIpSecManager = (IpSecManager) mContext.getSystemService(Context.IPSEC_SERVICE); + mNetworkCallback = new VpnIkev2Utils.Ikev2VpnNetworkCallback(TAG, this); } @Override public void run() { - // TODO: Build IKE config, start IKE session + // Explicitly use only the network that ConnectivityService thinks is the "best." In + // other words, only ever use the currently selected default network. This does mean + // that in both onLost() and onConnected(), any old sessions MUST be torn down. This + // does NOT include VPNs. + final ConnectivityManager cm = ConnectivityManager.from(mContext); + cm.requestNetwork(cm.getDefaultRequest(), mNetworkCallback); + } + + private boolean isActiveNetwork(@Nullable Network network) { + return Objects.equals(mActiveNetwork, network) && mIsRunning; + } + + /** + * Called when an IKE Child session has been opened, signalling completion of the startup. + * + * <p>This method is only ever called once per IkeSession, and MUST run on the mExecutor + * thread in order to ensure consistency of the Ikev2VpnRunner fields. + */ + public void onChildOpened( + @NonNull Network network, @NonNull ChildSessionConfiguration childConfig) { + if (!isActiveNetwork(network)) { + Log.d(TAG, "onOpened called for obsolete network " + network); + + // Do nothing; this signals that either: (1) a new/better Network was found, + // and the Ikev2VpnRunner has switched to it in onDefaultNetworkChanged, or (2) this + // IKE session was already shut down (exited, or an error was encountered somewhere + // else). In both cases, all resources and sessions are torn down via + // resetIkeState(). + return; + } + + try { + final String interfaceName = mTunnelIface.getInterfaceName(); + final int maxMtu = mProfile.getMaxMtu(); + final List<LinkAddress> internalAddresses = childConfig.getInternalAddresses(); + + final Collection<RouteInfo> newRoutes = VpnIkev2Utils.getRoutesFromTrafficSelectors( + childConfig.getOutboundTrafficSelectors()); + for (final LinkAddress address : internalAddresses) { + mTunnelIface.addAddress(address.getAddress(), address.getPrefixLength()); + } + + final NetworkAgent networkAgent; + final LinkProperties lp; + + synchronized (Vpn.this) { + mInterface = interfaceName; + mConfig.mtu = maxMtu; + mConfig.interfaze = mInterface; + + mConfig.addresses.clear(); + mConfig.addresses.addAll(internalAddresses); + + mConfig.routes.clear(); + mConfig.routes.addAll(newRoutes); + + // TODO: Add DNS servers from negotiation + + networkAgent = mNetworkAgent; + + // The below must be done atomically with the mConfig update, otherwise + // isRunningLocked() will be racy. + if (networkAgent == null) { + if (isSettingsVpnLocked()) { + prepareStatusIntent(); + } + agentConnect(); + return; // Link properties are already sent. + } + + lp = makeLinkProperties(); // Accesses VPN instance fields; must be locked + } + + networkAgent.sendLinkProperties(lp); + } catch (Exception e) { + Log.d(TAG, "Error in ChildOpened for network " + network, e); + onSessionLost(network); + } + } + + /** + * Called when an IPsec transform has been created, and should be applied. + * + * <p>This method is called multiple times over the lifetime of an IkeSession (or default + * network), and is MUST always be called on the mExecutor thread in order to ensure + * consistency of the Ikev2VpnRunner fields. + */ + public void onChildTransformCreated( + @NonNull Network network, @NonNull IpSecTransform transform, int direction) { + if (!isActiveNetwork(network)) { + Log.d(TAG, "ChildTransformCreated for obsolete network " + network); + + // Do nothing; this signals that either: (1) a new/better Network was found, + // and the Ikev2VpnRunner has switched to it in onDefaultNetworkChanged, or (2) this + // IKE session was already shut down (exited, or an error was encountered somewhere + // else). In both cases, all resources and sessions are torn down via + // resetIkeState(). + return; + } + + try { + // Transforms do not need to be persisted; the IkeSession will keep + // them alive for us + mIpSecManager.applyTunnelModeTransform(mTunnelIface, direction, transform); + } catch (IOException e) { + Log.d(TAG, "Transform application failed for network " + network, e); + onSessionLost(network); + } + } + + /** + * Called when a new default network is connected. + * + * <p>The Ikev2VpnRunner will unconditionally switch to the new network, killing the old IKE + * state in the process, and starting a new IkeSession instance. + * + * <p>This method is called multiple times over the lifetime of the Ikev2VpnRunner, and is + * called on the ConnectivityService thread. Thus, the actual work MUST be proxied to the + * mExecutor thread in order to ensure consistency of the Ikev2VpnRunner fields. + */ + public void onDefaultNetworkChanged(@NonNull Network network) { + Log.d(TAG, "Starting IKEv2/IPsec session on new network: " + network); + + // Proxy to the Ikev2VpnRunner (single-thread) executor to ensure consistency in lieu + // of locking. + mExecutor.execute(() -> { + try { + if (!mIsRunning) { + Log.d(TAG, "onDefaultNetworkChanged after exit"); + return; // VPN has been shut down. + } + + // Without MOBIKE, we have no way to seamlessly migrate. Close on old + // (non-default) network, and start the new one. + resetIkeState(); + mActiveNetwork = network; + + // TODO(b/149356682): Update this based on new IKE API + mEncapSocket = mIpSecManager.openUdpEncapsulationSocket(); + + // TODO(b/149356682): Update this based on new IKE API + final IkeSessionParams ikeSessionParams = + VpnIkev2Utils.buildIkeSessionParams(mProfile, mEncapSocket); + final ChildSessionParams childSessionParams = + VpnIkev2Utils.buildChildSessionParams(); + + // TODO: Remove the need for adding two unused addresses with + // IPsec tunnels. + mTunnelIface = + mIpSecManager.createIpSecTunnelInterface( + ikeSessionParams.getServerAddress() /* unused */, + ikeSessionParams.getServerAddress() /* unused */, + network); + mNetd.setInterfaceUp(mTunnelIface.getInterfaceName()); + + // Socket must be bound to prevent network switches from causing + // the IKE teardown to fail/timeout. + // TODO(b/149356682): Update this based on new IKE API + network.bindSocket(mEncapSocket.getFileDescriptor()); + + mSession = mIkev2SessionCreator.createIkeSession( + mContext, + ikeSessionParams, + childSessionParams, + mExecutor, + new VpnIkev2Utils.IkeSessionCallbackImpl( + TAG, IkeV2VpnRunner.this, network), + new VpnIkev2Utils.ChildSessionCallbackImpl( + TAG, IkeV2VpnRunner.this, network)); + Log.d(TAG, "Ike Session started for network " + network); + } catch (Exception e) { + Log.i(TAG, "Setup failed for network " + network + ". Aborting", e); + onSessionLost(network); + } + }); + } + + /** + * Handles loss of a session + * + * <p>The loss of a session might be due to an onLost() call, the IKE session getting torn + * down for any reason, or an error in updating state (transform application, VPN setup) + * + * <p>This method MUST always be called on the mExecutor thread in order to ensure + * consistency of the Ikev2VpnRunner fields. + */ + public void onSessionLost(@NonNull Network network) { + if (!isActiveNetwork(network)) { + Log.d(TAG, "onSessionLost() called for obsolete network " + network); + + // Do nothing; this signals that either: (1) a new/better Network was found, + // and the Ikev2VpnRunner has switched to it in onDefaultNetworkChanged, or (2) this + // IKE session was already shut down (exited, or an error was encountered somewhere + // else). In both cases, all resources and sessions are torn down via + // onSessionLost() and resetIkeState(). + return; + } + + mActiveNetwork = null; + + // Close all obsolete state, but keep VPN alive incase a usable network comes up. + // (Mirrors VpnService behavior) + Log.d(TAG, "Resetting state for network: " + network); + + synchronized (Vpn.this) { + // Since this method handles non-fatal errors only, set mInterface to null to + // prevent the NetworkManagementEventObserver from killing this VPN based on the + // interface going down (which we expect). + mInterface = null; + mConfig.interfaze = null; + + // Set as unroutable to prevent traffic leaking while the interface is down. + if (mConfig != null && mConfig.routes != null) { + final List<RouteInfo> oldRoutes = new ArrayList<>(mConfig.routes); + + mConfig.routes.clear(); + for (final RouteInfo route : oldRoutes) { + mConfig.routes.add(new RouteInfo(route.getDestination(), RTN_UNREACHABLE)); + } + if (mNetworkAgent != null) { + mNetworkAgent.sendLinkProperties(makeLinkProperties()); + } + } + } + + resetIkeState(); + } + + /** + * Cleans up all IKE state + * + * <p>This method MUST always be called on the mExecutor thread in order to ensure + * consistency of the Ikev2VpnRunner fields. + */ + private void resetIkeState() { + if (mTunnelIface != null) { + // No need to call setInterfaceDown(); the IpSecInterface is being fully torn down. + mTunnelIface.close(); + mTunnelIface = null; + } + if (mSession != null) { + mSession.kill(); // Kill here to make sure all resources are released immediately + mSession = null; + } + + // TODO(b/149356682): Update this based on new IKE API + if (mEncapSocket != null) { + try { + mEncapSocket.close(); + } catch (IOException e) { + Log.e(TAG, "Failed to close encap socket", e); + } + mEncapSocket = null; + } + } + + /** + * Triggers cleanup of outer class' state + * + * <p>Can be called from any thread, as it does not mutate state in the Ikev2VpnRunner. + */ + private void cleanupVpnState() { + synchronized (Vpn.this) { + agentDisconnect(); + } + } + + /** + * Cleans up all Ikev2VpnRunner internal state + * + * <p>This method MUST always be called on the mExecutor thread in order to ensure + * consistency of the Ikev2VpnRunner fields. + */ + private void shutdownVpnRunner() { + mActiveNetwork = null; + mIsRunning = false; + + resetIkeState(); + + final ConnectivityManager cm = ConnectivityManager.from(mContext); + cm.unregisterNetworkCallback(mNetworkCallback); + + mExecutor.shutdown(); } @Override public void exit() { - // TODO: Teardown IKE session & any resources. - agentDisconnect(); + // Cleanup outer class' state immediately, otherwise race conditions may ensue. + cleanupVpnState(); + + mExecutor.execute(() -> { + shutdownVpnRunner(); + }); } } @@ -2488,12 +2850,46 @@ public class Vpn { throw new IllegalArgumentException("No profile found for " + packageName); } - startVpnProfilePrivileged(profile); + startVpnProfilePrivileged(profile, packageName); }); } - private void startVpnProfilePrivileged(@NonNull VpnProfile profile) { - // TODO: Start PlatformVpnRunner + private void startVpnProfilePrivileged( + @NonNull VpnProfile profile, @NonNull String packageName) { + // Ensure that no other previous instance is running. + if (mVpnRunner != null) { + mVpnRunner.exit(); + mVpnRunner = null; + } + updateState(DetailedState.CONNECTING, "startPlatformVpn"); + + try { + // Build basic config + mConfig = new VpnConfig(); + mConfig.user = packageName; + mConfig.isMetered = profile.isMetered; + mConfig.startTime = SystemClock.elapsedRealtime(); + mConfig.proxyInfo = profile.proxy; + + switch (profile.type) { + case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS: + case VpnProfile.TYPE_IKEV2_IPSEC_PSK: + case VpnProfile.TYPE_IKEV2_IPSEC_RSA: + mVpnRunner = new IkeV2VpnRunner(Ikev2VpnProfile.fromVpnProfile(profile)); + mVpnRunner.start(); + break; + default: + updateState(DetailedState.FAILED, "Invalid platform VPN type"); + Log.d(TAG, "Unknown VPN profile type: " + profile.type); + break; + } + } catch (IOException | GeneralSecurityException e) { + // Reset mConfig + mConfig = null; + + updateState(DetailedState.FAILED, "VPN startup failed"); + throw new IllegalArgumentException("VPN startup failed", e); + } } /** @@ -2507,13 +2903,37 @@ public class Vpn { public synchronized void stopVpnProfile(@NonNull String packageName) { checkNotNull(packageName, "No package name provided"); - // To stop the VPN profile, the caller must be the current prepared package. Otherwise, - // the app is not prepared, and we can just return. - if (!isCurrentPreparedPackage(packageName)) { - // TODO: Also check to make sure that the running VPN is a VPN profile. + // To stop the VPN profile, the caller must be the current prepared package and must be + // running an Ikev2VpnProfile. + if (!isCurrentPreparedPackage(packageName) && mVpnRunner instanceof IkeV2VpnRunner) { return; } prepareInternal(VpnConfig.LEGACY_VPN); } + + /** + * Proxy to allow testing + * + * @hide + */ + @VisibleForTesting + public static class Ikev2SessionCreator { + /** Creates a IKE session */ + public IkeSession createIkeSession( + @NonNull Context context, + @NonNull IkeSessionParams ikeSessionParams, + @NonNull ChildSessionParams firstChildSessionParams, + @NonNull Executor userCbExecutor, + @NonNull IkeSessionCallback ikeSessionCallback, + @NonNull ChildSessionCallback firstChildSessionCallback) { + return new IkeSession( + context, + ikeSessionParams, + firstChildSessionParams, + userCbExecutor, + ikeSessionCallback, + firstChildSessionCallback); + } + } } diff --git a/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java new file mode 100644 index 000000000000..33fc32b78df7 --- /dev/null +++ b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java @@ -0,0 +1,390 @@ +/* + * 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; + +import static android.net.ConnectivityManager.NetworkCallback; +import static android.net.ipsec.ike.SaProposal.DH_GROUP_1024_BIT_MODP; +import static android.net.ipsec.ike.SaProposal.DH_GROUP_2048_BIT_MODP; +import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_CBC; +import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12; +import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16; +import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8; +import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96; +import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96; +import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128; +import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192; +import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256; +import static android.net.ipsec.ike.SaProposal.KEY_LEN_AES_128; +import static android.net.ipsec.ike.SaProposal.KEY_LEN_AES_192; +import static android.net.ipsec.ike.SaProposal.KEY_LEN_AES_256; +import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC; +import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1; + +import android.annotation.NonNull; +import android.net.Ikev2VpnProfile; +import android.net.InetAddresses; +import android.net.IpPrefix; +import android.net.IpSecManager.UdpEncapsulationSocket; +import android.net.IpSecTransform; +import android.net.Network; +import android.net.RouteInfo; +import android.net.eap.EapSessionConfig; +import android.net.ipsec.ike.ChildSaProposal; +import android.net.ipsec.ike.ChildSessionCallback; +import android.net.ipsec.ike.ChildSessionConfiguration; +import android.net.ipsec.ike.ChildSessionParams; +import android.net.ipsec.ike.IkeFqdnIdentification; +import android.net.ipsec.ike.IkeIdentification; +import android.net.ipsec.ike.IkeIpv4AddrIdentification; +import android.net.ipsec.ike.IkeIpv6AddrIdentification; +import android.net.ipsec.ike.IkeKeyIdIdentification; +import android.net.ipsec.ike.IkeRfc822AddrIdentification; +import android.net.ipsec.ike.IkeSaProposal; +import android.net.ipsec.ike.IkeSessionCallback; +import android.net.ipsec.ike.IkeSessionConfiguration; +import android.net.ipsec.ike.IkeSessionParams; +import android.net.ipsec.ike.IkeTrafficSelector; +import android.net.ipsec.ike.TunnelModeChildSessionParams; +import android.net.ipsec.ike.exceptions.IkeException; +import android.net.ipsec.ike.exceptions.IkeProtocolException; +import android.net.util.IpRange; +import android.system.OsConstants; +import android.util.Log; + +import com.android.internal.net.VpnProfile; +import com.android.internal.util.HexDump; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; + +/** + * Utility class to build and convert IKEv2/IPsec parameters. + * + * @hide + */ +public class VpnIkev2Utils { + static IkeSessionParams buildIkeSessionParams( + @NonNull Ikev2VpnProfile profile, @NonNull UdpEncapsulationSocket socket) { + // TODO(b/149356682): Update this based on new IKE API. Only numeric addresses supported + // until then. All others throw IAE (caught by caller). + final InetAddress serverAddr = InetAddresses.parseNumericAddress(profile.getServerAddr()); + final IkeIdentification localId = parseIkeIdentification(profile.getUserIdentity()); + final IkeIdentification remoteId = parseIkeIdentification(profile.getServerAddr()); + + // TODO(b/149356682): Update this based on new IKE API. + final IkeSessionParams.Builder ikeOptionsBuilder = + new IkeSessionParams.Builder() + .setServerAddress(serverAddr) + .setUdpEncapsulationSocket(socket) + .setLocalIdentification(localId) + .setRemoteIdentification(remoteId); + setIkeAuth(profile, ikeOptionsBuilder); + + for (final IkeSaProposal ikeProposal : getIkeSaProposals()) { + ikeOptionsBuilder.addSaProposal(ikeProposal); + } + + return ikeOptionsBuilder.build(); + } + + static ChildSessionParams buildChildSessionParams() { + final TunnelModeChildSessionParams.Builder childOptionsBuilder = + new TunnelModeChildSessionParams.Builder(); + + for (final ChildSaProposal childProposal : getChildSaProposals()) { + childOptionsBuilder.addSaProposal(childProposal); + } + + childOptionsBuilder.addInternalAddressRequest(OsConstants.AF_INET); + childOptionsBuilder.addInternalAddressRequest(OsConstants.AF_INET6); + childOptionsBuilder.addInternalDnsServerRequest(OsConstants.AF_INET); + childOptionsBuilder.addInternalDnsServerRequest(OsConstants.AF_INET6); + + return childOptionsBuilder.build(); + } + + private static void setIkeAuth( + @NonNull Ikev2VpnProfile profile, @NonNull IkeSessionParams.Builder builder) { + switch (profile.getType()) { + case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS: + final EapSessionConfig eapConfig = + new EapSessionConfig.Builder() + .setEapMsChapV2Config(profile.getUsername(), profile.getPassword()) + .build(); + builder.setAuthEap(profile.getServerRootCaCert(), eapConfig); + break; + case VpnProfile.TYPE_IKEV2_IPSEC_PSK: + builder.setAuthPsk(profile.getPresharedKey()); + break; + case VpnProfile.TYPE_IKEV2_IPSEC_RSA: + builder.setAuthDigitalSignature( + profile.getServerRootCaCert(), + profile.getUserCert(), + profile.getRsaPrivateKey()); + break; + default: + throw new IllegalArgumentException("Unknown auth method set"); + } + } + + private static List<IkeSaProposal> getIkeSaProposals() { + // TODO: filter this based on allowedAlgorithms + final List<IkeSaProposal> proposals = new ArrayList<>(); + + // Encryption Algorithms: Currently only AES_CBC is supported. + final IkeSaProposal.Builder normalModeBuilder = new IkeSaProposal.Builder(); + + // Currently only AES_CBC is supported. + normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_256); + normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_192); + normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_128); + + // Authentication/Integrity Algorithms + normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_512_256); + normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_384_192); + normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128); + normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_AES_XCBC_96); + normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA1_96); + + // Add AEAD options + final IkeSaProposal.Builder aeadBuilder = new IkeSaProposal.Builder(); + aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_256); + aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_256); + aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_256); + aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_192); + aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_192); + aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_192); + aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_128); + aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_128); + aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_128); + + // Add dh, prf for both builders + for (final IkeSaProposal.Builder builder : Arrays.asList(normalModeBuilder, aeadBuilder)) { + builder.addDhGroup(DH_GROUP_2048_BIT_MODP); + builder.addDhGroup(DH_GROUP_1024_BIT_MODP); + builder.addPseudorandomFunction(PSEUDORANDOM_FUNCTION_AES128_XCBC); + builder.addPseudorandomFunction(PSEUDORANDOM_FUNCTION_HMAC_SHA1); + } + + proposals.add(normalModeBuilder.build()); + proposals.add(aeadBuilder.build()); + return proposals; + } + + private static List<ChildSaProposal> getChildSaProposals() { + // TODO: filter this based on allowedAlgorithms + final List<ChildSaProposal> proposals = new ArrayList<>(); + + // Add non-AEAD options + final ChildSaProposal.Builder normalModeBuilder = new ChildSaProposal.Builder(); + + // Encryption Algorithms: Currently only AES_CBC is supported. + normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_256); + normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_192); + normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_128); + + // Authentication/Integrity Algorithms + normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_512_256); + normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_384_192); + normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128); + normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA1_96); + + // Add AEAD options + final ChildSaProposal.Builder aeadBuilder = new ChildSaProposal.Builder(); + aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_256); + aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_256); + aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_256); + aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_192); + aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_192); + aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_192); + aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_128); + aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_128); + aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_128); + + proposals.add(normalModeBuilder.build()); + proposals.add(aeadBuilder.build()); + return proposals; + } + + static class IkeSessionCallbackImpl implements IkeSessionCallback { + private final String mTag; + private final Vpn.IkeV2VpnRunnerCallback mCallback; + private final Network mNetwork; + + IkeSessionCallbackImpl(String tag, Vpn.IkeV2VpnRunnerCallback callback, Network network) { + mTag = tag; + mCallback = callback; + mNetwork = network; + } + + @Override + public void onOpened(@NonNull IkeSessionConfiguration ikeSessionConfig) { + Log.d(mTag, "IkeOpened for network " + mNetwork); + // Nothing to do here. + } + + @Override + public void onClosed() { + Log.d(mTag, "IkeClosed for network " + mNetwork); + mCallback.onSessionLost(mNetwork); // Server requested session closure. Retry? + } + + @Override + public void onClosedExceptionally(@NonNull IkeException exception) { + Log.d(mTag, "IkeClosedExceptionally for network " + mNetwork, exception); + mCallback.onSessionLost(mNetwork); + } + + @Override + public void onError(@NonNull IkeProtocolException exception) { + Log.d(mTag, "IkeError for network " + mNetwork, exception); + // Non-fatal, log and continue. + } + } + + static class ChildSessionCallbackImpl implements ChildSessionCallback { + private final String mTag; + private final Vpn.IkeV2VpnRunnerCallback mCallback; + private final Network mNetwork; + + ChildSessionCallbackImpl(String tag, Vpn.IkeV2VpnRunnerCallback callback, Network network) { + mTag = tag; + mCallback = callback; + mNetwork = network; + } + + @Override + public void onOpened(@NonNull ChildSessionConfiguration childConfig) { + Log.d(mTag, "ChildOpened for network " + mNetwork); + mCallback.onChildOpened(mNetwork, childConfig); + } + + @Override + public void onClosed() { + Log.d(mTag, "ChildClosed for network " + mNetwork); + mCallback.onSessionLost(mNetwork); + } + + @Override + public void onClosedExceptionally(@NonNull IkeException exception) { + Log.d(mTag, "ChildClosedExceptionally for network " + mNetwork, exception); + mCallback.onSessionLost(mNetwork); + } + + @Override + public void onIpSecTransformCreated(@NonNull IpSecTransform transform, int direction) { + Log.d(mTag, "ChildTransformCreated; Direction: " + direction + "; network " + mNetwork); + mCallback.onChildTransformCreated(mNetwork, transform, direction); + } + + @Override + public void onIpSecTransformDeleted(@NonNull IpSecTransform transform, int direction) { + // Nothing to be done; no references to the IpSecTransform are held by the + // Ikev2VpnRunner (or this callback class), and this transform will be closed by the + // IKE library. + Log.d(mTag, + "ChildTransformDeleted; Direction: " + direction + "; for network " + mNetwork); + } + } + + static class Ikev2VpnNetworkCallback extends NetworkCallback { + private final String mTag; + private final Vpn.IkeV2VpnRunnerCallback mCallback; + + Ikev2VpnNetworkCallback(String tag, Vpn.IkeV2VpnRunnerCallback callback) { + mTag = tag; + mCallback = callback; + } + + @Override + public void onAvailable(@NonNull Network network) { + Log.d(mTag, "Starting IKEv2/IPsec session on new network: " + network); + mCallback.onDefaultNetworkChanged(network); + } + + @Override + public void onLost(@NonNull Network network) { + Log.d(mTag, "Tearing down; lost network: " + network); + mCallback.onSessionLost(network); + } + } + + /** + * Identity parsing logic using similar logic to open source implementations of IKEv2 + * + * <p>This method does NOT support using type-prefixes (eg 'fqdn:' or 'keyid'), or ASN.1 encoded + * identities. + */ + private static IkeIdentification parseIkeIdentification(@NonNull String identityStr) { + // TODO: Add identity formatting to public API javadocs. + if (identityStr.contains("@")) { + if (identityStr.startsWith("@#")) { + // KEY_ID + final String hexStr = identityStr.substring(2); + return new IkeKeyIdIdentification(HexDump.hexStringToByteArray(hexStr)); + } else if (identityStr.startsWith("@@")) { + // RFC822 (USER_FQDN) + return new IkeRfc822AddrIdentification(identityStr.substring(2)); + } else if (identityStr.startsWith("@")) { + // FQDN + return new IkeFqdnIdentification(identityStr.substring(1)); + } else { + // RFC822 (USER_FQDN) + return new IkeRfc822AddrIdentification(identityStr); + } + } else if (InetAddresses.isNumericAddress(identityStr)) { + final InetAddress addr = InetAddresses.parseNumericAddress(identityStr); + if (addr instanceof Inet4Address) { + // IPv4 + return new IkeIpv4AddrIdentification((Inet4Address) addr); + } else if (addr instanceof Inet6Address) { + // IPv6 + return new IkeIpv6AddrIdentification((Inet6Address) addr); + } else { + throw new IllegalArgumentException("IP version not supported"); + } + } else { + if (identityStr.contains(":")) { + // KEY_ID + return new IkeKeyIdIdentification(identityStr.getBytes()); + } else { + // FQDN + return new IkeFqdnIdentification(identityStr); + } + } + } + + static Collection<RouteInfo> getRoutesFromTrafficSelectors( + List<IkeTrafficSelector> trafficSelectors) { + final HashSet<RouteInfo> routes = new HashSet<>(); + + for (final IkeTrafficSelector selector : trafficSelectors) { + for (final IpPrefix prefix : + new IpRange(selector.startingAddress, selector.endingAddress).asIpPrefixes()) { + routes.add(new RouteInfo(prefix, null)); + } + } + + return routes; + } +} diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 7c2ec78c1cbc..d9e30250ba2d 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -105,32 +105,57 @@ import java.util.stream.Collectors; */ public class HdmiControlService extends SystemService { private static final String TAG = "HdmiControlService"; - private final Locale HONG_KONG = new Locale("zh", "HK"); - private final Locale MACAU = new Locale("zh", "MO"); + private static final Locale HONG_KONG = new Locale("zh", "HK"); + private static final Locale MACAU = new Locale("zh", "MO"); - private static final Map<String, String> mTerminologyToBibliographicMap; - static { - mTerminologyToBibliographicMap = new HashMap<>(); + private static final Map<String, String> sTerminologyToBibliographicMap = + createsTerminologyToBibliographicMap(); + + private static Map<String, String> createsTerminologyToBibliographicMap() { + Map<String, String> temp = new HashMap<>(); // NOTE: (TERMINOLOGY_CODE, BIBLIOGRAPHIC_CODE) - mTerminologyToBibliographicMap.put("sqi", "alb"); // Albanian - mTerminologyToBibliographicMap.put("hye", "arm"); // Armenian - mTerminologyToBibliographicMap.put("eus", "baq"); // Basque - mTerminologyToBibliographicMap.put("mya", "bur"); // Burmese - mTerminologyToBibliographicMap.put("ces", "cze"); // Czech - mTerminologyToBibliographicMap.put("nld", "dut"); // Dutch - mTerminologyToBibliographicMap.put("kat", "geo"); // Georgian - mTerminologyToBibliographicMap.put("deu", "ger"); // German - mTerminologyToBibliographicMap.put("ell", "gre"); // Greek - mTerminologyToBibliographicMap.put("fra", "fre"); // French - mTerminologyToBibliographicMap.put("isl", "ice"); // Icelandic - mTerminologyToBibliographicMap.put("mkd", "mac"); // Macedonian - mTerminologyToBibliographicMap.put("mri", "mao"); // Maori - mTerminologyToBibliographicMap.put("msa", "may"); // Malay - mTerminologyToBibliographicMap.put("fas", "per"); // Persian - mTerminologyToBibliographicMap.put("ron", "rum"); // Romanian - mTerminologyToBibliographicMap.put("slk", "slo"); // Slovak - mTerminologyToBibliographicMap.put("bod", "tib"); // Tibetan - mTerminologyToBibliographicMap.put("cym", "wel"); // Welsh + temp.put("sqi", "alb"); // Albanian + temp.put("hye", "arm"); // Armenian + temp.put("eus", "baq"); // Basque + temp.put("mya", "bur"); // Burmese + temp.put("ces", "cze"); // Czech + temp.put("nld", "dut"); // Dutch + temp.put("kat", "geo"); // Georgian + temp.put("deu", "ger"); // German + temp.put("ell", "gre"); // Greek + temp.put("fra", "fre"); // French + temp.put("isl", "ice"); // Icelandic + temp.put("mkd", "mac"); // Macedonian + temp.put("mri", "mao"); // Maori + temp.put("msa", "may"); // Malay + temp.put("fas", "per"); // Persian + temp.put("ron", "rum"); // Romanian + temp.put("slk", "slo"); // Slovak + temp.put("bod", "tib"); // Tibetan + temp.put("cym", "wel"); // Welsh + return Collections.unmodifiableMap(temp); + } + + @VisibleForTesting static String localeToMenuLanguage(Locale locale) { + if (locale.equals(Locale.TAIWAN) || locale.equals(HONG_KONG) || locale.equals(MACAU)) { + // Android always returns "zho" for all Chinese variants. + // Use "bibliographic" code defined in CEC639-2 for traditional + // Chinese used in Taiwan/Hong Kong/Macau. + return "chi"; + } else { + String language = locale.getISO3Language(); + + // locale.getISO3Language() returns terminology code and need to + // send it as bibliographic code instead since the Bibliographic + // codes of ISO/FDIS 639-2 shall be used. + // NOTE: Chinese also has terminology/bibliographic code "zho" and "chi" + // But, as it depends on the locale, is not handled here. + if (sTerminologyToBibliographicMap.containsKey(language)) { + language = sTerminologyToBibliographicMap.get(language); + } + + return language; + } } static final String PERMISSION = "android.permission.HDMI_CEC"; @@ -208,8 +233,8 @@ public class HdmiControlService extends SystemService { } break; case Intent.ACTION_CONFIGURATION_CHANGED: - String language = getMenuLanguage(); - if (!mLanguage.equals(language)) { + String language = HdmiControlService.localeToMenuLanguage(Locale.getDefault()); + if (!mMenuLanguage.equals(language)) { onLanguageChanged(language); } break; @@ -221,28 +246,6 @@ public class HdmiControlService extends SystemService { } } - private String getMenuLanguage() { - Locale locale = Locale.getDefault(); - if (locale.equals(Locale.TAIWAN) || locale.equals(HONG_KONG) || locale.equals(MACAU)) { - // Android always returns "zho" for all Chinese variants. - // Use "bibliographic" code defined in CEC639-2 for traditional - // Chinese used in Taiwan/Hong Kong/Macau. - return "chi"; - } else { - String language = locale.getISO3Language(); - - // locale.getISO3Language() returns terminology code and need to - // send it as bibliographic code instead since the Bibliographic - // codes of ISO/FDIS 639-2 shall be used. - // NOTE: Chinese also has terminology/bibliographic code "zho" and "chi" - // But, as it depends on the locale, is not handled here. - if (mTerminologyToBibliographicMap.containsKey(language)) { - language = mTerminologyToBibliographicMap.get(language); - } - - return language; - } - } } // A thread to handle synchronous IO of CEC and MHL control service. @@ -339,7 +342,7 @@ public class HdmiControlService extends SystemService { private int mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY; @ServiceThreadOnly - private String mLanguage = Locale.getDefault().getISO3Language(); + private String mMenuLanguage = localeToMenuLanguage(Locale.getDefault()); @ServiceThreadOnly private boolean mStandbyMessageReceived = false; @@ -759,7 +762,7 @@ public class HdmiControlService extends SystemService { private void initializeCec(int initiatedBy) { mAddressAllocated = false; mCecController.setOption(OptionKey.SYSTEM_CEC_CONTROL, true); - mCecController.setLanguage(mLanguage); + mCecController.setLanguage(mMenuLanguage); initializeLocalDevices(initiatedBy); } @@ -2818,7 +2821,7 @@ public class HdmiControlService extends SystemService { @ServiceThreadOnly private void onLanguageChanged(String language) { assertRunOnServiceThread(); - mLanguage = language; + mMenuLanguage = language; if (isTvDeviceEnabled()) { tv().broadcastMenuLanguage(language); @@ -2826,10 +2829,19 @@ public class HdmiControlService extends SystemService { } } + /** + * Gets the CEC menu language. + * + * <p>This is the ISO/FDIS 639-2 3 letter language code sent in the CEC message @{code <Set Menu + * Language>}. + * See HDMI 1.4b section CEC 13.6.2 + * + * @see {@link Locale#getISO3Language()} + */ @ServiceThreadOnly String getLanguage() { assertRunOnServiceThread(); - return mLanguage; + return mMenuLanguage; } private void disableDevices(PendingActionClearedCallback callback) { @@ -2838,7 +2850,6 @@ public class HdmiControlService extends SystemService { device.disableDevice(mStandbyMessageReceived, callback); } } - mMhlController.clearAllLocalDevices(); } diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java index 68ced79b3e38..b9a30bb21edb 100644 --- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java +++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java @@ -23,6 +23,7 @@ import static android.content.Intent.EXTRA_PACKAGE_NAME; import static android.content.integrity.AppIntegrityManager.EXTRA_STATUS; import static android.content.integrity.AppIntegrityManager.STATUS_FAILURE; import static android.content.integrity.AppIntegrityManager.STATUS_SUCCESS; +import static android.content.integrity.InstallerAllowedByManifestFormula.INSTALLER_CERTIFICATE_NOT_EVALUATED; import static android.content.integrity.IntegrityUtils.getHexDigest; import static android.content.pm.PackageManager.EXTRA_VERIFICATION_ID; @@ -95,7 +96,7 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { * This string will be used as the "installer" for formula evaluation when the app is being * installed via ADB. */ - private static final String ADB_INSTALLER = "adb"; + public static final String ADB_INSTALLER = "adb"; private static final String TAG = "AppIntegrityManagerServiceImpl"; @@ -106,8 +107,6 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { private static final String ALLOWED_INSTALLER_DELIMITER = ","; private static final String INSTALLER_PACKAGE_CERT_DELIMITER = "\\|"; - private static final String INSTALLER_CERT_NOT_APPLICABLE = ""; - // Access to files inside mRulesDir is protected by mRulesLock; private final Context mContext; private final Handler mHandler; @@ -282,15 +281,16 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { builder.setInstallerName(getPackageNameNormalized(installerPackageName)); builder.setInstallerCertificates(installerCertificates); builder.setIsPreInstalled(isSystemApp(packageName)); + builder.setAllowedInstallersAndCert(getAllowedInstallers(packageInfo)); AppInstallMetadata appInstallMetadata = builder.build(); - Map<String, String> allowedInstallers = getAllowedInstallers(packageInfo); Slog.i( TAG, - "To be verified: " + appInstallMetadata + " installers " + allowedInstallers); + "To be verified: " + appInstallMetadata + " installers " + getAllowedInstallers( + packageInfo)); IntegrityCheckResult result = - mEvaluationEngine.evaluate(appInstallMetadata, allowedInstallers); + mEvaluationEngine.evaluate(appInstallMetadata); Slog.i( TAG, "Integrity check result: " @@ -449,9 +449,9 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { String packageName = getPackageNameNormalized(packageAndCert[0]); String cert = packageAndCert[1]; packageCertMap.put(packageName, cert); - } else if (packageAndCert.length == 1 - && packageAndCert[0].equals(ADB_INSTALLER)) { - packageCertMap.put(ADB_INSTALLER, INSTALLER_CERT_NOT_APPLICABLE); + } else if (packageAndCert.length == 1) { + packageCertMap.put(getPackageNameNormalized(packageAndCert[0]), + INSTALLER_CERTIFICATE_NOT_EVALUATED); } } } diff --git a/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java b/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java index 79e69e15ff67..61da45ddbfef 100644 --- a/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java +++ b/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java @@ -17,9 +17,6 @@ package com.android.server.integrity.engine; import android.content.integrity.AppInstallMetadata; -import android.content.integrity.AtomicFormula; -import android.content.integrity.CompoundFormula; -import android.content.integrity.IntegrityFormula; import android.content.integrity.Rule; import android.util.Slog; @@ -28,10 +25,8 @@ import com.android.server.integrity.IntegrityFileManager; import com.android.server.integrity.model.IntegrityCheckResult; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Collections; import java.util.List; -import java.util.Map; -import java.util.Optional; /** * The engine used to evaluate rules against app installs. @@ -69,16 +64,15 @@ public class RuleEvaluationEngine { * @return result of the integrity check */ public IntegrityCheckResult evaluate( - AppInstallMetadata appInstallMetadata, Map<String, String> allowedInstallers) { + AppInstallMetadata appInstallMetadata) { List<Rule> rules = loadRules(appInstallMetadata); - allowedInstallersRule(allowedInstallers).ifPresent(rules::add); return RuleEvaluator.evaluateRules(rules, appInstallMetadata); } private List<Rule> loadRules(AppInstallMetadata appInstallMetadata) { if (!mIntegrityFileManager.initialized()) { - Slog.w(TAG, "Integrity rule files are not available. Evaluating only manifest rules."); - return new ArrayList<>(); + Slog.w(TAG, "Integrity rule files are not available."); + return Collections.emptyList(); } try { @@ -88,41 +82,4 @@ public class RuleEvaluationEngine { return new ArrayList<>(); } } - - private static Optional<Rule> allowedInstallersRule(Map<String, String> allowedInstallers) { - if (allowedInstallers.isEmpty()) { - return Optional.empty(); - } - - List<IntegrityFormula> formulas = new ArrayList<>(allowedInstallers.size()); - allowedInstallers.forEach( - (installer, cert) -> { - formulas.add(allowedInstallerFormula(installer, cert)); - }); - - // We need this special case since OR-formulas require at least two operands. - IntegrityFormula allInstallersFormula = - formulas.size() == 1 - ? formulas.get(0) - : new CompoundFormula(CompoundFormula.OR, formulas); - - return Optional.of( - new Rule( - new CompoundFormula( - CompoundFormula.NOT, Arrays.asList(allInstallersFormula)), - Rule.DENY)); - } - - private static IntegrityFormula allowedInstallerFormula(String installer, String cert) { - return new CompoundFormula( - CompoundFormula.AND, - Arrays.asList( - new AtomicFormula.StringAtomicFormula( - AtomicFormula.INSTALLER_NAME, - installer, - /* isHashedValue= */ false), - new AtomicFormula.StringAtomicFormula( - AtomicFormula.INSTALLER_CERTIFICATE, cert, /* isHashedValue= */ - false))); - } } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 916b63bee0e8..d8cab82cfb06 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -932,7 +932,7 @@ public class NotificationManagerService extends SystemService { StatusBarNotification sbn = r.getSbn(); cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(), sbn.getId(), Notification.FLAG_AUTO_CANCEL, - FLAG_FOREGROUND_SERVICE, false, r.getUserId(), + FLAG_FOREGROUND_SERVICE | FLAG_BUBBLE, false, r.getUserId(), REASON_CLICK, nv.rank, nv.count, null); nv.recycle(); reportUserInteraction(r); @@ -1055,7 +1055,7 @@ public class NotificationManagerService extends SystemService { if (DBG) Slog.d(TAG, "Marking notification as visible " + nv.key); reportSeen(r); } - r.setVisibility(true, nv.rank, nv.count); + r.setVisibility(true, nv.rank, nv.count, mNotificationRecordLogger); mAssistants.notifyAssistantVisibilityChangedLocked(r.getSbn(), true); boolean isHun = (nv.location == NotificationVisibility.NotificationLocation.LOCATION_FIRST_HEADS_UP); @@ -1074,7 +1074,7 @@ public class NotificationManagerService extends SystemService { for (NotificationVisibility nv : noLongerVisibleKeys) { NotificationRecord r = mNotificationsByKey.get(nv.key); if (r == null) continue; - r.setVisibility(false, nv.rank, nv.count); + r.setVisibility(false, nv.rank, nv.count, mNotificationRecordLogger); mAssistants.notifyAssistantVisibilityChangedLocked(r.getSbn(), false); nv.recycle(); } @@ -6468,7 +6468,7 @@ public class NotificationManagerService extends SystemService { mUsageStats.registerPostedByApp(r); r.setInterruptive(isVisuallyInterruptive(null, r)); } else { - old = mNotificationList.get(index); + old = mNotificationList.get(index); // Potentially *changes* old mNotificationList.set(index, r); mUsageStats.registerUpdatedByApp(r, old); // Make sure we don't lose the foreground service state. @@ -6537,7 +6537,7 @@ public class NotificationManagerService extends SystemService { maybeRecordInterruptionLocked(r); // Log event to statsd - mNotificationRecordLogger.logNotificationReported(r, old, position, + mNotificationRecordLogger.maybeLogNotificationPosted(r, old, position, buzzBeepBlinkLoggingCode); } finally { int N = mEnqueuedNotifications.size(); @@ -7986,7 +7986,8 @@ public class NotificationManagerService extends SystemService { FlagChecker flagChecker = (int flags) -> { int flagsToCheck = FLAG_ONGOING_EVENT | FLAG_NO_CLEAR; - if (REASON_LISTENER_CANCEL_ALL == reason) { + if (REASON_LISTENER_CANCEL_ALL == reason + || REASON_CANCEL_ALL == reason) { flagsToCheck |= FLAG_BUBBLE; } if ((flags & flagsToCheck) != 0) { diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index 4785da9a5922..0ada58e1ce16 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -909,7 +909,8 @@ public final class NotificationRecord { /** * Set the visibility of the notification. */ - public void setVisibility(boolean visible, int rank, int count) { + public void setVisibility(boolean visible, int rank, int count, + NotificationRecordLogger notificationRecordLogger) { final long now = System.currentTimeMillis(); mVisibleSinceMs = visible ? now : mVisibleSinceMs; stats.onVisibilityChanged(visible); @@ -927,6 +928,7 @@ public final class NotificationRecord { getFreshnessMs(now), 0, // exposure time rank); + notificationRecordLogger.logNotificationVisibility(this, visible); } /** diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java index 2f7854226c5c..eaca066f026f 100644 --- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java +++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java @@ -41,13 +41,14 @@ import java.util.Objects; public interface NotificationRecordLogger { /** - * Logs a NotificationReported atom reflecting the posting or update of a notification. + * May log a NotificationReported atom reflecting the posting or update of a notification. * @param r The new NotificationRecord. If null, no action is taken. * @param old The previous NotificationRecord. Null if there was no previous record. * @param position The position at which this notification is ranked. * @param buzzBeepBlink Logging code reflecting whether this notification alerted the user. */ - void logNotificationReported(@Nullable NotificationRecord r, @Nullable NotificationRecord old, + void maybeLogNotificationPosted(@Nullable NotificationRecord r, + @Nullable NotificationRecord old, int position, int buzzBeepBlink); /** @@ -62,6 +63,14 @@ public interface NotificationRecordLogger { @NotificationStats.DismissalSurface int dismissalSurface); /** + * Logs a notification visibility change event using UiEventReported (event ids from the + * NotificationEvents enum). + * @param r The NotificationRecord. If null, no action is taken. + * @param visible True if the notification became visible. + */ + void logNotificationVisibility(@Nullable NotificationRecord r, boolean visible); + + /** * The UiEvent enums that this class can log. */ enum NotificationReportedEvent implements UiEventLogger.UiEventEnum { @@ -145,6 +154,7 @@ public interface NotificationRecordLogger { @Override public int getId() { return mId; } + public static NotificationCancelledEvent fromCancelReason( @NotificationListenerService.NotificationCancelReason int reason, @NotificationStats.DismissalSurface int surface) { @@ -191,6 +201,24 @@ public interface NotificationRecordLogger { } } + enum NotificationEvent implements UiEventLogger.UiEventEnum { + @UiEvent(doc = "Notification became visible.") + NOTIFICATION_OPEN(197), + @UiEvent(doc = "Notification stopped being visible.") + NOTIFICATION_CLOSE(198); + + private final int mId; + NotificationEvent(int id) { + mId = id; + } + @Override public int getId() { + return mId; + } + + public static NotificationEvent fromVisibility(boolean visible) { + return visible ? NOTIFICATION_OPEN : NOTIFICATION_CLOSE; + } + } /** * A helper for extracting logging information from one or two NotificationRecords. */ @@ -209,7 +237,7 @@ public interface NotificationRecordLogger { /** * @return True if old is null, alerted, or important logged fields have changed. */ - boolean shouldLog(int buzzBeepBlink) { + boolean shouldLogReported(int buzzBeepBlink) { if (r == null) { return false; } diff --git a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java index bb23d1e876dc..9fcac257d328 100644 --- a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java +++ b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java @@ -26,13 +26,13 @@ import com.android.internal.util.FrameworkStatsLog; */ public class NotificationRecordLoggerImpl implements NotificationRecordLogger { - UiEventLogger mUiEventLogger = new UiEventLoggerImpl(); + private UiEventLogger mUiEventLogger = new UiEventLoggerImpl(); @Override - public void logNotificationReported(NotificationRecord r, NotificationRecord old, + public void maybeLogNotificationPosted(NotificationRecord r, NotificationRecord old, int position, int buzzBeepBlink) { NotificationRecordPair p = new NotificationRecordPair(r, old); - if (!p.shouldLog(buzzBeepBlink)) { + if (!p.shouldLogReported(buzzBeepBlink)) { return; } FrameworkStatsLog.write(FrameworkStatsLog.NOTIFICATION_REPORTED, @@ -66,8 +66,19 @@ public class NotificationRecordLoggerImpl implements NotificationRecordLogger { @Override public void logNotificationCancelled(NotificationRecord r, int reason, int dismissalSurface) { - mUiEventLogger.logWithInstanceId( - NotificationCancelledEvent.fromCancelReason(reason, dismissalSurface), - r.getUid(), r.getSbn().getPackageName(), r.getSbn().getInstanceId()); + log(NotificationCancelledEvent.fromCancelReason(reason, dismissalSurface), r); + } + + @Override + public void logNotificationVisibility(NotificationRecord r, boolean visible) { + log(NotificationEvent.fromVisibility(visible), r); + } + + void log(UiEventLogger.UiEventEnum event, NotificationRecord r) { + if (r == null) { + return; + } + mUiEventLogger.logWithInstanceId(event, r.getUid(), r.getSbn().getPackageName(), + r.getSbn().getInstanceId()); } } diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java index d629b547992b..0fb889c8da22 100644 --- a/services/core/java/com/android/server/pm/AppsFilter.java +++ b/services/core/java/com/android/server/pm/AppsFilter.java @@ -69,7 +69,7 @@ public class AppsFilter { // Logs all filtering instead of enforcing private static final boolean DEBUG_ALLOW_ALL = false; private static final boolean DEBUG_LOGGING = false; - private static final boolean FEATURE_ENABLED_BY_DEFAULT = false; + private static final boolean FEATURE_ENABLED_BY_DEFAULT = true; /** * This contains a list of app UIDs that are implicitly queryable because another app explicitly diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index b98bb0831b0e..8ad3e9df8bdf 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -611,10 +611,10 @@ public class Installer extends SystemService { /** * Bind mount private volume CE and DE mirror storage. */ - public void onPrivateVolumeMounted(String volumeUuid) throws InstallerException { + public void tryMountDataMirror(String volumeUuid) throws InstallerException { if (!checkBeforeRemote()) return; try { - mInstalld.onPrivateVolumeMounted(volumeUuid); + mInstalld.tryMountDataMirror(volumeUuid); } catch (Exception e) { throw InstallerException.from(e); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 2c85d067a3ca..064fd3f4e0b2 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -4600,7 +4600,7 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mLock) { final AndroidPackage p = mPackages.get(packageName); if (p != null && p.isMatch(flags)) { - PackageSetting ps = getPackageSetting(p.getPackageName()); + PackageSetting ps = getPackageSettingInternal(p.getPackageName(), callingUid); if (shouldFilterApplicationLocked(ps, callingUid, userId)) { return -1; } @@ -5924,7 +5924,10 @@ public class PackageManagerService extends IPackageManager.Stub */ @Override public String[] getPackagesForUid(int uid) { - final int callingUid = Binder.getCallingUid(); + return getPackagesForUidInternal(uid, Binder.getCallingUid()); + } + + private String[] getPackagesForUidInternal(int uid, int callingUid) { final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null; final int userId = UserHandle.getUserId(uid); final int appId = UserHandle.getAppId(uid); @@ -17380,6 +17383,13 @@ public class PackageManagerService extends IPackageManager.Stub @GuardedBy("mLock") private String resolveInternalPackageNameLPr(String packageName, long versionCode) { + final int callingUid = Binder.getCallingUid(); + return resolveInternalPackageNameInternalLocked(packageName, versionCode, + callingUid); + } + + private String resolveInternalPackageNameInternalLocked( + String packageName, long versionCode, int callingUid) { // Handle renamed packages String normalizedPackageName = mSettings.getRenamedPackageLPr(packageName); packageName = normalizedPackageName != null ? normalizedPackageName : packageName; @@ -17393,12 +17403,12 @@ public class PackageManagerService extends IPackageManager.Stub // Figure out which lib versions the caller can see LongSparseLongArray versionsCallerCanSee = null; - final int callingAppId = UserHandle.getAppId(Binder.getCallingUid()); + final int callingAppId = UserHandle.getAppId(callingUid); if (callingAppId != Process.SYSTEM_UID && callingAppId != Process.SHELL_UID && callingAppId != Process.ROOT_UID) { versionsCallerCanSee = new LongSparseLongArray(); String libName = versionedLib.valueAt(0).getName(); - String[] uidPackages = getPackagesForUid(Binder.getCallingUid()); + String[] uidPackages = getPackagesForUidInternal(callingUid, callingUid); if (uidPackages != null) { for (String uidPackage : uidPackages) { PackageSetting ps = mSettings.getPackageLPr(uidPackage); @@ -23003,7 +23013,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public AndroidPackage getPackage(int uid) { synchronized (mLock) { - final String[] packageNames = getPackagesForUid(uid); + final String[] packageNames = getPackagesForUidInternal(uid, Process.SYSTEM_UID); AndroidPackage pkg = null; final int numPackages = packageNames == null ? 0 : packageNames.length; for (int i = 0; pkg == null && i < numPackages; i++) { @@ -24017,9 +24027,13 @@ public class PackageManagerService extends IPackageManager.Stub @Nullable public PackageSetting getPackageSetting(String packageName) { + return getPackageSettingInternal(packageName, Binder.getCallingUid()); + } + + private PackageSetting getPackageSettingInternal(String packageName, int callingUid) { synchronized (mLock) { - packageName = resolveInternalPackageNameLPr( - packageName, PackageManager.VERSION_CODE_HIGHEST); + packageName = resolveInternalPackageNameInternalLocked( + packageName, PackageManager.VERSION_CODE_HIGHEST, callingUid); return mSettings.mPackages.get(packageName); } } diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index 67a22d3e477c..39093aec07c0 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -866,6 +866,10 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { } } + default int getMaxWindowLayer() { + return 35; + } + /** * Return how to Z-order sub-windows in relation to the window they are attached to. * Return positive to have them ordered in front, negative for behind. diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java index b3edc91a4129..0d365b16e228 100644 --- a/services/core/java/com/android/server/wm/DisplayArea.java +++ b/services/core/java/com/android/server/wm/DisplayArea.java @@ -102,6 +102,11 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { } @Override + public String toString() { + return mName + "@" + System.identityHashCode(this); + } + + @Override public final void dumpDebug(ProtoOutputStream proto, long fieldId, int logLevel) { final long token = proto.start(fieldId); super.dumpDebug(proto, WINDOW_CONTAINER, logLevel); diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java index 9e93e1455f2c..0ec0c7b53875 100644 --- a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java @@ -16,8 +16,6 @@ package com.android.server.wm; -import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; - import android.content.res.Resources; import android.text.TextUtils; @@ -43,7 +41,7 @@ public abstract class DisplayAreaPolicy { /** * The Tasks container. Tasks etc. are automatically added to this container. */ - protected final TaskContainers mTaskContainers; + protected final DisplayArea<? extends ActivityStack> mTaskContainers; /** * Construct a new {@link DisplayAreaPolicy} @@ -58,7 +56,8 @@ public abstract class DisplayAreaPolicy { */ protected DisplayAreaPolicy(WindowManagerService wmService, DisplayContent content, DisplayArea.Root root, - DisplayArea<? extends WindowContainer> imeContainer, TaskContainers taskContainers) { + DisplayArea<? extends WindowContainer> imeContainer, + DisplayArea<? extends ActivityStack> taskContainers) { mWmService = wmService; mContent = content; mRoot = root; @@ -83,67 +82,15 @@ public abstract class DisplayAreaPolicy { */ public abstract void addWindow(WindowToken token); - /** - * Default policy that has no special features. - */ - public static class Default extends DisplayAreaPolicy { - - public Default(WindowManagerService wmService, DisplayContent content, - DisplayArea.Root root, + /** Provider for platform-default display area policy. */ + static final class DefaultProvider implements DisplayAreaPolicy.Provider { + @Override + public DisplayAreaPolicy instantiate(WindowManagerService wmService, + DisplayContent content, DisplayArea.Root root, DisplayArea<? extends WindowContainer> imeContainer, TaskContainers taskContainers) { - super(wmService, content, root, imeContainer, taskContainers); - } - - private final DisplayArea.Tokens mBelow = new DisplayArea.Tokens(mWmService, - DisplayArea.Type.BELOW_TASKS, "BelowTasks"); - private final DisplayArea<DisplayArea> mAbove = new DisplayArea<>(mWmService, - DisplayArea.Type.ABOVE_TASKS, "AboveTasks"); - private final DisplayArea.Tokens mAboveBelowIme = new DisplayArea.Tokens(mWmService, - DisplayArea.Type.ABOVE_TASKS, "AboveTasksBelowIme"); - private final DisplayArea.Tokens mAboveAboveIme = new DisplayArea.Tokens(mWmService, - DisplayArea.Type.ABOVE_TASKS, "AboveTasksAboveIme"); - - @Override - public void attachDisplayAreas() { - mRoot.addChild(mBelow, 0); - mRoot.addChild(mTaskContainers, 1); - mRoot.addChild(mAbove, 2); - - mAbove.addChild(mAboveBelowIme, 0); - mAbove.addChild(mImeContainer, 1); - mAbove.addChild(mAboveAboveIme, 2); - } - - @Override - public void addWindow(WindowToken token) { - switch (DisplayArea.Type.typeOf(token)) { - case ABOVE_TASKS: - if (token.getWindowLayerFromType() - < mWmService.mPolicy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD)) { - mAboveBelowIme.addChild(token); - } else { - mAboveAboveIme.addChild(token); - } - break; - case BELOW_TASKS: - mBelow.addChild(token); - break; - default: - throw new IllegalArgumentException("don't know how to sort " + token); - } - } - - /** Provider for {@link DisplayAreaPolicy.Default platform-default display area policy}. */ - static class Provider implements DisplayAreaPolicy.Provider { - @Override - public DisplayAreaPolicy instantiate(WindowManagerService wmService, - DisplayContent content, DisplayArea.Root root, - DisplayArea<? extends WindowContainer> imeContainer, - TaskContainers taskContainers) { - return new DisplayAreaPolicy.Default(wmService, content, root, imeContainer, - taskContainers); - } + return new DisplayAreaPolicyBuilder() + .build(wmService, content, root, imeContainer, taskContainers); } } @@ -172,7 +119,7 @@ public abstract class DisplayAreaPolicy { String name = res.getString( com.android.internal.R.string.config_deviceSpecificDisplayAreaPolicyProvider); if (TextUtils.isEmpty(name)) { - return new DisplayAreaPolicy.Default.Provider(); + return new DisplayAreaPolicy.DefaultProvider(); } try { return (Provider) Class.forName(name).newInstance(); diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java new file mode 100644 index 000000000000..885456a8488c --- /dev/null +++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java @@ -0,0 +1,371 @@ +/* + * 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.wm; + +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; +import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; +import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; +import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; +import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.policy.WindowManagerPolicy; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * A builder for instantiating a complex {@link DisplayAreaPolicy} + * + * <p>Given a set of features (that each target a set of window types), it builds the necessary + * DisplayArea hierarchy. + * + * <p>Example: <br /> + * + * <pre> + * // Feature for targeting everything below the magnification overlay: + * new DisplayAreaPolicyBuilder(...) + * .addFeature(new Feature.Builder(..., "Magnification") + * .upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY) + * .build()) + * .build(...) + * + * // Builds a policy with the following hierarchy: + * - DisplayArea.Root + * - Magnification + * - DisplayArea.Tokens (Wallpapers are attached here) + * - TaskContainers + * - DisplayArea.Tokens (windows above Tasks up to IME are attached here) + * - ImeContainers + * - DisplayArea.Tokens (windows above IME up to TYPE_ACCESSIBILITY_OVERLAY attached here) + * - DisplayArea.Tokens (TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY and up are attached here) + * + * </pre> + * + * // TODO(display-area): document more complex scenarios where we need multiple areas per feature. + */ +class DisplayAreaPolicyBuilder { + + private final ArrayList<Feature> mFeatures = new ArrayList<>(); + + /** + * A feature that requires {@link DisplayArea DisplayArea(s)}. + */ + static class Feature { + private final String mName; + private final boolean[] mWindowLayers; + + private Feature(String name, boolean[] windowLayers) { + mName = name; + mWindowLayers = windowLayers; + } + + static class Builder { + private final WindowManagerPolicy mPolicy; + private final String mName; + private final boolean[] mLayers; + + /** + * Build a new feature that applies to a set of window types as specified by the builder + * methods. + * + * <p>The set of types is updated iteratively in the order of the method invocations. + * For example, {@code all().except(TYPE_STATUS_BAR)} expresses that a feature should + * apply to all types except TYPE_STATUS_BAR. + * + * The builder starts out with the feature not applying to any types. + * + * @param name the name of the feature. + */ + Builder(WindowManagerPolicy policy, String name) { + mPolicy = policy; + mName = name; + mLayers = new boolean[mPolicy.getMaxWindowLayer()]; + } + + /** + * Set that the feature applies to all window types. + */ + Builder all() { + Arrays.fill(mLayers, true); + return this; + } + + /** + * Set that the feature applies to the given window types. + */ + Builder and(int... types) { + for (int i = 0; i < types.length; i++) { + int type = types[i]; + set(type, true); + } + return this; + } + + /** + * Set that the feature does not apply to the given window types. + */ + Builder except(int... types) { + for (int i = 0; i < types.length; i++) { + int type = types[i]; + set(type, false); + } + return this; + } + + /** + * Set that the feature applies window types that are layerd at or below the layer of + * the given window type. + */ + Builder upTo(int typeInclusive) { + final int max = layerFromType(typeInclusive, false); + for (int i = 0; i < max; i++) { + mLayers[i] = true; + } + set(typeInclusive, true); + return this; + } + + Feature build() { + return new Feature(mName, mLayers.clone()); + } + + private void set(int type, boolean value) { + mLayers[layerFromType(type, true)] = value; + if (type == TYPE_APPLICATION_OVERLAY) { + mLayers[layerFromType(type, true)] = value; + mLayers[layerFromType(TYPE_SYSTEM_ALERT, false)] = value; + mLayers[layerFromType(TYPE_SYSTEM_OVERLAY, false)] = value; + mLayers[layerFromType(TYPE_SYSTEM_ERROR, false)] = value; + } + } + + private int layerFromType(int type, boolean internalWindows) { + return mPolicy.getWindowLayerFromTypeLw(type, internalWindows); + } + } + } + + static class Result extends DisplayAreaPolicy { + private static final int LEAF_TYPE_TASK_CONTAINERS = 1; + private static final int LEAF_TYPE_IME_CONTAINERS = 2; + private static final int LEAF_TYPE_TOKENS = 0; + + private final int mMaxWindowLayer = mWmService.mPolicy.getMaxWindowLayer(); + + private final ArrayList<Feature> mFeatures; + private final Map<Feature, List<DisplayArea<? extends WindowContainer>>> mAreas; + private final DisplayArea.Tokens[] mAreaForLayer = new DisplayArea.Tokens[mMaxWindowLayer]; + + Result(WindowManagerService wmService, DisplayContent content, DisplayArea.Root root, + DisplayArea<? extends WindowContainer> imeContainer, + DisplayArea<? extends ActivityStack> taskStacks, ArrayList<Feature> features) { + super(wmService, content, root, imeContainer, taskStacks); + mFeatures = features; + mAreas = new HashMap<>(features.size()); + for (int i = 0; i < mFeatures.size(); i++) { + mAreas.put(mFeatures.get(i), new ArrayList<>()); + } + } + + @Override + public void attachDisplayAreas() { + // This method constructs the layer hierarchy with the following properties: + // (1) Every feature maps to a set of DisplayAreas + // (2) After adding a window, for every feature the window's type belongs to, + // it is a descendant of one of the corresponding DisplayAreas of the feature. + // (3) Z-order is maintained, i.e. if z-range(area) denotes the set of layers of windows + // within a DisplayArea: + // for every pair of DisplayArea siblings (a,b), where a is below b, it holds that + // max(z-range(a)) <= min(z-range(b)) + // + // The algorithm below iteratively creates such a hierarchy: + // - Initially, all windows are attached to the root. + // - For each feature we create a set of DisplayAreas, by looping over the layers + // - if the feature does apply to the current layer, we need to find a DisplayArea + // for it to satisfy (2) + // - we can re-use the previous layer's area if: + // the current feature also applies to the previous layer, (to satisfy (3)) + // and the last feature that applied to the previous layer is the same as + // the last feature that applied to the current layer (to satisfy (2)) + // - otherwise we create a new DisplayArea below the last feature that applied + // to the current layer + + + PendingArea[] areaForLayer = new PendingArea[mMaxWindowLayer]; + final PendingArea root = new PendingArea(null, 0, null); + Arrays.fill(areaForLayer, root); + + final int size = mFeatures.size(); + for (int i = 0; i < size; i++) { + PendingArea featureArea = null; + for (int layer = 0; layer < mMaxWindowLayer; layer++) { + final Feature feature = mFeatures.get(i); + if (feature.mWindowLayers[layer]) { + if (featureArea == null || featureArea.mParent != areaForLayer[layer]) { + // No suitable DisplayArea - create a new one under the previous area + // for this layer. + featureArea = new PendingArea(feature, layer, areaForLayer[layer]); + areaForLayer[layer].mChildren.add(featureArea); + } + areaForLayer[layer] = featureArea; + } else { + featureArea = null; + } + } + } + + PendingArea leafArea = null; + int leafType = LEAF_TYPE_TOKENS; + for (int layer = 0; layer < mMaxWindowLayer; layer++) { + int type = typeOfLayer(mWmService.mPolicy, layer); + if (leafArea == null || leafArea.mParent != areaForLayer[layer] + || type != leafType) { + leafArea = new PendingArea(null, layer, areaForLayer[layer]); + areaForLayer[layer].mChildren.add(leafArea); + leafType = type; + if (leafType == LEAF_TYPE_TASK_CONTAINERS) { + leafArea.mExisting = mTaskContainers; + } else if (leafType == LEAF_TYPE_IME_CONTAINERS) { + leafArea.mExisting = mImeContainer; + } + } + leafArea.mMaxLayer = layer; + } + root.computeMaxLayer(); + root.instantiateChildren(mRoot, mAreaForLayer, 0, mAreas); + } + + @Override + public void addWindow(WindowToken token) { + DisplayArea.Tokens area = findAreaForToken(token); + area.addChild(token); + } + + @VisibleForTesting + DisplayArea.Tokens findAreaForToken(WindowToken token) { + int windowLayerFromType = token.getWindowLayerFromType(); + if (windowLayerFromType == APPLICATION_LAYER) { + // TODO(display-area): Better handle AboveAppWindows in APPLICATION_LAYER + windowLayerFromType += 1; + } else if (token.mRoundedCornerOverlay) { + windowLayerFromType = mMaxWindowLayer - 1; + } + return mAreaForLayer[windowLayerFromType]; + } + + public List<DisplayArea<? extends WindowContainer>> getDisplayAreas(Feature feature) { + return mAreas.get(feature); + } + + private static int typeOfLayer(WindowManagerPolicy policy, int layer) { + if (layer == APPLICATION_LAYER) { + return LEAF_TYPE_TASK_CONTAINERS; + } else if (layer == policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD) + || layer == policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD_DIALOG)) { + return LEAF_TYPE_IME_CONTAINERS; + } else { + return LEAF_TYPE_TOKENS; + } + } + } + + DisplayAreaPolicyBuilder addFeature(Feature feature) { + mFeatures.add(feature); + return this; + } + + Result build(WindowManagerService wmService, + DisplayContent content, DisplayArea.Root root, + DisplayArea<? extends WindowContainer> imeContainer, + DisplayArea<? extends ActivityStack> taskContainers) { + + return new Result(wmService, content, root, imeContainer, taskContainers, new ArrayList<>( + mFeatures)); + } + + static class PendingArea { + final int mMinLayer; + final ArrayList<PendingArea> mChildren = new ArrayList<>(); + final Feature mFeature; + final PendingArea mParent; + int mMaxLayer; + DisplayArea mExisting; + + PendingArea(Feature feature, + int minLayer, + PendingArea parent) { + mMinLayer = minLayer; + mFeature = feature; + mParent = parent; + } + + int computeMaxLayer() { + for (int i = 0; i < mChildren.size(); i++) { + mMaxLayer = Math.max(mMaxLayer, mChildren.get(i).computeMaxLayer()); + } + return mMaxLayer; + } + + void instantiateChildren(DisplayArea<DisplayArea> parent, + DisplayArea.Tokens[] areaForLayer, int level, Map<Feature, List<DisplayArea<? + extends WindowContainer>>> areas) { + mChildren.sort(Comparator.comparingInt(pendingArea -> pendingArea.mMinLayer)); + for (int i = 0; i < mChildren.size(); i++) { + final PendingArea child = mChildren.get(i); + final DisplayArea area = child.createArea(parent, areaForLayer); + parent.addChild(area, WindowContainer.POSITION_TOP); + if (mFeature != null) { + areas.get(mFeature).add(area); + } + child.instantiateChildren(area, areaForLayer, level + 1, areas); + } + } + + private DisplayArea createArea(DisplayArea<DisplayArea> parent, + DisplayArea.Tokens[] areaForLayer) { + if (mExisting != null) { + return mExisting; + } + DisplayArea.Type type; + if (mMinLayer > APPLICATION_LAYER) { + type = DisplayArea.Type.ABOVE_TASKS; + } else if (mMaxLayer < APPLICATION_LAYER) { + type = DisplayArea.Type.BELOW_TASKS; + } else { + type = DisplayArea.Type.ANY; + } + if (mFeature == null) { + final DisplayArea.Tokens leaf = new DisplayArea.Tokens(parent.mWmService, type, + "Leaf:" + mMinLayer + ":" + mMaxLayer); + for (int i = mMinLayer; i <= mMaxLayer; i++) { + areaForLayer[i] = leaf; + } + return leaf; + } else { + return new DisplayArea(parent.mWmService, type, mFeature.mName + ":" + + mMinLayer + ":" + mMaxLayer); + } + } + } +} diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index e469067d23b4..aa5dafce0450 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -57,6 +57,7 @@ import static android.app.admin.DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLE import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME; import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS; import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW; +import static android.app.admin.DevicePolicyManager.NON_ORG_OWNED_PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER; import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC; @@ -522,7 +523,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { /** Keyguard features that are allowed to be set on a managed profile */ private static final int PROFILE_KEYGUARD_FEATURES = - PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER | PROFILE_KEYGUARD_FEATURES_PROFILE_ONLY; + NON_ORG_OWNED_PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER + | PROFILE_KEYGUARD_FEATURES_PROFILE_ONLY; private static final int DEVICE_ADMIN_DEACTIVATE_TIMEOUT = 10000; @@ -8168,16 +8170,20 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Objects.requireNonNull(who, "ComponentName is null"); final int userHandle = mInjector.userHandleGetCallingUserId(); - if (isManagedProfile(userHandle)) { - if (parent) { - which = which & PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER; - } else { - which = which & PROFILE_KEYGUARD_FEATURES; - } - } synchronized (getLockObject()) { ActiveAdmin ap = getActiveAdminForCallerLocked( who, DeviceAdminInfo.USES_POLICY_DISABLE_KEYGUARD_FEATURES, parent); + if (isManagedProfile(userHandle)) { + if (parent) { + if (isProfileOwnerOfOrganizationOwnedDevice(ap)) { + which = which & PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER; + } else { + which = which & NON_ORG_OWNED_PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER; + } + } else { + which = which & PROFILE_KEYGUARD_FEATURES; + } + } if (ap.disabledKeyguardFeatures != which) { ap.disabledKeyguardFeatures = which; saveSettingsLocked(userHandle); diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java index c8673f833e71..a904b42f47db 100644 --- a/services/people/java/com/android/server/people/data/DataManager.java +++ b/services/people/java/com/android/server/people/data/DataManager.java @@ -322,7 +322,8 @@ public class DataManager { private void updateDefaultDialer(@NonNull UserData userData) { TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class); String defaultDialer = telecomManager != null - ? telecomManager.getDefaultDialerPackage(userData.getUserId()) : null; + ? telecomManager.getDefaultDialerPackage( + new UserHandle(userData.getUserId())) : null; userData.setDefaultDialer(defaultDialer); } diff --git a/services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/DummyAppTwoCerts.apk b/services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/DummyAppTwoCerts.apk Binary files differnew file mode 100644 index 000000000000..9161d869f3a1 --- /dev/null +++ b/services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/DummyAppTwoCerts.apk diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index f2f8ad157b71..8f70ccaaa9ba 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -2188,6 +2188,42 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertThat(actualAccounts).containsExactlyElementsIn(expectedAccounts); } + public void testSetKeyguardDisabledFeaturesWithDO() throws Exception { + mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; + setupDeviceOwner(); + + dpm.setKeyguardDisabledFeatures(admin1, DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA); + + assertThat(dpm.getKeyguardDisabledFeatures(admin1)).isEqualTo( + DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA); + } + + public void testSetKeyguardDisabledFeaturesWithPO() throws Exception { + setupProfileOwner(); + + dpm.setKeyguardDisabledFeatures(admin1, DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT); + + assertThat(dpm.getKeyguardDisabledFeatures(admin1)).isEqualTo( + DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT); + } + + public void testSetKeyguardDisabledFeaturesWithPOOfOrganizationOwnedDevice() + throws Exception { + final int MANAGED_PROFILE_USER_ID = DpmMockContext.CALLER_USER_HANDLE; + final int MANAGED_PROFILE_ADMIN_UID = + UserHandle.getUid(MANAGED_PROFILE_USER_ID, DpmMockContext.SYSTEM_UID); + mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID; + + addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1); + configureProfileOwnerOfOrgOwnedDevice(admin1, DpmMockContext.CALLER_USER_HANDLE); + + parentDpm.setKeyguardDisabledFeatures(admin1, + DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA); + + assertThat(parentDpm.getKeyguardDisabledFeatures(admin1)).isEqualTo( + DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA); + } + public void testSetApplicationHiddenWithDO() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceStaticTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceStaticTest.java new file mode 100644 index 000000000000..607cd816d7dd --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceStaticTest.java @@ -0,0 +1,67 @@ +/* + * 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.hdmi; + +import static com.google.common.truth.Truth.assertThat; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.Locale; + +/** + * Tests for static methods of {@link HdmiControlService} class. + */ +@SmallTest +@RunWith(JUnit4.class) +public class HdmiControlServiceStaticTest { + + @Test + public void localToMenuLanguage_english() { + assertThat(HdmiControlService.localeToMenuLanguage(Locale.ENGLISH)).isEqualTo("eng"); + } + + @Test + public void localToMenuLanguage_german() { + assertThat(HdmiControlService.localeToMenuLanguage(Locale.GERMAN)).isEqualTo("ger"); + } + + @Test + public void localToMenuLanguage_taiwan() { + assertThat(HdmiControlService.localeToMenuLanguage(Locale.TAIWAN)).isEqualTo("chi"); + } + + @Test + public void localToMenuLanguage_macau() { + assertThat(HdmiControlService.localeToMenuLanguage(new Locale("zh", "MO"))).isEqualTo( + "chi"); + } + + @Test + public void localToMenuLanguage_hongkong() { + assertThat(HdmiControlService.localeToMenuLanguage(new Locale("zh", "HK"))).isEqualTo( + "chi"); + } + + @Test + public void localToMenuLanguage_chinese() { + assertThat(HdmiControlService.localeToMenuLanguage(Locale.CHINESE)).isEqualTo("zho"); + } + +} diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java index 8dae48cafd7b..0d4c6e82d951 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java @@ -19,6 +19,7 @@ package com.android.server.integrity; import static android.content.integrity.AppIntegrityManager.EXTRA_STATUS; import static android.content.integrity.AppIntegrityManager.STATUS_FAILURE; import static android.content.integrity.AppIntegrityManager.STATUS_SUCCESS; +import static android.content.integrity.InstallerAllowedByManifestFormula.INSTALLER_CERTIFICATE_NOT_EVALUATED; import static android.content.pm.PackageManager.EXTRA_VERIFICATION_ID; import static android.content.pm.PackageManager.EXTRA_VERIFICATION_INSTALLER_PACKAGE; import static android.content.pm.PackageManager.EXTRA_VERIFICATION_INSTALLER_UID; @@ -39,6 +40,8 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; + import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -65,6 +68,7 @@ import com.android.server.integrity.engine.RuleEvaluationEngine; import com.android.server.integrity.model.IntegrityCheckResult; import com.android.server.testutils.TestUtils; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -76,8 +80,9 @@ import org.mockito.junit.MockitoRule; import java.io.File; import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; import java.util.Arrays; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -87,6 +92,9 @@ public class AppIntegrityManagerServiceImplTest { private static final String TEST_APP_PATH = "/data/local/tmp/AppIntegrityManagerServiceTestApp.apk"; + private static final String TEST_APP_TWO_CERT_PATH = + "AppIntegrityManagerServiceImplTest/DummyAppTwoCerts.apk"; + private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive"; private static final String VERSION = "version"; private static final String TEST_FRAMEWORK_PACKAGE = "com.android.frameworks.servicestests"; @@ -105,6 +113,11 @@ public class AppIntegrityManagerServiceImplTest { private static final String INSTALLER_SHA256 = "30F41A7CBF96EE736A54DD6DF759B50ED3CC126ABCEF694E167C324F5976C227"; + private static final String DUMMY_APP_TWO_CERTS_CERT_1 = + "C0369C2A1096632429DFA8433068AECEAD00BAC337CA92A175036D39CC9AFE94"; + private static final String DUMMY_APP_TWO_CERTS_CERT_2 = + "94366E0A80F3A3F0D8171A15760B88E228CD6E1101F0414C98878724FBE70147"; + private static final String PLAY_STORE_PKG = "com.android.vending"; private static final String ADB_INSTALLER = "adb"; private static final String PLAY_STORE_CERT = "play_store_cert"; @@ -128,6 +141,7 @@ public class AppIntegrityManagerServiceImplTest { private PackageManager mSpyPackageManager; private File mTestApk; + private File mTestApkTwoCerts; private final Context mRealContext = InstrumentationRegistry.getTargetContext(); // under test @@ -136,6 +150,10 @@ public class AppIntegrityManagerServiceImplTest { @Before public void setup() throws Exception { mTestApk = new File(TEST_APP_PATH); + mTestApkTwoCerts = File.createTempFile("AppIntegrity", ".apk"); + try (InputStream inputStream = mRealContext.getAssets().open(TEST_APP_TWO_CERT_PATH)) { + Files.copy(inputStream, mTestApkTwoCerts.toPath(), REPLACE_EXISTING); + } mService = new AppIntegrityManagerServiceImpl( @@ -154,6 +172,11 @@ public class AppIntegrityManagerServiceImplTest { when(mIntegrityFileManager.initialized()).thenReturn(true); } + @After + public void tearDown() throws Exception { + mTestApkTwoCerts.delete(); + } + @Test public void updateRuleSet_notAuthorized() throws Exception { makeUsSystemApp(); @@ -268,20 +291,16 @@ public class AppIntegrityManagerServiceImplTest { verify(mMockContext) .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any()); Intent intent = makeVerificationIntent(); - when(mRuleEvaluationEngine.evaluate(any(), any())).thenReturn(IntegrityCheckResult.allow()); + when(mRuleEvaluationEngine.evaluate(any())).thenReturn(IntegrityCheckResult.allow()); broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent); runJobInHandler(); ArgumentCaptor<AppInstallMetadata> metadataCaptor = ArgumentCaptor.forClass(AppInstallMetadata.class); - Map<String, String> allowedInstallers = new HashMap<>(); - ArgumentCaptor<Map<String, String>> allowedInstallersCaptor = - ArgumentCaptor.forClass(allowedInstallers.getClass()); verify(mRuleEvaluationEngine) - .evaluate(metadataCaptor.capture(), allowedInstallersCaptor.capture()); + .evaluate(metadataCaptor.capture()); AppInstallMetadata appInstallMetadata = metadataCaptor.getValue(); - allowedInstallers = allowedInstallersCaptor.getValue(); assertEquals(PACKAGE_NAME, appInstallMetadata.getPackageName()); assertThat(appInstallMetadata.getAppCertificates()).containsExactly(APP_CERT); assertEquals(INSTALLER_SHA256, appInstallMetadata.getInstallerName()); @@ -289,9 +308,34 @@ public class AppIntegrityManagerServiceImplTest { assertEquals(VERSION_CODE, appInstallMetadata.getVersionCode()); assertFalse(appInstallMetadata.isPreInstalled()); // These are hardcoded in the test apk android manifest + Map<String, String> allowedInstallers = + appInstallMetadata.getAllowedInstallersAndCertificates(); assertEquals(2, allowedInstallers.size()); assertEquals(PLAY_STORE_CERT, allowedInstallers.get(PLAY_STORE_PKG)); - assertEquals(ADB_CERT, allowedInstallers.get(ADB_INSTALLER)); + assertEquals(INSTALLER_CERTIFICATE_NOT_EVALUATED, allowedInstallers.get(ADB_INSTALLER)); + } + + @Test + public void handleBroadcast_correctArgs_multipleCerts() throws Exception { + whitelistUsAsRuleProvider(); + makeUsSystemApp(); + ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = + ArgumentCaptor.forClass(BroadcastReceiver.class); + verify(mMockContext) + .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any()); + Intent intent = makeVerificationIntent(); + intent.setDataAndType(Uri.fromFile(mTestApkTwoCerts), PACKAGE_MIME_TYPE); + when(mRuleEvaluationEngine.evaluate(any())).thenReturn(IntegrityCheckResult.allow()); + + broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent); + runJobInHandler(); + + ArgumentCaptor<AppInstallMetadata> metadataCaptor = + ArgumentCaptor.forClass(AppInstallMetadata.class); + verify(mRuleEvaluationEngine).evaluate(metadataCaptor.capture()); + AppInstallMetadata appInstallMetadata = metadataCaptor.getValue(); + assertThat(appInstallMetadata.getAppCertificates()).containsExactly( + DUMMY_APP_TWO_CERTS_CERT_1, DUMMY_APP_TWO_CERTS_CERT_2); } @Test @@ -303,7 +347,7 @@ public class AppIntegrityManagerServiceImplTest { verify(mMockContext) .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any()); Intent intent = makeVerificationIntent(); - when(mRuleEvaluationEngine.evaluate(any(), any())).thenReturn(IntegrityCheckResult.allow()); + when(mRuleEvaluationEngine.evaluate(any())).thenReturn(IntegrityCheckResult.allow()); broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent); runJobInHandler(); @@ -321,7 +365,7 @@ public class AppIntegrityManagerServiceImplTest { ArgumentCaptor.forClass(BroadcastReceiver.class); verify(mMockContext) .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any()); - when(mRuleEvaluationEngine.evaluate(any(), any())) + when(mRuleEvaluationEngine.evaluate(any())) .thenReturn( IntegrityCheckResult.deny( Arrays.asList( @@ -349,7 +393,7 @@ public class AppIntegrityManagerServiceImplTest { verify(mMockContext) .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any()); Intent intent = makeVerificationIntent(); - when(mRuleEvaluationEngine.evaluate(any(), any())).thenReturn(IntegrityCheckResult.allow()); + when(mRuleEvaluationEngine.evaluate(any())).thenReturn(IntegrityCheckResult.allow()); broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent); runJobInHandler(); @@ -377,7 +421,7 @@ public class AppIntegrityManagerServiceImplTest { verify(mMockContext, atLeastOnce()) .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any()); Intent intent = makeVerificationIntent(TEST_FRAMEWORK_PACKAGE); - when(mRuleEvaluationEngine.evaluate(any(), any())) + when(mRuleEvaluationEngine.evaluate(any())) .thenReturn(IntegrityCheckResult.deny(/* rule= */ null)); broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent); diff --git a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java index b0b9596829f1..0488745c2434 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java @@ -22,6 +22,8 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; import android.content.integrity.AppInstallMetadata; +import android.content.integrity.IntegrityFormula; +import android.content.integrity.Rule; import com.android.server.integrity.IntegrityFileManager; import com.android.server.integrity.model.IntegrityCheckResult; @@ -33,7 +35,6 @@ import org.junit.runners.JUnit4; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -60,13 +61,14 @@ public class RuleEvaluationEngineTest { mEngine = new RuleEvaluationEngine(mIntegrityFileManager); - when(mIntegrityFileManager.readRules(any())).thenReturn(new ArrayList<>()); + when(mIntegrityFileManager.readRules(any())).thenReturn(Collections.singletonList(new Rule( + IntegrityFormula.Installer.notAllowedByManifest(), Rule.DENY))); + + when(mIntegrityFileManager.initialized()).thenReturn(true); } @Test public void testAllowedInstallers_empty() { - Map<String, String> allowedInstallers = Collections.emptyMap(); - AppInstallMetadata appInstallMetadata1 = getAppInstallMetadataBuilder() .setInstallerName(INSTALLER_1) @@ -83,11 +85,11 @@ public class RuleEvaluationEngineTest { .setInstallerCertificates(Collections.singletonList(RANDOM_INSTALLER_CERT)) .build(); - assertThat(mEngine.evaluate(appInstallMetadata1, allowedInstallers).getEffect()) + assertThat(mEngine.evaluate(appInstallMetadata1).getEffect()) .isEqualTo(IntegrityCheckResult.Effect.ALLOW); - assertThat(mEngine.evaluate(appInstallMetadata2, allowedInstallers).getEffect()) + assertThat(mEngine.evaluate(appInstallMetadata2).getEffect()) .isEqualTo(IntegrityCheckResult.Effect.ALLOW); - assertThat(mEngine.evaluate(appInstallMetadata3, allowedInstallers).getEffect()) + assertThat(mEngine.evaluate(appInstallMetadata3).getEffect()) .isEqualTo(IntegrityCheckResult.Effect.ALLOW); } @@ -100,32 +102,36 @@ public class RuleEvaluationEngineTest { getAppInstallMetadataBuilder() .setInstallerName(INSTALLER_1) .setInstallerCertificates(Collections.singletonList(INSTALLER_1_CERT)) + .setAllowedInstallersAndCert(allowedInstallers) .build(); - assertThat(mEngine.evaluate(appInstallMetadata1, allowedInstallers).getEffect()) + assertThat(mEngine.evaluate(appInstallMetadata1).getEffect()) .isEqualTo(IntegrityCheckResult.Effect.ALLOW); AppInstallMetadata appInstallMetadata2 = getAppInstallMetadataBuilder() .setInstallerName(RANDOM_INSTALLER) + .setAllowedInstallersAndCert(allowedInstallers) .setInstallerCertificates(Collections.singletonList(INSTALLER_1_CERT)) .build(); - assertThat(mEngine.evaluate(appInstallMetadata2, allowedInstallers).getEffect()) + assertThat(mEngine.evaluate(appInstallMetadata2).getEffect()) .isEqualTo(IntegrityCheckResult.Effect.DENY); AppInstallMetadata appInstallMetadata3 = getAppInstallMetadataBuilder() .setInstallerName(INSTALLER_1) + .setAllowedInstallersAndCert(allowedInstallers) .setInstallerCertificates(Collections.singletonList(RANDOM_INSTALLER_CERT)) .build(); - assertThat(mEngine.evaluate(appInstallMetadata3, allowedInstallers).getEffect()) + assertThat(mEngine.evaluate(appInstallMetadata3).getEffect()) .isEqualTo(IntegrityCheckResult.Effect.DENY); AppInstallMetadata appInstallMetadata4 = getAppInstallMetadataBuilder() .setInstallerName(INSTALLER_1) + .setAllowedInstallersAndCert(allowedInstallers) .setInstallerCertificates(Collections.singletonList(RANDOM_INSTALLER_CERT)) .build(); - assertThat(mEngine.evaluate(appInstallMetadata4, allowedInstallers).getEffect()) + assertThat(mEngine.evaluate(appInstallMetadata4).getEffect()) .isEqualTo(IntegrityCheckResult.Effect.DENY); } @@ -138,57 +144,37 @@ public class RuleEvaluationEngineTest { AppInstallMetadata appInstallMetadata1 = getAppInstallMetadataBuilder() .setInstallerName(INSTALLER_1) + .setAllowedInstallersAndCert(allowedInstallers) .setInstallerCertificates(Collections.singletonList(INSTALLER_1_CERT)) .build(); - assertThat(mEngine.evaluate(appInstallMetadata1, allowedInstallers).getEffect()) + assertThat(mEngine.evaluate(appInstallMetadata1).getEffect()) .isEqualTo(IntegrityCheckResult.Effect.ALLOW); AppInstallMetadata appInstallMetadata2 = getAppInstallMetadataBuilder() .setInstallerName(INSTALLER_2) + .setAllowedInstallersAndCert(allowedInstallers) .setInstallerCertificates(Collections.singletonList(INSTALLER_2_CERT)) .build(); - assertThat(mEngine.evaluate(appInstallMetadata2, allowedInstallers).getEffect()) + assertThat(mEngine.evaluate(appInstallMetadata2).getEffect()) .isEqualTo(IntegrityCheckResult.Effect.ALLOW); AppInstallMetadata appInstallMetadata3 = getAppInstallMetadataBuilder() .setInstallerName(INSTALLER_1) + .setAllowedInstallersAndCert(allowedInstallers) .setInstallerCertificates(Collections.singletonList(INSTALLER_2_CERT)) .build(); - assertThat(mEngine.evaluate(appInstallMetadata3, allowedInstallers).getEffect()) + assertThat(mEngine.evaluate(appInstallMetadata3).getEffect()) .isEqualTo(IntegrityCheckResult.Effect.DENY); AppInstallMetadata appInstallMetadata4 = getAppInstallMetadataBuilder() .setInstallerName(INSTALLER_2) + .setAllowedInstallersAndCert(allowedInstallers) .setInstallerCertificates(Collections.singletonList(INSTALLER_1_CERT)) .build(); - assertThat(mEngine.evaluate(appInstallMetadata4, allowedInstallers).getEffect()) - .isEqualTo(IntegrityCheckResult.Effect.DENY); - } - - @Test - public void manifestBasedRuleEvaluationWorksEvenWhenIntegrityFilesAreUnavailable() { - when(mIntegrityFileManager.initialized()).thenReturn(false); - - Map<String, String> allowedInstallers = - Collections.singletonMap(INSTALLER_1, INSTALLER_1_CERT); - - AppInstallMetadata appInstallMetadata1 = - getAppInstallMetadataBuilder() - .setInstallerName(INSTALLER_1) - .setInstallerCertificates(Collections.singletonList(INSTALLER_1_CERT)) - .build(); - assertThat(mEngine.evaluate(appInstallMetadata1, allowedInstallers).getEffect()) - .isEqualTo(IntegrityCheckResult.Effect.ALLOW); - - AppInstallMetadata appInstallMetadata2 = - getAppInstallMetadataBuilder() - .setInstallerName(RANDOM_INSTALLER) - .setInstallerCertificates(Collections.singletonList(INSTALLER_1_CERT)) - .build(); - assertThat(mEngine.evaluate(appInstallMetadata2, allowedInstallers).getEffect()) + assertThat(mEngine.evaluate(appInstallMetadata4).getEffect()) .isEqualTo(IntegrityCheckResult.Effect.DENY); } diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java index 498d8886eec3..6769faaa4c5d 100644 --- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java @@ -142,7 +142,8 @@ public final class DataManagerTest { when(mContext.getSystemService(Context.TELECOM_SERVICE)).thenReturn(mTelecomManager); when(mContext.getSystemServiceName(TelecomManager.class)).thenReturn( Context.TELECOM_SERVICE); - when(mTelecomManager.getDefaultDialerPackage(anyInt())).thenReturn(TEST_PKG_NAME); + when(mTelecomManager.getDefaultDialerPackage(any(UserHandle.class))) + .thenReturn(TEST_PKG_NAME); when(mExecutorService.scheduleAtFixedRate(any(Runnable.class), anyLong(), anyLong(), any( TimeUnit.class))).thenReturn(mScheduledFuture); diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index e768205c2cf4..9e577636c1b3 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -145,6 +145,7 @@ public class AppStandbyControllerTests { static class MyInjector extends AppStandbyController.Injector { long mElapsedRealtime; boolean mIsAppIdleEnabled = true; + boolean mIsCharging; List<String> mPowerSaveWhitelistExceptIdle = new ArrayList<>(); boolean mDisplayOn; DisplayManager.DisplayListener mDisplayListener; @@ -179,6 +180,11 @@ public class AppStandbyControllerTests { } @Override + boolean isCharging() { + return mIsCharging; + } + + @Override boolean isPowerSaveWhitelistExceptIdleApp(String packageName) throws RemoteException { return mPowerSaveWhitelistExceptIdle.contains(packageName); } @@ -281,6 +287,13 @@ public class AppStandbyControllerTests { } catch (PackageManager.NameNotFoundException nnfe) {} } + private void setChargingState(AppStandbyController controller, boolean charging) { + mInjector.mIsCharging = charging; + if (controller != null) { + controller.setChargingState(charging); + } + } + private void setAppIdleEnabled(AppStandbyController controller, boolean enabled) { mInjector.mIsAppIdleEnabled = enabled; if (controller != null) { @@ -297,6 +310,7 @@ public class AppStandbyControllerTests { controller.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); mInjector.setDisplayOn(false); mInjector.setDisplayOn(true); + setChargingState(controller, false); controller.checkIdleStates(USER_ID); assertNotEquals(STANDBY_BUCKET_EXEMPTED, controller.getAppStandbyBucket(PACKAGE_1, USER_ID, @@ -324,6 +338,46 @@ public class AppStandbyControllerTests { mInjector.mElapsedRealtime, false)); } + @Test + public void testIsAppIdle_Charging() throws Exception { + setChargingState(mController, false); + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, + REASON_MAIN_FORCED_BY_SYSTEM); + assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1)); + assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0)); + assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false)); + + setChargingState(mController, true); + assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1)); + assertFalse(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0)); + assertFalse(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false)); + + setChargingState(mController, false); + assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1)); + assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0)); + assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false)); + } + + @Test + public void testIsAppIdle_Enabled() throws Exception { + setChargingState(mController, false); + setAppIdleEnabled(mController, true); + mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, + REASON_MAIN_FORCED_BY_SYSTEM); + assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1)); + assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0)); + assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false)); + + setAppIdleEnabled(mController, false); + assertFalse(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0)); + assertFalse(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false)); + + setAppIdleEnabled(mController, true); + assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1)); + assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0)); + assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, false)); + } + private void assertTimeout(AppStandbyController controller, long elapsedTime, int bucket) { mInjector.mElapsedRealtime = elapsedTime; controller.checkIdleStates(USER_ID); diff --git a/services/tests/servicestests/test-apps/AppIntegrityManagerServiceTestApp/AndroidManifest.xml b/services/tests/servicestests/test-apps/AppIntegrityManagerServiceTestApp/AndroidManifest.xml index f5dbf43cdfd6..98572d4df369 100644 --- a/services/tests/servicestests/test-apps/AppIntegrityManagerServiceTestApp/AndroidManifest.xml +++ b/services/tests/servicestests/test-apps/AppIntegrityManagerServiceTestApp/AndroidManifest.xml @@ -22,7 +22,7 @@ <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="28" /> <application android:hasCode="false"> - <meta-data android:name="allowed-installers" android:value="com.android.vending|play_store_cert,adb|"/> + <meta-data android:name="allowed-installers" android:value="com.android.vending|play_store_cert,adb"/> </application> </manifest> 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 a0f7f5b801ec..92d47c3a57b6 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -51,8 +51,6 @@ import static android.os.Build.VERSION_CODES.P; import static android.os.UserHandle.USER_SYSTEM; import static android.service.notification.Adjustment.KEY_IMPORTANCE; import static android.service.notification.Adjustment.KEY_USER_SENTIMENT; -import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL; -import static android.service.notification.NotificationListenerService.REASON_CANCEL; import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE; import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL; @@ -1144,14 +1142,15 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test - public void testEnqueueNotificationWithTag_WritesExpectedLog() throws Exception { + public void testEnqueueNotificationWithTag_WritesExpectedLogs() throws Exception { final String tag = "testEnqueueNotificationWithTag_WritesExpectedLog"; mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0, generateNotificationRecord(null).getNotification(), 0); waitForIdle(); assertEquals(1, mNotificationRecordLogger.getCalls().size()); + NotificationRecordLoggerFake.CallRecord call = mNotificationRecordLogger.get(0); - assertTrue(call.shouldLog); + assertTrue(call.shouldLogReported); assertEquals(NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED, call.event); assertNotNull(call.r); @@ -1161,7 +1160,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertEquals(PKG, call.r.getSbn().getPackageName()); assertEquals(0, call.r.getSbn().getId()); assertEquals(tag, call.r.getSbn().getTag()); - assertNotNull(call.r.getSbn().getInstanceId()); assertEquals(0, call.getInstanceId()); // Fake instance IDs are assigned in order } @@ -1180,13 +1178,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { waitForIdle(); assertEquals(2, mNotificationRecordLogger.getCalls().size()); - assertTrue(mNotificationRecordLogger.get(0).shouldLog); + assertTrue(mNotificationRecordLogger.get(0).shouldLogReported); assertEquals( NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED, mNotificationRecordLogger.get(0).event); assertEquals(0, mNotificationRecordLogger.get(0).getInstanceId()); - assertTrue(mNotificationRecordLogger.get(1).shouldLog); + assertTrue(mNotificationRecordLogger.get(1).shouldLogReported); assertEquals( NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_UPDATED, mNotificationRecordLogger.get(1).event); @@ -1195,16 +1193,37 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test - public void testEnqueueNotificationWithTag_DoesNotLogOnMinorUpdates() throws Exception { - final String tag = "testEnqueueNotificationWithTag_DoesNotLogOnMinorUpdates"; + public void testEnqueueNotificationWithTag_DoesNotLogOnMinorUpdate() throws Exception { + final String tag = "testEnqueueNotificationWithTag_DoesNotLogOnMinorUpdate"; mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0, generateNotificationRecord(null).getNotification(), 0); mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0, generateNotificationRecord(null).getNotification(), 0); waitForIdle(); assertEquals(2, mNotificationRecordLogger.getCalls().size()); - assertTrue(mNotificationRecordLogger.get(0).shouldLog); - assertFalse(mNotificationRecordLogger.get(1).shouldLog); + assertTrue(mNotificationRecordLogger.get(0).shouldLogReported); + assertEquals( + NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED, + mNotificationRecordLogger.get(0).event); + assertFalse(mNotificationRecordLogger.get(1).shouldLogReported); + assertNull(mNotificationRecordLogger.get(1).event); + } + + @Test + public void testEnqueueNotificationWithTag_DoesNotLogOnTitleUpdate() throws Exception { + final String tag = "testEnqueueNotificationWithTag_DoesNotLogOnTitleUpdate"; + mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0, + generateNotificationRecord(null).getNotification(), + 0); + final Notification notif = generateNotificationRecord(null).getNotification(); + notif.extras.putString(Notification.EXTRA_TITLE, "Changed title"); + mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0, notif, 0); + waitForIdle(); + assertEquals(2, mNotificationRecordLogger.getCalls().size()); + assertEquals( + NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED, + mNotificationRecordLogger.get(0).event); + assertNull(mNotificationRecordLogger.get(1).event); } @Test @@ -1224,20 +1243,18 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertEquals( NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED, mNotificationRecordLogger.get(0).event); - assertTrue(mNotificationRecordLogger.get(0).shouldLog); + assertTrue(mNotificationRecordLogger.get(0).shouldLogReported); assertEquals(0, mNotificationRecordLogger.get(0).getInstanceId()); - assertEquals(REASON_APP_CANCEL, mNotificationRecordLogger.get(1).reason); assertEquals( NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_APP_CANCEL, mNotificationRecordLogger.get(1).event); - assertTrue(mNotificationRecordLogger.get(1).shouldLog); assertEquals(0, mNotificationRecordLogger.get(1).getInstanceId()); assertEquals( NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED, mNotificationRecordLogger.get(2).event); - assertTrue(mNotificationRecordLogger.get(2).shouldLog); + assertTrue(mNotificationRecordLogger.get(2).shouldLogReported); // New instance ID because notification was canceled before re-post assertEquals(1, mNotificationRecordLogger.get(2).getInstanceId()); } @@ -3396,11 +3413,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // so we only get the cancel notification. assertEquals(1, mNotificationRecordLogger.getCalls().size()); - assertEquals(REASON_CANCEL, mNotificationRecordLogger.get(0).reason); assertEquals( NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_USER_AOD, mNotificationRecordLogger.get(0).event); - assertTrue(mNotificationRecordLogger.get(0).shouldLog); assertEquals(0, mNotificationRecordLogger.get(0).getInstanceId()); } @@ -4326,6 +4341,34 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testOnNotificationVisibilityChanged_triggersVisibilityLog() { + final NotificationRecord r = generateNotificationRecord( + mTestNotificationChannel, 1, null, true); + r.setTextChanged(true); + mService.addNotification(r); + + mService.mNotificationDelegate.onNotificationVisibilityChanged(new NotificationVisibility[] + {NotificationVisibility.obtain(r.getKey(), 1, 1, true)}, + new NotificationVisibility[]{}); + + assertEquals(1, mNotificationRecordLogger.getCalls().size()); + assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_OPEN, + mNotificationRecordLogger.get(0).event); + assertEquals(0, mNotificationRecordLogger.get(0).getInstanceId()); + + mService.mNotificationDelegate.onNotificationVisibilityChanged( + new NotificationVisibility[]{}, + new NotificationVisibility[] + {NotificationVisibility.obtain(r.getKey(), 1, 1, true)} + ); + + assertEquals(2, mNotificationRecordLogger.getCalls().size()); + assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_CLOSE, + mNotificationRecordLogger.get(1).event); + assertEquals(0, mNotificationRecordLogger.get(1).getInstanceId()); + } + + @Test public void testSetNotificationsShownFromListener_triggersInterruptionUsageStat() throws RemoteException { final NotificationRecord r = generateNotificationRecord( @@ -5373,21 +5416,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test - public void testCancelAllNotifications_cancelsBubble() throws Exception { - final NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel); - nr.getSbn().getNotification().flags |= FLAG_BUBBLE; - mService.addNotification(nr); - - mBinderService.cancelAllNotifications(PKG, nr.getSbn().getUserId()); - waitForIdle(); - - StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG); - assertEquals(0, notifs.length); - assertEquals(0, mService.getNotificationRecordCount()); - } - - @Test - public void testAppCancelNotifications_cancelsBubbles() throws Exception { + public void testCancelNotificationsFromApp_cancelsBubbles() throws Exception { final NotificationRecord nrBubble = generateNotificationRecord(mTestNotificationChannel); nrBubble.getSbn().getNotification().flags |= FLAG_BUBBLE; @@ -5413,6 +5442,20 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testCancelAllNotificationsFromApp_cancelsBubble() throws Exception { + final NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel); + nr.getSbn().getNotification().flags |= FLAG_BUBBLE; + mService.addNotification(nr); + + mBinderService.cancelAllNotifications(PKG, nr.getSbn().getUserId()); + waitForIdle(); + + StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG); + assertEquals(0, notifs.length); + assertEquals(0, mService.getNotificationRecordCount()); + } + + @Test public void testCancelAllNotificationsFromListener_ignoresBubbles() throws Exception { final NotificationRecord nrNormal = generateNotificationRecord(mTestNotificationChannel); final NotificationRecord nrBubble = generateNotificationRecord(mTestNotificationChannel); @@ -5448,6 +5491,25 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testCancelAllNotificationsFromStatusBar_ignoresBubble() throws Exception { + // GIVEN a notification bubble + final NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel); + nr.getSbn().getNotification().flags |= FLAG_BUBBLE; + mService.addNotification(nr); + + // WHEN the status bar clears all notifications + mService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(), + nr.getSbn().getUserId()); + waitForIdle(); + + // THEN the bubble notification does not get removed + StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG); + assertEquals(1, notifs.length); + assertEquals(1, mService.getNotificationRecordCount()); + } + + + @Test public void testGetAllowedAssistantAdjustments() throws Exception { List<String> capabilities = mBinderService.getAllowedAssistantAdjustments(null); assertNotNull(capabilities); @@ -6105,6 +6167,26 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testNotificationBubbles_bubbleStays_whenClicked() + throws Exception { + // GIVEN a notification that has the auto cancels flag (cancel on click) and is a bubble + setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, true /* channel */); + final NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel); + nr.getSbn().getNotification().flags |= FLAG_BUBBLE | FLAG_AUTO_CANCEL; + mService.addNotification(nr); + + // WHEN we click the notification + final NotificationVisibility nv = NotificationVisibility.obtain(nr.getKey(), 1, 2, true); + mService.mNotificationDelegate.onNotificationClick(mUid, Binder.getCallingPid(), + nr.getKey(), nv); + waitForIdle(); + + // THEN the bubble should still exist + StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG); + assertEquals(1, notifsAfter.length); + } + + @Test public void testLoadDefaultApprovedServices_emptyResources() { TestableResources tr = mContext.getOrCreateTestableResources(); tr.addOverride(com.android.internal.R.string.config_defaultListenerAccessPackages, ""); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java index b120dbee03c5..2a17bae57c8e 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java @@ -26,24 +26,26 @@ import java.util.List; */ class NotificationRecordLoggerFake implements NotificationRecordLogger { static class CallRecord extends NotificationRecordPair { - static final int INVALID = -1; - public int position = INVALID, buzzBeepBlink = INVALID, reason = INVALID; - public boolean shouldLog; public UiEventLogger.UiEventEnum event; + + // The following fields are only relevant to maybeLogNotificationPosted() calls. + static final int INVALID = -1; + public int position = INVALID, buzzBeepBlink = INVALID; + public boolean shouldLogReported; + CallRecord(NotificationRecord r, NotificationRecord old, int position, int buzzBeepBlink) { super(r, old); - this.position = position; this.buzzBeepBlink = buzzBeepBlink; - shouldLog = shouldLog(buzzBeepBlink); - event = NotificationReportedEvent.fromRecordPair(this); + shouldLogReported = shouldLogReported(buzzBeepBlink); + event = shouldLogReported ? NotificationReportedEvent.fromRecordPair(this) : null; } - CallRecord(NotificationRecord r, int reason, int dismissalSurface) { + + CallRecord(NotificationRecord r, UiEventLogger.UiEventEnum event) { super(r, null); - this.reason = reason; - shouldLog = true; - event = NotificationCancelledEvent.fromCancelReason(reason, dismissalSurface); + shouldLogReported = false; + this.event = event; } } private List<CallRecord> mCalls = new ArrayList<>(); @@ -57,14 +59,19 @@ class NotificationRecordLoggerFake implements NotificationRecordLogger { } @Override - public void logNotificationReported(NotificationRecord r, NotificationRecord old, + public void maybeLogNotificationPosted(NotificationRecord r, NotificationRecord old, int position, int buzzBeepBlink) { mCalls.add(new CallRecord(r, old, position, buzzBeepBlink)); } @Override public void logNotificationCancelled(NotificationRecord r, int reason, int dismissalSurface) { - mCalls.add(new CallRecord(r, reason, dismissalSurface)); + mCalls.add(new CallRecord(r, + NotificationCancelledEvent.fromCancelReason(reason, dismissalSurface))); } + @Override + public void logNotificationVisibility(NotificationRecord r, boolean visible) { + mCalls.add(new CallRecord(r, NotificationEvent.fromVisibility(visible))); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java new file mode 100644 index 000000000000..8ac1d24333be --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java @@ -0,0 +1,214 @@ +/* + * 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.wm; + +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; +import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; +import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION; +import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; +import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER; + +import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS; +import static com.android.server.wm.DisplayArea.Type.ANY; +import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature; + +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import static java.util.stream.Collectors.toList; + +import android.platform.test.annotations.Presubmit; +import android.view.SurfaceControl; + +import org.hamcrest.CustomTypeSafeMatcher; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.junit.Rule; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Collectors; + +@Presubmit +public class DisplayAreaPolicyBuilderTest { + + @Rule + public final SystemServicesTestRule mSystemServices = new SystemServicesTestRule(); + + private TestWindowManagerPolicy mPolicy = new TestWindowManagerPolicy(null, null); + + @Test + public void testBuilder() { + WindowManagerService wms = mSystemServices.getWindowManagerService(); + DisplayArea.Root root = new SurfacelessDisplayAreaRoot(wms); + DisplayArea<WindowContainer> ime = new DisplayArea<>(wms, ABOVE_TASKS, "Ime"); + DisplayArea<ActivityStack> tasks = new DisplayArea<>(wms, ANY, "Tasks"); + + final Feature foo; + final Feature bar; + + DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder() + .addFeature(foo = new Feature.Builder(mPolicy, "Foo") + .upTo(TYPE_STATUS_BAR) + .and(TYPE_NAVIGATION_BAR) + .build()) + .addFeature(bar = new Feature.Builder(mPolicy, "Bar") + .all() + .except(TYPE_STATUS_BAR) + .build()) + .build(wms, mock(DisplayContent.class), root, ime, tasks); + + policy.attachDisplayAreas(); + + assertThat(policy.getDisplayAreas(foo), is(not(empty()))); + assertThat(policy.getDisplayAreas(bar), is(not(empty()))); + + assertThat(policy.findAreaForToken(tokenOfType(TYPE_STATUS_BAR)), + is(decendantOfOneOf(policy.getDisplayAreas(foo)))); + assertThat(policy.findAreaForToken(tokenOfType(TYPE_STATUS_BAR)), + is(not(decendantOfOneOf(policy.getDisplayAreas(bar))))); + + assertThat(tasks, + is(decendantOfOneOf(policy.getDisplayAreas(foo)))); + assertThat(tasks, + is(decendantOfOneOf(policy.getDisplayAreas(bar)))); + + assertThat(ime, + is(decendantOfOneOf(policy.getDisplayAreas(foo)))); + assertThat(ime, + is(decendantOfOneOf(policy.getDisplayAreas(bar)))); + + List<DisplayArea<?>> actualOrder = collectLeafAreas(root); + Map<DisplayArea<?>, Set<Integer>> zSets = calculateZSets(policy, root, ime, tasks); + actualOrder = actualOrder.stream().filter(zSets::containsKey).collect(toList()); + + Map<DisplayArea<?>, Integer> expectedByMinLayer = mapValues(zSets, + v -> v.stream().min(Integer::compareTo).get()); + Map<DisplayArea<?>, Integer> expectedByMaxLayer = mapValues(zSets, + v -> v.stream().max(Integer::compareTo).get()); + + assertThat(expectedByMinLayer, is(equalTo(expectedByMaxLayer))); + assertThat(actualOrder, is(equalTo(expectedByMaxLayer))); + } + + private <K, V, R> Map<K, R> mapValues(Map<K, V> zSets, Function<V, R> f) { + return zSets.entrySet().stream().collect(Collectors.toMap( + Map.Entry::getKey, + e -> f.apply(e.getValue()))); + } + + private List<DisplayArea<?>> collectLeafAreas(DisplayArea<?> root) { + ArrayList<DisplayArea<?>> leafs = new ArrayList<>(); + traverseLeafAreas(root, leafs::add); + return leafs; + } + + private Map<DisplayArea<?>, Set<Integer>> calculateZSets( + DisplayAreaPolicyBuilder.Result policy, DisplayArea.Root root, + DisplayArea<WindowContainer> ime, + DisplayArea<ActivityStack> tasks) { + Map<DisplayArea<?>, Set<Integer>> zSets = new HashMap<>(); + int[] types = {TYPE_STATUS_BAR, TYPE_NAVIGATION_BAR, TYPE_PRESENTATION, + TYPE_APPLICATION_OVERLAY}; + for (int type : types) { + WindowToken token = tokenOfType(type); + recordLayer(policy.findAreaForToken(token), token.getWindowLayerFromType(), zSets); + } + recordLayer(tasks, APPLICATION_LAYER, zSets); + recordLayer(ime, mPolicy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD), zSets); + return zSets; + } + + private void recordLayer(DisplayArea<?> area, int layer, + Map<DisplayArea<?>, Set<Integer>> zSets) { + zSets.computeIfAbsent(area, k -> new HashSet<>()).add(layer); + } + + private Matcher<WindowContainer> decendantOfOneOf(List<? extends WindowContainer> expected) { + return new CustomTypeSafeMatcher<WindowContainer>("descendant of one of " + expected) { + @Override + protected boolean matchesSafely(WindowContainer actual) { + for (WindowContainer expected : expected) { + WindowContainer candidate = actual; + while (candidate != null && candidate.getParent() != candidate) { + if (candidate.getParent() == expected) { + return true; + } + candidate = candidate.getParent(); + } + } + return false; + } + + @Override + protected void describeMismatchSafely(WindowContainer item, + Description description) { + description.appendText("was ").appendValue(item); + while (item != null && item.getParent() != item) { + item = item.getParent(); + description.appendText(", child of ").appendValue(item); + } + } + }; + } + + private WindowToken tokenOfType(int type) { + WindowToken m = mock(WindowToken.class); + when(m.getWindowLayerFromType()).thenReturn(mPolicy.getWindowLayerFromTypeLw(type)); + return m; + } + + private static void traverseLeafAreas(DisplayArea<?> root, Consumer<DisplayArea<?>> consumer) { + boolean leaf = true; + for (int i = 0; i < root.getChildCount(); i++) { + WindowContainer child = root.getChildAt(i); + if (child instanceof DisplayArea<?>) { + traverseLeafAreas((DisplayArea<?>) child, consumer); + leaf = false; + } + } + if (leaf) { + consumer.accept(root); + } + } + + private static class SurfacelessDisplayAreaRoot extends DisplayArea.Root { + + SurfacelessDisplayAreaRoot(WindowManagerService wms) { + super(wms); + } + + @Override + SurfaceControl.Builder makeChildSurface(WindowContainer child) { + return new MockSurfaceControlBuilder(); + } + } + +} diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java index c1a1d5ecd3c8..31206315618e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java @@ -34,13 +34,13 @@ public class DisplayAreaProviderTest { @Test public void testFromResources_emptyProvider() { Assert.assertThat(DisplayAreaPolicy.Provider.fromResources(resourcesWithProvider("")), - Matchers.instanceOf(DisplayAreaPolicy.Default.Provider.class)); + Matchers.instanceOf(DisplayAreaPolicy.DefaultProvider.class)); } @Test public void testFromResources_nullProvider() { Assert.assertThat(DisplayAreaPolicy.Provider.fromResources(resourcesWithProvider(null)), - Matchers.instanceOf(DisplayAreaPolicy.Default.Provider.class)); + Matchers.instanceOf(DisplayAreaPolicy.DefaultProvider.class)); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java index f517881d835b..8ad75053060f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java +++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java @@ -56,6 +56,7 @@ import android.os.PowerManagerInternal; import android.os.PowerSaveState; import android.os.StrictMode; import android.os.UserHandle; +import android.util.Log; import android.view.InputChannel; import android.view.Surface; import android.view.SurfaceControl; @@ -120,11 +121,22 @@ public class SystemServicesTestRule implements TestRule { return new Statement() { @Override public void evaluate() throws Throwable { + Throwable throwable = null; try { runWithDexmakerShareClassLoader(SystemServicesTestRule.this::setUp); base.evaluate(); + } catch (Throwable t) { + throwable = t; } finally { - tearDown(); + try { + tearDown(); + } catch (Throwable t) { + if (throwable != null) { + Log.e("SystemServicesTestRule", "Suppressed: ", throwable); + t.addSuppressed(throwable); + } + throw t; + } } } }; diff --git a/startop/iorap/functional_tests/AndroidTest.xml b/startop/iorap/functional_tests/AndroidTest.xml index 41109b43ab82..ef56fc827420 100644 --- a/startop/iorap/functional_tests/AndroidTest.xml +++ b/startop/iorap/functional_tests/AndroidTest.xml @@ -34,6 +34,11 @@ <option name="run-command" value="rm -r /data/misc/iorapd/*" /> <option name="run-command" value="sleep 1" /> + <!-- Set system properties to enable perfetto tracing, readahead and detailed logging. --> + <option name="run-command" value="setprop iorapd.perfetto.enable true" /> + <option name="run-command" value="setprop iorapd.readahead.enable true" /> + <option name="run-command" value="setprop iorapd.log.verbose true" /> + <option name="run-command" value="start iorapd" /> <!-- give it some time to restart the service; otherwise the first unit test might fail --> @@ -45,9 +50,5 @@ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> </test> - <!-- using DeviceSetup again does not work. we simply leave the device in a semi-bad - state. there is no way to clean this up as far as I know. - --> - </configuration> diff --git a/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java b/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java index bd8a45c2ca00..40023878af19 100644 --- a/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java +++ b/startop/iorap/functional_tests/src/com/google/android/startop/iorap/IorapWorkFlowTest.java @@ -67,7 +67,7 @@ public class IorapWorkFlowTest { private static final String TEST_ACTIVITY_NAME = "com.android.settings.Settings"; private static final String DB_PATH = "/data/misc/iorapd/sqlite.db"; - private static final Duration TIMEOUT = Duration.ofSeconds(20L); + private static final Duration TIMEOUT = Duration.ofSeconds(300L); private static final String READAHEAD_INDICATOR = "Description = /data/misc/iorapd/com.android.settings/none/com.android.settings.Settings/compiled_traces/compiled_trace.pb"; @@ -88,7 +88,7 @@ public class IorapWorkFlowTest { mDevice.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)), TIMEOUT.getSeconds()); } - @Test + @Test (timeout = 300000) public void testApp() throws Exception { assertThat(mDevice, notNullValue()); @@ -247,7 +247,7 @@ public class IorapWorkFlowTest { if (supplier.getAsBoolean()) { return true; } - TimeUnit.SECONDS.sleep(totalSleepTimeSeconds); + TimeUnit.SECONDS.sleep(sleepIntervalSeconds); totalSleepTimeSeconds += sleepIntervalSeconds; if (totalSleepTimeSeconds > timeout.getSeconds()) { return false; @@ -367,7 +367,7 @@ public class IorapWorkFlowTest { * * <p> This should be run as root.</p> */ - private String executeShellCommand(String cmd) throws Exception { + private static String executeShellCommand(String cmd) throws Exception { Log.i(TAG, "Execute: " + cmd); return UiDevice.getInstance( InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd); diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java index ec99f36f6e70..52213d8c4fae 100644 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -17,6 +17,7 @@ package android.telecom; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; @@ -458,8 +459,14 @@ public final class Call { /** Call supports the deflect feature. */ public static final int CAPABILITY_SUPPORT_DEFLECT = 0x01000000; + /** + * Call supports adding participants to the call via + * {@link #addConferenceParticipants(List)}. + * @hide + */ + public static final int CAPABILITY_ADD_PARTICIPANT = 0x02000000; //****************************************************************************************** - // Next CAPABILITY value: 0x02000000 + // Next CAPABILITY value: 0x04000000 //****************************************************************************************** /** @@ -539,7 +546,7 @@ public final class Call { * * @see TelecomManager#EXTRA_USE_ASSISTED_DIALING */ - public static final int PROPERTY_ASSISTED_DIALING_USED = 0x00000200; + public static final int PROPERTY_ASSISTED_DIALING = 0x00000200; /** * Indicates that the call is an RTT call. Use {@link #getRttCall()} to get the @@ -689,6 +696,9 @@ public final class Call { if (can(capabilities, CAPABILITY_SUPPORT_DEFLECT)) { builder.append(" CAPABILITY_SUPPORT_DEFLECT"); } + if (can(capabilities, CAPABILITY_ADD_PARTICIPANT)) { + builder.append(" CAPABILITY_ADD_PARTICIPANT"); + } builder.append("]"); return builder.toString(); } @@ -744,7 +754,7 @@ public final class Call { if (hasProperty(properties, PROPERTY_HAS_CDMA_VOICE_PRIVACY)) { builder.append(" PROPERTY_HAS_CDMA_VOICE_PRIVACY"); } - if (hasProperty(properties, PROPERTY_ASSISTED_DIALING_USED)) { + if (hasProperty(properties, PROPERTY_ASSISTED_DIALING)) { builder.append(" PROPERTY_ASSISTED_DIALING_USED"); } if (hasProperty(properties, PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL)) { @@ -1703,6 +1713,17 @@ public final class Call { } /** + * Pulls participants to existing call by forming a conference call. + * See {@link Details#CAPABILITY_ADD_PARTICIPANT}. + * + * @param participants participants to be pulled to existing call. + * @hide + */ + public void addConferenceParticipants(@NonNull List<Uri> participants) { + mInCallAdapter.addConferenceParticipants(mTelecomCallId, participants); + } + + /** * Initiates a request to the {@link ConnectionService} to pull an external call to the local * device. * <p> diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java index 56acdff530eb..f019a9d33005 100644 --- a/telecomm/java/android/telecom/Conference.java +++ b/telecomm/java/android/telecom/Conference.java @@ -16,8 +16,13 @@ package android.telecom; +import static android.Manifest.permission.MODIFY_PHONE_STATE; + +import android.annotation.ElapsedRealtimeLong; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.TestApi; import android.net.Uri; @@ -319,6 +324,13 @@ public abstract class Conference extends Conferenceable { public void onConnectionAdded(Connection connection) {} /** + * Notifies the {@link Conference} of a request to add a new participants to the conference call + * @param participants that will be added to this conference call + * @hide + */ + public void onAddConferenceParticipants(@NonNull List<Uri> participants) {} + + /** * Notifies this Conference, which is in {@code STATE_RINGING}, of * a request to accept. * For managed {@link ConnectionService}s, this will be called when the user answers a call via @@ -625,12 +637,12 @@ public abstract class Conference extends Conferenceable { * Should be specified in wall-clock time returned by {@link System#currentTimeMillis()}. * <p> * When setting the connection time, you should always set the connection elapsed time via - * {@link #setConnectionStartElapsedRealTime(long)} to ensure the duration is reflected. + * {@link #setConnectionStartElapsedRealtimeMillis(long)} to ensure the duration is reflected. * * @param connectionTimeMillis The connection time, in milliseconds, as returned by * {@link System#currentTimeMillis()}. */ - public final void setConnectionTime(long connectionTimeMillis) { + public final void setConnectionTime(@IntRange(from = 0) long connectionTimeMillis) { mConnectTimeMillis = connectionTimeMillis; } @@ -646,8 +658,28 @@ public abstract class Conference extends Conferenceable { * * @param connectionStartElapsedRealTime The connection time, as measured by * {@link SystemClock#elapsedRealtime()}. + * @deprecated use {@link #setConnectionStartElapsedRealtimeMillis(long)} instead. */ + @Deprecated public final void setConnectionStartElapsedRealTime(long connectionStartElapsedRealTime) { + setConnectionStartElapsedRealtimeMillis(connectionStartElapsedRealTime); + } + + /** + * Sets the start time of the {@link Conference} which is the basis for the determining the + * duration of the {@link Conference}. + * <p> + * You should use a value returned by {@link SystemClock#elapsedRealtime()} to ensure that time + * zone changes do not impact the conference duration. + * <p> + * When setting this, you should also set the connection time via + * {@link #setConnectionTime(long)}. + * + * @param connectionStartElapsedRealTime The connection time, as measured by + * {@link SystemClock#elapsedRealtime()}. + */ + public final void setConnectionStartElapsedRealtimeMillis( + @ElapsedRealtimeLong long connectionStartElapsedRealTime) { mConnectionStartElapsedRealTime = connectionStartElapsedRealTime; } @@ -668,7 +700,7 @@ public abstract class Conference extends Conferenceable { * * @return The time at which the {@code Conference} was connected. */ - public final long getConnectionTime() { + public final @IntRange(from = 0) long getConnectionTime() { return mConnectTimeMillis; } @@ -685,11 +717,8 @@ public abstract class Conference extends Conferenceable { * has no general use other than to the Telephony framework. * * @return The elapsed time at which the {@link Conference} was connected. - * @hide */ - @SystemApi - @TestApi - public final long getConnectionStartElapsedRealTime() { + public final @ElapsedRealtimeLong long getConnectionStartElapsedRealtimeMillis() { return mConnectionStartElapsedRealTime; } @@ -987,6 +1016,7 @@ public abstract class Conference extends Conferenceable { */ @SystemApi @TestApi + @RequiresPermission(MODIFY_PHONE_STATE) public void setConferenceState(boolean isConference) { for (Listener l : mListeners) { l.onConferenceStateChanged(this, isConference); @@ -1007,6 +1037,7 @@ public abstract class Conference extends Conferenceable { */ @SystemApi @TestApi + @RequiresPermission(MODIFY_PHONE_STATE) public final void setAddress(@NonNull Uri address, @TelecomManager.Presentation int presentation) { Log.d(this, "setAddress %s", address); @@ -1113,12 +1144,52 @@ public abstract class Conference extends Conferenceable { } /** - * Sends an event associated with this {@code Conference} with associated event extras to the - * {@link InCallService} (note: this is identical in concept to - * {@link Connection#sendConnectionEvent(String, Bundle)}). - * @see Connection#sendConnectionEvent(String, Bundle) + * Sends an event associated with this {@link Conference} with associated event extras to the + * {@link InCallService}. + * <p> + * Connection events are used to communicate point in time information from a + * {@link ConnectionService} to an {@link InCallService} implementation. An example of a + * custom connection event includes notifying the UI when a WIFI call has been handed over to + * LTE, which the InCall UI might use to inform the user that billing charges may apply. The + * Android Telephony framework will send the {@link Connection#EVENT_MERGE_COMPLETE} + * connection event when a call to {@link Call#mergeConference()} has completed successfully. + * <p> + * Events are exposed to {@link InCallService} implementations via + * {@link Call.Callback#onConnectionEvent(Call, String, Bundle)}. + * <p> + * No assumptions should be made as to how an In-Call UI or service will handle these events. + * The {@link ConnectionService} must assume that the In-Call UI could even chose to ignore + * some events altogether. + * <p> + * Events should be fully qualified (e.g. {@code com.example.event.MY_EVENT}) to avoid + * conflicts between {@link ConnectionService} implementations. Further, custom + * {@link ConnectionService} implementations shall not re-purpose events in the + * {@code android.*} namespace, nor shall they define new event types in this namespace. When + * defining a custom event type, ensure the contents of the extras {@link Bundle} is clearly + * defined. Extra keys for this bundle should be named similar to the event type (e.g. + * {@code com.example.extra.MY_EXTRA}). + * <p> + * When defining events and the associated extras, it is important to keep their behavior + * consistent when the associated {@link ConnectionService} is updated. Support for deprecated + * events/extras should me maintained to ensure backwards compatibility with older + * {@link InCallService} implementations which were built to support the older behavior. + * <p> + * Expected connection events from the Telephony stack are: + * <p> + * <ul> + * <li>{@link Connection#EVENT_CALL_HOLD_FAILED} with {@code null} {@code extras} when the + * {@link Conference} could not be held.</li> + * <li>{@link Connection#EVENT_MERGE_START} with {@code null} {@code extras} when a new + * call is being merged into the conference.</li> + * <li>{@link Connection#EVENT_MERGE_COMPLETE} with {@code null} {@code extras} a new call + * has completed being merged into the conference.</li> + * <li>{@link Connection#EVENT_CALL_MERGE_FAILED} with {@code null} {@code extras} a new + * call has failed to merge into the conference (the dialer app can determine which call + * failed to merge based on the fact that the call still exists outside of the conference + * at the end of the merge process).</li> + * </ul> * - * @param event The connection event. + * @param event The conference event. * @param extras Optional bundle containing extra information associated with the event. */ public void sendConferenceEvent(@NonNull String event, @Nullable Bundle extras) { diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index 8049459cf3f4..3b0ba2548660 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -16,9 +16,14 @@ package android.telecom; +import static android.Manifest.permission.MODIFY_PHONE_STATE; + +import android.annotation.ElapsedRealtimeLong; import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.TestApi; import android.app.Notification; @@ -376,8 +381,14 @@ public abstract class Connection extends Conferenceable { /** Call supports the deflect feature. */ public static final int CAPABILITY_SUPPORT_DEFLECT = 0x02000000; + /** + * When set, indicates that this {@link Connection} supports initiation of a conference call + * by directly adding participants using {@link #onAddConferenceParticipants(List)}. + * @hide + */ + public static final int CAPABILITY_ADD_PARTICIPANT = 0x04000000; //********************************************************************************************** - // Next CAPABILITY value: 0x04000000 + // Next CAPABILITY value: 0x08000000 //********************************************************************************************** /** @@ -474,7 +485,7 @@ public abstract class Connection extends Conferenceable { * * @see TelecomManager#EXTRA_USE_ASSISTED_DIALING */ - public static final int PROPERTY_ASSISTED_DIALING_USED = 1 << 9; + public static final int PROPERTY_ASSISTED_DIALING = 1 << 9; /** * Set by the framework to indicate that the network has identified a Connection as an emergency @@ -953,7 +964,9 @@ public abstract class Connection extends Conferenceable { if ((capabilities & CAPABILITY_SUPPORT_DEFLECT) == CAPABILITY_SUPPORT_DEFLECT) { builder.append(isLong ? " CAPABILITY_SUPPORT_DEFLECT" : " sup_def"); } - + if ((capabilities & CAPABILITY_ADD_PARTICIPANT) == CAPABILITY_ADD_PARTICIPANT) { + builder.append(isLong ? " CAPABILITY_ADD_PARTICIPANT" : " add_participant"); + } builder.append("]"); return builder.toString(); } @@ -2109,19 +2122,24 @@ public abstract class Connection extends Conferenceable { */ @SystemApi @TestApi - public final long getConnectTimeMillis() { + public final @IntRange(from = 0) long getConnectTimeMillis() { return mConnectTimeMillis; } /** * Retrieves the connection start time of the {@link Connection}, if specified. A value of * {@link Conference#CONNECT_TIME_NOT_SPECIFIED} indicates that Telecom should determine the - * start time of the conference. + * start time of the connection. * <p> * Based on the value of {@link SystemClock#elapsedRealtime()}, which ensures that wall-clock * changes do not impact the call duration. * <p> * Used internally in Telephony when migrating conference participant data for IMS conferences. + * <p> + * The value returned is the same one set using + * {@link #setConnectionStartElapsedRealtimeMillis(long)}. This value is never updated from + * the Telecom framework, so no permission enforcement occurs when retrieving the value with + * this method. * * @return The time at which the {@link Connection} was connected. * @@ -2129,7 +2147,7 @@ public abstract class Connection extends Conferenceable { */ @SystemApi @TestApi - public final long getConnectElapsedTimeMillis() { + public final @ElapsedRealtimeLong long getConnectionStartElapsedRealtimeMillis() { return mConnectElapsedTimeMillis; } @@ -2550,6 +2568,9 @@ public abstract class Connection extends Conferenceable { * Sets the time at which a call became active on this Connection. This is set only * when a conference call becomes active on this connection. * <p> + * This time corresponds to the date/time of connection and is stored in the call log in + * {@link android.provider.CallLog.Calls#DATE}. + * <p> * Used by telephony to maintain calls associated with an IMS Conference. * * @param connectTimeMillis The connection time, in milliseconds. Should be set using a value @@ -2559,7 +2580,8 @@ public abstract class Connection extends Conferenceable { */ @SystemApi @TestApi - public final void setConnectTimeMillis(long connectTimeMillis) { + @RequiresPermission(MODIFY_PHONE_STATE) + public final void setConnectTimeMillis(@IntRange(from = 0) long connectTimeMillis) { mConnectTimeMillis = connectTimeMillis; } @@ -2567,15 +2589,23 @@ public abstract class Connection extends Conferenceable { * Sets the time at which a call became active on this Connection. This is set only * when a conference call becomes active on this connection. * <p> + * This time is used to establish the duration of a call. It uses + * {@link SystemClock#elapsedRealtime()} to ensure that the call duration is not impacted by + * time zone changes during a call. The difference between the current + * {@link SystemClock#elapsedRealtime()} and the value set at the connection start time is used + * to populate {@link android.provider.CallLog.Calls#DURATION} in the call log. + * <p> * Used by telephony to maintain calls associated with an IMS Conference. + * * @param connectElapsedTimeMillis The connection time, in milliseconds. Stored in the format * {@link SystemClock#elapsedRealtime()}. - * * @hide */ @SystemApi @TestApi - public final void setConnectionStartElapsedRealTime(long connectElapsedTimeMillis) { + @RequiresPermission(MODIFY_PHONE_STATE) + public final void setConnectionStartElapsedRealtimeMillis( + @ElapsedRealtimeLong long connectElapsedTimeMillis) { mConnectElapsedTimeMillis = connectElapsedTimeMillis; } @@ -2953,6 +2983,14 @@ public abstract class Connection extends Conferenceable { public void onSeparate() {} /** + * Supports initiation of a conference call by directly adding participants to an ongoing call. + * + * @param participants with which conference call will be formed. + * @hide + */ + public void onAddConferenceParticipants(@NonNull List<Uri> participants) {} + + /** * Notifies this Connection of a request to abort. */ public void onAbort() {} diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java index 00c2918837ac..2aea723cf418 100644 --- a/telecomm/java/android/telecom/ConnectionService.java +++ b/telecomm/java/android/telecom/ConnectionService.java @@ -18,7 +18,6 @@ package android.telecom; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SystemApi; import android.app.Service; @@ -142,6 +141,7 @@ public abstract class ConnectionService extends Service { private static final String SESSION_SPLIT_CONFERENCE = "CS.sFC"; private static final String SESSION_MERGE_CONFERENCE = "CS.mC"; private static final String SESSION_SWAP_CONFERENCE = "CS.sC"; + private static final String SESSION_ADD_PARTICIPANT = "CS.aP"; private static final String SESSION_POST_DIAL_CONT = "CS.oPDC"; private static final String SESSION_PULL_EXTERNAL_CALL = "CS.pEC"; private static final String SESSION_SEND_CALL_EVENT = "CS.sCE"; @@ -195,6 +195,7 @@ public abstract class ConnectionService extends Service { private static final int MSG_CREATE_CONFERENCE_COMPLETE = 36; private static final int MSG_CREATE_CONFERENCE_FAILED = 37; private static final int MSG_REJECT_WITH_REASON = 38; + private static final int MSG_ADD_PARTICIPANT = 39; private static Connection sNullConnection; @@ -627,6 +628,21 @@ public abstract class ConnectionService extends Service { } @Override + public void addConferenceParticipants(String callId, List<Uri> participants, + Session.Info sessionInfo) { + Log.startSession(sessionInfo, SESSION_ADD_PARTICIPANT); + try { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = callId; + args.arg2 = participants; + args.arg3 = Log.createSubsession(); + mHandler.obtainMessage(MSG_ADD_PARTICIPANT, args).sendToTarget(); + } finally { + Log.endSession(); + } + } + + @Override public void onPostDialContinue(String callId, boolean proceed, Session.Info sessionInfo) { Log.startSession(sessionInfo, SESSION_POST_DIAL_CONT); try { @@ -1224,6 +1240,19 @@ public abstract class ConnectionService extends Service { } break; } + case MSG_ADD_PARTICIPANT: { + SomeArgs args = (SomeArgs) msg.obj; + try { + Log.continueSession((Session) args.arg3, + SESSION_HANDLER + SESSION_ADD_PARTICIPANT); + addConferenceParticipants((String) args.arg1, (List<Uri>)args.arg2); + } finally { + args.recycle(); + Log.endSession(); + } + break; + } + case MSG_ON_POST_DIAL_CONTINUE: { SomeArgs args = (SomeArgs) msg.obj; try { @@ -1778,7 +1807,7 @@ public abstract class ConnectionService extends Service { null : conference.getVideoProvider().getInterface(), conference.getVideoState(), conference.getConnectTimeMillis(), - conference.getConnectionStartElapsedRealTime(), + conference.getConnectionStartElapsedRealtimeMillis(), conference.getStatusHints(), conference.getExtras(), conference.getAddress(), @@ -1884,7 +1913,7 @@ public abstract class ConnectionService extends Service { connection.isRingbackRequested(), connection.getAudioModeIsVoip(), connection.getConnectTimeMillis(), - connection.getConnectElapsedTimeMillis(), + connection.getConnectionStartElapsedRealtimeMillis(), connection.getStatusHints(), connection.getDisconnectCause(), createIdList(connection.getConferenceables()), @@ -2152,6 +2181,17 @@ public abstract class ConnectionService extends Service { } } + private void addConferenceParticipants(String callId, List<Uri> participants) { + Log.d(this, "addConferenceParticipants(%s)", callId); + if (mConnectionById.containsKey(callId)) { + findConnectionForAction(callId, "addConferenceParticipants") + .onAddConferenceParticipants(participants); + } else { + findConferenceForAction(callId, "addConferenceParticipants") + .onAddConferenceParticipants(participants); + } + } + /** * Notifies a {@link Connection} of a request to pull an external call. * @@ -2374,7 +2414,7 @@ public abstract class ConnectionService extends Service { null : conference.getVideoProvider().getInterface(), conference.getVideoState(), conference.getConnectTimeMillis(), - conference.getConnectionStartElapsedRealTime(), + conference.getConnectionStartElapsedRealtimeMillis(), conference.getStatusHints(), conference.getExtras(), conference.getAddress(), @@ -2465,7 +2505,7 @@ public abstract class ConnectionService extends Service { connection.isRingbackRequested(), connection.getAudioModeIsVoip(), connection.getConnectTimeMillis(), - connection.getConnectElapsedTimeMillis(), + connection.getConnectionStartElapsedRealtimeMillis(), connection.getStatusHints(), connection.getDisconnectCause(), emptyList, diff --git a/telecomm/java/android/telecom/InCallAdapter.java b/telecomm/java/android/telecom/InCallAdapter.java index 594c1eb392b3..9d29174059ad 100644 --- a/telecomm/java/android/telecom/InCallAdapter.java +++ b/telecomm/java/android/telecom/InCallAdapter.java @@ -283,6 +283,20 @@ public final class InCallAdapter { } /** + * Instructs Telecom to pull participants to existing call + * + * @param callId The unique ID of the call. + * @param participants participants to be pulled to existing call. + */ + public void addConferenceParticipants(String callId, List<Uri> participants) { + try { + mAdapter.addConferenceParticipants(callId, participants); + } catch (RemoteException ignored) { + } + } + + + /** * Instructs Telecom to split the specified call from any conference call with which it may be * connected. * diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java index ebfa3a15639a..982e5f30e28c 100644 --- a/telecomm/java/android/telecom/InCallService.java +++ b/telecomm/java/android/telecom/InCallService.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.SdkConstant; import android.annotation.SystemApi; import android.app.Service; +import android.app.UiModeManager; import android.bluetooth.BluetoothDevice; import android.content.Intent; import android.hardware.camera2.CameraManager; @@ -43,12 +44,32 @@ import java.util.List; * phone calls. * <h2>Becoming the Default Phone App</h2> * The default dialer/phone app is one which provides the in-call user interface while the device is - * in a call. A device is bundled with a system provided default dialer/phone app. The user may - * choose a single app to take over this role from the system app. An app which wishes to fulfill - * one this role uses the {@code android.app.role.RoleManager} to request that they fill the role. + * in a call. It also provides the user with a means to initiate calls and see a history of calls + * on their device. A device is bundled with a system provided default dialer/phone app. The user + * may choose a single app to take over this role from the system app. An app which wishes to + * fulfill one this role uses the {@link android.app.role.RoleManager} to request that they fill the + * {@link android.app.role.RoleManager#ROLE_DIALER} role. * <p> - * An app filling the role of the default phone app provides a user interface while the device is in - * a call, and the device is not in car mode. + * The default phone app provides a user interface while the device is in a call, and the device is + * not in car mode (i.e. {@link UiModeManager#getCurrentModeType()} is not + * {@link android.content.res.Configuration#UI_MODE_TYPE_CAR}). + * <p> + * In order to fill the {@link android.app.role.RoleManager#ROLE_DIALER} role, an app must meet a + * number of requirements: + * <ul> + * <li>It must handle the {@link Intent#ACTION_DIAL} intent. This means the app must provide + * a dial pad UI for the user to initiate outgoing calls.</li> + * <li>It must fully implement the {@link InCallService} API and provide both an incoming call + * UI, as well as an ongoing call UI.</li> + * </ul> + * <p> + * Note: If the app filling the {@link android.app.role.RoleManager#ROLE_DIALER} crashes during + * {@link InCallService} binding, the Telecom framework will automatically fall back to using the + * dialer app pre-loaded on the device. The system will display a notification to the user to let + * them know that the app has crashed and that their call was continued using the pre-loaded dialer + * app. + * <p> + * Further, the pre-loaded dialer will ALWAYS be used when the user places an emergency call. * <p> * Below is an example manifest registration for an {@code InCallService}. The meta-data * {@link TelecomManager#METADATA_IN_CALL_SERVICE_UI} indicates that this particular @@ -82,6 +103,11 @@ import java.util.List; * <action android:name="android.intent.action.DIAL" /> * <category android:name="android.intent.category.DEFAULT" /> * </intent-filter> + * <intent-filter> + * <action android:name="android.intent.action.DIAL" /> + * <category android:name="android.intent.category.DEFAULT" /> + * <data android:scheme="tel" /> + * </intent-filter> * </activity> * } * </pre> @@ -111,6 +137,7 @@ import java.util.List; * } * } * } + * } * </pre> * <p id="incomingCallNotification"> * <h3>Showing the Incoming Call Notification</h3> diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java index f00432b5fad3..4e6e1a53113e 100644 --- a/telecomm/java/android/telecom/PhoneAccount.java +++ b/telecomm/java/android/telecom/PhoneAccount.java @@ -16,7 +16,10 @@ package android.telecom; +import static android.Manifest.permission.MODIFY_PHONE_STATE; + import android.annotation.NonNull; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.TestApi; import android.content.Intent; @@ -614,7 +617,8 @@ public final class PhoneAccount implements Parcelable { * time. By default, there is no group Id for a {@link PhoneAccount} (an empty String). Only * grouped {@link PhoneAccount}s with the same {@link ConnectionService} can be replaced. * <p> - * Note: This is an API specific to the Telephony stack. + * Note: This is an API specific to the Telephony stack; the group Id will be ignored for + * callers not holding the correct permission. * * @param groupId The group Id of the {@link PhoneAccount} that will replace any other * registered {@link PhoneAccount} in Telecom with the same Group Id. @@ -623,6 +627,7 @@ public final class PhoneAccount implements Parcelable { */ @SystemApi @TestApi + @RequiresPermission(MODIFY_PHONE_STATE) public @NonNull Builder setGroupId(@NonNull String groupId) { if (groupId != null) { mGroupId = groupId; diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index a28cc4f69155..5d7d6490ba3e 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -819,8 +819,8 @@ public class TelecomManager { * automatically add dialing prefixes when placing international calls. * <p> * Setting this extra on the outgoing call extras will cause the - * {@link Connection#PROPERTY_ASSISTED_DIALING_USED} property and - * {@link Call.Details#PROPERTY_ASSISTED_DIALING_USED} property to be set on the + * {@link Connection#PROPERTY_ASSISTED_DIALING} property and + * {@link Call.Details#PROPERTY_ASSISTED_DIALING} property to be set on the * {@link Connection}/{@link Call} in question. When the call is logged to the call log, the * {@link android.provider.CallLog.Calls#FEATURES_ASSISTED_DIALING_USED} call feature is set to * indicate that assisted dialing was used for the call. @@ -1412,7 +1412,7 @@ public class TelecomManager { /** * Used to determine the currently selected default dialer package for a specific user. * - * @param userId the user id to query the default dialer package for. + * @param userHandle the user id to query the default dialer package for. * @return package name for the default dialer package or null if no package has been * selected as the default dialer. * @hide @@ -1420,10 +1420,11 @@ public class TelecomManager { @SystemApi @TestApi @RequiresPermission(READ_PRIVILEGED_PHONE_STATE) - public @Nullable String getDefaultDialerPackage(int userId) { + public @Nullable String getDefaultDialerPackage(@NonNull UserHandle userHandle) { try { if (isServiceConnected()) { - return getTelecomService().getDefaultDialerPackageForUser(userId); + return getTelecomService().getDefaultDialerPackageForUser( + userHandle.getIdentifier()); } } catch (RemoteException e) { Log.e(TAG, "RemoteException attempting to get the default dialer package name.", e); diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl index 4249dff151c7..a397d77db2f6 100644 --- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl +++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl @@ -104,6 +104,9 @@ oneway interface IConnectionService { void swapConference(String conferenceCallId, in Session.Info sessionInfo); + void addConferenceParticipants(String CallId, in List<Uri> participants, + in Session.Info sessionInfo); + void onPostDialContinue(String callId, boolean proceed, in Session.Info sessionInfo); void pullExternalCall(String callId, in Session.Info sessionInfo); diff --git a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl index eb2d714fe3f4..9beff22ce52e 100644 --- a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl +++ b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl @@ -67,6 +67,8 @@ oneway interface IInCallAdapter { void swapConference(String callId); + void addConferenceParticipants(String callId, in List<Uri> participants); + void turnOnProximitySensor(); void turnOffProximitySensor(boolean screenOnImmediately); diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java index d2a5905f7a99..a27c4802c306 100644 --- a/telephony/java/android/telephony/Annotation.java +++ b/telephony/java/android/telephony/Annotation.java @@ -661,4 +661,16 @@ public class Annotation { }) @Retention(RetentionPolicy.SOURCE) public @interface Skip464XlatStatus {} + + /** + * Override network type + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "OVERRIDE_NETWORK_TYPE_", value = { + DisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, + DisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA, + DisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO, + DisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA, + DisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE}) + public @interface OverrideNetworkType {} } diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index ebb53c50ca98..51b4a31ea8b2 100755 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1090,6 +1090,14 @@ public class CarrierConfigManager { "support_adhoc_conference_calls_bool"; /** + * Determines whether conference participants can be added to existing call. When {@code true}, + * adding conference participants to existing call is supported, {@code false otherwise}. + * @hide + */ + public static final String KEY_SUPPORT_ADD_CONFERENCE_PARTICIPANTS_BOOL = + "support_add_conference_participants_bool"; + + /** * Determines whether conference calls are supported by a carrier. When {@code true}, * conference calling is supported, {@code false otherwise}. */ @@ -4004,6 +4012,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_IGNORE_RTT_MODE_SETTING_BOOL, false); sDefaults.putInt(KEY_CDMA_3WAYCALL_FLASH_DELAY_INT , 0); sDefaults.putBoolean(KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL, false); + sDefaults.putBoolean(KEY_SUPPORT_ADD_CONFERENCE_PARTICIPANTS_BOOL, false); sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true); sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL, true); sDefaults.putBoolean(KEY_SUPPORT_MANAGE_IMS_CONFERENCE_CALL_BOOL, true); diff --git a/telephony/java/android/telephony/DisplayInfo.aidl b/telephony/java/android/telephony/DisplayInfo.aidl new file mode 100644 index 000000000000..861b0fe04848 --- /dev/null +++ b/telephony/java/android/telephony/DisplayInfo.aidl @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.telephony; + +parcelable DisplayInfo; diff --git a/telephony/java/android/telephony/DisplayInfo.java b/telephony/java/android/telephony/DisplayInfo.java new file mode 100644 index 000000000000..d54bcf931c33 --- /dev/null +++ b/telephony/java/android/telephony/DisplayInfo.java @@ -0,0 +1,172 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; +import android.telephony.Annotation.NetworkType; +import android.telephony.Annotation.OverrideNetworkType; + +import java.util.Objects; + +/** + * DisplayInfo contains telephony-related information used for display purposes only. This + * information is provided in accordance with carrier policy and branding preferences; it is not + * necessarily a precise or accurate representation of the current state and should be treated + * accordingly. + */ +public final class DisplayInfo implements Parcelable { + /** + * No override. {@link #getNetworkType()} should be used for display network + * type. + */ + public static final int OVERRIDE_NETWORK_TYPE_NONE = 0; + + /** + * Override network type when the device is connected to + * {@link TelephonyManager#NETWORK_TYPE_LTE} cellular network and is using carrier aggregation. + */ + public static final int OVERRIDE_NETWORK_TYPE_LTE_CA = 1; + + /** + * Override network type when the device is connected to advanced pro + * {@link TelephonyManager#NETWORK_TYPE_LTE} cellular network. + */ + public static final int OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO = 2; + + /** + * Override network type when the device is connected to + * {@link TelephonyManager#NETWORK_TYPE_LTE} network and has E-UTRA-NR Dual Connectivity(EN-DC) + * capability or is currently connected to the secondary + * {@link TelephonyManager#NETWORK_TYPE_NR} cellular network. + */ + public static final int OVERRIDE_NETWORK_TYPE_NR_NSA = 3; + + /** + * Override network type when the device is connected to + * {@link TelephonyManager#NETWORK_TYPE_LTE} network and has E-UTRA-NR Dual Connectivity(EN-DC) + * capability or is currently connected to the secondary + * {@link TelephonyManager#NETWORK_TYPE_NR} cellular network on millimeter wave bands. + * + * @see AccessNetworkConstants.NgranBands#FREQUENCY_RANGE_GROUP_2 + */ + public static final int OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE = 4; + + @NetworkType + private final int mNetworkType; + + @OverrideNetworkType + private final int mOverrideNetworkType; + + /** + * Constructor + * + * @param networkType Current packet-switching cellular network type + * @param overrideNetworkType The override network type + * + * @hide + */ + public DisplayInfo(@NetworkType int networkType, @OverrideNetworkType int overrideNetworkType) { + mNetworkType = networkType; + mOverrideNetworkType = overrideNetworkType; + } + + /** @hide */ + public DisplayInfo(Parcel p) { + mNetworkType = p.readInt(); + mOverrideNetworkType = p.readInt(); + } + + /** + * Get current packet-switching cellular network type. This is the actual network type the + * device is camped on. + * + * @return The network type. + */ + @NetworkType + public int getNetworkType() { + return mNetworkType; + } + + /** + * Get the override network type. Note the override network type is for market branding + * or visualization purposes only. It cannot be treated as the actual network type device is + * camped on. + * + * @return The override network type. + */ + @OverrideNetworkType + public int getOverrideNetworkType() { + return mOverrideNetworkType; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mNetworkType); + dest.writeInt(mOverrideNetworkType); + } + + public static final @NonNull Parcelable.Creator<DisplayInfo> CREATOR = + new Parcelable.Creator<DisplayInfo>() { + @Override + public DisplayInfo createFromParcel(Parcel source) { + return new DisplayInfo(source); + } + + @Override + public DisplayInfo[] newArray(int size) { + return new DisplayInfo[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DisplayInfo that = (DisplayInfo) o; + return mNetworkType == that.mNetworkType + && mOverrideNetworkType == that.mOverrideNetworkType; + } + + @Override + public int hashCode() { + return Objects.hash(mNetworkType, mOverrideNetworkType); + } + + private static String overrideNetworkTypeToString(@OverrideNetworkType int type) { + switch (type) { + case OVERRIDE_NETWORK_TYPE_NONE: return "NONE"; + case OVERRIDE_NETWORK_TYPE_LTE_CA: return "LTE_CA"; + case OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO: return "LTE_ADV_PRO"; + case OVERRIDE_NETWORK_TYPE_NR_NSA: return "NR_NSA"; + case OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE: return "NR_NSA_MMWAVE"; + default: return "UNKNOWN"; + } + } + + @Override + public String toString() { + return "DisplayInfo {network=" + TelephonyManager.getNetworkTypeName(mNetworkType) + + ", override=" + overrideNetworkTypeToString(mOverrideNetworkType); + } +} diff --git a/telephony/java/android/telephony/PreciseDisconnectCause.java b/telephony/java/android/telephony/PreciseDisconnectCause.java index 54980a29c0a6..250d9e8b212e 100644 --- a/telephony/java/android/telephony/PreciseDisconnectCause.java +++ b/telephony/java/android/telephony/PreciseDisconnectCause.java @@ -256,337 +256,6 @@ public final class PreciseDisconnectCause { /** Access Blocked by CDMA network. */ public static final int CDMA_ACCESS_BLOCKED = 1009; - /** Mapped from ImsReasonInfo */ - // TODO: remove ImsReasonInfo from preciseDisconnectCause - /* The passed argument is an invalid */ - /** @hide */ - public static final int LOCAL_ILLEGAL_ARGUMENT = 1200; - // The operation is invoked in invalid call state - /** @hide */ - public static final int LOCAL_ILLEGAL_STATE = 1201; - // IMS service internal error - /** @hide */ - public static final int LOCAL_INTERNAL_ERROR = 1202; - // IMS service goes down (service connection is lost) - /** @hide */ - public static final int LOCAL_IMS_SERVICE_DOWN = 1203; - // No pending incoming call exists - /** @hide */ - public static final int LOCAL_NO_PENDING_CALL = 1204; - // Service unavailable; by power off - /** @hide */ - public static final int LOCAL_POWER_OFF = 1205; - // Service unavailable; by low battery - /** @hide */ - public static final int LOCAL_LOW_BATTERY = 1206; - // Service unavailable; by out of service (data service state) - /** @hide */ - public static final int LOCAL_NETWORK_NO_SERVICE = 1207; - /* Service unavailable; by no LTE coverage - * (VoLTE is not supported even though IMS is registered) - */ - /** @hide */ - public static final int LOCAL_NETWORK_NO_LTE_COVERAGE = 1208; - /** Service unavailable; by located in roaming area */ - /** @hide */ - public static final int LOCAL_NETWORK_ROAMING = 1209; - /** Service unavailable; by IP changed */ - /** @hide */ - public static final int LOCAL_NETWORK_IP_CHANGED = 1210; - /** Service unavailable; other */ - /** @hide */ - public static final int LOCAL_SERVICE_UNAVAILABLE = 1211; - /* Service unavailable; IMS connection is lost (IMS is not registered) */ - /** @hide */ - public static final int LOCAL_NOT_REGISTERED = 1212; - /** Max call exceeded */ - /** @hide */ - public static final int LOCAL_MAX_CALL_EXCEEDED = 1213; - /** Call decline */ - /** @hide */ - public static final int LOCAL_CALL_DECLINE = 1214; - /** SRVCC is in progress */ - /** @hide */ - public static final int LOCAL_CALL_VCC_ON_PROGRESSING = 1215; - /** Resource reservation is failed (QoS precondition) */ - /** @hide */ - public static final int LOCAL_CALL_RESOURCE_RESERVATION_FAILED = 1216; - /** Retry CS call; VoLTE service can't be provided by the network or remote end - * Resolve the extra code(EXTRA_CODE_CALL_RETRY_*) if the below code is set - * @hide - */ - public static final int LOCAL_CALL_CS_RETRY_REQUIRED = 1217; - /** Retry VoLTE call; VoLTE service can't be provided by the network temporarily */ - /** @hide */ - public static final int LOCAL_CALL_VOLTE_RETRY_REQUIRED = 1218; - /** IMS call is already terminated (in TERMINATED state) */ - /** @hide */ - public static final int LOCAL_CALL_TERMINATED = 1219; - /** Handover not feasible */ - /** @hide */ - public static final int LOCAL_HO_NOT_FEASIBLE = 1220; - - /** 1xx waiting timer is expired after sending INVITE request (MO only) */ - /** @hide */ - public static final int TIMEOUT_1XX_WAITING = 1221; - /** User no answer during call setup operation (MO/MT) - * MO : 200 OK to INVITE request is not received, - * MT : No action from user after alerting the call - * @hide - */ - public static final int TIMEOUT_NO_ANSWER = 1222; - /** User no answer during call update operation (MO/MT) - * MO : 200 OK to re-INVITE request is not received, - * MT : No action from user after alerting the call - * @hide - */ - public static final int TIMEOUT_NO_ANSWER_CALL_UPDATE = 1223; - - /** - * STATUSCODE (SIP response code) (IMS -> Telephony) - */ - /** SIP request is redirected */ - /** @hide */ - public static final int SIP_REDIRECTED = 1300; - /** 4xx responses */ - /** 400 : Bad Request */ - /** @hide */ - public static final int SIP_BAD_REQUEST = 1310; - /** 403 : Forbidden */ - /** @hide */ - public static final int SIP_FORBIDDEN = 1311; - /** 404 : Not Found */ - /** @hide */ - public static final int SIP_NOT_FOUND = 1312; - /** 415 : Unsupported Media Type - * 416 : Unsupported URI Scheme - * 420 : Bad Extension - */ - /** @hide */ - public static final int SIP_NOT_SUPPORTED = 1313; - /** 408 : Request Timeout */ - /** @hide */ - public static final int SIP_REQUEST_TIMEOUT = 1314; - /** 480 : Temporarily Unavailable */ - /** @hide */ - public static final int SIP_TEMPRARILY_UNAVAILABLE = 1315; - /** 484 : Address Incomplete */ - /** @hide */ - public static final int SIP_BAD_ADDRESS = 1316; - /** 486 : Busy Here - * 600 : Busy Everywhere - */ - /** @hide */ - public static final int SIP_BUSY = 1317; - /** 487 : Request Terminated */ - /** @hide */ - public static final int SIP_REQUEST_CANCELLED = 1318; - /** 406 : Not Acceptable - * 488 : Not Acceptable Here - * 606 : Not Acceptable - */ - /** @hide */ - public static final int SIP_NOT_ACCEPTABLE = 1319; - /** 410 : Gone - * 604 : Does Not Exist Anywhere - */ - /** @hide */ - public static final int SIP_NOT_REACHABLE = 1320; - /** Others */ - /** @hide */ - public static final int SIP_CLIENT_ERROR = 1321; - /** 481 : Transaction Does Not Exist */ - /** @hide */ - public static final int SIP_TRANSACTION_DOES_NOT_EXIST = 1322; - /** 5xx responses - * 501 : Server Internal Error - */ - /** @hide */ - public static final int SIP_SERVER_INTERNAL_ERROR = 1330; - /** 503 : Service Unavailable */ - /** @hide */ - public static final int SIP_SERVICE_UNAVAILABLE = 1331; - /** 504 : Server Time-out */ - /** @hide */ - public static final int SIP_SERVER_TIMEOUT = 1332; - /** Others */ - /** @hide */ - public static final int SIP_SERVER_ERROR = 1333; - /** 6xx responses - * 603 : Decline - */ - /** @hide */ - public static final int SIP_USER_REJECTED = 1340; - /** Others */ - /** @hide */ - public static final int SIP_GLOBAL_ERROR = 1341; - /** Emergency failure */ - /** @hide */ - public static final int EMERGENCY_TEMP_FAILURE = 1342; - /** @hide */ - public static final int EMERGENCY_PERM_FAILURE = 1343; - /** Media resource initialization failed */ - /** @hide */ - public static final int MEDIA_INIT_FAILED = 1400; - /** RTP timeout (no audio / video traffic in the session) */ - /** @hide */ - public static final int MEDIA_NO_DATA = 1401; - /** Media is not supported; so dropped the call */ - /** @hide */ - public static final int MEDIA_NOT_ACCEPTABLE = 1402; - /** Unknown media related errors */ - /** @hide */ - public static final int MEDIA_UNSPECIFIED = 1403; - /** User triggers the call end */ - /** @hide */ - public static final int USER_TERMINATED = 1500; - /** No action while an incoming call is ringing */ - /** @hide */ - public static final int USER_NOANSWER = 1501; - /** User ignores an incoming call */ - /** @hide */ - public static final int USER_IGNORE = 1502; - /** User declines an incoming call */ - /** @hide */ - public static final int USER_DECLINE = 1503; - /** Device declines/ends a call due to low battery */ - /** @hide */ - public static final int LOW_BATTERY = 1504; - /** Device declines call due to blacklisted call ID */ - /** @hide */ - public static final int BLACKLISTED_CALL_ID = 1505; - /** The call is terminated by the network or remote user */ - /** @hide */ - public static final int USER_TERMINATED_BY_REMOTE = 1510; - - /** - * UT - */ - /** @hide */ - public static final int UT_NOT_SUPPORTED = 1800; - /** @hide */ - public static final int UT_SERVICE_UNAVAILABLE = 1801; - /** @hide */ - public static final int UT_OPERATION_NOT_ALLOWED = 1802; - /** @hide */ - public static final int UT_NETWORK_ERROR = 1803; - /** @hide */ - public static final int UT_CB_PASSWORD_MISMATCH = 1804; - - /** - * ECBM - * @hide - */ - public static final int ECBM_NOT_SUPPORTED = 1900; - - /** - * Fail code used to indicate that Multi-endpoint is not supported by the Ims framework. - * @hide - */ - public static final int MULTIENDPOINT_NOT_SUPPORTED = 1901; - - /** - * CALL DROP error codes (Call could drop because of many reasons like Network not available, - * handover, failed, etc) - */ - - /** - * CALL DROP error code for the case when a device is ePDG capable and when the user is on an - * active wifi call and at the edge of coverage and there is no qualified LTE network available - * to handover the call to. We get a handover NOT_TRIGERRED message from the modem. This error - * code is received as part of the handover message. - * @hide - */ - public static final int CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE = 2000; - - /** - * MT call has ended due to a release from the network - * because the call was answered elsewhere - * @hide - */ - public static final int ANSWERED_ELSEWHERE = 2100; - - /** - * For MultiEndpoint - Call Pull request has failed - * @hide - */ - public static final int CALL_PULL_OUT_OF_SYNC = 2101; - - /** - * For MultiEndpoint - Call has been pulled from primary to secondary - * @hide - */ - public static final int CALL_PULLED = 2102; - - /** - * Supplementary services (HOLD/RESUME) failure error codes. - * Values for Supplemetary services failure - Failed, Cancelled and Re-Invite collision. - * @hide - */ - public static final int SUPP_SVC_FAILED = 2300; - /** @hide */ - public static final int SUPP_SVC_CANCELLED = 2301; - /** @hide */ - public static final int SUPP_SVC_REINVITE_COLLISION = 2302; - - /** - * DPD Procedure received no response or send failed - * @hide - */ - public static final int IWLAN_DPD_FAILURE = 2400; - - /** - * Establishment of the ePDG Tunnel Failed - * @hide - */ - public static final int EPDG_TUNNEL_ESTABLISH_FAILURE = 2500; - - /** - * Re-keying of the ePDG Tunnel Failed; may not always result in teardown - * @hide - */ - public static final int EPDG_TUNNEL_REKEY_FAILURE = 2501; - - /** - * Connection to the packet gateway is lost - * @hide - */ - public static final int EPDG_TUNNEL_LOST_CONNECTION = 2502; - - /** - * The maximum number of calls allowed has been reached. Used in a multi-endpoint scenario - * where the number of calls across all connected devices has reached the maximum. - * @hide - */ - public static final int MAXIMUM_NUMBER_OF_CALLS_REACHED = 2503; - - /** - * Similar to {@link #CODE_LOCAL_CALL_DECLINE}, except indicates that a remote device has - * declined the call. Used in a multi-endpoint scenario where a remote device declined an - * incoming call. - * @hide - */ - public static final int REMOTE_CALL_DECLINE = 2504; - - /** - * Indicates the call was disconnected due to the user reaching their data limit. - * @hide - */ - public static final int DATA_LIMIT_REACHED = 2505; - - /** - * Indicates the call was disconnected due to the user disabling cellular data. - * @hide - */ - public static final int DATA_DISABLED = 2506; - - /** - * Indicates a call was disconnected due to loss of wifi signal. - * @hide - */ - public static final int WIFI_LOST = 2507; - - /* OEM specific error codes. To be used by OEMs when they don't want to reveal error code which would be replaced by ERROR_UNSPECIFIED */ public static final int OEM_CAUSE_1 = 0xf001; diff --git a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java index 20d0e9690e8e..ae20cae36839 100644 --- a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java +++ b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java @@ -430,10 +430,9 @@ public class ApkVerityTest extends BaseHostJUnit4Test { private void verifyInstalledFiles(String... filenames) throws DeviceNotAvailableException { String apkPath = getApkPath(TARGET_PACKAGE); String appDir = apkPath.substring(0, apkPath.lastIndexOf("/")); + // Exclude directories since we only care about files. HashSet<String> actualFiles = new HashSet<>(Arrays.asList( - expectRemoteCommandToSucceed("ls " + appDir).split("\n"))); - assertTrue(actualFiles.remove("lib")); - assertTrue(actualFiles.remove("oat")); + expectRemoteCommandToSucceed("ls -p " + appDir + " | grep -v '/'").split("\n"))); HashSet<String> expectedFiles = new HashSet<>(Arrays.asList(filenames)); assertEquals(expectedFiles, actualFiles); diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java index 155c61f3f8c7..eb78529e8715 100644 --- a/tests/net/java/com/android/server/connectivity/VpnTest.java +++ b/tests/net/java/com/android/server/connectivity/VpnTest.java @@ -148,6 +148,7 @@ public class VpnTest { @Mock private AppOpsManager mAppOps; @Mock private NotificationManager mNotificationManager; @Mock private Vpn.SystemServices mSystemServices; + @Mock private Vpn.Ikev2SessionCreator mIkev2SessionCreator; @Mock private ConnectivityManager mConnectivityManager; @Mock private KeyStore mKeyStore; private final VpnProfile mVpnProfile = new VpnProfile("key"); @@ -867,7 +868,8 @@ public class VpnTest { * Mock some methods of vpn object. */ private Vpn createVpn(@UserIdInt int userId) { - return new Vpn(Looper.myLooper(), mContext, mNetService, userId, mSystemServices); + return new Vpn(Looper.myLooper(), mContext, mNetService, + userId, mSystemServices, mIkev2SessionCreator); } private static void assertBlocked(Vpn vpn, int... uids) { diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index b6f4490a1872..f693315c6cff 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -28,6 +28,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; import android.app.ActivityManager; @@ -1621,11 +1622,13 @@ public class WifiManager { * @param wifiConfiguration WifiConfiguration object corresponding to the network * user selected. */ + @SuppressLint("CallbackMethodName") default void select(@NonNull WifiConfiguration wifiConfiguration) {} /** * User rejected the app's request. */ + @SuppressLint("CallbackMethodName") default void reject() {} } diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java index 99901808ec3e..9c01d3643c19 100644 --- a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java +++ b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java @@ -738,7 +738,16 @@ public final class Credential implements Parcelable { @Override public String toString() { StringBuilder builder = new StringBuilder(); - builder.append("IMSI: ").append(mImsi).append("\n"); + String imsi; + if (mImsi != null) { + if (mImsi.length() > 6 && mImsi.charAt(6) != '*') { + // Truncate the full IMSI from the log + imsi = mImsi.substring(0, 6) + "****"; + } else { + imsi = mImsi; + } + builder.append("IMSI: ").append(imsi).append("\n"); + } builder.append("EAPType: ").append(mEapType).append("\n"); return builder.toString(); } diff --git a/wifi/java/android/net/wifi/wificond/WifiCondManager.java b/wifi/java/android/net/wifi/wificond/WifiNl80211Manager.java index 61f18e0b7191..89f642fdbb66 100644 --- a/wifi/java/android/net/wifi/wificond/WifiCondManager.java +++ b/wifi/java/android/net/wifi/wificond/WifiNl80211Manager.java @@ -49,15 +49,16 @@ import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; /** - * This class encapsulates the interface the wificond daemon presents to the Wi-Fi framework. The + * This class encapsulates the interface the wificond daemon presents to the Wi-Fi framework - used + * to encapsulate the Wi-Fi 80211nl management interface. The * interface is only for use by the Wi-Fi framework and access is protected by SELinux permissions. * * @hide */ @SystemApi -@SystemService(Context.WIFI_COND_SERVICE) -public class WifiCondManager { - private static final String TAG = "WifiCondManager"; +@SystemService(Context.WIFI_NL80211_SERVICE) +public class WifiNl80211Manager { + private static final String TAG = "WifiNl80211Manager"; private boolean mVerboseLoggingEnabled = false; /** @@ -316,14 +317,14 @@ public class WifiCondManager { public static final int SEND_MGMT_FRAME_ERROR_ALREADY_STARTED = 5; /** @hide */ - public WifiCondManager(Context context) { + public WifiNl80211Manager(Context context) { mAlarmManager = context.getSystemService(AlarmManager.class); mEventHandler = new Handler(context.getMainLooper()); } /** @hide */ @VisibleForTesting - public WifiCondManager(Context context, IWificond wificond) { + public WifiNl80211Manager(Context context, IWificond wificond) { this(context); mWificond = wificond; } @@ -485,7 +486,7 @@ public class WifiCondManager { } /** - * Enable or disable verbose logging of the WifiCondManager module. + * Enable or disable verbose logging of the WifiNl80211Manager module. * @param enable True to enable verbose logging. False to disable verbose logging. */ public void enableVerboseLogging(boolean enable) { @@ -493,7 +494,7 @@ public class WifiCondManager { } /** - * Register a death notification for the WifiCondManager which acts as a proxy for the + * Register a death notification for the WifiNl80211Manager which acts as a proxy for the * wificond daemon (i.e. the death listener will be called when and if the wificond daemon * dies). * @@ -518,7 +519,7 @@ public class WifiCondManager { // We already have a wificond handle. return true; } - IBinder binder = ServiceManager.getService(Context.WIFI_COND_SERVICE); + IBinder binder = ServiceManager.getService(Context.WIFI_NL80211_SERVICE); mWificond = IWificond.Stub.asInterface(binder); if (mWificond == null) { Log.e(TAG, "Failed to get reference to wificond"); diff --git a/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java b/wifi/tests/src/android/net/wifi/wificond/WifiNl80211ManagerTest.java index b745a341b459..a8184068ff5a 100644 --- a/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java +++ b/wifi/tests/src/android/net/wifi/wificond/WifiNl80211ManagerTest.java @@ -71,10 +71,10 @@ import java.util.List; import java.util.Set; /** - * Unit tests for {@link android.net.wifi.WifiCondManager}. + * Unit tests for {@link android.net.wifi.wificond.WifiNl80211Manager}. */ @SmallTest -public class WifiCondManagerTest { +public class WifiNl80211ManagerTest { @Mock private IWificond mWificond; @Mock @@ -86,21 +86,21 @@ public class WifiCondManagerTest { @Mock private IApInterface mApInterface; @Mock - private WifiCondManager.SoftApCallback mSoftApListener; + private WifiNl80211Manager.SoftApCallback mSoftApListener; @Mock - private WifiCondManager.SendMgmtFrameCallback mSendMgmtFrameCallback; + private WifiNl80211Manager.SendMgmtFrameCallback mSendMgmtFrameCallback; @Mock - private WifiCondManager.ScanEventCallback mNormalScanCallback; + private WifiNl80211Manager.ScanEventCallback mNormalScanCallback; @Mock - private WifiCondManager.ScanEventCallback mPnoScanCallback; + private WifiNl80211Manager.ScanEventCallback mPnoScanCallback; @Mock - private WifiCondManager.PnoScanRequestCallback mPnoScanRequestCallback; + private WifiNl80211Manager.PnoScanRequestCallback mPnoScanRequestCallback; @Mock private Context mContext; private TestLooper mLooper; private TestAlarmManager mTestAlarmManager; private AlarmManager mAlarmManager; - private WifiCondManager mWificondControl; + private WifiNl80211Manager mWificondControl; private static final String TEST_INTERFACE_NAME = "test_wlan_if"; private static final String TEST_INTERFACE_NAME1 = "test_wlan_if1"; private static final String TEST_INVALID_INTERFACE_NAME = "asdf"; @@ -180,7 +180,7 @@ public class WifiCondManagerTest { when(mWificond.tearDownApInterface(any())).thenReturn(true); when(mClientInterface.getWifiScannerImpl()).thenReturn(mWifiScannerImpl); when(mClientInterface.getInterfaceName()).thenReturn(TEST_INTERFACE_NAME); - mWificondControl = new WifiCondManager(mContext, mWificond); + mWificondControl = new WifiNl80211Manager(mContext, mWificond); assertEquals(true, mWificondControl.setupInterfaceForClientMode(TEST_INTERFACE_NAME, Runnable::run, mNormalScanCallback, mPnoScanCallback)); @@ -492,7 +492,7 @@ public class WifiCondManagerTest { // getScanResults should fail. assertEquals(0, mWificondControl.getScanResults(TEST_INTERFACE_NAME, - WifiCondManager.SCAN_TYPE_SINGLE_SCAN).size()); + WifiNl80211Manager.SCAN_TYPE_SINGLE_SCAN).size()); } /** @@ -786,10 +786,10 @@ public class WifiCondManagerTest { */ @Test public void testSendMgmtFrameCalledTwiceBeforeFinished() throws Exception { - WifiCondManager.SendMgmtFrameCallback cb1 = mock( - WifiCondManager.SendMgmtFrameCallback.class); - WifiCondManager.SendMgmtFrameCallback cb2 = mock( - WifiCondManager.SendMgmtFrameCallback.class); + WifiNl80211Manager.SendMgmtFrameCallback cb1 = mock( + WifiNl80211Manager.SendMgmtFrameCallback.class); + WifiNl80211Manager.SendMgmtFrameCallback cb2 = mock( + WifiNl80211Manager.SendMgmtFrameCallback.class); mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, TEST_PROBE_FRAME, TEST_MCS_RATE, Runnable::run, cb1); @@ -800,7 +800,7 @@ public class WifiCondManagerTest { mWificondControl.sendMgmtFrame(TEST_INTERFACE_NAME, TEST_PROBE_FRAME, TEST_MCS_RATE, Runnable::run, cb2); - verify(cb2).onFailure(WifiCondManager.SEND_MGMT_FRAME_ERROR_ALREADY_STARTED); + verify(cb2).onFailure(WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_ALREADY_STARTED); // verify SendMgmtFrame() still was only called once i.e. not called again verify(mClientInterface, times(1)) .SendMgmtFrame(any(), any(), anyInt()); @@ -811,8 +811,8 @@ public class WifiCondManagerTest { */ @Test public void testSendMgmtFrameThrowsException() throws Exception { - WifiCondManager.SendMgmtFrameCallback cb = mock( - WifiCondManager.SendMgmtFrameCallback.class); + WifiNl80211Manager.SendMgmtFrameCallback cb = mock( + WifiNl80211Manager.SendMgmtFrameCallback.class); final ArgumentCaptor<ISendMgmtFrameEvent> sendMgmtFrameEventCaptor = ArgumentCaptor.forClass(ISendMgmtFrameEvent.class); @@ -834,7 +834,7 @@ public class WifiCondManagerTest { verify(mAlarmManager).cancel(eq(alarmListenerCaptor.getValue())); sendMgmtFrameEventCaptor.getValue().OnFailure( - WifiCondManager.SEND_MGMT_FRAME_ERROR_UNKNOWN); + WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_UNKNOWN); mLooper.dispatchAll(); handlerCaptor.getValue().post(() -> alarmListenerCaptor.getValue().onAlarm()); @@ -848,8 +848,8 @@ public class WifiCondManagerTest { */ @Test public void testSendMgmtFrameSuccess() throws Exception { - WifiCondManager.SendMgmtFrameCallback cb = mock( - WifiCondManager.SendMgmtFrameCallback.class); + WifiNl80211Manager.SendMgmtFrameCallback cb = mock( + WifiNl80211Manager.SendMgmtFrameCallback.class); final ArgumentCaptor<ISendMgmtFrameEvent> sendMgmtFrameEventCaptor = ArgumentCaptor.forClass(ISendMgmtFrameEvent.class); @@ -882,8 +882,8 @@ public class WifiCondManagerTest { */ @Test public void testSendMgmtFrameFailure() throws Exception { - WifiCondManager.SendMgmtFrameCallback cb = mock( - WifiCondManager.SendMgmtFrameCallback.class); + WifiNl80211Manager.SendMgmtFrameCallback cb = mock( + WifiNl80211Manager.SendMgmtFrameCallback.class); final ArgumentCaptor<ISendMgmtFrameEvent> sendMgmtFrameEventCaptor = ArgumentCaptor.forClass(ISendMgmtFrameEvent.class); @@ -898,10 +898,10 @@ public class WifiCondManagerTest { Runnable::run, cb); sendMgmtFrameEventCaptor.getValue().OnFailure( - WifiCondManager.SEND_MGMT_FRAME_ERROR_UNKNOWN); + WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_UNKNOWN); mLooper.dispatchAll(); verify(cb, never()).onAck(anyInt()); - verify(cb).onFailure(WifiCondManager.SEND_MGMT_FRAME_ERROR_UNKNOWN); + verify(cb).onFailure(WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_UNKNOWN); verify(mAlarmManager).cancel(eq(alarmListenerCaptor.getValue())); // verify that even if timeout is triggered afterwards, SendMgmtFrameCallback is not @@ -917,8 +917,8 @@ public class WifiCondManagerTest { */ @Test public void testSendMgmtFrameTimeout() throws Exception { - WifiCondManager.SendMgmtFrameCallback cb = mock( - WifiCondManager.SendMgmtFrameCallback.class); + WifiNl80211Manager.SendMgmtFrameCallback cb = mock( + WifiNl80211Manager.SendMgmtFrameCallback.class); final ArgumentCaptor<ISendMgmtFrameEvent> sendMgmtFrameEventCaptor = ArgumentCaptor.forClass(ISendMgmtFrameEvent.class); @@ -935,7 +935,7 @@ public class WifiCondManagerTest { handlerCaptor.getValue().post(() -> alarmListenerCaptor.getValue().onAlarm()); mLooper.dispatchAll(); verify(cb, never()).onAck(anyInt()); - verify(cb).onFailure(WifiCondManager.SEND_MGMT_FRAME_ERROR_TIMEOUT); + verify(cb).onFailure(WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_TIMEOUT); // verify that even if onAck() callback is triggered after timeout, // SendMgmtFrameCallback is not triggered again @@ -1006,7 +1006,8 @@ public class WifiCondManagerTest { sendMgmtFrameEventCaptor.getValue().OnAck(TEST_SEND_MGMT_FRAME_ELAPSED_TIME_MS); mLooper.dispatchAll(); verify(mSendMgmtFrameCallback, never()).onAck(anyInt()); - verify(mSendMgmtFrameCallback).onFailure(WifiCondManager.SEND_MGMT_FRAME_ERROR_TIMEOUT); + verify(mSendMgmtFrameCallback).onFailure( + WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_TIMEOUT); } /** @@ -1032,9 +1033,10 @@ public class WifiCondManagerTest { handlerCaptor.getValue().post(() -> alarmListenerCaptor.getValue().onAlarm()); // OnFailure posts to the handler sendMgmtFrameEventCaptor.getValue().OnFailure( - WifiCondManager.SEND_MGMT_FRAME_ERROR_UNKNOWN); + WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_UNKNOWN); mLooper.dispatchAll(); - verify(mSendMgmtFrameCallback).onFailure(WifiCondManager.SEND_MGMT_FRAME_ERROR_TIMEOUT); + verify(mSendMgmtFrameCallback).onFailure( + WifiNl80211Manager.SEND_MGMT_FRAME_ERROR_TIMEOUT); } /** |