diff options
866 files changed, 16799 insertions, 7389 deletions
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobInfo.java b/apex/blobstore/framework/java/android/app/blob/BlobInfo.java index 73ef310c7b40..ba92d95b483e 100644 --- a/apex/blobstore/framework/java/android/app/blob/BlobInfo.java +++ b/apex/blobstore/framework/java/android/app/blob/BlobInfo.java @@ -48,7 +48,6 @@ public final class BlobInfo implements Parcelable { mLeaseInfos = leaseInfos; } - @SuppressWarnings("UnsafeParcelApi") private BlobInfo(Parcel in) { mId = in.readLong(); mExpiryTimeMs = in.readLong(); diff --git a/apex/jobscheduler/framework/java/android/app/AlarmManager.java b/apex/jobscheduler/framework/java/android/app/AlarmManager.java index 66767e21a2e7..9c0c3657bff3 100644 --- a/apex/jobscheduler/framework/java/android/app/AlarmManager.java +++ b/apex/jobscheduler/framework/java/android/app/AlarmManager.java @@ -1408,7 +1408,6 @@ public class AlarmManager { * Use the {@link #CREATOR} * @hide */ - @SuppressWarnings("UnsafeParcelApi") AlarmClockInfo(Parcel in) { mTriggerTime = in.readLong(); mShowIntent = in.readParcelable(PendingIntent.class.getClassLoader()); diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java index b9673f25d680..0e6006a62397 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java @@ -881,7 +881,6 @@ public class JobInfo implements Parcelable { return hashCode; } - @SuppressWarnings("UnsafeParcelApi") private JobInfo(Parcel in) { jobId = in.readInt(); extras = in.readPersistableBundle(); 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 86d7a5a5a62e..3fb1fadba062 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -54,6 +54,8 @@ import android.content.pm.PackageManagerInternal; import android.content.pm.ParceledListSlice; import android.content.pm.ServiceInfo; import android.net.Uri; +import android.os.BatteryManager; +import android.os.BatteryManagerInternal; import android.os.BatteryStats; import android.os.BatteryStatsInternal; import android.os.Binder; @@ -250,8 +252,6 @@ public class JobSchedulerService extends com.android.server.SystemService */ private final List<RestrictingController> mRestrictiveControllers; /** Need direct access to this for testing. */ - private final BatteryController mBatteryController; - /** Need direct access to this for testing. */ private final StorageController mStorageController; /** Need directly for sending uid state changes */ private final DeviceIdleJobsController mDeviceIdleJobsController; @@ -268,6 +268,9 @@ public class JobSchedulerService extends com.android.server.SystemService */ private final List<JobRestriction> mJobRestrictions; + @GuardedBy("mLock") + private final BatteryStateTracker mBatteryStateTracker; + @NonNull private final String mSystemGalleryPackage; @@ -1697,6 +1700,9 @@ public class JobSchedulerService extends com.android.server.SystemService // Initialize the job store and set up any persisted jobs mJobs = JobStore.initAndGet(this); + mBatteryStateTracker = new BatteryStateTracker(); + mBatteryStateTracker.startTracking(); + // Create the controllers. mControllers = new ArrayList<StateController>(); final ConnectivityController connectivityController = new ConnectivityController(this); @@ -1704,8 +1710,8 @@ public class JobSchedulerService extends com.android.server.SystemService mControllers.add(new TimeController(this)); final IdleController idleController = new IdleController(this); mControllers.add(idleController); - mBatteryController = new BatteryController(this); - mControllers.add(mBatteryController); + final BatteryController batteryController = new BatteryController(this); + mControllers.add(batteryController); mStorageController = new StorageController(this); mControllers.add(mStorageController); final BackgroundJobsController backgroundJobsController = @@ -1725,7 +1731,7 @@ public class JobSchedulerService extends com.android.server.SystemService mControllers.add(mTareController); mRestrictiveControllers = new ArrayList<>(); - mRestrictiveControllers.add(mBatteryController); + mRestrictiveControllers.add(batteryController); mRestrictiveControllers.add(connectivityController); mRestrictiveControllers.add(idleController); @@ -2818,6 +2824,129 @@ public class JobSchedulerService extends com.android.server.SystemService return adjustJobBias(bias, job); } + private final class BatteryStateTracker extends BroadcastReceiver { + /** + * Track whether we're "charging", where charging means that we're ready to commit to + * doing work. + */ + private boolean mCharging; + /** Keep track of whether the battery is charged enough that we want to do work. */ + private boolean mBatteryNotLow; + /** Sequence number of last broadcast. */ + private int mLastBatterySeq = -1; + + private BroadcastReceiver mMonitor; + + BatteryStateTracker() { + } + + public void startTracking() { + IntentFilter filter = new IntentFilter(); + + // Battery health. + filter.addAction(Intent.ACTION_BATTERY_LOW); + filter.addAction(Intent.ACTION_BATTERY_OKAY); + // Charging/not charging. + filter.addAction(BatteryManager.ACTION_CHARGING); + filter.addAction(BatteryManager.ACTION_DISCHARGING); + getTestableContext().registerReceiver(this, filter); + + // Initialise tracker state. + BatteryManagerInternal batteryManagerInternal = + LocalServices.getService(BatteryManagerInternal.class); + mBatteryNotLow = !batteryManagerInternal.getBatteryLevelLow(); + mCharging = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY); + } + + public void setMonitorBatteryLocked(boolean enabled) { + if (enabled) { + if (mMonitor == null) { + mMonitor = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + onReceiveInternal(intent); + } + }; + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_BATTERY_CHANGED); + getTestableContext().registerReceiver(mMonitor, filter); + } + } else if (mMonitor != null) { + getTestableContext().unregisterReceiver(mMonitor); + mMonitor = null; + } + } + + public boolean isCharging() { + return mCharging; + } + + public boolean isBatteryNotLow() { + return mBatteryNotLow; + } + + public boolean isMonitoring() { + return mMonitor != null; + } + + public int getSeq() { + return mLastBatterySeq; + } + + @Override + public void onReceive(Context context, Intent intent) { + onReceiveInternal(intent); + } + + @VisibleForTesting + public void onReceiveInternal(Intent intent) { + synchronized (mLock) { + final String action = intent.getAction(); + boolean changed = false; + if (Intent.ACTION_BATTERY_LOW.equals(action)) { + if (DEBUG) { + Slog.d(TAG, "Battery life too low @ " + sElapsedRealtimeClock.millis()); + } + if (mBatteryNotLow) { + mBatteryNotLow = false; + changed = true; + } + } else if (Intent.ACTION_BATTERY_OKAY.equals(action)) { + if (DEBUG) { + Slog.d(TAG, "Battery high enough @ " + sElapsedRealtimeClock.millis()); + } + if (!mBatteryNotLow) { + mBatteryNotLow = true; + changed = true; + } + } else if (BatteryManager.ACTION_CHARGING.equals(action)) { + if (DEBUG) { + Slog.d(TAG, "Battery charging @ " + sElapsedRealtimeClock.millis()); + } + if (!mCharging) { + mCharging = true; + changed = true; + } + } else if (BatteryManager.ACTION_DISCHARGING.equals(action)) { + if (DEBUG) { + Slog.d(TAG, "Disconnected from power @ " + sElapsedRealtimeClock.millis()); + } + if (mCharging) { + mCharging = false; + changed = true; + } + } + mLastBatterySeq = + intent.getIntExtra(BatteryManager.EXTRA_SEQUENCE, mLastBatterySeq); + if (changed) { + for (int c = mControllers.size() - 1; c >= 0; --c) { + mControllers.get(c).onBatteryStateChangedLocked(); + } + } + } + } + } + final class LocalService implements JobSchedulerInternal { /** @@ -3417,29 +3546,27 @@ public class JobSchedulerService extends com.android.server.SystemService void setMonitorBattery(boolean enabled) { synchronized (mLock) { - if (mBatteryController != null) { - mBatteryController.getTracker().setMonitorBatteryLocked(enabled); - } + mBatteryStateTracker.setMonitorBatteryLocked(enabled); } } int getBatterySeq() { synchronized (mLock) { - return mBatteryController != null ? mBatteryController.getTracker().getSeq() : -1; + return mBatteryStateTracker.getSeq(); } } - boolean getBatteryCharging() { + /** Return {@code true} if the device is currently charging. */ + public boolean isBatteryCharging() { synchronized (mLock) { - return mBatteryController != null - ? mBatteryController.getTracker().isOnStablePower() : false; + return mBatteryStateTracker.isCharging(); } } - boolean getBatteryNotLow() { + /** Return {@code true} if the battery is not low. */ + public boolean isBatteryNotLow() { synchronized (mLock) { - return mBatteryController != null - ? mBatteryController.getTracker().isBatteryNotLow() : false; + return mBatteryStateTracker.isBatteryNotLow(); } } @@ -3614,6 +3741,16 @@ public class JobSchedulerService extends com.android.server.SystemService mQuotaTracker.dump(pw); pw.println(); + pw.print("Battery charging: "); + pw.println(mBatteryStateTracker.isCharging()); + pw.print("Battery not low: "); + pw.println(mBatteryStateTracker.isBatteryNotLow()); + if (mBatteryStateTracker.isMonitoring()) { + pw.print("MONITORING: seq="); + pw.println(mBatteryStateTracker.getSeq()); + } + pw.println(); + pw.println("Started users: " + Arrays.toString(mStartedUsers)); pw.print("Registered "); pw.print(mJobs.size()); diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java index cc202130ab07..27268d267001 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java @@ -293,13 +293,13 @@ public final class JobSchedulerShellCommand extends BasicShellCommandHandler { } private int getBatteryCharging(PrintWriter pw) { - boolean val = mInternal.getBatteryCharging(); + boolean val = mInternal.isBatteryCharging(); pw.println(val); return 0; } private int getBatteryNotLow(PrintWriter pw) { - boolean val = mInternal.getBatteryNotLow(); + boolean val = mInternal.isBatteryNotLow(); pw.println(val); return 0; } diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java index 657f4700bd5c..f7338494384d 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java @@ -18,12 +18,6 @@ package com.android.server.job.controllers; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.BatteryManager; -import android.os.BatteryManagerInternal; import android.os.UserHandle; import android.util.ArraySet; import android.util.IndentingPrintWriter; @@ -31,8 +25,8 @@ import android.util.Log; import android.util.Slog; import android.util.proto.ProtoOutputStream; -import com.android.internal.annotations.VisibleForTesting; -import com.android.server.LocalServices; +import com.android.internal.annotations.GuardedBy; +import com.android.server.JobSchedulerBackgroundThread; import com.android.server.job.JobSchedulerService; import com.android.server.job.StateControllerProto; @@ -49,17 +43,9 @@ public final class BatteryController extends RestrictingController { || Log.isLoggable(TAG, Log.DEBUG); private final ArraySet<JobStatus> mTrackedTasks = new ArraySet<>(); - private ChargingTracker mChargeTracker; - - @VisibleForTesting - public ChargingTracker getTracker() { - return mChargeTracker; - } public BatteryController(JobSchedulerService service) { super(service); - mChargeTracker = new ChargingTracker(); - mChargeTracker.startTracking(); } @Override @@ -68,9 +54,9 @@ public final class BatteryController extends RestrictingController { final long nowElapsed = sElapsedRealtimeClock.millis(); mTrackedTasks.add(taskStatus); taskStatus.setTrackingController(JobStatus.TRACKING_BATTERY); - taskStatus.setChargingConstraintSatisfied(nowElapsed, mChargeTracker.isOnStablePower()); - taskStatus.setBatteryNotLowConstraintSatisfied( - nowElapsed, mChargeTracker.isBatteryNotLow()); + taskStatus.setChargingConstraintSatisfied(nowElapsed, + mService.isBatteryCharging() && mService.isBatteryNotLow()); + taskStatus.setBatteryNotLowConstraintSatisfied(nowElapsed, mService.isBatteryNotLow()); } } @@ -93,9 +79,21 @@ public final class BatteryController extends RestrictingController { } } + @Override + @GuardedBy("mLock") + public void onBatteryStateChangedLocked() { + // Update job bookkeeping out of band. + JobSchedulerBackgroundThread.getHandler().post(() -> { + synchronized (mLock) { + maybeReportNewChargingStateLocked(); + } + }); + } + + @GuardedBy("mLock") private void maybeReportNewChargingStateLocked() { - final boolean stablePower = mChargeTracker.isOnStablePower(); - final boolean batteryNotLow = mChargeTracker.isBatteryNotLow(); + final boolean stablePower = mService.isBatteryCharging() && mService.isBatteryNotLow(); + final boolean batteryNotLow = mService.isBatteryNotLow(); if (DEBUG) { Slog.d(TAG, "maybeReportNewChargingStateLocked: " + stablePower); } @@ -116,133 +114,11 @@ public final class BatteryController extends RestrictingController { } } - public final class ChargingTracker extends BroadcastReceiver { - /** - * Track whether we're "charging", where charging means that we're ready to commit to - * doing work. - */ - private boolean mCharging; - /** Keep track of whether the battery is charged enough that we want to do work. */ - private boolean mBatteryHealthy; - /** Sequence number of last broadcast. */ - private int mLastBatterySeq = -1; - - private BroadcastReceiver mMonitor; - - public ChargingTracker() { - } - - public void startTracking() { - IntentFilter filter = new IntentFilter(); - - // Battery health. - filter.addAction(Intent.ACTION_BATTERY_LOW); - filter.addAction(Intent.ACTION_BATTERY_OKAY); - // Charging/not charging. - filter.addAction(BatteryManager.ACTION_CHARGING); - filter.addAction(BatteryManager.ACTION_DISCHARGING); - mContext.registerReceiver(this, filter); - - // Initialise tracker state. - BatteryManagerInternal batteryManagerInternal = - LocalServices.getService(BatteryManagerInternal.class); - mBatteryHealthy = !batteryManagerInternal.getBatteryLevelLow(); - mCharging = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY); - } - - public void setMonitorBatteryLocked(boolean enabled) { - if (enabled) { - if (mMonitor == null) { - mMonitor = new BroadcastReceiver() { - @Override public void onReceive(Context context, Intent intent) { - ChargingTracker.this.onReceive(context, intent); - } - }; - IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_BATTERY_CHANGED); - mContext.registerReceiver(mMonitor, filter); - } - } else { - if (mMonitor != null) { - mContext.unregisterReceiver(mMonitor); - mMonitor = null; - } - } - } - - public boolean isOnStablePower() { - return mCharging && mBatteryHealthy; - } - - public boolean isBatteryNotLow() { - return mBatteryHealthy; - } - - public boolean isMonitoring() { - return mMonitor != null; - } - - public int getSeq() { - return mLastBatterySeq; - } - - @Override - public void onReceive(Context context, Intent intent) { - onReceiveInternal(intent); - } - - @VisibleForTesting - public void onReceiveInternal(Intent intent) { - synchronized (mLock) { - final String action = intent.getAction(); - if (Intent.ACTION_BATTERY_LOW.equals(action)) { - if (DEBUG) { - Slog.d(TAG, "Battery life too low to do work. @ " - + sElapsedRealtimeClock.millis()); - } - // If we get this action, the battery is discharging => it isn't plugged in so - // there's no work to cancel. We track this variable for the case where it is - // charging, but hasn't been for long enough to be healthy. - mBatteryHealthy = false; - maybeReportNewChargingStateLocked(); - } else if (Intent.ACTION_BATTERY_OKAY.equals(action)) { - if (DEBUG) { - Slog.d(TAG, "Battery life healthy enough to do work. @ " - + sElapsedRealtimeClock.millis()); - } - mBatteryHealthy = true; - maybeReportNewChargingStateLocked(); - } else if (BatteryManager.ACTION_CHARGING.equals(action)) { - if (DEBUG) { - Slog.d(TAG, "Received charging intent, fired @ " - + sElapsedRealtimeClock.millis()); - } - mCharging = true; - maybeReportNewChargingStateLocked(); - } else if (BatteryManager.ACTION_DISCHARGING.equals(action)) { - if (DEBUG) { - Slog.d(TAG, "Disconnected from power."); - } - mCharging = false; - maybeReportNewChargingStateLocked(); - } - mLastBatterySeq = intent.getIntExtra(BatteryManager.EXTRA_SEQUENCE, - mLastBatterySeq); - } - } - } - @Override public void dumpControllerStateLocked(IndentingPrintWriter pw, Predicate<JobStatus> predicate) { - pw.println("Stable power: " + mChargeTracker.isOnStablePower()); - pw.println("Not low: " + mChargeTracker.isBatteryNotLow()); - - if (mChargeTracker.isMonitoring()) { - pw.print("MONITORING: seq="); - pw.println(mChargeTracker.getSeq()); - } - pw.println(); + pw.println("Stable power: " + (mService.isBatteryCharging() && mService.isBatteryNotLow())); + pw.println("Not low: " + mService.isBatteryNotLow()); for (int i = 0; i < mTrackedTasks.size(); i++) { final JobStatus js = mTrackedTasks.valueAt(i); @@ -264,14 +140,9 @@ public final class BatteryController extends RestrictingController { final long mToken = proto.start(StateControllerProto.BATTERY); proto.write(StateControllerProto.BatteryController.IS_ON_STABLE_POWER, - mChargeTracker.isOnStablePower()); + mService.isBatteryCharging() && mService.isBatteryNotLow()); proto.write(StateControllerProto.BatteryController.IS_BATTERY_NOT_LOW, - mChargeTracker.isBatteryNotLow()); - - proto.write(StateControllerProto.BatteryController.IS_MONITORING, - mChargeTracker.isMonitoring()); - proto.write(StateControllerProto.BatteryController.LAST_BROADCAST_SEQUENCE_NUMBER, - mChargeTracker.getSeq()); + mService.isBatteryNotLow()); for (int i = 0; i < mTrackedTasks.size(); i++) { final JobStatus js = mTrackedTasks.valueAt(i); diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java index 524d68fb72e7..9fb7ab594607 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java @@ -25,18 +25,12 @@ import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.job.JobInfo; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkPolicyManager; import android.net.NetworkRequest; -import android.os.BatteryManager; -import android.os.BatteryManagerInternal; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -55,6 +49,7 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.JobSchedulerBackgroundThread; import com.android.server.LocalServices; import com.android.server.job.JobSchedulerService; import com.android.server.job.JobSchedulerService.Constants; @@ -107,8 +102,6 @@ public final class ConnectivityController extends RestrictingController implemen private final ConnectivityManager mConnManager; private final NetworkPolicyManagerInternal mNetPolicyManagerInternal; - private final ChargingTracker mChargingTracker; - /** List of tracked jobs keyed by source UID. */ @GuardedBy("mLock") private final SparseArray<ArraySet<JobStatus>> mTrackedJobs = new SparseArray<>(); @@ -237,9 +230,6 @@ public final class ConnectivityController extends RestrictingController implemen // network changes against the active network for each UID with jobs. final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build(); mConnManager.registerNetworkCallback(request, mNetworkCallback); - - mChargingTracker = new ChargingTracker(); - mChargingTracker.startTracking(); } @GuardedBy("mLock") @@ -535,6 +525,17 @@ public final class ConnectivityController extends RestrictingController implemen } } + @Override + @GuardedBy("mLock") + public void onBatteryStateChangedLocked() { + // Update job bookkeeping out of band to avoid blocking broadcast progress. + JobSchedulerBackgroundThread.getHandler().post(() -> { + synchronized (mLock) { + updateTrackedJobsLocked(-1, null); + } + }); + } + private boolean isUsable(NetworkCapabilities capabilities) { return capabilities != null && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED); @@ -591,7 +592,7 @@ public final class ConnectivityController extends RestrictingController implemen // Minimum chunk size isn't defined. Check using the estimated upload/download sizes. if (capabilities.hasCapability(NET_CAPABILITY_NOT_METERED) - && mChargingTracker.isCharging()) { + && mService.isBatteryCharging()) { // We're charging and on an unmetered network. We don't have to be as conservative about // making sure the job will run within its max execution time. Let's just hope the app // supports interruptible work. @@ -1072,51 +1073,6 @@ public final class ConnectivityController extends RestrictingController implemen } } - private final class ChargingTracker extends BroadcastReceiver { - /** - * Track whether we're "charging", where charging means that we're ready to commit to - * doing work. - */ - private boolean mCharging; - - ChargingTracker() {} - - public void startTracking() { - IntentFilter filter = new IntentFilter(); - filter.addAction(BatteryManager.ACTION_CHARGING); - filter.addAction(BatteryManager.ACTION_DISCHARGING); - mContext.registerReceiver(this, filter); - - // Initialise tracker state. - final BatteryManagerInternal batteryManagerInternal = - LocalServices.getService(BatteryManagerInternal.class); - mCharging = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY); - } - - public boolean isCharging() { - return mCharging; - } - - @Override - public void onReceive(Context context, Intent intent) { - synchronized (mLock) { - final String action = intent.getAction(); - if (BatteryManager.ACTION_CHARGING.equals(action)) { - if (mCharging) { - return; - } - mCharging = true; - } else if (BatteryManager.ACTION_DISCHARGING.equals(action)) { - if (!mCharging) { - return; - } - mCharging = false; - } - updateTrackedJobsLocked(-1, null); - } - } - } - private final NetworkCallback mNetworkCallback = new NetworkCallback() { @Override public void onAvailable(Network network) { diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java index c147ef83dcf0..65e1d49d1510 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java @@ -39,15 +39,11 @@ import android.app.IUidObserver; import android.app.usage.UsageEvents; import android.app.usage.UsageStatsManagerInternal; import android.app.usage.UsageStatsManagerInternal.UsageEventListener; -import android.content.BroadcastReceiver; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.BatteryManager; -import android.os.BatteryManagerInternal; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -319,7 +315,6 @@ public final class QuotaController extends StateController { private final SparseLongArray mTopAppGraceCache = new SparseLongArray(); private final AlarmManager mAlarmManager; - private final ChargingTracker mChargeTracker; private final QcHandler mHandler; private final QcConstants mQcConstants; @@ -542,8 +537,6 @@ public final class QuotaController extends StateController { @NonNull ConnectivityController connectivityController) { super(service); mHandler = new QcHandler(mContext.getMainLooper()); - mChargeTracker = new ChargingTracker(); - mChargeTracker.startTracking(); mAlarmManager = mContext.getSystemService(AlarmManager.class); mQcConstants = new QcConstants(); mBackgroundJobsController = backgroundJobsController; @@ -705,6 +698,11 @@ public final class QuotaController extends StateController { mTopAppTrackers.delete(userId); } + @Override + public void onBatteryStateChangedLocked() { + handleNewChargingStateLocked(); + } + /** Drop all historical stats and stop tracking any active sessions for the specified app. */ public void clearAppStatsLocked(int userId, @NonNull String packageName) { mTrackedJobs.delete(userId, packageName); @@ -766,7 +764,7 @@ public final class QuotaController extends StateController { if (!jobStatus.shouldTreatAsExpeditedJob()) { // If quota is currently "free", then the job can run for the full amount of time, // regardless of bucket (hence using charging instead of isQuotaFreeLocked()). - if (mChargeTracker.isChargingLocked() + if (mService.isBatteryCharging() || mTopAppCache.get(jobStatus.getSourceUid()) || isTopStartedJobLocked(jobStatus) || isUidInForeground(jobStatus.getSourceUid())) { @@ -777,7 +775,7 @@ public final class QuotaController extends StateController { } // Expedited job. - if (mChargeTracker.isChargingLocked()) { + if (mService.isBatteryCharging()) { return mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS; } if (mTopAppCache.get(jobStatus.getSourceUid()) || isTopStartedJobLocked(jobStatus)) { @@ -864,7 +862,7 @@ public final class QuotaController extends StateController { @GuardedBy("mLock") private boolean isQuotaFreeLocked(final int standbyBucket) { // Quota constraint is not enforced while charging. - if (mChargeTracker.isChargingLocked()) { + if (mService.isBatteryCharging()) { // Restricted jobs require additional constraints when charging, so don't immediately // mark quota as free when charging. return standbyBucket != RESTRICTED_INDEX; @@ -1538,15 +1536,19 @@ public final class QuotaController extends StateController { private void handleNewChargingStateLocked() { mTimerChargingUpdateFunctor.setStatus(sElapsedRealtimeClock.millis(), - mChargeTracker.isChargingLocked()); + mService.isBatteryCharging()); if (DEBUG) { - Slog.d(TAG, "handleNewChargingStateLocked: " + mChargeTracker.isChargingLocked()); + Slog.d(TAG, "handleNewChargingStateLocked: " + mService.isBatteryCharging()); } // Deal with Timers first. mEJPkgTimers.forEach(mTimerChargingUpdateFunctor); mPkgTimers.forEach(mTimerChargingUpdateFunctor); - // Now update jobs. - maybeUpdateAllConstraintsLocked(); + // Now update jobs out of band so broadcast processing can proceed. + JobSchedulerBackgroundThread.getHandler().post(() -> { + synchronized (mLock) { + maybeUpdateAllConstraintsLocked(); + } + }); } private void maybeUpdateAllConstraintsLocked() { @@ -1811,58 +1813,6 @@ public final class QuotaController extends StateController { return false; } - private final class ChargingTracker extends BroadcastReceiver { - /** - * Track whether we're charging. This has a slightly different definition than that of - * BatteryController. - */ - @GuardedBy("mLock") - private boolean mCharging; - - ChargingTracker() { - } - - public void startTracking() { - IntentFilter filter = new IntentFilter(); - - // Charging/not charging. - filter.addAction(BatteryManager.ACTION_CHARGING); - filter.addAction(BatteryManager.ACTION_DISCHARGING); - mContext.registerReceiver(this, filter); - - // Initialise tracker state. - BatteryManagerInternal batteryManagerInternal = - LocalServices.getService(BatteryManagerInternal.class); - mCharging = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY); - } - - @GuardedBy("mLock") - public boolean isChargingLocked() { - return mCharging; - } - - @Override - public void onReceive(Context context, Intent intent) { - synchronized (mLock) { - final String action = intent.getAction(); - if (BatteryManager.ACTION_CHARGING.equals(action)) { - if (DEBUG) { - Slog.d(TAG, "Received charging intent, fired @ " - + sElapsedRealtimeClock.millis()); - } - mCharging = true; - handleNewChargingStateLocked(); - } else if (BatteryManager.ACTION_DISCHARGING.equals(action)) { - if (DEBUG) { - Slog.d(TAG, "Disconnected from power."); - } - mCharging = false; - handleNewChargingStateLocked(); - } - } - } - } - @VisibleForTesting static final class TimingSession { // Start timestamp in elapsed realtime timebase. @@ -2127,6 +2077,12 @@ public final class QuotaController extends StateController { final long topAppGracePeriodEndElapsed = mTopAppGraceCache.get(mUid); final boolean hasTopAppExemption = !mRegularJobTimer && (mTopAppCache.get(mUid) || nowElapsed < topAppGracePeriodEndElapsed); + if (DEBUG) { + Slog.d(TAG, "quotaFree=" + isQuotaFreeLocked(standbyBucket) + + " isFG=" + mForegroundUids.get(mUid) + + " tempEx=" + hasTempAllowlistExemption + + " topEx=" + hasTopAppExemption); + } return !isQuotaFreeLocked(standbyBucket) && !mForegroundUids.get(mUid) && !hasTempAllowlistExemption && !hasTopAppExemption; @@ -3938,7 +3894,6 @@ public final class QuotaController extends StateController { public void dumpControllerStateLocked(final IndentingPrintWriter pw, final Predicate<JobStatus> predicate) { pw.println("Is enabled: " + mIsEnabled); - pw.println("Is charging: " + mChargeTracker.isChargingLocked()); pw.println("Current elapsed time: " + sElapsedRealtimeClock.millis()); pw.println(); @@ -4116,7 +4071,7 @@ public final class QuotaController extends StateController { final long mToken = proto.start(StateControllerProto.QUOTA); proto.write(StateControllerProto.QuotaController.IS_CHARGING, - mChargeTracker.isChargingLocked()); + mService.isBatteryCharging()); proto.write(StateControllerProto.QuotaController.ELAPSED_REALTIME, sElapsedRealtimeClock.millis()); diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java index 5e73f42ff1cb..509fb6963a3c 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java @@ -133,6 +133,13 @@ public abstract class StateController { } /** + * Called when the battery status changes. + */ + @GuardedBy("mLock") + public void onBatteryStateChangedLocked() { + } + + /** * Called when a UID's base bias has changed. The more positive the bias, the more * important the UID is. */ diff --git a/apex/media/OWNERS b/apex/media/OWNERS index bed38954a70c..2c5965c300e3 100644 --- a/apex/media/OWNERS +++ b/apex/media/OWNERS @@ -1,6 +1,5 @@ # Bug component: 1344 hdmoon@google.com -hkuang@google.com jinpark@google.com klhyun@google.com lnilsson@google.com diff --git a/apex/media/framework/jarjar_rules.txt b/apex/media/framework/jarjar_rules.txt index 1bd5b3616693..91489dcee0a1 100644 --- a/apex/media/framework/jarjar_rules.txt +++ b/apex/media/framework/jarjar_rules.txt @@ -1,4 +1,2 @@ rule com.android.modules.** android.media.internal.@1 rule com.google.android.exoplayer2.** android.media.internal.exo.@1 -rule com.google.common.** android.media.internal.guava_common.@1 -rule com.google.thirdparty.** android.media.internal.guava_thirdparty.@1 diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java index 7daebd0155a1..b6f85c7eac6f 100644 --- a/apex/media/framework/java/android/media/MediaParser.java +++ b/apex/media/framework/java/android/media/MediaParser.java @@ -62,8 +62,8 @@ import com.google.android.exoplayer2.extractor.wav.WavExtractor; import com.google.android.exoplayer2.upstream.DataReader; import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.TimestampAdjuster; +import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.video.ColorInfo; -import com.google.common.base.Ascii; import java.io.EOFException; import java.io.IOException; @@ -978,7 +978,7 @@ public final class MediaParser { @ParserName public static List<String> getParserNames(@NonNull MediaFormat mediaFormat) { String mimeType = mediaFormat.getString(MediaFormat.KEY_MIME); - mimeType = mimeType == null ? null : Ascii.toLowerCase(mimeType); + mimeType = mimeType == null ? null : Util.toLowerInvariant(mimeType.trim()); if (TextUtils.isEmpty(mimeType)) { // No MIME type provided. Return all. return Collections.unmodifiableList( @@ -1420,7 +1420,7 @@ public final class MediaParser { int flags = 0; TimestampAdjuster timestampAdjuster = null; if (mIgnoreTimestampOffset) { - timestampAdjuster = new TimestampAdjuster(TimestampAdjuster.MODE_NO_OFFSET); + timestampAdjuster = new TimestampAdjuster(TimestampAdjuster.DO_NOT_OFFSET); } switch (parserName) { case PARSER_NAME_MATROSKA: diff --git a/apex/media/framework/java/android/media/Session2CommandGroup.java b/apex/media/framework/java/android/media/Session2CommandGroup.java index 13aabfc45ab7..af8184a27f0d 100644 --- a/apex/media/framework/java/android/media/Session2CommandGroup.java +++ b/apex/media/framework/java/android/media/Session2CommandGroup.java @@ -68,7 +68,7 @@ public final class Session2CommandGroup implements Parcelable { /** * Used by parcelable creator. */ - @SuppressWarnings("WeakerAccess") /* synthetic access */ + @SuppressWarnings({"WeakerAccess", "UnsafeParcelApi"}) /* synthetic access */ Session2CommandGroup(Parcel in) { Parcelable[] commands = in.readParcelableArray(Session2Command.class.getClassLoader()); if (commands != null) { diff --git a/apex/media/service/java/com/android/server/media/MediaCommunicationService.java b/apex/media/service/java/com/android/server/media/MediaCommunicationService.java index e48f234c5569..7d47e250f99d 100644 --- a/apex/media/service/java/com/android/server/media/MediaCommunicationService.java +++ b/apex/media/service/java/com/android/server/media/MediaCommunicationService.java @@ -79,7 +79,7 @@ public class MediaCommunicationService extends SystemService { final Executor mRecordExecutor = Executors.newSingleThreadExecutor(); @GuardedBy("mLock") - final List<CallbackRecord> mCallbackRecords = new ArrayList<>(); + final ArrayList<CallbackRecord> mCallbackRecords = new ArrayList<>(); final NotificationManager mNotificationManager; MediaSessionManager mSessionManager; @@ -150,8 +150,8 @@ public class MediaCommunicationService extends SystemService { return null; } - List<Session2Token> getSession2TokensLocked(int userId) { - List<Session2Token> list = new ArrayList<>(); + ArrayList<Session2Token> getSession2TokensLocked(int userId) { + ArrayList<Session2Token> list = new ArrayList<>(); if (userId == ALL.getIdentifier()) { int size = mUserRecords.size(); for (int i = 0; i < size; i++) { @@ -237,28 +237,29 @@ public class MediaCommunicationService extends SystemService { } void dispatchSession2Changed(int userId) { - MediaParceledListSlice<Session2Token> allSession2Tokens; - MediaParceledListSlice<Session2Token> userSession2Tokens; + ArrayList<Session2Token> allSession2Tokens; + ArrayList<Session2Token> userSession2Tokens; synchronized (mLock) { - allSession2Tokens = - new MediaParceledListSlice<>(getSession2TokensLocked(ALL.getIdentifier())); - userSession2Tokens = new MediaParceledListSlice<>(getSession2TokensLocked(userId)); - } - allSession2Tokens.setInlineCountLimit(1); - userSession2Tokens.setInlineCountLimit(1); + allSession2Tokens = getSession2TokensLocked(ALL.getIdentifier()); + userSession2Tokens = getSession2TokensLocked(userId); - synchronized (mLock) { for (CallbackRecord record : mCallbackRecords) { if (record.mUserId == ALL.getIdentifier()) { try { - record.mCallback.onSession2Changed(allSession2Tokens); + MediaParceledListSlice<Session2Token> toSend = + new MediaParceledListSlice<>(allSession2Tokens); + toSend.setInlineCountLimit(0); + record.mCallback.onSession2Changed(toSend); } catch (RemoteException e) { Log.w(TAG, "Failed to notify session2 tokens changed " + record); } } else if (record.mUserId == userId) { try { - record.mCallback.onSession2Changed(userSession2Tokens); + MediaParceledListSlice<Session2Token> toSend = + new MediaParceledListSlice<>(userSession2Tokens); + toSend.setInlineCountLimit(0); + record.mCallback.onSession2Changed(toSend); } catch (RemoteException e) { Log.w(TAG, "Failed to notify session2 tokens changed " + record); } @@ -382,7 +383,7 @@ public class MediaCommunicationService extends SystemService { try { // Check that they can make calls on behalf of the user and get the final user id int resolvedUserId = handleIncomingUser(pid, uid, userId, null); - List<Session2Token> result; + ArrayList<Session2Token> result; synchronized (mLock) { result = getSession2TokensLocked(resolvedUserId); } diff --git a/api/Android.bp b/api/Android.bp index 2e49a0c56778..70f995a44c86 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -24,6 +24,19 @@ package { default_applicable_licenses: ["frameworks_base_license"], } +bootstrap_go_package { + name: "soong-api", + pkgPath: "android/soong/api", + deps: [ + "blueprint", + "soong", + "soong-android", + "soong-genrule", + ], + srcs: ["api.go"], + pluginFor: ["soong_build"], +} + python_defaults { name: "python3_version_defaults", version: { diff --git a/api/api.go b/api/api.go new file mode 100644 index 000000000000..976b140f407f --- /dev/null +++ b/api/api.go @@ -0,0 +1,224 @@ +// Copyright (C) 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package api + +import ( + "github.com/google/blueprint/proptools" + + "android/soong/android" + "android/soong/genrule" +) + +// The intention behind this soong plugin is to generate a number of "merged" +// API-related modules that would otherwise require a large amount of very +// similar Android.bp boilerplate to define. For example, the merged current.txt +// API definitions (created by merging the non-updatable current.txt with all +// the module current.txts). This simplifies the addition of new android +// modules, by reducing the number of genrules etc a new module must be added to. + +// The properties of the combined_apis module type. +type CombinedApisProperties struct { + // Module libraries that have public APIs + Public []string + // Module libraries that have system APIs + System []string + // Module libraries that have module_library APIs + Module_lib []string + // Module libraries that have system_server APIs + System_server []string + // ART module library. The only API library not removed from the filtered api database, because + // 1) ART apis are available by default to all modules, while other module-to-module deps are + // explicit and probably receive more scrutiny anyway + // 2) The number of ART/libcore APIs is large, so not linting them would create a large gap + // 3) It's a compromise. Ideally we wouldn't be filtering out any module APIs, and have + // per-module lint databases that excludes just that module's APIs. Alas, that's more + // difficult to achieve. + Art_module string +} + +type CombinedApis struct { + android.ModuleBase + + properties CombinedApisProperties +} + +func init() { + registerBuildComponents(android.InitRegistrationContext) +} + +func registerBuildComponents(ctx android.RegistrationContext) { + ctx.RegisterModuleType("combined_apis", combinedApisModuleFactory) +} + +var PrepareForCombinedApisTest = android.FixtureRegisterWithContext(registerBuildComponents) + +func (a *CombinedApis) GenerateAndroidBuildActions(ctx android.ModuleContext) { +} + +type genruleProps struct { + Name *string + Cmd *string + Dists []android.Dist + Out []string + Srcs []string + Tools []string + Visibility []string +} + +// Struct to pass parameters for the various merged [current|removed].txt file modules we create. +type MergedTxtDefinition struct { + // "current.txt" or "removed.txt" + TxtFilename string + // The module for the non-updatable / non-module part of the api. + BaseTxt string + // The list of modules that are relevant for this merged txt. + Modules []string + // The output tag for each module to use.e.g. {.public.api.txt} for current.txt + ModuleTag string + // public, system, module-lib or system-server + Scope string +} + +func createMergedTxt(ctx android.LoadHookContext, txt MergedTxtDefinition) { + metalavaCmd := "$(location metalava)" + // Silence reflection warnings. See b/168689341 + metalavaCmd += " -J--add-opens=java.base/java.util=ALL-UNNAMED " + metalavaCmd += " --quiet --no-banner --format=v2 " + + filename := txt.TxtFilename + if txt.Scope != "public" { + filename = txt.Scope + "-" + filename + } + + props := genruleProps{} + props.Name = proptools.StringPtr(ctx.ModuleName() + "-" + filename) + props.Tools = []string{"metalava"} + props.Out = []string{txt.TxtFilename} + props.Cmd = proptools.StringPtr(metalavaCmd + "$(in) --api $(out)") + props.Srcs = createSrcs(txt.BaseTxt, txt.Modules, txt.ModuleTag) + props.Dists = []android.Dist{ + { + Targets: []string{"droidcore"}, + Dir: proptools.StringPtr("api"), + Dest: proptools.StringPtr(filename), + }, + { + Targets: []string{"sdk"}, + Dir: proptools.StringPtr("apistubs/android/" + txt.Scope + "/api"), + Dest: proptools.StringPtr(txt.TxtFilename), + }, + } + props.Visibility = []string{"//visibility:public"} + ctx.CreateModule(genrule.GenRuleFactory, &props) +} + +func createMergedStubsSrcjar(ctx android.LoadHookContext, modules []string) { + props := genruleProps{} + props.Name = proptools.StringPtr(ctx.ModuleName() + "-current.srcjar") + props.Tools = []string{"merge_zips"} + props.Out = []string{"current.srcjar"} + props.Cmd = proptools.StringPtr("$(location merge_zips) $(out) $(in)") + props.Srcs = createSrcs(":api-stubs-docs-non-updatable", modules, "{.public.stubs.source}") + props.Visibility = []string{"//visibility:private"} // Used by make module in //development, mind + ctx.CreateModule(genrule.GenRuleFactory, &props) +} + +func createFilteredApiVersions(ctx android.LoadHookContext, modules []string) { + props := genruleProps{} + props.Name = proptools.StringPtr("api-versions-xml-public-filtered") + props.Tools = []string{"api_versions_trimmer"} + props.Out = []string{"api-versions-public-filtered.xml"} + props.Cmd = proptools.StringPtr("$(location api_versions_trimmer) $(out) $(in)") + // Note: order matters: first parameter is the full api-versions.xml + // after that the stubs files in any order + // stubs files are all modules that export API surfaces EXCEPT ART + props.Srcs = createSrcs(":framework-doc-stubs{.api_versions.xml}", modules, ".stubs{.jar}") + props.Dists = []android.Dist{{Targets: []string{"sdk"}}} + ctx.CreateModule(genrule.GenRuleFactory, &props) +} + +func createSrcs(base string, modules []string, tag string) []string { + a := make([]string, 0, len(modules)+1) + a = append(a, base) + for _, module := range modules { + a = append(a, ":"+module+tag) + } + return a +} + +func remove(s []string, v string) []string { + s2 := make([]string, 0, len(s)) + for _, sv := range s { + if sv != v { + s2 = append(s2, sv) + } + } + return s2 +} + +func createMergedTxts(ctx android.LoadHookContext, props CombinedApisProperties) { + var textFiles []MergedTxtDefinition + tagSuffix := []string{".api.txt}", ".removed-api.txt}"} + for i, f := range []string{"current.txt", "removed.txt"} { + textFiles = append(textFiles, MergedTxtDefinition{ + TxtFilename: f, + BaseTxt: ":non-updatable-" + f, + Modules: props.Public, + ModuleTag: "{.public" + tagSuffix[i], + Scope: "public", + }) + textFiles = append(textFiles, MergedTxtDefinition{ + TxtFilename: f, + BaseTxt: ":non-updatable-system-" + f, + Modules: props.System, + ModuleTag: "{.system" + tagSuffix[i], + Scope: "system", + }) + textFiles = append(textFiles, MergedTxtDefinition{ + TxtFilename: f, + BaseTxt: ":non-updatable-module-lib-" + f, + Modules: props.Module_lib, + ModuleTag: "{.module-lib" + tagSuffix[i], + Scope: "module-lib", + }) + textFiles = append(textFiles, MergedTxtDefinition{ + TxtFilename: f, + BaseTxt: ":non-updatable-system-server-" + f, + Modules: props.System_server, + ModuleTag: "{.system-server" + tagSuffix[i], + Scope: "system-server", + }) + } + for _, txt := range textFiles { + createMergedTxt(ctx, txt) + } +} + +func (a *CombinedApis) createInternalModules(ctx android.LoadHookContext) { + createMergedTxts(ctx, a.properties) + + createMergedStubsSrcjar(ctx, a.properties.Public) + + // For the filtered api versions, we prune all APIs except art module's APIs. + createFilteredApiVersions(ctx, remove(a.properties.Public, a.properties.Art_module)) +} + +func combinedApisModuleFactory() android.Module { + module := &CombinedApis{} + module.AddProperties(&module.properties) + android.InitAndroidModule(module) + android.AddLoadHook(module, func(ctx android.LoadHookContext) { module.createInternalModules(ctx) }) + return module +} diff --git a/core/api/current.txt b/core/api/current.txt index edf69449f4e9..8557e90d6328 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -1312,6 +1312,7 @@ package android { field public static final int shouldDisableView = 16843246; // 0x10101ee field public static final int shouldUseDefaultUnfoldTransition = 16844364; // 0x101064c field public static final int showAsAction = 16843481; // 0x10102d9 + field public static final int showClockAndComplications; field public static final int showDefault = 16843258; // 0x10101fa field public static final int showDividers = 16843561; // 0x1010329 field public static final int showForAllUsers = 16844015; // 0x10104ef @@ -9742,13 +9743,14 @@ package android.bluetooth { field public static final int ERROR_BLUETOOTH_NOT_ALLOWED = 2; // 0x2 field public static final int ERROR_BLUETOOTH_NOT_ENABLED = 1; // 0x1 field public static final int ERROR_DEVICE_NOT_BONDED = 3; // 0x3 - field public static final int ERROR_FEATURE_NOT_SUPPORTED = 10; // 0xa - field public static final int ERROR_GATT_WRITE_NOT_ALLOWED = 101; // 0x65 - field public static final int ERROR_GATT_WRITE_REQUEST_BUSY = 102; // 0x66 + field public static final int ERROR_GATT_WRITE_NOT_ALLOWED = 200; // 0xc8 + field public static final int ERROR_GATT_WRITE_REQUEST_BUSY = 201; // 0xc9 field public static final int ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION = 6; // 0x6 field public static final int ERROR_MISSING_BLUETOOTH_PRIVILEGED_PERMISSION = 8; // 0x8 field public static final int ERROR_PROFILE_SERVICE_NOT_BOUND = 9; // 0x9 field public static final int ERROR_UNKNOWN = 2147483647; // 0x7fffffff + field public static final int FEATURE_NOT_SUPPORTED = 11; // 0xb + field public static final int FEATURE_SUPPORTED = 10; // 0xa field public static final int SUCCESS = 0; // 0x0 } @@ -10875,6 +10877,8 @@ package android.content { method @Deprecated @RequiresPermission(allOf={"android.permission.INTERACT_ACROSS_USERS", android.Manifest.permission.BROADCAST_STICKY}) public abstract void removeStickyBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle); method public abstract void revokeUriPermission(android.net.Uri, int); method public abstract void revokeUriPermission(String, android.net.Uri, int); + method public void selfRevokePermission(@NonNull String); + method public void selfRevokePermissions(@NonNull java.util.Collection<java.lang.String>); method public abstract void sendBroadcast(@RequiresPermission android.content.Intent); method public abstract void sendBroadcast(@RequiresPermission android.content.Intent, @Nullable String); method @RequiresPermission("android.permission.INTERACT_ACROSS_USERS") public abstract void sendBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle); @@ -12761,11 +12765,16 @@ package android.content.pm { method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.content.pm.PackageInstaller.SessionInfo> CREATOR; field public static final int INVALID_ID = -1; // 0xffffffff - field public static final int STAGED_SESSION_ACTIVATION_FAILED = 2; // 0x2 - field public static final int STAGED_SESSION_CONFLICT = 4; // 0x4 - field public static final int STAGED_SESSION_NO_ERROR = 0; // 0x0 - field public static final int STAGED_SESSION_UNKNOWN = 3; // 0x3 - field public static final int STAGED_SESSION_VERIFICATION_FAILED = 1; // 0x1 + field public static final int SESSION_ACTIVATION_FAILED = 2; // 0x2 + field public static final int SESSION_CONFLICT = 4; // 0x4 + field public static final int SESSION_NO_ERROR = 0; // 0x0 + field public static final int SESSION_UNKNOWN_ERROR = 3; // 0x3 + field public static final int SESSION_VERIFICATION_FAILED = 1; // 0x1 + field @Deprecated public static final int STAGED_SESSION_ACTIVATION_FAILED = 2; // 0x2 + field @Deprecated public static final int STAGED_SESSION_CONFLICT = 4; // 0x4 + field @Deprecated public static final int STAGED_SESSION_NO_ERROR = 0; // 0x0 + field @Deprecated public static final int STAGED_SESSION_UNKNOWN = 3; // 0x3 + field @Deprecated public static final int STAGED_SESSION_VERIFICATION_FAILED = 1; // 0x1 } public static class PackageInstaller.SessionParams implements android.os.Parcelable { @@ -21893,16 +21902,29 @@ package android.media { method public android.media.Image acquireNextImage(); method public void close(); method public void discardFreeBuffers(); + method public long getDataSpace(); + method public int getHardwareBufferFormat(); method public int getHeight(); method public int getImageFormat(); method public int getMaxImages(); method public android.view.Surface getSurface(); + method public long getUsage(); method public int getWidth(); method @NonNull public static android.media.ImageReader newInstance(@IntRange(from=1) int, @IntRange(from=1) int, int, @IntRange(from=1) int); method @NonNull public static android.media.ImageReader newInstance(@IntRange(from=1) int, @IntRange(from=1) int, int, @IntRange(from=1) int, long); method public void setOnImageAvailableListener(android.media.ImageReader.OnImageAvailableListener, android.os.Handler); } + public static final class ImageReader.Builder { + ctor public ImageReader.Builder(@IntRange(from=1) int, @IntRange(from=1) int); + method @NonNull public android.media.ImageReader build(); + method @NonNull public android.media.ImageReader.Builder setDefaultDataSpace(long); + method @NonNull public android.media.ImageReader.Builder setDefaultHardwareBufferFormat(int); + method @NonNull public android.media.ImageReader.Builder setImageFormat(int); + method @NonNull public android.media.ImageReader.Builder setMaxImages(int); + method @NonNull public android.media.ImageReader.Builder setUsage(long); + } + public static interface ImageReader.OnImageAvailableListener { method public void onImageAvailable(android.media.ImageReader); } @@ -27363,11 +27385,13 @@ package android.net { method @NonNull public android.net.VpnService.Builder addDnsServer(@NonNull java.net.InetAddress); method @NonNull public android.net.VpnService.Builder addDnsServer(@NonNull String); method @NonNull public android.net.VpnService.Builder addRoute(@NonNull java.net.InetAddress, int); + method @NonNull public android.net.VpnService.Builder addRoute(@NonNull android.net.IpPrefix); method @NonNull public android.net.VpnService.Builder addRoute(@NonNull String, int); method @NonNull public android.net.VpnService.Builder addSearchDomain(@NonNull String); method @NonNull public android.net.VpnService.Builder allowBypass(); method @NonNull public android.net.VpnService.Builder allowFamily(int); method @Nullable public android.os.ParcelFileDescriptor establish(); + method @NonNull public android.net.VpnService.Builder excludeRoute(@NonNull android.net.IpPrefix); method @NonNull public android.net.VpnService.Builder setBlocking(boolean); method @NonNull public android.net.VpnService.Builder setConfigureIntent(@NonNull android.app.PendingIntent); method @NonNull public android.net.VpnService.Builder setHttpProxy(@NonNull android.net.ProxyInfo); @@ -27685,6 +27709,23 @@ package android.net.sip { package android.net.vcn { + public final class VcnCellUnderlyingNetworkTemplate extends android.net.vcn.VcnUnderlyingNetworkTemplate { + method @NonNull public java.util.Set<java.lang.String> getOperatorPlmnIds(); + method public int getOpportunistic(); + method public int getRoaming(); + method @NonNull public java.util.Set<java.lang.Integer> getSimSpecificCarrierIds(); + } + + public static final class VcnCellUnderlyingNetworkTemplate.Builder { + ctor public VcnCellUnderlyingNetworkTemplate.Builder(); + method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate build(); + method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setMetered(int); + method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setOperatorPlmnIds(@NonNull java.util.Set<java.lang.String>); + method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setOpportunistic(int); + method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setRoaming(int); + method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setSimSpecificCarrierIds(@NonNull java.util.Set<java.lang.Integer>); + } + public final class VcnConfig implements android.os.Parcelable { method public int describeContents(); method @NonNull public java.util.Set<android.net.vcn.VcnGatewayConnectionConfig> getGatewayConnectionConfigs(); @@ -27703,6 +27744,7 @@ package android.net.vcn { method @NonNull public String getGatewayConnectionName(); method @IntRange(from=0x500) public int getMaxMtu(); method @NonNull public long[] getRetryIntervalsMillis(); + method @NonNull public java.util.List<android.net.vcn.VcnUnderlyingNetworkTemplate> getVcnUnderlyingNetworkPriorities(); } public static final class VcnGatewayConnectionConfig.Builder { @@ -27712,6 +27754,7 @@ package android.net.vcn { method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder removeExposedCapability(int); method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setMaxMtu(@IntRange(from=0x500) int); method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setRetryIntervalsMillis(@NonNull long[]); + method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setVcnUnderlyingNetworkPriorities(@NonNull java.util.List<android.net.vcn.VcnUnderlyingNetworkTemplate>); } public class VcnManager { @@ -27735,6 +27778,24 @@ package android.net.vcn { method public abstract void onStatusChanged(int); } + public abstract class VcnUnderlyingNetworkTemplate { + method public int getMetered(); + field public static final int MATCH_ANY = 0; // 0x0 + field public static final int MATCH_FORBIDDEN = 2; // 0x2 + field public static final int MATCH_REQUIRED = 1; // 0x1 + } + + public final class VcnWifiUnderlyingNetworkTemplate extends android.net.vcn.VcnUnderlyingNetworkTemplate { + method @NonNull public java.util.Set<java.lang.String> getSsids(); + } + + public static final class VcnWifiUnderlyingNetworkTemplate.Builder { + ctor public VcnWifiUnderlyingNetworkTemplate.Builder(); + method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate build(); + method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate.Builder setMetered(int); + method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate.Builder setSsids(@NonNull java.util.Set<java.lang.String>); + } + } package android.nfc { @@ -40267,6 +40328,7 @@ package android.telecom { field public static final int PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL = 2048; // 0x800 field public static final int PROPERTY_RTT = 1024; // 0x400 field public static final int PROPERTY_SELF_MANAGED = 256; // 0x100 + field public static final int PROPERTY_TETHERED_CALL = 32768; // 0x8000 field public static final int PROPERTY_VOIP_AUDIO_MODE = 4096; // 0x1000 field public static final int PROPERTY_WIFI = 8; // 0x8 } @@ -40295,6 +40357,7 @@ package android.telecom { field @NonNull public static final android.os.Parcelable.Creator<android.telecom.CallAudioState> CREATOR; field public static final int ROUTE_BLUETOOTH = 2; // 0x2 field public static final int ROUTE_EARPIECE = 1; // 0x1 + field public static final int ROUTE_EXTERNAL = 16; // 0x10 field public static final int ROUTE_SPEAKER = 8; // 0x8 field public static final int ROUTE_WIRED_HEADSET = 4; // 0x4 field public static final int ROUTE_WIRED_OR_EARPIECE = 5; // 0x5 @@ -40569,6 +40632,7 @@ package android.telecom { field public static final int PROPERTY_IS_RTT = 256; // 0x100 field public static final int PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL = 1024; // 0x400 field public static final int PROPERTY_SELF_MANAGED = 128; // 0x80 + field public static final int PROPERTY_TETHERED_CALL = 16384; // 0x4000 field public static final int PROPERTY_WIFI = 8; // 0x8 field public static final int STATE_ACTIVE = 4; // 0x4 field public static final int STATE_DIALING = 3; // 0x3 @@ -41662,21 +41726,101 @@ package android.telephony { field public static final String PROTOCOL_IPV6 = "IPV6"; } + public static final class CarrierConfigManager.Bsf { + field public static final String KEY_BSF_SERVER_FQDN_STRING = "bsf.bsf_server_fqdn_string"; + field public static final String KEY_BSF_SERVER_PORT_INT = "bsf.bsf_server_port_int"; + field public static final String KEY_BSF_TRANSPORT_TYPE_INT = "bsf.bsf_transport type_int"; + field public static final String KEY_PREFIX = "bsf."; + } + public static final class CarrierConfigManager.Gps { field public static final String KEY_PERSIST_LPP_MODE_BOOL = "gps.persist_lpp_mode_bool"; field public static final String KEY_PREFIX = "gps."; } public static final class CarrierConfigManager.Ims { + field public static final int E911_RTCP_INACTIVITY_ON_CONNECTED = 3; // 0x3 + field public static final int E911_RTP_INACTIVITY_ON_CONNECTED = 4; // 0x4 + field public static final int GEOLOCATION_PIDF_FOR_EMERGENCY_ON_CELLULAR = 4; // 0x4 + field public static final int GEOLOCATION_PIDF_FOR_EMERGENCY_ON_WIFI = 2; // 0x2 + field public static final int GEOLOCATION_PIDF_FOR_NON_EMERGENCY_ON_CELLULAR = 3; // 0x3 + field public static final int GEOLOCATION_PIDF_FOR_NON_EMERGENCY_ON_WIFI = 1; // 0x1 + field public static final int IPSEC_AUTHENTICATION_ALGORITHM_HMAC_MD5 = 0; // 0x0 + field public static final int IPSEC_AUTHENTICATION_ALGORITHM_HMAC_SHA1 = 1; // 0x1 + field public static final int IPSEC_ENCRYPTION_ALGORITHM_AES_CBC = 2; // 0x2 + field public static final int IPSEC_ENCRYPTION_ALGORITHM_DES_EDE3_CBC = 1; // 0x1 + field public static final int IPSEC_ENCRYPTION_ALGORITHM_NULL = 0; // 0x0 field public static final String KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL = "ims.enable_presence_capability_exchange_bool"; field public static final String KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL = "ims.enable_presence_group_subscribe_bool"; field public static final String KEY_ENABLE_PRESENCE_PUBLISH_BOOL = "ims.enable_presence_publish_bool"; + field public static final String KEY_GEOLOCATION_PIDF_IN_SIP_INVITE_SUPPORT_INT_ARRAY = "ims.geolocation_pidf_in_sip_invite_support_int_array"; + field public static final String KEY_GEOLOCATION_PIDF_IN_SIP_REGISTER_SUPPORT_INT_ARRAY = "ims.geolocation_pidf_in_sip_register_support_int_array"; + field public static final String KEY_GRUU_ENABLED_BOOL = "ims.gruu_enabled_bool"; + field public static final String KEY_IMS_PDN_ENABLED_IN_NO_VOPS_SUPPORT_INT_ARRAY = "ims.ims_pdn_enabled_in_no_vops_support_int_array"; field public static final String KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL = "ims.ims_single_registration_required_bool"; + field public static final String KEY_IMS_USER_AGENT_STRING = "ims.ims_user_agent_string"; + field public static final String KEY_IPSEC_AUTHENTICATION_ALGORITHMS_INT_ARRAY = "ims.ipsec_authentication_algorithms_int_array"; + field public static final String KEY_IPSEC_ENCRYPTION_ALGORITHMS_INT_ARRAY = "ims.ipsec_encryption_algorithms_int_array"; + field public static final String KEY_IPV4_SIP_MTU_SIZE_CELLULAR_INT = "ims.ipv4_sip_mtu_size_cellular_int"; + field public static final String KEY_IPV6_SIP_MTU_SIZE_CELLULAR_INT = "ims.ipv6_sip_mtu_size_cellular_int"; + field public static final String KEY_KEEP_PDN_UP_IN_NO_VOPS_BOOL = "ims.keep_pdn_up_in_no_vops_bool"; field public static final String KEY_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC_INT = "ims.non_rcs_capabilities_cache_expiration_sec_int"; + field public static final String KEY_PHONE_CONTEXT_DOMAIN_NAME_STRING = "ims.phone_context_domain_name_string"; field public static final String KEY_PREFIX = "ims."; field public static final String KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL = "ims.rcs_bulk_capability_exchange_bool"; field public static final String KEY_RCS_FEATURE_TAG_ALLOWED_STRING_ARRAY = "ims.rcs_feature_tag_allowed_string_array"; + field public static final String KEY_REGISTRATION_EVENT_PACKAGE_SUPPORTED_BOOL = "ims.registration_event_package_supported_bool"; + field public static final String KEY_REGISTRATION_EXPIRY_TIMER_SEC_INT = "ims.registration_expiry_timer_sec_int"; + field public static final String KEY_REGISTRATION_RETRY_BASE_TIMER_MILLIS_INT = "ims.registration_retry_base_timer_millis_int"; + field public static final String KEY_REGISTRATION_RETRY_MAX_TIMER_MILLIS_INT = "ims.registration_retry_max_timer_millis_int"; + field public static final String KEY_REGISTRATION_SUBSCRIBE_EXPIRY_TIMER_SEC_INT = "ims.registration_subscribe_expiry_timer_sec_int"; + field public static final String KEY_REQUEST_URI_TYPE_INT = "ims.request_uri_type_int"; + field public static final String KEY_SIP_OVER_IPSEC_ENABLED_BOOL = "ims.sip_over_ipsec_enabled_bool"; + field public static final String KEY_SIP_PREFERRED_TRANSPORT_INT = "ims.sip_preferred_transport_int"; + field public static final String KEY_SIP_SERVER_PORT_NUMBER_INT = "ims.sip_server_port_number_int"; + field public static final String KEY_SIP_TIMER_B_MILLIS_INT = "ims.sip_timer_b_millis_int"; + field public static final String KEY_SIP_TIMER_C_MILLIS_INT = "ims.sip_timer_c_millis_int"; + field public static final String KEY_SIP_TIMER_D_MILLIS_INT = "ims.sip_timer_d_millis_int"; + field public static final String KEY_SIP_TIMER_F_MILLIS_INT = "ims.sip_timer_f_millis_int"; + field public static final String KEY_SIP_TIMER_H_MILLIS_INT = "ims.sip_timer_h_millis_int"; + field public static final String KEY_SIP_TIMER_J_MILLIS_INT = "ims.sip_timer_j_millis_int"; + field public static final String KEY_SIP_TIMER_T1_MILLIS_INT = "ims.sip_timer_t1_millis_int"; + field public static final String KEY_SIP_TIMER_T2_MILLIS_INT = "ims.sip_timer_t2_millis_int"; + field public static final String KEY_SIP_TIMER_T4_MILLIS_INT = "ims.sip_timer_t4_millis_int"; field public static final String KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT = "ims.wifi_off_deferring_time_millis_int"; + field public static final int NETWORK_TYPE_HOME = 0; // 0x0 + field public static final int NETWORK_TYPE_ROAMING = 1; // 0x1 + field public static final int PREFERRED_TRANSPORT_DYNAMIC_UDP_TCP = 2; // 0x2 + field public static final int PREFERRED_TRANSPORT_TCP = 1; // 0x1 + field public static final int PREFERRED_TRANSPORT_TLS = 3; // 0x3 + field public static final int PREFERRED_TRANSPORT_UDP = 0; // 0x0 + field public static final int REQUEST_URI_FORMAT_SIP = 1; // 0x1 + field public static final int REQUEST_URI_FORMAT_TEL = 0; // 0x0 + field public static final int RTCP_INACTIVITY_ON_CONNECTED = 1; // 0x1 + field public static final int RTCP_INACTIVITY_ON_HOLD = 0; // 0x0 + field public static final int RTP_INACTIVITY_ON_CONNECTED = 2; // 0x2 + } + + public static final class CarrierConfigManager.ImsEmergency { + field public static final String KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL = "imsemergency.emergency_callback_mode_supported_bool"; + field public static final String KEY_EMERGENCY_OVER_IMS_SUPPORTED_RATS_INT_ARRAY = "imsemergency.emergency_over_ims_supported_rats_int_array"; + field public static final String KEY_EMERGENCY_QOS_PRECONDITION_SUPPORTED_BOOL = "imsemergency.emergency_qos_precondition_supported_bool"; + field public static final String KEY_EMERGENCY_REGISTRATION_TIMER_MILLIS_INT = "imsemergency.emergency_registration_timer_millis_int"; + field public static final String KEY_PREFIX = "imsemergency."; + field public static final String KEY_RETRY_EMERGENCY_ON_IMS_PDN_BOOL = "imsemergency.retry_emergency_on_ims_pdn_bool"; + } + + public static final class CarrierConfigManager.ImsRtt { + field public static final String KEY_PREFIX = "imsrtt."; + field public static final String KEY_RED_PAYLOAD_TYPE_INT = "imsrtt.red_payload_type_int"; + field public static final String KEY_T140_PAYLOAD_TYPE_INT = "imsrtt.t140_payload_type_int"; + field public static final String KEY_TEXT_AS_BANDWIDTH_KBPS_INT = "imsrtt.text_as_bandwidth_kbps_int"; + field public static final String KEY_TEXT_CODEC_CAPABILITY_PAYLOAD_TYPES_BUNDLE = "imsrtt.text_codec_capability_payload_types_bundle"; + field public static final String KEY_TEXT_INACTIVITY_CALL_END_REASONS_INT_ARRAY = "imsrtt.text_inactivity_call_end_reasons_int_array"; + field public static final String KEY_TEXT_ON_DEFAULT_BEARER_SUPPORTED_BOOL = "imsrtt.text_on_default_bearer_supported_bool"; + field public static final String KEY_TEXT_QOS_PRECONDITION_SUPPORTED_BOOL = "imsrtt.text_qos_precondition_supported_bool"; + field public static final String KEY_TEXT_RR_BANDWIDTH_BPS_INT = "imsrtt.text_rr_bandwidth_bps_int"; + field public static final String KEY_TEXT_RS_BANDWIDTH_BPS_INT = "imsrtt.text_rs_bandwidth_bps_int"; } public static final class CarrierConfigManager.ImsServiceEntitlement { @@ -41687,6 +41831,167 @@ package android.telephony { field public static final String KEY_SHOW_VOWIFI_WEBVIEW_BOOL = "imsserviceentitlement.show_vowifi_webview_bool"; } + public static final class CarrierConfigManager.ImsSms { + field public static final String KEY_PREFIX = "imssms."; + field public static final String KEY_SMS_CSFB_RETRY_ON_FAILURE_BOOL = "imssms.sms_csfb_retry_on_failure_bool"; + field public static final String KEY_SMS_OVER_IMS_FORMAT_INT = "imssms.sms_over_ims_format_int"; + field public static final String KEY_SMS_OVER_IMS_SUPPORTED_BOOL = "imssms.sms_over_ims_supported_bool"; + field public static final String KEY_SMS_OVER_IMS_SUPPORTED_RATS_INT_ARRAY = "imssms.sms_over_ims_supported_rats_int_array"; + field public static final int SMS_FORMAT_3GPP = 0; // 0x0 + field public static final int SMS_FORMAT_3GPP2 = 1; // 0x1 + } + + public static final class CarrierConfigManager.ImsSs { + field public static final String KEY_NETWORK_INITIATED_USSD_OVER_IMS_SUPPORTED_BOOL = "imsss.network_initiated_ussd_over_ims_supported_bool"; + field public static final String KEY_PREFIX = "imsss."; + field public static final String KEY_USE_CSFB_ON_XCAP_OVER_UT_FAILURE_BOOL = "imsss.use_csfb_on_xcap_over_ut_failure_bool"; + field public static final String KEY_UT_AS_SERVER_FQDN_STRING = "imsss.ut_as_server_fqdn_string"; + field public static final String KEY_UT_AS_SERVER_PORT_INT = "imsss.ut_as_server_port_int"; + field public static final String KEY_UT_IPTYPE_HOME_INT = "imsss.ut_iptype_home_int"; + field public static final String KEY_UT_IPTYPE_ROAMING_INT = "imsss.ut_iptype_roaming_int"; + field public static final String KEY_UT_REQUIRES_IMS_REGISTRATION_BOOL = "imsss.ut_requires_ims_registration_bool"; + field public static final String KEY_UT_SERVER_BASED_SERVICES_INT_ARRAY = "imsss.ut_server_based_services_int_array"; + field public static final String KEY_UT_SUPPORTED_WHEN_PS_DATA_OFF_BOOL = "imsss.ut_supported_when_ps_data_off_bool"; + field public static final String KEY_UT_TERMINAL_BASED_SERVICES_INT_ARRAY = "imsss.ut_terminal_based_services_int_array"; + field public static final String KEY_UT_TRANSPORT_TYPE_INT = "imsss.ut_transport_type_int"; + field public static final String KEY_XCAP_OVER_UT_SUPPORTED_RATS_INT_ARRAY = "imsss.xcap_over_ut_supported_rats_int_array"; + field public static final int SUPPLEMENTARY_SERVICE_CB_ACR = 20; // 0x14 + field public static final int SUPPLEMENTARY_SERVICE_CB_ALL = 12; // 0xc + field public static final int SUPPLEMENTARY_SERVICE_CB_BAIC = 18; // 0x12 + field public static final int SUPPLEMENTARY_SERVICE_CB_BAOC = 14; // 0xe + field public static final int SUPPLEMENTARY_SERVICE_CB_BIC_ROAM = 19; // 0x13 + field public static final int SUPPLEMENTARY_SERVICE_CB_BIL = 21; // 0x15 + field public static final int SUPPLEMENTARY_SERVICE_CB_BOIC = 15; // 0xf + field public static final int SUPPLEMENTARY_SERVICE_CB_BOIC_EXHC = 16; // 0x10 + field public static final int SUPPLEMENTARY_SERVICE_CB_IBS = 17; // 0x11 + field public static final int SUPPLEMENTARY_SERVICE_CB_OBS = 13; // 0xd + field public static final int SUPPLEMENTARY_SERVICE_CF_ALL = 1; // 0x1 + field public static final int SUPPLEMENTARY_SERVICE_CF_ALL_CONDITONAL_FORWARDING = 3; // 0x3 + field public static final int SUPPLEMENTARY_SERVICE_CF_CFB = 4; // 0x4 + field public static final int SUPPLEMENTARY_SERVICE_CF_CFNL = 7; // 0x7 + field public static final int SUPPLEMENTARY_SERVICE_CF_CFNRC = 6; // 0x6 + field public static final int SUPPLEMENTARY_SERVICE_CF_CFNRY = 5; // 0x5 + field public static final int SUPPLEMENTARY_SERVICE_CF_CFU = 2; // 0x2 + field public static final int SUPPLEMENTARY_SERVICE_CW = 0; // 0x0 + field public static final int SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIP = 8; // 0x8 + field public static final int SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIR = 10; // 0xa + field public static final int SUPPLEMENTARY_SERVICE_IDENTIFICATION_TIP = 9; // 0x9 + field public static final int SUPPLEMENTARY_SERVICE_IDENTIFICATION_TIR = 11; // 0xb + } + + public static final class CarrierConfigManager.ImsVoice { + field public static final int ALERTING_SRVCC_SUPPORT = 1; // 0x1 + field public static final int BANDWIDTH_EFFICIENT = 0; // 0x0 + field public static final int BASIC_SRVCC_SUPPORT = 0; // 0x0 + field public static final int CONFERENCE_SUBSCRIBE_TYPE_IN_DIALOG = 0; // 0x0 + field public static final int CONFERENCE_SUBSCRIBE_TYPE_OUT_OF_DIALOG = 1; // 0x1 + field public static final int EVS_ENCODED_BW_TYPE_FB = 3; // 0x3 + field public static final int EVS_ENCODED_BW_TYPE_NB = 0; // 0x0 + field public static final int EVS_ENCODED_BW_TYPE_NB_WB = 4; // 0x4 + field public static final int EVS_ENCODED_BW_TYPE_NB_WB_SWB = 5; // 0x5 + field public static final int EVS_ENCODED_BW_TYPE_NB_WB_SWB_FB = 6; // 0x6 + field public static final int EVS_ENCODED_BW_TYPE_SWB = 2; // 0x2 + field public static final int EVS_ENCODED_BW_TYPE_WB = 1; // 0x1 + field public static final int EVS_ENCODED_BW_TYPE_WB_SWB = 7; // 0x7 + field public static final int EVS_ENCODED_BW_TYPE_WB_SWB_FB = 8; // 0x8 + field public static final int EVS_OPERATIONAL_MODE_AMRWB_IO = 1; // 0x1 + field public static final int EVS_OPERATIONAL_MODE_PRIMARY = 0; // 0x0 + field public static final int EVS_PRIMARY_MODE_BITRATE_128_0_KBPS = 11; // 0xb + field public static final int EVS_PRIMARY_MODE_BITRATE_13_2_KBPS = 4; // 0x4 + field public static final int EVS_PRIMARY_MODE_BITRATE_16_4_KBPS = 5; // 0x5 + field public static final int EVS_PRIMARY_MODE_BITRATE_24_4_KBPS = 6; // 0x6 + field public static final int EVS_PRIMARY_MODE_BITRATE_32_0_KBPS = 7; // 0x7 + field public static final int EVS_PRIMARY_MODE_BITRATE_48_0_KBPS = 8; // 0x8 + field public static final int EVS_PRIMARY_MODE_BITRATE_5_9_KBPS = 0; // 0x0 + field public static final int EVS_PRIMARY_MODE_BITRATE_64_0_KBPS = 9; // 0x9 + field public static final int EVS_PRIMARY_MODE_BITRATE_7_2_KBPS = 1; // 0x1 + field public static final int EVS_PRIMARY_MODE_BITRATE_8_0_KBPS = 2; // 0x2 + field public static final int EVS_PRIMARY_MODE_BITRATE_96_0_KBPS = 10; // 0xa + field public static final int EVS_PRIMARY_MODE_BITRATE_9_6_KBPS = 3; // 0x3 + field public static final String KEY_AMRNB_PAYLOAD_DESCRIPTION_BUNDLE = "imsvoice.amrnb_payload_description_bundle"; + field public static final String KEY_AMRNB_PAYLOAD_TYPE_INT_ARRAY = "imsvoice.amrnb_payload_type_int_array"; + field public static final String KEY_AMRWB_PAYLOAD_DESCRIPTION_BUNDLE = "imsvoice.amrwb_payload_description_bundle"; + field public static final String KEY_AMRWB_PAYLOAD_TYPE_INT_ARRAY = "imsvoice.amrwb_payload_type_int_array"; + field public static final String KEY_AMR_CODEC_ATTRIBUTE_MODESET_INT_ARRAY = "imsvoice.amr_codec_attribute_modeset_int_array"; + field public static final String KEY_AMR_CODEC_ATTRIBUTE_PAYLOAD_FORMAT_INT = "imsvoice.amr_codec_attribute_payload_format_int"; + field public static final String KEY_AUDIO_AS_BANDWIDTH_KBPS_INT = "imsvoice.audio_as_bandwidth_kbps_int"; + field public static final String KEY_AUDIO_CODEC_CAPABILITY_PAYLOAD_TYPES_BUNDLE = "imsvoice.audio_codec_capability_payload_types_bundle"; + field public static final String KEY_AUDIO_INACTIVITY_CALL_END_REASONS_INT_ARRAY = "imsvoice.audio_inactivity_call_end_reasons_int_array"; + field public static final String KEY_AUDIO_RR_BANDWIDTH_BPS_INT = "imsvoice.audio_rr_bandwidth_bps_int"; + field public static final String KEY_AUDIO_RS_BANDWIDTH_BPS_INT = "imsvoice.audio_rs_bandwidth_bps_int"; + field public static final String KEY_AUDIO_RTCP_INACTIVITY_TIMER_MILLIS_INT = "imsvoice.audio_rtcp_inactivity_timer_millis_int"; + field public static final String KEY_AUDIO_RTP_INACTIVITY_TIMER_MILLIS_INT = "imsvoice.audio_rtp_inactivity_timer_millis_int"; + field public static final String KEY_CARRIER_VOLTE_ROAMING_AVAILABLE_BOOL = "imsvoice.carrier_volte_roaming_available_bool"; + field public static final String KEY_CODEC_ATTRIBUTE_MODE_CHANGE_CAPABILITY_INT = "imsvoice.codec_attribute_mode_change_capability_int"; + field public static final String KEY_CODEC_ATTRIBUTE_MODE_CHANGE_NEIGHBOR_INT = "imsvoice.codec_attribute_mode_change_neighbor_int"; + field public static final String KEY_CODEC_ATTRIBUTE_MODE_CHANGE_PERIOD_INT = "imsvoice.codec_attribute_mode_change_period_int"; + field public static final String KEY_CONFERENCE_FACTORY_URI_STRING = "imsvoice.conference_factory_uri_string"; + field public static final String KEY_CONFERENCE_SUBSCRIBE_TYPE_INT = "imsvoice.conference_subscribe_type_int"; + field public static final String KEY_DEDICATED_BEARER_WAIT_TIMER_MILLIS_INT = "imsvoice.dedicated_bearer_wait_timer_millis_int"; + field public static final String KEY_DTMFNB_PAYLOAD_TYPE_INT_ARRAY = "imsvoice.dtmfnb_payload_type_int_array"; + field public static final String KEY_DTMFWB_PAYLOAD_TYPE_INT_ARRAY = "imsvoice.dtmfwb_payload_type_int_array"; + field public static final String KEY_EVS_CODEC_ATTRIBUTE_BANDWIDTH_INT = "imsvoice.evs_codec_attribute_bandwidth_int"; + field public static final String KEY_EVS_CODEC_ATTRIBUTE_BITRATE_INT_ARRAY = "imsvoice.evs_codec_attribute_bitrate_int_array"; + field public static final String KEY_EVS_CODEC_ATTRIBUTE_CHANNELS_INT = "imsvoice.evs_codec_attribute_channels_int"; + field public static final String KEY_EVS_CODEC_ATTRIBUTE_CH_AW_RECV_INT = "imsvoice.evs_codec_attribute_ch_aw_recv_int"; + field public static final String KEY_EVS_CODEC_ATTRIBUTE_CMR_INT = "imsvoice.codec_attribute_cmr_int"; + field public static final String KEY_EVS_CODEC_ATTRIBUTE_DTX_BOOL = "imsvoice.evs_codec_attribute_dtx_bool"; + field public static final String KEY_EVS_CODEC_ATTRIBUTE_DTX_RECV_BOOL = "imsvoice.evs_codec_attribute_dtx_recv_bool"; + field public static final String KEY_EVS_CODEC_ATTRIBUTE_HF_ONLY_INT = "imsvoice.evs_codec_attribute_hf_only_int"; + field public static final String KEY_EVS_CODEC_ATTRIBUTE_MODE_SWITCH_INT = "imsvoice.evs_codec_attribute_mode_switch_int"; + field public static final String KEY_EVS_PAYLOAD_DESCRIPTION_BUNDLE = "imsvoice.evs_payload_description_bundle"; + field public static final String KEY_EVS_PAYLOAD_TYPE_INT_ARRAY = "imsvoice.evs_payload_type_int_array"; + field public static final String KEY_INCLUDE_CALLER_ID_SERVICE_CODES_IN_SIP_INVITE_BOOL = "imsvoice.include_caller_id_service_codes_in_sip_invite_bool"; + field public static final String KEY_MINIMUM_SESSION_EXPIRES_TIMER_SEC_INT = "imsvoice.minimum_session_expires_timer_sec_int"; + field public static final String KEY_MO_CALL_REQUEST_TIMEOUT_MILLIS_INT = "imsvoice.mo_call_request_timeout_millis_int"; + field public static final String KEY_MULTIENDPOINT_SUPPORTED_BOOL = "imsvoice.multiendpoint_supported_bool"; + field public static final String KEY_OIP_SOURCE_FROM_HEADER_BOOL = "imsvoice.oip_source_from_header_bool"; + field public static final String KEY_PRACK_SUPPORTED_FOR_18X_BOOL = "imsvoice.prack_supported_for_18x_bool"; + field public static final String KEY_PREFIX = "imsvoice."; + field public static final String KEY_RINGBACK_TIMER_MILLIS_INT = "imsvoice.ringback_timer_millis_int"; + field public static final String KEY_RINGING_TIMER_MILLIS_INT = "imsvoice.ringing_timer_millis_int"; + field public static final String KEY_SESSION_EXPIRES_TIMER_SEC_INT = "imsvoice.session_expires_timer_sec_int"; + field public static final String KEY_SESSION_REFRESHER_TYPE_INT = "imsvoice.session_refresher_type_int"; + field public static final String KEY_SESSION_REFRESH_METHOD_INT = "imsvoice.session_refresh_method_int"; + field public static final String KEY_SESSION_TIMER_SUPPORTED_BOOL = "imsvoice.session_timer_supported_bool"; + field public static final String KEY_SRVCC_TYPE_INT_ARRAY = "imsvoice.srvcc_type_int_array"; + field public static final String KEY_VOICE_ON_DEFAULT_BEARER_SUPPORTED_BOOL = "imsvoice.voice_on_default_bearer_supported_bool"; + field public static final String KEY_VOICE_QOS_PRECONDITION_SUPPORTED_BOOL = "imsvoice.voice_qos_precondition_supported_bool"; + field public static final int MIDCALL_SRVCC_SUPPORT = 3; // 0x3 + field public static final int OCTET_ALIGNED = 1; // 0x1 + field public static final int PREALERTING_SRVCC_SUPPORT = 2; // 0x2 + field public static final int SESSION_REFRESHER_TYPE_UAC = 1; // 0x1 + field public static final int SESSION_REFRESHER_TYPE_UAS = 2; // 0x2 + field public static final int SESSION_REFRESHER_TYPE_UNKNOWN = 0; // 0x0 + field public static final int SESSION_REFRESH_METHOD_INVITE = 0; // 0x0 + field public static final int SESSION_REFRESH_METHOD_UPDATE_PREFERRED = 1; // 0x1 + } + + public static final class CarrierConfigManager.ImsVt { + field public static final String KEY_H264_PAYLOAD_DESCRIPTION_BUNDLE = "imsvt.h264_payload_description_bundle"; + field public static final String KEY_H264_PAYLOAD_TYPE_INT_ARRAY = "imsvt.h264_payload_type_int_array"; + field public static final String KEY_H264_VIDEO_CODEC_ATTRIBUTE_PROFILE_LEVEL_ID_STRING = "imsvt.h264_video_codec_attribute_profile_level_id_string"; + field public static final String KEY_PREFIX = "imsvt."; + field public static final String KEY_VIDEO_AS_BANDWIDTH_KBPS_INT = "imsvt.video_as_bandwidth_kbps_int"; + field public static final String KEY_VIDEO_CODEC_ATTRIBUTE_FRAME_RATE_INT = "imsvt.video_codec_attribute_frame_rate_int"; + field public static final String KEY_VIDEO_CODEC_ATTRIBUTE_PACKETIZATION_MODE_INT = "imsvt.video_codec_attribute_packetization_mode_int"; + field public static final String KEY_VIDEO_CODEC_ATTRIBUTE_RESOLUTION_INT_ARRAY = "imsvt.video_codec_attribute_resolution_int_array"; + field public static final String KEY_VIDEO_CODEC_CAPABILITY_PAYLOAD_TYPES_BUNDLE = "imsvt.video_codec_capability_payload_types_bundle"; + field public static final String KEY_VIDEO_ON_DEFAULT_BEARER_SUPPORTED_BOOL = "imsvt.video_on_default_bearer_supported_bool"; + field public static final String KEY_VIDEO_QOS_PRECONDITION_SUPPORTED_BOOL = "imsvt.video_qos_precondition_supported_bool"; + field public static final String KEY_VIDEO_RR_BANDWIDTH_BPS_INT = "imsvt.video_rr_bandwidth_bps_int"; + field public static final String KEY_VIDEO_RS_BANDWIDTH_BPS_INT = "imsvt.video_rs_bandwidth_bps_int"; + field public static final String KEY_VIDEO_RTCP_INACTIVITY_TIMER_MILLIS_INT = "imsvt.video_rtcp_inactivity_timer_millis_int"; + field public static final String KEY_VIDEO_RTP_DSCP_INT = "imsvt.video_rtp_dscp_int"; + field public static final String KEY_VIDEO_RTP_INACTIVITY_TIMER_MILLIS_INT = "imsvt.video_rtp_inactivity_timer_millis_int"; + } + + public static final class CarrierConfigManager.ImsWfc { + field public static final String KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL = "imswfc.emergency_call_over_emergency_pdn_bool"; + field public static final String KEY_PIDF_SHORT_CODE_STRING_ARRAY = "imswfc.pidf_short_code_string_array"; + field public static final String KEY_PREFIX = "imswfc."; + } + public static final class CarrierConfigManager.Iwlan { field public static final int AUTHENTICATION_METHOD_CERT = 1; // 0x1 field public static final int AUTHENTICATION_METHOD_EAP_ONLY = 0; // 0x0 @@ -51947,6 +52252,7 @@ package android.view.accessibility { field public static final int TYPE_ACCESSIBILITY_OVERLAY = 4; // 0x4 field public static final int TYPE_APPLICATION = 1; // 0x1 field public static final int TYPE_INPUT_METHOD = 2; // 0x2 + field public static final int TYPE_MAGNIFICATION_OVERLAY = 6; // 0x6 field public static final int TYPE_SPLIT_SCREEN_DIVIDER = 5; // 0x5 field public static final int TYPE_SYSTEM = 3; // 0x3 } diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index 53bc8a6e0323..4d8453725205 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -88,6 +88,14 @@ package android.content { field public static final String TEST_NETWORK_SERVICE = "test_network"; } + public class Intent implements java.lang.Cloneable android.os.Parcelable { + field public static final String ACTION_SETTING_RESTORED = "android.os.action.SETTING_RESTORED"; + field public static final String EXTRA_SETTING_NAME = "setting_name"; + field public static final String EXTRA_SETTING_NEW_VALUE = "new_value"; + field public static final String EXTRA_SETTING_PREVIOUS_VALUE = "previous_value"; + field public static final String EXTRA_SETTING_RESTORED_FROM_SDK_INT = "restored_from_sdk_int"; + } + } package android.content.pm { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index e26d3c419039..3a7077dde0a5 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -165,6 +165,7 @@ package android { field public static final String MANAGE_ROLE_HOLDERS = "android.permission.MANAGE_ROLE_HOLDERS"; field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS"; field public static final String MANAGE_ROTATION_RESOLVER = "android.permission.MANAGE_ROTATION_RESOLVER"; + field public static final String MANAGE_SAFETY_CENTER = "android.permission.MANAGE_SAFETY_CENTER"; field public static final String MANAGE_SEARCH_UI = "android.permission.MANAGE_SEARCH_UI"; field public static final String MANAGE_SENSOR_PRIVACY = "android.permission.MANAGE_SENSOR_PRIVACY"; field public static final String MANAGE_SMARTSPACE = "android.permission.MANAGE_SMARTSPACE"; @@ -226,7 +227,6 @@ package android { field public static final String READ_APP_SPECIFIC_LOCALES = "android.permission.READ_APP_SPECIFIC_LOCALES"; field public static final String READ_CARRIER_APP_INFO = "android.permission.READ_CARRIER_APP_INFO"; field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS"; - field public static final String READ_COMMUNAL_STATE = "android.permission.READ_COMMUNAL_STATE"; field public static final String READ_CONTENT_RATING_SYSTEMS = "android.permission.READ_CONTENT_RATING_SYSTEMS"; field public static final String READ_DEVICE_CONFIG = "android.permission.READ_DEVICE_CONFIG"; field public static final String READ_DREAM_STATE = "android.permission.READ_DREAM_STATE"; @@ -404,6 +404,7 @@ package android { field public static final int config_systemAmbientAudioIntelligence = 17039411; // 0x1040033 field public static final int config_systemAppProtectionService; field public static final int config_systemAudioIntelligence = 17039412; // 0x1040034 + field public static final int config_systemAutomotiveCalendarSyncManager; field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028 field public static final int config_systemAutomotiveProjection = 17039401; // 0x1040029 field public static final int config_systemCompanionDeviceProvider = 17039417; // 0x1040039 @@ -1046,6 +1047,7 @@ package android.app.admin { field @Deprecated public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL"; field public static final String EXTRA_PROVISIONING_ORGANIZATION_NAME = "android.app.extra.PROVISIONING_ORGANIZATION_NAME"; field public static final String EXTRA_PROVISIONING_RETURN_BEFORE_POLICY_COMPLIANCE = "android.app.extra.PROVISIONING_RETURN_BEFORE_POLICY_COMPLIANCE"; + field public static final String EXTRA_PROVISIONING_ROLE_HOLDER_CUSTOM_USER_CONSENT_INTENT = "android.app.extra.PROVISIONING_ROLE_HOLDER_CUSTOM_USER_CONSENT_INTENT"; field public static final String EXTRA_PROVISIONING_SKIP_OWNERSHIP_DISCLAIMER = "android.app.extra.PROVISIONING_SKIP_OWNERSHIP_DISCLAIMER"; field public static final String EXTRA_PROVISIONING_SUPPORTED_MODES = "android.app.extra.PROVISIONING_SUPPORTED_MODES"; field public static final String EXTRA_PROVISIONING_SUPPORT_URL = "android.app.extra.PROVISIONING_SUPPORT_URL"; @@ -1391,13 +1393,6 @@ package android.app.backup { package android.app.communal { public final class CommunalManager { - method @RequiresPermission(android.Manifest.permission.READ_COMMUNAL_STATE) public void addCommunalModeListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.communal.CommunalManager.CommunalModeListener); - method @RequiresPermission(android.Manifest.permission.READ_COMMUNAL_STATE) public boolean isCommunalMode(); - method @RequiresPermission(android.Manifest.permission.READ_COMMUNAL_STATE) public void removeCommunalModeListener(@NonNull android.app.communal.CommunalManager.CommunalModeListener); - } - - @java.lang.FunctionalInterface public static interface CommunalManager.CommunalModeListener { - method public void onCommunalModeChanged(boolean); } } @@ -2226,9 +2221,12 @@ package android.bluetooth { public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile { method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean connect(android.bluetooth.BluetoothDevice); + method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int connectAudio(); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disconnect(android.bluetooth.BluetoothDevice); + method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int disconnectAudio(); + method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getAudioState(@NonNull android.bluetooth.BluetoothDevice); method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice); - method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isInbandRingingEnabled(); + method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean isInbandRingingEnabled(); method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int); method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean startScoUsingVirtualVoiceCall(); method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean stopScoUsingVirtualVoiceCall(); @@ -2307,6 +2305,14 @@ package android.bluetooth { public final class BluetoothStatusCodes { field public static final int ERROR_ANOTHER_ACTIVE_OOB_REQUEST = 1000; // 0x3e8 + field public static final int ERROR_AUDIO_DEVICE_ALREADY_CONNECTED = 1116; // 0x45c + field public static final int ERROR_AUDIO_DEVICE_ALREADY_DISCONNECTED = 1117; // 0x45d + field public static final int ERROR_AUDIO_ROUTE_BLOCKED = 1118; // 0x45e + field public static final int ERROR_CALL_ACTIVE = 1119; // 0x45f + field public static final int ERROR_NOT_ACTIVE_DEVICE = 12; // 0xc + field public static final int ERROR_NO_ACTIVE_DEVICES = 13; // 0xd + field public static final int ERROR_PROFILE_NOT_CONNECTED = 14; // 0xe + field public static final int ERROR_TIMEOUT = 15; // 0xf } public final class BluetoothUuid { @@ -2436,6 +2442,25 @@ package android.bluetooth { package android.bluetooth.le { + public final class AdvertiseSettings implements android.os.Parcelable { + method public int getOwnAddressType(); + } + + public static final class AdvertiseSettings.Builder { + method @NonNull public android.bluetooth.le.AdvertiseSettings.Builder setOwnAddressType(int); + } + + public final class AdvertisingSetParameters implements android.os.Parcelable { + method public int getOwnAddressType(); + field public static final int ADDRESS_TYPE_DEFAULT = -1; // 0xffffffff + field public static final int ADDRESS_TYPE_PUBLIC = 0; // 0x0 + field public static final int ADDRESS_TYPE_RANDOM = 1; // 0x1 + } + + public static final class AdvertisingSetParameters.Builder { + method @NonNull public android.bluetooth.le.AdvertisingSetParameters.Builder setOwnAddressType(int); + } + public final class BluetoothLeScanner { method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.UPDATE_DEVICE_STATS}) public void startScanFromSource(android.os.WorkSource, android.bluetooth.le.ScanCallback); method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.UPDATE_DEVICE_STATS}) public void startScanFromSource(java.util.List<android.bluetooth.le.ScanFilter>, android.bluetooth.le.ScanSettings, android.os.WorkSource, android.bluetooth.le.ScanCallback); @@ -3031,6 +3056,7 @@ package android.content.pm { method @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS}) public abstract void updatePermissionFlags(@NonNull String, @NonNull String, @android.content.pm.PackageManager.PermissionFlags int, @android.content.pm.PackageManager.PermissionFlags int, @NonNull android.os.UserHandle); method @Deprecated @RequiresPermission(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT) public abstract void verifyIntentFilter(int, int, @NonNull java.util.List<java.lang.String>); field public static final String ACTION_REQUEST_PERMISSIONS = "android.content.pm.action.REQUEST_PERMISSIONS"; + field public static final String ACTION_REQUEST_PERMISSIONS_FOR_OTHER = "android.content.pm.action.REQUEST_PERMISSIONS_FOR_OTHER"; field public static final String EXTRA_REQUEST_PERMISSIONS_NAMES = "android.content.pm.extra.REQUEST_PERMISSIONS_NAMES"; field public static final String EXTRA_REQUEST_PERMISSIONS_RESULTS = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS"; field public static final String FEATURE_BROADCAST_RADIO = "android.hardware.broadcastradio"; @@ -6323,6 +6349,7 @@ package android.media.tv { method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public boolean captureFrame(String, android.view.Surface, android.media.tv.TvStreamConfig); method @NonNull public java.util.List<java.lang.String> getAvailableExtensionInterfaceNames(@NonNull String); method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public java.util.List<android.media.tv.TvStreamConfig> getAvailableTvStreamConfigList(String); + method public int getClientPriority(int, @Nullable String); method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_TUNED_INFO) public java.util.List<android.media.tv.TunedInfo> getCurrentTunedInfos(); method @NonNull @RequiresPermission("android.permission.DVB_DEVICE") public java.util.List<android.media.tv.DvbDeviceInfo> getDvbDeviceList(); method @Nullable public android.os.IBinder getExtensionInterface(@NonNull String, @NonNull String); @@ -6442,7 +6469,9 @@ package android.media.tv.tuner { } public class Lnb implements java.lang.AutoCloseable { + method public void addCallback(@NonNull android.media.tv.tuner.LnbCallback, @NonNull java.util.concurrent.Executor); method public void close(); + method public boolean removeCallback(@NonNull android.media.tv.tuner.LnbCallback); method public int sendDiseqcMessage(@NonNull byte[]); method public int setSatellitePosition(int); method public int setTone(int); @@ -6479,6 +6508,7 @@ package android.media.tv.tuner { method public void clearOnTuneEventListener(); method public void clearResourceLostListener(); method public void close(); + method public void closeFrontend(); method public int connectCiCam(int); method public int connectFrontendToCiCam(int); method public int disconnectCiCam(); @@ -6507,6 +6537,7 @@ package android.media.tv.tuner { method public void setOnTuneEventListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.frontend.OnTuneEventListener); method public void setResourceLostListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.Tuner.OnResourceLostListener); method public void shareFrontendFromTuner(@NonNull android.media.tv.tuner.Tuner); + method public int transferOwner(@NonNull android.media.tv.tuner.Tuner); method public int tune(@NonNull android.media.tv.tuner.frontend.FrontendSettings); method @RequiresPermission("android.permission.TUNER_RESOURCE_ACCESS") public void updateResourcePriority(int, int); field public static final int INVALID_AV_SYNC_ID = -1; // 0xffffffff @@ -6720,8 +6751,8 @@ package android.media.tv.tuner.filter { method @Nullable public String acquireSharedFilterToken(); method public void close(); method public int configure(@NonNull android.media.tv.tuner.filter.FilterConfiguration); - method public int delayCallbackUntilBufferFilled(int); - method public int delayCallbackUntilTimeMillis(long); + method public int delayCallbackUntilBytesAccumulated(int); + method public int delayCallbackUntilMillisElapsed(long); method public int flush(); method public void freeSharedFilterToken(@NonNull String); method @Deprecated public int getId(); @@ -6940,12 +6971,14 @@ package android.media.tv.tuner.filter { } public abstract class SectionSettings extends android.media.tv.tuner.filter.Settings { + method public int getBitWidthOfLengthField(); method public boolean isCrcEnabled(); method public boolean isRaw(); method public boolean isRepeat(); } public abstract static class SectionSettings.Builder<T extends android.media.tv.tuner.filter.SectionSettings.Builder<T>> { + method @NonNull public T setBitWidthOfLengthField(@IntRange(from=0) int); method @NonNull public T setCrcEnabled(boolean); method @NonNull public T setRaw(boolean); method @NonNull public T setRepeat(boolean); @@ -9346,6 +9379,7 @@ package android.os { method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isUserOfType(@NonNull String); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isUserUnlockingOrUnlocked(@NonNull android.os.UserHandle); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean removeUser(@NonNull android.os.UserHandle); + method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public int removeUserWhenPossible(@NonNull android.os.UserHandle, boolean); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setUserIcon(@NonNull android.graphics.Bitmap) throws android.os.UserManager.UserOperationException; method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setUserName(@Nullable String); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean someUserHasAccount(@NonNull String, @NonNull String); @@ -9353,6 +9387,10 @@ package android.os { field public static final String ACTION_USER_RESTRICTIONS_CHANGED = "android.os.action.USER_RESTRICTIONS_CHANGED"; field @Deprecated public static final String DISALLOW_OEM_UNLOCK = "no_oem_unlock"; field public static final String DISALLOW_RUN_IN_BACKGROUND = "no_run_in_background"; + field public static final int REMOVE_RESULT_ALREADY_BEING_REMOVED = 2; // 0x2 + field public static final int REMOVE_RESULT_DEFERRED = 1; // 0x1 + field public static final int REMOVE_RESULT_ERROR = 3; // 0x3 + field public static final int REMOVE_RESULT_REMOVED = 0; // 0x0 field public static final int RESTRICTION_NOT_SET = 0; // 0x0 field public static final int RESTRICTION_SOURCE_DEVICE_OWNER = 2; // 0x2 field public static final int RESTRICTION_SOURCE_PROFILE_OWNER = 4; // 0x4 @@ -9582,6 +9620,7 @@ package android.permission { method @Deprecated @BinderThread public void onRestoreRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable); method @BinderThread public abstract void onRevokeRuntimePermission(@NonNull String, @NonNull String, @NonNull Runnable); method @BinderThread public abstract void onRevokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull String, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,java.util.List<java.lang.String>>>); + method @BinderThread public void onSelfRevokePermissions(@NonNull String, @NonNull java.util.List<java.lang.String>, @NonNull Runnable); method @Deprecated @BinderThread public abstract void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull String, @NonNull String, int, @NonNull java.util.function.Consumer<java.lang.Boolean>); method @BinderThread public void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull android.permission.AdminPermissionControlParams, @NonNull java.util.function.Consumer<java.lang.Boolean>); method @BinderThread public void onStageAndApplyRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable); @@ -10797,7 +10836,7 @@ package android.service.games { public class GameService extends android.app.Service { ctor public GameService(); - method @Nullable public android.os.IBinder onBind(@Nullable android.content.Intent); + method @Nullable public final android.os.IBinder onBind(@Nullable android.content.Intent); method public void onConnected(); method public void onDisconnected(); field public static final String ACTION_GAME_SERVICE = "android.service.games.action.GAME_SERVICE"; @@ -10812,7 +10851,7 @@ package android.service.games { public abstract class GameSessionService extends android.app.Service { ctor public GameSessionService(); - method @Nullable public android.os.IBinder onBind(@Nullable android.content.Intent); + method @Nullable public final android.os.IBinder onBind(@Nullable android.content.Intent); method @NonNull public abstract android.service.games.GameSession onNewSession(@NonNull android.service.games.CreateGameSessionRequest); field public static final String ACTION_GAME_SESSION_SERVICE = "android.service.games.action.GAME_SESSION_SERVICE"; } diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 0710781a3155..66250fcebe92 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -36,6 +36,7 @@ package android { field public static final String RECORD_BACKGROUND_AUDIO = "android.permission.RECORD_BACKGROUND_AUDIO"; field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS"; field public static final String RESET_APP_ERRORS = "android.permission.RESET_APP_ERRORS"; + field public static final String REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL = "android.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL"; field public static final String SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS = "android.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS"; field public static final String START_TASKS_FROM_RECENTS = "android.permission.START_TASKS_FROM_RECENTS"; field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS"; @@ -66,6 +67,7 @@ package android { public static final class R.string { field public static final int config_defaultAssistant = 17039393; // 0x1040021 field public static final int config_defaultDialer = 17039395; // 0x1040023 + field public static final int config_systemAutomotiveCalendarSyncManager; field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028 field public static final int config_systemAutomotiveProjection = 17039401; // 0x1040029 field public static final int config_systemGallery = 17039399; // 0x1040027 @@ -1822,13 +1824,21 @@ package android.os { public static final class VibrationEffect.WaveformBuilder { method @NonNull public android.os.VibrationEffect.WaveformBuilder addRamp(@FloatRange(from=0.0f, to=1.0f) float, @IntRange(from=0) int); - method @NonNull public android.os.VibrationEffect.WaveformBuilder addRamp(@FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=-1.0F, to=1.0f) float, @IntRange(from=0) int); + method @NonNull public android.os.VibrationEffect.WaveformBuilder addRamp(@FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=1.0f) float, @IntRange(from=0) int); method @NonNull public android.os.VibrationEffect.WaveformBuilder addStep(@FloatRange(from=0.0f, to=1.0f) float, @IntRange(from=0) int); - method @NonNull public android.os.VibrationEffect.WaveformBuilder addStep(@FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=-1.0F, to=1.0f) float, @IntRange(from=0) int); + method @NonNull public android.os.VibrationEffect.WaveformBuilder addStep(@FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=1.0f) float, @IntRange(from=0) int); method @NonNull public android.os.VibrationEffect build(); method @NonNull public android.os.VibrationEffect build(int); } + public abstract class Vibrator { + method public int getDefaultVibrationIntensity(int); + field public static final int VIBRATION_INTENSITY_HIGH = 3; // 0x3 + field public static final int VIBRATION_INTENSITY_LOW = 1; // 0x1 + field public static final int VIBRATION_INTENSITY_MEDIUM = 2; // 0x2 + field public static final int VIBRATION_INTENSITY_OFF = 0; // 0x0 + } + public class VintfObject { method public static String[] getHalNamesAndVersions(); method @NonNull public static String getPlatformSepolicyVersion(); @@ -1982,9 +1992,9 @@ package android.os.vibrator { method public int describeContents(); method public long getDuration(); method public float getEndAmplitude(); - method public float getEndFrequency(); + method public float getEndFrequencyHz(); method public float getStartAmplitude(); - method public float getStartFrequency(); + method public float getStartFrequencyHz(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.os.vibrator.RampSegment> CREATOR; } @@ -1993,7 +2003,7 @@ package android.os.vibrator { method public int describeContents(); method public float getAmplitude(); method public long getDuration(); - method public float getFrequency(); + method public float getFrequencyHz(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.os.vibrator.StepSegment> CREATOR; } @@ -2036,6 +2046,7 @@ package android.permission { method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.permission.PermGroupUsage> getIndicatorAppOpUsageData(); method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.permission.PermGroupUsage> getIndicatorAppOpUsageData(boolean); method @NonNull public android.content.AttributionSource registerAttributionSource(@NonNull android.content.AttributionSource); + method public void revokePostNotificationPermissionWithoutKillForTest(@NonNull String, int); } } @@ -2309,6 +2320,7 @@ package android.service.dreams { method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent); method public abstract void onStartDream(@NonNull android.view.WindowManager.LayoutParams); method public final void requestExit(); + method public final boolean shouldShowComplications(); } } diff --git a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java index 8e01779c6fac..3c9b23251191 100644 --- a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java +++ b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java @@ -172,7 +172,7 @@ public final class AccessibilityGestureEvent implements Parcelable { private AccessibilityGestureEvent(@NonNull Parcel parcel) { mGestureId = parcel.readInt(); mDisplayId = parcel.readInt(); - ParceledListSlice<MotionEvent> slice = parcel.readParcelable(getClass().getClassLoader(), android.content.pm.ParceledListSlice.class); + ParceledListSlice<MotionEvent> slice = parcel.readParcelable(getClass().getClassLoader()); mMotionEvents = slice.getList(); } diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java index 1167d0b1034f..04c784ea1c17 100644 --- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java +++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java @@ -1094,8 +1094,8 @@ public class AccessibilityServiceInfo implements Parcelable { mInteractiveUiTimeout = parcel.readInt(); flags = parcel.readInt(); crashed = parcel.readInt() != 0; - mComponentName = parcel.readParcelable(this.getClass().getClassLoader(), android.content.ComponentName.class); - mResolveInfo = parcel.readParcelable(null, android.content.pm.ResolveInfo.class); + mComponentName = parcel.readParcelable(this.getClass().getClassLoader()); + mResolveInfo = parcel.readParcelable(null); mSettingsActivityName = parcel.readString(); mCapabilities = parcel.readInt(); mSummaryResId = parcel.readInt(); diff --git a/core/java/android/accessibilityservice/GestureDescription.java b/core/java/android/accessibilityservice/GestureDescription.java index a821dadf4948..857c5416b3bc 100644 --- a/core/java/android/accessibilityservice/GestureDescription.java +++ b/core/java/android/accessibilityservice/GestureDescription.java @@ -525,7 +525,7 @@ public final class GestureDescription { public GestureStep(Parcel parcel) { timeSinceGestureStart = parcel.readLong(); Parcelable[] parcelables = - parcel.readParcelableArray(TouchPoint.class.getClassLoader()); + parcel.readParcelableArray(TouchPoint.class.getClassLoader(), TouchPoint.class); numTouchPoints = (parcelables == null) ? 0 : parcelables.length; touchPoints = new TouchPoint[numTouchPoints]; for (int i = 0; i < numTouchPoints; i++) { diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index a1409839ff63..9f8d24662c8d 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -1903,7 +1903,7 @@ public class ActivityManager { public void readFromParcel(Parcel source) { id = source.readInt(); persistentId = source.readInt(); - childrenTaskInfos = source.readArrayList(RecentTaskInfo.class.getClassLoader(), android.app.ActivityManager.RecentTaskInfo.class); + childrenTaskInfos = source.readArrayList(RecentTaskInfo.class.getClassLoader()); lastSnapshotData.taskSize = source.readTypedObject(Point.CREATOR); lastSnapshotData.contentInsets = source.readTypedObject(Rect.CREATOR); lastSnapshotData.bufferSize = source.readTypedObject(Point.CREATOR); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index b2c9439d5760..7ac4bddbed57 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -4178,15 +4178,20 @@ public final class ActivityThread extends ClientTransactionHandler view.requestLayout(); view.getViewTreeObserver().addOnDrawListener(new ViewTreeObserver.OnDrawListener() { + private boolean mHandled = false; @Override public void onDraw() { + if (mHandled) { + return; + } + mHandled = true; // Transfer the splash screen view from shell to client. // Call syncTransferSplashscreenViewTransaction at the first onDraw so we can ensure // the client view is ready to show and we can use applyTransactionOnDraw to make // all transitions happen at the same frame. syncTransferSplashscreenViewTransaction( view, r.token, decorView, startingWindowLeash); - view.postOnAnimation(() -> view.getViewTreeObserver().removeOnDrawListener(this)); + view.post(() -> view.getViewTreeObserver().removeOnDrawListener(this)); } }); } @@ -4530,6 +4535,12 @@ public final class ActivityThread extends ClientTransactionHandler // we are back active so skip it. unscheduleGcIdler(); + // To investigate "duplciate Application objects" bug (b/185177290) + if (UserHandle.myUserId() != UserHandle.getUserId(data.info.applicationInfo.uid)) { + Slog.wtf(TAG, "handleCreateService called with wrong appinfo UID: myUserId=" + + UserHandle.myUserId() + " appinfo.uid=" + data.info.applicationInfo.uid); + } + LoadedApk packageInfo = getPackageInfoNoCheck( data.info.applicationInfo, data.compatInfo); Service service = null; diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 68c69e555bda..565f69090c6b 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -4058,7 +4058,7 @@ public class AppOpsManager { LongSparseArray<NoteOpEvent> array = new LongSparseArray<>(numEntries); for (int i = 0; i < numEntries; i++) { - array.put(source.readLong(), source.readParcelable(null, android.app.AppOpsManager.NoteOpEvent.class)); + array.put(source.readLong(), source.readParcelable(null)); } return array; @@ -5178,7 +5178,7 @@ public class AppOpsManager { final int[] uids = parcel.createIntArray(); if (!ArrayUtils.isEmpty(uids)) { final ParceledListSlice<HistoricalUidOps> listSlice = parcel.readParcelable( - HistoricalOps.class.getClassLoader(), android.content.pm.ParceledListSlice.class); + HistoricalOps.class.getClassLoader()); final List<HistoricalUidOps> uidOps = (listSlice != null) ? listSlice.getList() : null; if (uidOps == null) { @@ -10000,7 +10000,7 @@ public class AppOpsManager { private static @Nullable List<AttributedOpEntry> readDiscreteAccessArrayFromParcel( @NonNull Parcel parcel) { - final ParceledListSlice<AttributedOpEntry> listSlice = parcel.readParcelable(null, android.content.pm.ParceledListSlice.class); + final ParceledListSlice<AttributedOpEntry> listSlice = parcel.readParcelable(null); return listSlice == null ? null : listSlice.getList(); } diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java index c0aebeed596a..7a806bdf473d 100644 --- a/core/java/android/app/AutomaticZenRule.java +++ b/core/java/android/app/AutomaticZenRule.java @@ -118,11 +118,11 @@ public final class AutomaticZenRule implements Parcelable { name = source.readString(); } interruptionFilter = source.readInt(); - conditionId = source.readParcelable(null, android.net.Uri.class); - owner = source.readParcelable(null, android.content.ComponentName.class); - configurationActivity = source.readParcelable(null, android.content.ComponentName.class); + conditionId = source.readParcelable(null); + owner = source.readParcelable(null); + configurationActivity = source.readParcelable(null); creationTime = source.readLong(); - mZenPolicy = source.readParcelable(null, android.service.notification.ZenPolicy.class); + mZenPolicy = source.readParcelable(null); mModified = source.readInt() == ENABLED; mPkg = source.readString(); } diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java index 0bb6ffa3a035..7812aba210e7 100644 --- a/core/java/android/app/BroadcastOptions.java +++ b/core/java/android/app/BroadcastOptions.java @@ -430,8 +430,8 @@ public class BroadcastOptions extends ComponentOptions { * permissions set by {@link #setRequireAllOfPermissions(String[])}, and none of the * permissions set by {@link #setRequireNoneOfPermissions(String[])} to get the broadcast. * - * @param requiredPermissions a list of Strings of permission the receiver must have, or null - * to clear any previously set value. + * @param requiredPermissions a list of Strings of permission the receiver must have. Set to + * null or an empty array to clear any previously set value. * @hide */ @SystemApi @@ -449,8 +449,8 @@ public class BroadcastOptions extends ComponentOptions { * permissions set by {@link #setRequireAllOfPermissions(String[])}, and none of the * permissions set by {@link #setRequireNoneOfPermissions(String[])} to get the broadcast. * - * @param excludedPermissions a list of Strings of permission the receiver must not have, - * or null to clear any previously set value. + * @param excludedPermissions a list of Strings of permission the receiver must not have. Set to + * null or an empty array to clear any previously set value. * @hide */ @SystemApi diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index f3e9f105500e..fe512ffbaac3 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -113,6 +113,7 @@ import java.nio.ByteOrder; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.Set; @@ -2170,6 +2171,11 @@ class ContextImpl extends Context { } @Override + public void selfRevokePermissions(@NonNull Collection<String> permissions) { + getSystemService(PermissionManager.class).selfRevokePermissions(permissions); + } + + @Override public int checkCallingPermission(String permission) { if (permission == null) { throw new IllegalArgumentException("permission is null"); diff --git a/core/java/android/app/GrantedUriPermission.java b/core/java/android/app/GrantedUriPermission.java index a71cb4a11af8..48d5b8cc126b 100644 --- a/core/java/android/app/GrantedUriPermission.java +++ b/core/java/android/app/GrantedUriPermission.java @@ -68,7 +68,7 @@ public class GrantedUriPermission implements Parcelable { }; private GrantedUriPermission(Parcel in) { - uri = in.readParcelable(null, android.net.Uri.class); + uri = in.readParcelable(null); packageName = in.readString(); } } diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index b052bc53ddbf..a9ec11edd680 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -178,17 +178,16 @@ interface IActivityTaskManager { Point getAppTaskThumbnailSize(); /** * Only callable from the system. This token grants a temporary permission to call - * #startActivityAsCallerWithToken. The token will time out after - * START_AS_CALLER_TOKEN_TIMEOUT if it is not used. + * #startActivityAsCaller. The token will time out after START_AS_CALLER_TOKEN_TIMEOUT + * if it is not used. * - * @param delegatorToken The Binder token referencing the system Activity that wants to delegate - * the #startActivityAsCaller to another app. The "caller" will be the caller of this - * activity's token, not the delegate's caller (which is probably the delegator itself). + * @param componentName The component name of the delegated component that is allowed to + * call #startActivityAsCaller with the returned token. * * @return Returns a token that can be given to a "delegate" app that may call * #startActivityAsCaller */ - IBinder requestStartActivityPermissionToken(in IBinder delegatorToken); + IBinder requestStartActivityPermissionToken(in ComponentName componentName); oneway void releaseSomeActivities(in IApplicationThread app); Bitmap getTaskDescriptionIcon(in String filename, int userId); diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index 77af474a04ac..4e32e9a41869 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -1341,15 +1341,43 @@ public final class LoadedApk { return mResources; } + /** + * Used to investigate "duplicate app objects" bug (b/185177290). + * makeApplication() should only be called on the main thread, so no synchronization should + * be needed, but syncing anyway just in case. + */ + @GuardedBy("sApplicationCache") + private static final ArrayMap<String, Application> sApplicationCache = new ArrayMap<>(4); + @UnsupportedAppUsage public Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) { if (mApplication != null) { return mApplication; } - Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "makeApplication"); + // For b/185177290. + final boolean wrongUser = + UserHandle.myUserId() != UserHandle.getUserId(mApplicationInfo.uid); + if (wrongUser) { + Slog.wtf(TAG, "makeApplication called with wrong appinfo UID: myUserId=" + + UserHandle.myUserId() + " appinfo.uid=" + mApplicationInfo.uid); + } + synchronized (sApplicationCache) { + final Application cached = sApplicationCache.get(mPackageName); + if (cached != null) { + // Looks like this is always happening for the system server, because + // the LoadedApk created in systemMain() -> attach() isn't cached properly? + if (!"android".equals(mPackageName)) { + Slog.wtf(TAG, "App instance already created for package=" + mPackageName + + " instance=" + cached); + } + // TODO Return the cached one, unles it's for the wrong user? + // For now, we just add WTF checks. + } + } + Application app = null; final String myProcessName = Process.myProcessName(); @@ -1397,6 +1425,9 @@ public final class LoadedApk { } mActivityThread.mAllApplications.add(app); mApplication = app; + synchronized (sApplicationCache) { + sApplicationCache.put(mPackageName, app); + } if (instrumentation != null) { try { diff --git a/core/java/android/app/NotificationChannelGroup.java b/core/java/android/app/NotificationChannelGroup.java index f97415ca20c8..cd6df0b231d9 100644 --- a/core/java/android/app/NotificationChannelGroup.java +++ b/core/java/android/app/NotificationChannelGroup.java @@ -100,7 +100,7 @@ public final class NotificationChannelGroup implements Parcelable { } else { mDescription = null; } - in.readParcelableList(mChannels, NotificationChannel.class.getClassLoader(), android.app.NotificationChannel.class); + in.readParcelableList(mChannels, NotificationChannel.class.getClassLoader()); mBlocked = in.readBoolean(); mUserLockedFields = in.readInt(); } diff --git a/core/java/android/app/RemoteInputHistoryItem.java b/core/java/android/app/RemoteInputHistoryItem.java index 32f89819fb1f..091db3f142ae 100644 --- a/core/java/android/app/RemoteInputHistoryItem.java +++ b/core/java/android/app/RemoteInputHistoryItem.java @@ -48,7 +48,7 @@ public class RemoteInputHistoryItem implements Parcelable { protected RemoteInputHistoryItem(Parcel in) { mText = in.readCharSequence(); mMimeType = in.readStringNoHelper(); - mUri = in.readParcelable(Uri.class.getClassLoader(), android.net.Uri.class); + mUri = in.readParcelable(Uri.class.getClassLoader()); } public static final Creator<RemoteInputHistoryItem> CREATOR = diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 7969cda7b84d..73ebdf6a4545 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -2885,6 +2885,38 @@ public class DevicePolicyManager { public static final int RESULT_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER_UNRECOVERABLE_ERROR = 2; /** + * An {@link Intent} extra which resolves to a custom user consent screen. + * + * <p>If this extra is provided to the device management role holder via either {@link + * #ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} or {@link + * #ACTION_ROLE_HOLDER_PROVISION_MANAGED_PROFILE}, the device management role holder must + * launch this intent which shows the custom user consent screen, replacing its own standard + * consent screen. + * + * <p>If this extra is provided, it is the responsibility of the intent handler to show the + * list of disclaimers which are normally shown by the standard consent screen: + * <ul> + * <li>Disclaimers set by the IT admin via the {@link #EXTRA_PROVISIONING_DISCLAIMERS} + * provisioning extra</li> + * <li>For fully-managed device provisioning, disclaimers defined in system apps via the + * {@link #EXTRA_PROVISIONING_DISCLAIMER_HEADER} and {@link + * #EXTRA_PROVISIONING_DISCLAIMER_CONTENT} metadata in their manifests</li> + * <li>General disclaimer relevant to the provisioning mode</li> + * </ul> + * + * <p>If the custom consent screens are granted by the user {@link Activity#RESULT_OK} is + * returned, otherwise {@link Activity#RESULT_CANCELED} is returned. The device management + * role holder should ensure that the provisioning flow terminates immediately if consent + * is not granted by the user. + * + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + @SystemApi + public static final String EXTRA_PROVISIONING_ROLE_HOLDER_CUSTOM_USER_CONSENT_INTENT = + "android.app.extra.PROVISIONING_ROLE_HOLDER_CUSTOM_USER_CONSENT_INTENT"; + + /** * Maximum supported password length. Kind-of arbitrary. * @hide */ diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java index 6e49e956fe7e..e1f6af0cc128 100644 --- a/core/java/android/app/assist/AssistStructure.java +++ b/core/java/android/app/assist/AssistStructure.java @@ -815,13 +815,13 @@ public class AssistStructure implements Parcelable { mAutofillHints = in.readStringArray(); } if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_VALUE) != 0) { - mAutofillValue = in.readParcelable(null, android.view.autofill.AutofillValue.class); + mAutofillValue = in.readParcelable(null); } if ((autofillFlags & AUTOFILL_FLAGS_HAS_AUTOFILL_OPTIONS) != 0) { mAutofillOptions = in.readCharSequenceArray(); } if ((autofillFlags & AUTOFILL_FLAGS_HAS_HTML_INFO) != 0) { - mHtmlInfo = in.readParcelable(null, android.view.ViewStructure.HtmlInfo.class); + mHtmlInfo = in.readParcelable(null); } if ((autofillFlags & AUTOFILL_FLAGS_HAS_MIN_TEXT_EMS) != 0) { mMinEms = in.readInt(); @@ -886,7 +886,7 @@ public class AssistStructure implements Parcelable { mWebDomain = in.readString(); } if ((flags&FLAGS_HAS_LOCALE_LIST) != 0) { - mLocaleList = in.readParcelable(null, android.os.LocaleList.class); + mLocaleList = in.readParcelable(null); } if ((flags & FLAGS_HAS_MIME_TYPES) != 0) { mReceiveContentMimeTypes = in.readStringArray(); diff --git a/core/java/android/app/communal/CommunalManager.java b/core/java/android/app/communal/CommunalManager.java index 22bd9d17715a..c7368ade2dcc 100644 --- a/core/java/android/app/communal/CommunalManager.java +++ b/core/java/android/app/communal/CommunalManager.java @@ -17,7 +17,6 @@ package android.app.communal; import android.Manifest; -import android.annotation.NonNull; import android.annotation.RequiresFeature; import android.annotation.RequiresPermission; import android.annotation.SystemApi; @@ -26,9 +25,6 @@ import android.annotation.TestApi; import android.content.Context; import android.content.pm.PackageManager; import android.os.RemoteException; -import android.util.ArrayMap; - -import java.util.concurrent.Executor; /** * System private class for talking with the @@ -41,12 +37,10 @@ import java.util.concurrent.Executor; @RequiresFeature(PackageManager.FEATURE_COMMUNAL_MODE) public final class CommunalManager { private final ICommunalManager mService; - private final ArrayMap<CommunalModeListener, ICommunalModeListener> mCommunalModeListeners; /** @hide */ public CommunalManager(ICommunalManager service) { mService = service; - mCommunalModeListeners = new ArrayMap<>(); } /** @@ -65,72 +59,4 @@ public final class CommunalManager { throw e.rethrowFromSystemServer(); } } - - /** - * Checks whether or not the communal view is currently showing over the lockscreen. - */ - @RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE) - public boolean isCommunalMode() { - try { - return mService.isCommunalMode(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Listener for communal state changes. - */ - @FunctionalInterface - public interface CommunalModeListener { - /** - * Callback function that executes when the communal state changes. - */ - void onCommunalModeChanged(boolean isCommunalMode); - } - - /** - * Registers a callback to execute when the communal state changes. - * - * @param listener The listener to add to receive communal state changes. - * @param executor {@link Executor} to dispatch to. To dispatch the callback to the main - * thread of your application, use - * {@link android.content.Context#getMainExecutor()}. - */ - @RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE) - public void addCommunalModeListener(@NonNull Executor executor, - @NonNull CommunalModeListener listener) { - synchronized (mCommunalModeListeners) { - try { - ICommunalModeListener iListener = new ICommunalModeListener.Stub() { - @Override - public void onCommunalModeChanged(boolean isCommunalMode) { - executor.execute(() -> listener.onCommunalModeChanged(isCommunalMode)); - } - }; - mService.addCommunalModeListener(iListener); - mCommunalModeListeners.put(listener, iListener); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - } - - /** - * Unregisters a callback that executes when communal state changes. - */ - @RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE) - public void removeCommunalModeListener(@NonNull CommunalModeListener listener) { - synchronized (mCommunalModeListeners) { - ICommunalModeListener iListener = mCommunalModeListeners.get(listener); - if (iListener != null) { - try { - mService.removeCommunalModeListener(iListener); - mCommunalModeListeners.remove(listener); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - } - } } diff --git a/core/java/android/app/communal/ICommunalManager.aidl b/core/java/android/app/communal/ICommunalManager.aidl index 869891e079e0..df9d7cec05f7 100644 --- a/core/java/android/app/communal/ICommunalManager.aidl +++ b/core/java/android/app/communal/ICommunalManager.aidl @@ -16,8 +16,6 @@ package android.app.communal; -import android.app.communal.ICommunalModeListener; - /** * System private API for talking with the communal manager service that handles communal mode * state. @@ -26,7 +24,4 @@ import android.app.communal.ICommunalModeListener; */ interface ICommunalManager { oneway void setCommunalViewShowing(boolean isShowing); - boolean isCommunalMode(); - void addCommunalModeListener(in ICommunalModeListener listener); - void removeCommunalModeListener(in ICommunalModeListener listener); }
\ No newline at end of file diff --git a/core/java/android/app/people/ConversationChannel.java b/core/java/android/app/people/ConversationChannel.java index ab350f225e52..2bf71b0183c6 100644 --- a/core/java/android/app/people/ConversationChannel.java +++ b/core/java/android/app/people/ConversationChannel.java @@ -83,16 +83,16 @@ public final class ConversationChannel implements Parcelable { } public ConversationChannel(Parcel in) { - mShortcutInfo = in.readParcelable(ShortcutInfo.class.getClassLoader(), android.content.pm.ShortcutInfo.class); + mShortcutInfo = in.readParcelable(ShortcutInfo.class.getClassLoader()); mUid = in.readInt(); - mNotificationChannel = in.readParcelable(NotificationChannel.class.getClassLoader(), android.app.NotificationChannel.class); + mNotificationChannel = in.readParcelable(NotificationChannel.class.getClassLoader()); mNotificationChannelGroup = - in.readParcelable(NotificationChannelGroup.class.getClassLoader(), android.app.NotificationChannelGroup.class); + in.readParcelable(NotificationChannelGroup.class.getClassLoader()); mLastEventTimestamp = in.readLong(); mHasActiveNotifications = in.readBoolean(); mHasBirthdayToday = in.readBoolean(); mStatuses = new ArrayList<>(); - in.readParcelableList(mStatuses, ConversationStatus.class.getClassLoader(), android.app.people.ConversationStatus.class); + in.readParcelableList(mStatuses, ConversationStatus.class.getClassLoader()); } @Override diff --git a/core/java/android/app/people/ConversationStatus.java b/core/java/android/app/people/ConversationStatus.java index a7b61b37d14e..8038158b1f97 100644 --- a/core/java/android/app/people/ConversationStatus.java +++ b/core/java/android/app/people/ConversationStatus.java @@ -126,7 +126,7 @@ public final class ConversationStatus implements Parcelable { mActivity = p.readInt(); mAvailability = p.readInt(); mDescription = p.readCharSequence(); - mIcon = p.readParcelable(Icon.class.getClassLoader(), android.graphics.drawable.Icon.class); + mIcon = p.readParcelable(Icon.class.getClassLoader()); mStartTimeMs = p.readLong(); mEndTimeMs = p.readLong(); } diff --git a/core/java/android/app/people/PeopleSpaceTile.java b/core/java/android/app/people/PeopleSpaceTile.java index 4337111636a0..e11861f49be8 100644 --- a/core/java/android/app/people/PeopleSpaceTile.java +++ b/core/java/android/app/people/PeopleSpaceTile.java @@ -472,9 +472,9 @@ public class PeopleSpaceTile implements Parcelable { public PeopleSpaceTile(Parcel in) { mId = in.readString(); mUserName = in.readCharSequence(); - mUserIcon = in.readParcelable(Icon.class.getClassLoader(), android.graphics.drawable.Icon.class); - mContactUri = in.readParcelable(Uri.class.getClassLoader(), android.net.Uri.class); - mUserHandle = in.readParcelable(UserHandle.class.getClassLoader(), android.os.UserHandle.class); + mUserIcon = in.readParcelable(Icon.class.getClassLoader()); + mContactUri = in.readParcelable(Uri.class.getClassLoader()); + mUserHandle = in.readParcelable(UserHandle.class.getClassLoader()); mPackageName = in.readString(); mBirthdayText = in.readString(); mLastInteractionTimestamp = in.readLong(); @@ -483,12 +483,12 @@ public class PeopleSpaceTile implements Parcelable { mNotificationContent = in.readCharSequence(); mNotificationSender = in.readCharSequence(); mNotificationCategory = in.readString(); - mNotificationDataUri = in.readParcelable(Uri.class.getClassLoader(), android.net.Uri.class); + mNotificationDataUri = in.readParcelable(Uri.class.getClassLoader()); mMessagesCount = in.readInt(); - mIntent = in.readParcelable(Intent.class.getClassLoader(), android.content.Intent.class); + mIntent = in.readParcelable(Intent.class.getClassLoader()); mNotificationTimestamp = in.readLong(); mStatuses = new ArrayList<>(); - in.readParcelableList(mStatuses, ConversationStatus.class.getClassLoader(), android.app.people.ConversationStatus.class); + in.readParcelableList(mStatuses, ConversationStatus.class.getClassLoader()); mCanBypassDnd = in.readBoolean(); mIsPackageSuspended = in.readBoolean(); mIsUserQuieted = in.readBoolean(); diff --git a/core/java/android/app/prediction/AppTargetEvent.java b/core/java/android/app/prediction/AppTargetEvent.java index 51e3953ead4f..963e750e4fd1 100644 --- a/core/java/android/app/prediction/AppTargetEvent.java +++ b/core/java/android/app/prediction/AppTargetEvent.java @@ -72,7 +72,7 @@ public final class AppTargetEvent implements Parcelable { } private AppTargetEvent(Parcel parcel) { - mTarget = parcel.readParcelable(null, android.app.prediction.AppTarget.class); + mTarget = parcel.readParcelable(null); mLocation = parcel.readString(); mAction = parcel.readInt(); } diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java index 30a6c311bd1e..fbb37db52014 100644 --- a/core/java/android/app/servertransaction/ClientTransaction.java +++ b/core/java/android/app/servertransaction/ClientTransaction.java @@ -197,11 +197,11 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem { if (readActivityToken) { mActivityToken = in.readStrongBinder(); } - mLifecycleStateRequest = in.readParcelable(getClass().getClassLoader(), android.app.servertransaction.ActivityLifecycleItem.class); + mLifecycleStateRequest = in.readParcelable(getClass().getClassLoader()); final boolean readActivityCallbacks = in.readBoolean(); if (readActivityCallbacks) { mActivityCallbacks = new ArrayList<>(); - in.readParcelableList(mActivityCallbacks, getClass().getClassLoader(), android.app.servertransaction.ClientTransactionItem.class); + in.readParcelableList(mActivityCallbacks, getClass().getClassLoader()); } } diff --git a/core/java/android/app/smartspace/SmartspaceTargetEvent.java b/core/java/android/app/smartspace/SmartspaceTargetEvent.java index 89caab764591..61f8723ca393 100644 --- a/core/java/android/app/smartspace/SmartspaceTargetEvent.java +++ b/core/java/android/app/smartspace/SmartspaceTargetEvent.java @@ -96,7 +96,7 @@ public final class SmartspaceTargetEvent implements Parcelable { } private SmartspaceTargetEvent(Parcel parcel) { - mSmartspaceTarget = parcel.readParcelable(null, android.app.smartspace.SmartspaceTarget.class); + mSmartspaceTarget = parcel.readParcelable(null); mSmartspaceActionId = parcel.readString(); mEventType = parcel.readInt(); } diff --git a/core/java/android/app/time/ExternalTimeSuggestion.java b/core/java/android/app/time/ExternalTimeSuggestion.java index 0f98b4451983..8e281c07c45d 100644 --- a/core/java/android/app/time/ExternalTimeSuggestion.java +++ b/core/java/android/app/time/ExternalTimeSuggestion.java @@ -101,11 +101,11 @@ public final class ExternalTimeSuggestion implements Parcelable { } private static ExternalTimeSuggestion createFromParcel(Parcel in) { - TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */, android.os.TimestampedValue.class); + TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */); ExternalTimeSuggestion suggestion = new ExternalTimeSuggestion(utcTime.getReferenceTimeMillis(), utcTime.getValue()); @SuppressWarnings("unchecked") - ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */, java.lang.String.class); + ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */); suggestion.mDebugInfo = debugInfo; return suggestion; } diff --git a/core/java/android/app/time/TimeCapabilitiesAndConfig.java b/core/java/android/app/time/TimeCapabilitiesAndConfig.java index 71fce14a80b1..4a1044760064 100644 --- a/core/java/android/app/time/TimeCapabilitiesAndConfig.java +++ b/core/java/android/app/time/TimeCapabilitiesAndConfig.java @@ -59,8 +59,8 @@ public final class TimeCapabilitiesAndConfig implements Parcelable { @NonNull private static TimeCapabilitiesAndConfig readFromParcel(Parcel in) { - TimeCapabilities capabilities = in.readParcelable(null, android.app.time.TimeCapabilities.class); - TimeConfiguration configuration = in.readParcelable(null, android.app.time.TimeConfiguration.class); + TimeCapabilities capabilities = in.readParcelable(null); + TimeConfiguration configuration = in.readParcelable(null); return new TimeCapabilitiesAndConfig(capabilities, configuration); } diff --git a/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java b/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java index cd91b0431b28..a9ea76f77958 100644 --- a/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java +++ b/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java @@ -61,8 +61,8 @@ public final class TimeZoneCapabilitiesAndConfig implements Parcelable { @NonNull private static TimeZoneCapabilitiesAndConfig createFromParcel(Parcel in) { - TimeZoneCapabilities capabilities = in.readParcelable(null, android.app.time.TimeZoneCapabilities.class); - TimeZoneConfiguration configuration = in.readParcelable(null, android.app.time.TimeZoneConfiguration.class); + TimeZoneCapabilities capabilities = in.readParcelable(null); + TimeZoneConfiguration configuration = in.readParcelable(null); return new TimeZoneCapabilitiesAndConfig(capabilities, configuration); } diff --git a/core/java/android/app/timedetector/GnssTimeSuggestion.java b/core/java/android/app/timedetector/GnssTimeSuggestion.java index 8ccff6227c79..6478a2dd2aa9 100644 --- a/core/java/android/app/timedetector/GnssTimeSuggestion.java +++ b/core/java/android/app/timedetector/GnssTimeSuggestion.java @@ -66,10 +66,10 @@ public final class GnssTimeSuggestion implements Parcelable { } private static GnssTimeSuggestion createFromParcel(Parcel in) { - TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */, android.os.TimestampedValue.class); + TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */); GnssTimeSuggestion suggestion = new GnssTimeSuggestion(utcTime); @SuppressWarnings("unchecked") - ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */, java.lang.String.class); + ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */); suggestion.mDebugInfo = debugInfo; return suggestion; } diff --git a/core/java/android/app/timedetector/ManualTimeSuggestion.java b/core/java/android/app/timedetector/ManualTimeSuggestion.java index 1699a5f8c8ae..299e9518e329 100644 --- a/core/java/android/app/timedetector/ManualTimeSuggestion.java +++ b/core/java/android/app/timedetector/ManualTimeSuggestion.java @@ -66,10 +66,10 @@ public final class ManualTimeSuggestion implements Parcelable { } private static ManualTimeSuggestion createFromParcel(Parcel in) { - TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */, android.os.TimestampedValue.class); + TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */); ManualTimeSuggestion suggestion = new ManualTimeSuggestion(utcTime); @SuppressWarnings("unchecked") - ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */, java.lang.String.class); + ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */); suggestion.mDebugInfo = debugInfo; return suggestion; } diff --git a/core/java/android/app/timedetector/NetworkTimeSuggestion.java b/core/java/android/app/timedetector/NetworkTimeSuggestion.java index 20300832d2fc..a5259c27ec42 100644 --- a/core/java/android/app/timedetector/NetworkTimeSuggestion.java +++ b/core/java/android/app/timedetector/NetworkTimeSuggestion.java @@ -66,10 +66,10 @@ public final class NetworkTimeSuggestion implements Parcelable { } private static NetworkTimeSuggestion createFromParcel(Parcel in) { - TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */, android.os.TimestampedValue.class); + TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */); NetworkTimeSuggestion suggestion = new NetworkTimeSuggestion(utcTime); @SuppressWarnings("unchecked") - ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */, java.lang.String.class); + ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */); suggestion.mDebugInfo = debugInfo; return suggestion; } diff --git a/core/java/android/app/timedetector/TelephonyTimeSuggestion.java b/core/java/android/app/timedetector/TelephonyTimeSuggestion.java index 52d0bbea701e..6c3a304ed3a7 100644 --- a/core/java/android/app/timedetector/TelephonyTimeSuggestion.java +++ b/core/java/android/app/timedetector/TelephonyTimeSuggestion.java @@ -77,10 +77,10 @@ public final class TelephonyTimeSuggestion implements Parcelable { private static TelephonyTimeSuggestion createFromParcel(Parcel in) { int slotIndex = in.readInt(); TelephonyTimeSuggestion suggestion = new TelephonyTimeSuggestion.Builder(slotIndex) - .setUtcTime(in.readParcelable(null /* classLoader */, android.os.TimestampedValue.class)) + .setUtcTime(in.readParcelable(null /* classLoader */)) .build(); @SuppressWarnings("unchecked") - ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */, java.lang.String.class); + ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */); if (debugInfo != null) { suggestion.addDebugInfo(debugInfo); } diff --git a/core/java/android/app/timezone/RulesState.java b/core/java/android/app/timezone/RulesState.java index 516ad033a936..ee88ec54d08f 100644 --- a/core/java/android/app/timezone/RulesState.java +++ b/core/java/android/app/timezone/RulesState.java @@ -195,12 +195,12 @@ public final class RulesState implements Parcelable { private static RulesState createFromParcel(Parcel in) { String baseRulesVersion = in.readString(); - DistroFormatVersion distroFormatVersionSupported = in.readParcelable(null, android.app.timezone.DistroFormatVersion.class); + DistroFormatVersion distroFormatVersionSupported = in.readParcelable(null); boolean operationInProgress = in.readByte() == BYTE_TRUE; int distroStagedState = in.readByte(); - DistroRulesVersion stagedDistroRulesVersion = in.readParcelable(null, android.app.timezone.DistroRulesVersion.class); + DistroRulesVersion stagedDistroRulesVersion = in.readParcelable(null); int installedDistroStatus = in.readByte(); - DistroRulesVersion installedDistroRulesVersion = in.readParcelable(null, android.app.timezone.DistroRulesVersion.class); + DistroRulesVersion installedDistroRulesVersion = in.readParcelable(null); return new RulesState(baseRulesVersion, distroFormatVersionSupported, operationInProgress, distroStagedState, stagedDistroRulesVersion, installedDistroStatus, installedDistroRulesVersion); diff --git a/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java b/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java index 387319edc5e7..01a60b1fa025 100644 --- a/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java +++ b/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java @@ -65,7 +65,7 @@ public final class ManualTimeZoneSuggestion implements Parcelable { String zoneId = in.readString(); ManualTimeZoneSuggestion suggestion = new ManualTimeZoneSuggestion(zoneId); @SuppressWarnings("unchecked") - ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */, java.lang.String.class); + ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */); suggestion.mDebugInfo = debugInfo; return suggestion; } diff --git a/core/java/android/app/timezonedetector/TelephonyTimeZoneSuggestion.java b/core/java/android/app/timezonedetector/TelephonyTimeZoneSuggestion.java index e5b4e46ba285..eb6750f06d25 100644 --- a/core/java/android/app/timezonedetector/TelephonyTimeZoneSuggestion.java +++ b/core/java/android/app/timezonedetector/TelephonyTimeZoneSuggestion.java @@ -165,7 +165,7 @@ public final class TelephonyTimeZoneSuggestion implements Parcelable { .setQuality(in.readInt()) .build(); List<String> debugInfo = - in.readArrayList(TelephonyTimeZoneSuggestion.class.getClassLoader(), java.lang.String.class); + in.readArrayList(TelephonyTimeZoneSuggestion.class.getClassLoader()); if (debugInfo != null) { suggestion.addDebugInfo(debugInfo); } diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java index 65b2775fd66b..177de835554b 100644 --- a/core/java/android/app/trust/TrustManager.java +++ b/core/java/android/app/trust/TrustManager.java @@ -156,7 +156,7 @@ public class TrustManager { @Override public void onTrustError(CharSequence message) { - Message m = mHandler.obtainMessage(MSG_TRUST_ERROR); + Message m = mHandler.obtainMessage(MSG_TRUST_ERROR, trustListener); m.getData().putCharSequence(DATA_MESSAGE, message); m.sendToTarget(); } diff --git a/core/java/android/app/usage/CacheQuotaHint.java b/core/java/android/app/usage/CacheQuotaHint.java index ba6bcdc936ba..0ccb058d11cf 100644 --- a/core/java/android/app/usage/CacheQuotaHint.java +++ b/core/java/android/app/usage/CacheQuotaHint.java @@ -148,7 +148,7 @@ public final class CacheQuotaHint implements Parcelable { return builder.setVolumeUuid(in.readString()) .setUid(in.readInt()) .setQuota(in.readLong()) - .setUsageStats(in.readParcelable(UsageStats.class.getClassLoader(), android.app.usage.UsageStats.class)) + .setUsageStats(in.readParcelable(UsageStats.class.getClassLoader())) .build(); } diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 856a8e1f8199..a695f6d2e7a7 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -2249,17 +2249,17 @@ public final class BluetoothAdapter { /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(value = { - BluetoothStatusCodes.SUCCESS, + BluetoothStatusCodes.FEATURE_SUPPORTED, BluetoothStatusCodes.ERROR_UNKNOWN, BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED, - BluetoothStatusCodes.ERROR_FEATURE_NOT_SUPPORTED, + BluetoothStatusCodes.FEATURE_NOT_SUPPORTED, }) public @interface LeFeatureReturnValues {} /** - * Returns {@link BluetoothStatusCodes#SUCCESS} if the LE audio feature is - * supported, returns {@link BluetoothStatusCodes#ERROR_FEATURE_NOT_SUPPORTED} if - * the feature is not supported or an error code. + * Returns {@link BluetoothStatusCodes#FEATURE_SUPPORTED} if the LE audio feature is + * supported, {@link BluetoothStatusCodes#FEATURE_NOT_SUPPORTED} if the feature is not + * supported, or an error code. * * @return whether the LE audio is supported */ @@ -2282,9 +2282,10 @@ public final class BluetoothAdapter { } /** - * Returns {@link BluetoothStatusCodes#SUCCESS} if LE Periodic Advertising Sync Transfer Sender - * feature is supported, returns {@link BluetoothStatusCodes#ERROR_FEATURE_NOT_SUPPORTED} if the - * feature is not supported or an error code + * Returns {@link BluetoothStatusCodes#FEATURE_SUPPORTED} if LE Periodic Advertising Sync + * Transfer Sender feature is supported, + * {@link BluetoothStatusCodes#FEATURE_NOT_SUPPORTED} if the feature is not supported, or + * an error code * * @return whether the chipset supports the LE Periodic Advertising Sync Transfer Sender feature */ @@ -3064,6 +3065,9 @@ public final class BluetoothAdapter { BluetoothCsipSetCoordinator csipSetCoordinator = new BluetoothCsipSetCoordinator(context, listener, this); return true; + } else if (profile == BluetoothProfile.LE_CALL_CONTROL) { + BluetoothLeCallControl tbs = new BluetoothLeCallControl(context, listener); + return true; } else { return false; } @@ -3166,6 +3170,10 @@ public final class BluetoothAdapter { (BluetoothCsipSetCoordinator) proxy; csipSetCoordinator.close(); break; + case BluetoothProfile.LE_CALL_CONTROL: + BluetoothLeCallControl tbs = (BluetoothLeCallControl) proxy; + tbs.close(); + break; } } diff --git a/core/java/android/bluetooth/BluetoothGattCharacteristic.java b/core/java/android/bluetooth/BluetoothGattCharacteristic.java index 053e0db3d8a6..c5e986e895b2 100644 --- a/core/java/android/bluetooth/BluetoothGattCharacteristic.java +++ b/core/java/android/bluetooth/BluetoothGattCharacteristic.java @@ -313,7 +313,7 @@ public class BluetoothGattCharacteristic implements Parcelable { }; private BluetoothGattCharacteristic(Parcel in) { - mUuid = ((ParcelUuid) in.readParcelable(null, android.os.ParcelUuid.class)).getUuid(); + mUuid = ((ParcelUuid) in.readParcelable(null)).getUuid(); mInstance = in.readInt(); mProperties = in.readInt(); mPermissions = in.readInt(); diff --git a/core/java/android/bluetooth/BluetoothGattDescriptor.java b/core/java/android/bluetooth/BluetoothGattDescriptor.java index 6ed4706b5fba..a35d5b99fd7b 100644 --- a/core/java/android/bluetooth/BluetoothGattDescriptor.java +++ b/core/java/android/bluetooth/BluetoothGattDescriptor.java @@ -187,7 +187,7 @@ public class BluetoothGattDescriptor implements Parcelable { }; private BluetoothGattDescriptor(Parcel in) { - mUuid = ((ParcelUuid) in.readParcelable(null, android.os.ParcelUuid.class)).getUuid(); + mUuid = ((ParcelUuid) in.readParcelable(null)).getUuid(); mInstance = in.readInt(); mPermissions = in.readInt(); } diff --git a/core/java/android/bluetooth/BluetoothGattIncludedService.java b/core/java/android/bluetooth/BluetoothGattIncludedService.java index 1ae2ca0a92e1..5580619033a6 100644 --- a/core/java/android/bluetooth/BluetoothGattIncludedService.java +++ b/core/java/android/bluetooth/BluetoothGattIncludedService.java @@ -76,7 +76,7 @@ public class BluetoothGattIncludedService implements Parcelable { }; private BluetoothGattIncludedService(Parcel in) { - mUuid = ((ParcelUuid) in.readParcelable(null, android.os.ParcelUuid.class)).getUuid(); + mUuid = ((ParcelUuid) in.readParcelable(null)).getUuid(); mInstanceId = in.readInt(); mServiceType = in.readInt(); } diff --git a/core/java/android/bluetooth/BluetoothGattService.java b/core/java/android/bluetooth/BluetoothGattService.java index 36bc4772e016..f64d09fc30d9 100644 --- a/core/java/android/bluetooth/BluetoothGattService.java +++ b/core/java/android/bluetooth/BluetoothGattService.java @@ -180,7 +180,7 @@ public class BluetoothGattService implements Parcelable { }; private BluetoothGattService(Parcel in) { - mUuid = ((ParcelUuid) in.readParcelable(null, android.os.ParcelUuid.class)).getUuid(); + mUuid = ((ParcelUuid) in.readParcelable(null)).getUuid(); mInstanceId = in.readInt(); mServiceType = in.readInt(); diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java index f2a6276ce8fe..2ed1eb40f8a4 100644 --- a/core/java/android/bluetooth/BluetoothHeadset.java +++ b/core/java/android/bluetooth/BluetoothHeadset.java @@ -18,6 +18,7 @@ package android.bluetooth; import static android.bluetooth.BluetoothUtils.getSyncTimeout; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -44,6 +45,8 @@ import android.util.Log; import com.android.modules.utils.SynchronousResultReceiver; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeoutException; @@ -895,17 +898,36 @@ public final class BluetoothHeadset implements BluetoothProfile { com.android.internal.R.bool.config_bluetooth_sco_off_call); } + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = { + BluetoothHeadset.STATE_AUDIO_DISCONNECTED, + BluetoothHeadset.STATE_AUDIO_CONNECTING, + BluetoothHeadset.STATE_AUDIO_CONNECTED, + BluetoothStatusCodes.ERROR_TIMEOUT + }) + public @interface GetAudioStateReturnValues {} + /** * Get the current audio state of the Headset. - * Note: This is an internal function and shouldn't be exposed + * + * @param device is the Bluetooth device for which the audio state is being queried + * @return the audio state of the device or an error code + * @throws IllegalArgumentException if the device is null * * @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @SystemApi @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public int getAudioState(BluetoothDevice device) { + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) + public @GetAudioStateReturnValues int getAudioState(@NonNull BluetoothDevice device) { if (VDBG) log("getAudioState"); + if (device == null) { + throw new IllegalArgumentException("device cannot be null"); + } final IBluetoothHeadset service = mService; final int defaultValue = BluetoothHeadset.STATE_AUDIO_DISCONNECTED; if (service == null) { @@ -916,8 +938,12 @@ public final class BluetoothHeadset implements BluetoothProfile { final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); service.getAudioState(device, mAttributionSource, recv); return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { + } catch (RemoteException e) { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); + throw e.rethrowFromSystemServer(); + } catch (TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); + return BluetoothStatusCodes.ERROR_TIMEOUT; } } return defaultValue; @@ -1005,103 +1031,112 @@ public final class BluetoothHeadset implements BluetoothProfile { } } - /** - * Check if at least one headset's SCO audio is connected or connecting - * - * @return true if at least one device's SCO audio is connected or connecting, false otherwise - * or on error - * @hide - */ - @RequiresLegacyBluetoothPermission - @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean isAudioOn() { - if (VDBG) log("isAudioOn()"); - final IBluetoothHeadset service = mService; - final boolean defaultValue = false; - if (service == null) { - Log.w(TAG, "Proxy not attached to service"); - if (DBG) log(Log.getStackTraceString(new Throwable())); - } else if (isEnabled()) { - try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); - service.isAudioOn(mAttributionSource, recv); - return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { - Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); - } - } - return defaultValue; - } + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = { + BluetoothStatusCodes.SUCCESS, + BluetoothStatusCodes.ERROR_UNKNOWN, + BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND, + BluetoothStatusCodes.ERROR_TIMEOUT, + BluetoothStatusCodes.ERROR_AUDIO_DEVICE_ALREADY_CONNECTED, + BluetoothStatusCodes.ERROR_NO_ACTIVE_DEVICES, + BluetoothStatusCodes.ERROR_NOT_ACTIVE_DEVICE, + BluetoothStatusCodes.ERROR_AUDIO_ROUTE_BLOCKED, + BluetoothStatusCodes.ERROR_CALL_ACTIVE, + BluetoothStatusCodes.ERROR_PROFILE_NOT_CONNECTED + }) + public @interface ConnectAudioReturnValues {} /** - * Initiates a connection of headset audio to the current active device - * - * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}. - * If this function returns true, this intent will be broadcasted with - * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_CONNECTING}. - * - * <p> {@link #EXTRA_STATE} will transition from - * {@link #STATE_AUDIO_CONNECTING} to {@link #STATE_AUDIO_CONNECTED} when - * audio connection is established and to {@link #STATE_AUDIO_DISCONNECTED} - * in case of failure to establish the audio connection. - * - * Note that this intent will not be sent if {@link BluetoothHeadset#isAudioOn()} is true - * before calling this method + * Initiates a connection of SCO audio to the current active HFP device. The active HFP device + * can be identified with {@link BluetoothAdapter#getActiveDevices(int)}. + * <p> + * If this function returns {@link BluetoothStatusCodes#SUCCESS}, the intent + * {@link #ACTION_AUDIO_STATE_CHANGED} will be broadcasted twice. First with {@link #EXTRA_STATE} + * set to {@link #STATE_AUDIO_CONNECTING}. This will be followed by a broadcast with + * {@link #EXTRA_STATE} set to either {@link #STATE_AUDIO_CONNECTED} if the audio connection is + * established or {@link #STATE_AUDIO_DISCONNECTED} if there was a failure in establishing the + * audio connection. * - * @return false if there was some error such as there is no active headset + * @return whether the connection was successfully initiated or an error code on failure * @hide */ - @UnsupportedAppUsage + @SystemApi @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean connectAudio() { + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) + public @ConnectAudioReturnValues int connectAudio() { if (VDBG) log("connectAudio()"); final IBluetoothHeadset service = mService; - final boolean defaultValue = false; + final int defaultValue = BluetoothStatusCodes.ERROR_UNKNOWN; if (service == null) { Log.w(TAG, "Proxy not attached to service"); if (DBG) log(Log.getStackTraceString(new Throwable())); } else if (isEnabled()) { try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); + final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); service.connectAudio(mAttributionSource, recv); return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { + } catch (RemoteException e) { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); + throw e.rethrowFromSystemServer(); + } catch (TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); + return BluetoothStatusCodes.ERROR_TIMEOUT; } } return defaultValue; } + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = { + BluetoothStatusCodes.SUCCESS, + BluetoothStatusCodes.ERROR_UNKNOWN, + BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND, + BluetoothStatusCodes.ERROR_TIMEOUT, + BluetoothStatusCodes.ERROR_PROFILE_NOT_CONNECTED, + BluetoothStatusCodes.ERROR_AUDIO_DEVICE_ALREADY_DISCONNECTED + }) + public @interface DisconnectAudioReturnValues {} + /** - * Initiates a disconnection of HFP SCO audio. - * Tear down voice recognition or virtual voice call if any. + * Initiates a disconnection of HFP SCO audio from actively connected devices. It also tears + * down voice recognition or virtual voice call, if any exists. * - * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}. - * If this function returns true, this intent will be broadcasted with - * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_DISCONNECTED}. + * <p> If this function returns {@link BluetoothStatusCodes#SUCCESS}, the intent + * {@link #ACTION_AUDIO_STATE_CHANGED} will be broadcasted with {@link #EXTRA_STATE} set to + * {@link #STATE_AUDIO_DISCONNECTED}. * - * @return false if audio is not connected, or on error, true otherwise + * @return whether the disconnection was initiated successfully or an error code on failure * @hide */ - @UnsupportedAppUsage + @SystemApi @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) - public boolean disconnectAudio() { + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) + public @DisconnectAudioReturnValues int disconnectAudio() { if (VDBG) log("disconnectAudio()"); final IBluetoothHeadset service = mService; - final boolean defaultValue = false; + final int defaultValue = BluetoothStatusCodes.ERROR_UNKNOWN; if (service == null) { Log.w(TAG, "Proxy not attached to service"); if (DBG) log(Log.getStackTraceString(new Throwable())); } else if (isEnabled()) { try { - final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); + final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); service.disconnectAudio(mAttributionSource, recv); return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); - } catch (RemoteException | TimeoutException e) { + } catch (RemoteException e) { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); + throw e.rethrowFromSystemServer(); + } catch (TimeoutException e) { + Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); + return BluetoothStatusCodes.ERROR_TIMEOUT; } } return defaultValue; @@ -1386,7 +1421,10 @@ public final class BluetoothHeadset implements BluetoothProfile { @SystemApi @RequiresLegacyBluetoothPermission @RequiresBluetoothConnectPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean isInbandRingingEnabled() { if (DBG) log("isInbandRingingEnabled()"); final IBluetoothHeadset service = mService; diff --git a/core/java/android/bluetooth/BluetoothHeadsetClientCall.java b/core/java/android/bluetooth/BluetoothHeadsetClientCall.java index e9dd761efdf6..032b507f5d3c 100644 --- a/core/java/android/bluetooth/BluetoothHeadsetClientCall.java +++ b/core/java/android/bluetooth/BluetoothHeadsetClientCall.java @@ -292,7 +292,7 @@ public final class BluetoothHeadsetClientCall implements Parcelable, Attributabl new Parcelable.Creator<BluetoothHeadsetClientCall>() { @Override public BluetoothHeadsetClientCall createFromParcel(Parcel in) { - return new BluetoothHeadsetClientCall((BluetoothDevice) in.readParcelable(null, android.bluetooth.BluetoothDevice.class), + return new BluetoothHeadsetClientCall((BluetoothDevice) in.readParcelable(null), in.readInt(), UUID.fromString(in.readString()), in.readInt(), in.readString(), in.readInt() == 1, in.readInt() == 1, in.readInt() == 1); diff --git a/core/java/android/bluetooth/BluetoothLeCall.java b/core/java/android/bluetooth/BluetoothLeCall.java new file mode 100644 index 000000000000..fb7789db25c7 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothLeCall.java @@ -0,0 +1,285 @@ +/* + * Copyright 2021 HIMSA II K/S - www.himsa.com. + * Represented by EHIMA - www.ehima.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.ParcelUuid; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; +import java.util.UUID; + +/** + * Representation of Call + * + * @hide + */ +public final class BluetoothLeCall implements Parcelable { + + /** @hide */ + @IntDef(prefix = "STATE_", value = { + STATE_INCOMING, + STATE_DIALING, + STATE_ALERTING, + STATE_ACTIVE, + STATE_LOCALLY_HELD, + STATE_REMOTELY_HELD, + STATE_LOCALLY_AND_REMOTELY_HELD + }) + @Retention(RetentionPolicy.SOURCE) + public @interface State { + } + + /** + * A remote party is calling (incoming call). + * + * @hide + */ + public static final int STATE_INCOMING = 0x00; + + /** + * The process to call the remote party has started but the remote party is not + * being alerted (outgoing call). + * + * @hide + */ + public static final int STATE_DIALING = 0x01; + + /** + * A remote party is being alerted (outgoing call). + * + * @hide + */ + public static final int STATE_ALERTING = 0x02; + + /** + * The call is in an active conversation. + * + * @hide + */ + public static final int STATE_ACTIVE = 0x03; + + /** + * The call is connected but held locally. “Locally Held” implies that either + * the server or the client can affect the state. + * + * @hide + */ + public static final int STATE_LOCALLY_HELD = 0x04; + + /** + * The call is connected but held remotely. “Remotely Held” means that the state + * is controlled by the remote party of a call. + * + * @hide + */ + public static final int STATE_REMOTELY_HELD = 0x05; + + /** + * The call is connected but held both locally and remotely. + * + * @hide + */ + public static final int STATE_LOCALLY_AND_REMOTELY_HELD = 0x06; + + /** + * Whether the call direction is outgoing. + * + * @hide + */ + public static final int FLAG_OUTGOING_CALL = 0x00000001; + + /** + * Whether the call URI and Friendly Name are withheld by server. + * + * @hide + */ + public static final int FLAG_WITHHELD_BY_SERVER = 0x00000002; + + /** + * Whether the call URI and Friendly Name are withheld by network. + * + * @hide + */ + public static final int FLAG_WITHHELD_BY_NETWORK = 0x00000004; + + /** Unique UUID that identifies this call */ + private UUID mUuid; + + /** Remote Caller URI */ + private String mUri; + + /** Caller friendly name */ + private String mFriendlyName; + + /** Call state */ + private @State int mState; + + /** Call flags */ + private int mCallFlags; + + /** @hide */ + public BluetoothLeCall(@NonNull BluetoothLeCall that) { + mUuid = new UUID(that.getUuid().getMostSignificantBits(), + that.getUuid().getLeastSignificantBits()); + mUri = that.mUri; + mFriendlyName = that.mFriendlyName; + mState = that.mState; + mCallFlags = that.mCallFlags; + } + + /** @hide */ + public BluetoothLeCall(@NonNull UUID uuid, @NonNull String uri, @NonNull String friendlyName, + @State int state, int callFlags) { + mUuid = uuid; + mUri = uri; + mFriendlyName = friendlyName; + mState = state; + mCallFlags = callFlags; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + BluetoothLeCall that = (BluetoothLeCall) o; + return mUuid.equals(that.mUuid) && mUri.equals(that.mUri) + && mFriendlyName.equals(that.mFriendlyName) && mState == that.mState + && mCallFlags == that.mCallFlags; + } + + @Override + public int hashCode() { + return Objects.hash(mUuid, mUri, mFriendlyName, mState, mCallFlags); + } + + /** + * Returns a string representation of this BluetoothLeCall. + * + * <p> + * Currently this is the UUID. + * + * @return string representation of this BluetoothLeCall + */ + @Override + public String toString() { + return mUuid.toString(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeParcelable(new ParcelUuid(mUuid), 0); + out.writeString(mUri); + out.writeString(mFriendlyName); + out.writeInt(mState); + out.writeInt(mCallFlags); + } + + public static final @android.annotation.NonNull Parcelable.Creator<BluetoothLeCall> CREATOR = + new Parcelable.Creator<BluetoothLeCall>() { + public BluetoothLeCall createFromParcel(Parcel in) { + return new BluetoothLeCall(in); + } + + public BluetoothLeCall[] newArray(int size) { + return new BluetoothLeCall[size]; + } + }; + + private BluetoothLeCall(Parcel in) { + mUuid = ((ParcelUuid) in.readParcelable(null)).getUuid(); + mUri = in.readString(); + mFriendlyName = in.readString(); + mState = in.readInt(); + mCallFlags = in.readInt(); + } + + /** + * Returns an UUID of this BluetoothLeCall. + * + * <p> + * An UUID is unique identifier of a BluetoothLeCall. + * + * @return UUID of this BluetoothLeCall + * @hide + */ + public @NonNull UUID getUuid() { + return mUuid; + } + + /** + * Returns a URI of the remote party of this BluetoothLeCall. + * + * @return string representation of this BluetoothLeCall + * @hide + */ + public @NonNull String getUri() { + return mUri; + } + + /** + * Returns a friendly name of the call. + * + * @return friendly name representation of this BluetoothLeCall + * @hide + */ + public @NonNull String getFriendlyName() { + return mFriendlyName; + } + + /** + * Returns the call state. + * + * @return the state of this BluetoothLeCall + * @hide + */ + public @State int getState() { + return mState; + } + + /** + * Returns the call flags. + * + * @return call flags + * @hide + */ + public int getCallFlags() { + return mCallFlags; + } + + /** + * Whether the call direction is incoming. + * + * @return true if incoming call, false otherwise + * @hide + */ + public boolean isIncomingCall() { + return (mCallFlags & FLAG_OUTGOING_CALL) == 0; + } +} diff --git a/core/java/android/bluetooth/BluetoothLeCallControl.java b/core/java/android/bluetooth/BluetoothLeCallControl.java new file mode 100644 index 000000000000..fb080c9ec3e3 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothLeCallControl.java @@ -0,0 +1,899 @@ +/* + * Copyright 2019 HIMSA II K/S - www.himsa.com. + * Represented by EHIMA - www.ehima.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth; + +import android.Manifest; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.bluetooth.annotations.RequiresBluetoothConnectPermission; +import android.content.ComponentName; +import android.content.Context; +import android.os.Binder; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.ParcelUuid; +import android.os.RemoteException; +import android.util.Log; +import android.annotation.SuppressLint; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +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.UUID; +import java.util.concurrent.Executor; + +/** + * This class provides the APIs to control the Call Control profile. + * + * <p> + * This class provides Bluetooth Telephone Bearer Service functionality, + * allowing applications to expose a GATT Service based interface to control the + * state of the calls by remote devices such as LE audio devices. + * + * <p> + * BluetoothLeCallControl is a proxy object for controlling the Bluetooth Telephone Bearer + * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get the + * BluetoothLeCallControl proxy object. + * + * @hide + */ +public final class BluetoothLeCallControl implements BluetoothProfile { + private static final String TAG = "BluetoothLeCallControl"; + private static final boolean DBG = true; + private static final boolean VDBG = false; + + /** @hide */ + @IntDef(prefix = "RESULT_", value = { + RESULT_SUCCESS, + RESULT_ERROR_UNKNOWN_CALL_ID, + RESULT_ERROR_INVALID_URI, + RESULT_ERROR_APPLICATION + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Result { + } + + /** + * Opcode write was successful. + * + * @hide + */ + public static final int RESULT_SUCCESS = 0; + + /** + * Unknown call Id has been used in the operation. + * + * @hide + */ + public static final int RESULT_ERROR_UNKNOWN_CALL_ID = 1; + + /** + * The URI provided in {@link Callback#onPlaceCallRequest} is invalid. + * + * @hide + */ + public static final int RESULT_ERROR_INVALID_URI = 2; + + /** + * Application internal error. + * + * @hide + */ + public static final int RESULT_ERROR_APPLICATION = 3; + + /** @hide */ + @IntDef(prefix = "TERMINATION_REASON_", value = { + TERMINATION_REASON_INVALID_URI, + TERMINATION_REASON_FAIL, + TERMINATION_REASON_REMOTE_HANGUP, + TERMINATION_REASON_SERVER_HANGUP, + TERMINATION_REASON_LINE_BUSY, + TERMINATION_REASON_NETWORK_CONGESTION, + TERMINATION_REASON_CLIENT_HANGUP, + TERMINATION_REASON_NO_SERVICE, + TERMINATION_REASON_NO_ANSWER + }) + @Retention(RetentionPolicy.SOURCE) + public @interface TerminationReason { + } + + /** + * Remote Caller ID value used to place a call was formed improperly. + * + * @hide + */ + public static final int TERMINATION_REASON_INVALID_URI = 0x00; + + /** + * Call fail. + * + * @hide + */ + public static final int TERMINATION_REASON_FAIL = 0x01; + + /** + * Remote party ended call. + * + * @hide + */ + public static final int TERMINATION_REASON_REMOTE_HANGUP = 0x02; + + /** + * Call ended from the server. + * + * @hide + */ + public static final int TERMINATION_REASON_SERVER_HANGUP = 0x03; + + /** + * Line busy. + * + * @hide + */ + public static final int TERMINATION_REASON_LINE_BUSY = 0x04; + + /** + * Network congestion. + * + * @hide + */ + public static final int TERMINATION_REASON_NETWORK_CONGESTION = 0x05; + + /** + * Client terminated. + * + * @hide + */ + public static final int TERMINATION_REASON_CLIENT_HANGUP = 0x06; + + /** + * No service. + * + * @hide + */ + public static final int TERMINATION_REASON_NO_SERVICE = 0x07; + + /** + * No answer. + * + * @hide + */ + public static final int TERMINATION_REASON_NO_ANSWER = 0x08; + + /* + * Flag indicating support for hold/unhold call feature. + * + * @hide + */ + public static final int CAPABILITY_HOLD_CALL = 0x00000001; + + /** + * Flag indicating support for joining calls feature. + * + * @hide + */ + public static final int CAPABILITY_JOIN_CALLS = 0x00000002; + + private static final int MESSAGE_TBS_SERVICE_CONNECTED = 102; + private static final int MESSAGE_TBS_SERVICE_DISCONNECTED = 103; + + private static final int REG_TIMEOUT = 10000; + + /** + * The template class is used to call callback functions on events from the TBS + * server. Callback functions are wrapped in this class and registered to the + * Android system during app registration. + * + * @hide + */ + public abstract static class Callback { + + private static final String TAG = "BluetoothLeCallControl.Callback"; + + /** + * Called when a remote client requested to accept the call. + * + * <p> + * An application must call {@link BluetoothLeCallControl#requestResult} to complete the + * request. + * + * @param requestId The Id of the request + * @param callId The call Id requested to be accepted + * @hide + */ + public abstract void onAcceptCall(int requestId, @NonNull UUID callId); + + /** + * A remote client has requested to terminate the call. + * + * <p> + * An application must call {@link BluetoothLeCallControl#requestResult} to complete the + * request. + * + * @param requestId The Id of the request + * @param callId The call Id requested to terminate + * @hide + */ + public abstract void onTerminateCall(int requestId, @NonNull UUID callId); + + /** + * A remote client has requested to hold the call. + * + * <p> + * An application must call {@link BluetoothLeCallControl#requestResult} to complete the + * request. + * + * @param requestId The Id of the request + * @param callId The call Id requested to be put on hold + * @hide + */ + public void onHoldCall(int requestId, @NonNull UUID callId) { + Log.e(TAG, "onHoldCall: unimplemented, however CAPABILITY_HOLD_CALL is set!"); + } + + /** + * A remote client has requested to unhold the call. + * + * <p> + * An application must call {@link BluetoothLeCallControl#requestResult} to complete the + * request. + * + * @param requestId The Id of the request + * @param callId The call Id requested to unhold + * @hide + */ + public void onUnholdCall(int requestId, @NonNull UUID callId) { + Log.e(TAG, "onUnholdCall: unimplemented, however CAPABILITY_HOLD_CALL is set!"); + } + + /** + * A remote client has requested to place a call. + * + * <p> + * An application must call {@link BluetoothLeCallControl#requestResult} to complete the + * request. + * + * @param requestId The Id of the request + * @param callId The Id to be assigned for the new call + * @param uri The caller URI requested + * @hide + */ + public abstract void onPlaceCall(int requestId, @NonNull UUID callId, @NonNull String uri); + + /** + * A remote client has requested to join the calls. + * + * <p> + * An application must call {@link BluetoothLeCallControl#requestResult} to complete the + * request. + * + * @param requestId The Id of the request + * @param callIds The call Id list requested to join + * @hide + */ + public void onJoinCalls(int requestId, @NonNull List<UUID> callIds) { + Log.e(TAG, "onJoinCalls: unimplemented, however CAPABILITY_JOIN_CALLS is set!"); + } + } + + private class CallbackWrapper extends IBluetoothLeCallControlCallback.Stub { + + private final Executor mExecutor; + private final Callback mCallback; + + CallbackWrapper(Executor executor, Callback callback) { + mExecutor = executor; + mCallback = callback; + } + + @Override + public void onBearerRegistered(int ccid) { + if (mCallback != null) { + mCcid = ccid; + } else { + // registration timeout + Log.e(TAG, "onBearerRegistered: mCallback is null"); + } + } + + @Override + public void onAcceptCall(int requestId, ParcelUuid uuid) { + final long identityToken = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onAcceptCall(requestId, uuid.getUuid())); + } finally { + Binder.restoreCallingIdentity(identityToken); + } + } + + @Override + public void onTerminateCall(int requestId, ParcelUuid uuid) { + final long identityToken = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onTerminateCall(requestId, uuid.getUuid())); + } finally { + Binder.restoreCallingIdentity(identityToken); + } + } + + @Override + public void onHoldCall(int requestId, ParcelUuid uuid) { + final long identityToken = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onHoldCall(requestId, uuid.getUuid())); + } finally { + Binder.restoreCallingIdentity(identityToken); + } + } + + @Override + public void onUnholdCall(int requestId, ParcelUuid uuid) { + final long identityToken = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onUnholdCall(requestId, uuid.getUuid())); + } finally { + Binder.restoreCallingIdentity(identityToken); + } + } + + @Override + public void onPlaceCall(int requestId, ParcelUuid uuid, String uri) { + final long identityToken = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onPlaceCall(requestId, uuid.getUuid(), uri)); + } finally { + Binder.restoreCallingIdentity(identityToken); + } + } + + @Override + public void onJoinCalls(int requestId, List<ParcelUuid> parcelUuids) { + List<UUID> uuids = new ArrayList<>(); + for (ParcelUuid parcelUuid : parcelUuids) { + uuids.add(parcelUuid.getUuid()); + } + + final long identityToken = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.onJoinCalls(requestId, uuids)); + } finally { + Binder.restoreCallingIdentity(identityToken); + } + } + }; + + private Context mContext; + private ServiceListener mServiceListener; + private volatile IBluetoothLeCallControl mService; + private BluetoothAdapter mAdapter; + private int mCcid = 0; + private String mToken; + private Callback mCallback = null; + + private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = + new IBluetoothStateChangeCallback.Stub() { + public void onBluetoothStateChange(boolean up) { + if (DBG) + Log.d(TAG, "onBluetoothStateChange: up=" + up); + if (!up) { + doUnbind(); + } else { + doBind(); + } + } + }; + + /** + * Create a BluetoothLeCallControl proxy object for interacting with the local Bluetooth + * telephone bearer service. + */ + /* package */ BluetoothLeCallControl(Context context, ServiceListener listener) { + mContext = context; + mAdapter = BluetoothAdapter.getDefaultAdapter(); + mServiceListener = listener; + + IBluetoothManager mgr = mAdapter.getBluetoothManager(); + if (mgr != null) { + try { + mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + } + + doBind(); + } + + private boolean doBind() { + synchronized (mConnection) { + if (mService == null) { + if (VDBG) + Log.d(TAG, "Binding service..."); + try { + return mAdapter.getBluetoothManager(). + bindBluetoothProfileService(BluetoothProfile.LE_CALL_CONTROL, + mConnection); + } catch (RemoteException e) { + Log.e(TAG, "Unable to bind TelephoneBearerService", e); + } + } + } + return false; + } + + private void doUnbind() { + synchronized (mConnection) { + if (mService != null) { + if (VDBG) + Log.d(TAG, "Unbinding service..."); + try { + mAdapter.getBluetoothManager(). + unbindBluetoothProfileService(BluetoothProfile.LE_CALL_CONTROL, + mConnection); + } catch (RemoteException e) { + Log.e(TAG, "Unable to unbind TelephoneBearerService", e); + } finally { + mService = null; + } + } + } + } + + /* package */ void close() { + if (VDBG) + log("close()"); + unregisterBearer(); + + IBluetoothManager mgr = mAdapter.getBluetoothManager(); + if (mgr != null) { + try { + mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); + } catch (RemoteException re) { + Log.e(TAG, "", re); + } + } + mServiceListener = null; + doUnbind(); + } + + private IBluetoothLeCallControl getService() { + return mService; + } + + /** + * Not supported + * + * @throws UnsupportedOperationException + */ + @Override + public int getConnectionState(@Nullable BluetoothDevice device) { + throw new UnsupportedOperationException("not supported"); + } + + /** + * Not supported + * + * @throws UnsupportedOperationException + */ + @Override + public @NonNull List<BluetoothDevice> getConnectedDevices() { + throw new UnsupportedOperationException("not supported"); + } + + /** + * Not supported + * + * @throws UnsupportedOperationException + */ + @Override + public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates( + @NonNull int[] states) { + throw new UnsupportedOperationException("not supported"); + } + + /** + * Register Telephone Bearer exposing the interface that allows remote devices + * to track and control the call states. + * + * <p> + * This is an asynchronous call. The callback is used to notify success or + * failure if the function returns true. + * + * <p> + * Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * <!-- The UCI is a String identifier of the telephone bearer as defined at + * https://www.bluetooth.com/specifications/assigned-numbers/uniform-caller-identifiers + * (login required). --> + * + * <!-- The examples of common URI schemes can be found in + * https://iana.org/assignments/uri-schemes/uri-schemes.xhtml --> + * + * <!-- The Technology is an integer value. The possible values are defined at + * https://www.bluetooth.com/specifications/assigned-numbers (login required). + * --> + * + * @param uci Bearer Unique Client Identifier + * @param uriSchemes URI Schemes supported list + * @param capabilities bearer capabilities + * @param provider Network provider name + * @param technology Network technology + * @param executor {@link Executor} object on which callback will be + * executed. The Executor object is required. + * @param callback {@link Callback} object to which callback messages will + * be sent. The Callback object is required. + * @return true on success, false otherwise + * @hide + */ + @SuppressLint("ExecutorRegistration") + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean registerBearer(@Nullable String uci, + @NonNull List<String> uriSchemes, int capabilities, + @NonNull String provider, int technology, + @NonNull Executor executor, @NonNull Callback callback) { + if (DBG) { + Log.d(TAG, "registerBearer"); + } + if (callback == null) { + throw new IllegalArgumentException("null parameter: " + callback); + } + if (mCcid != 0) { + return false; + } + + mToken = uci; + + final IBluetoothLeCallControl service = getService(); + if (service != null) { + if (mCallback != null) { + Log.e(TAG, "Bearer can be opened only once"); + return false; + } + + mCallback = callback; + try { + CallbackWrapper callbackWrapper = new CallbackWrapper(executor, callback); + service.registerBearer(mToken, callbackWrapper, uci, uriSchemes, capabilities, + provider, technology); + } catch (RemoteException e) { + Log.e(TAG, "", e); + mCallback = null; + return false; + } + + if (mCcid == 0) { + mCallback = null; + return false; + } + + return true; + } + + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + + return false; + } + + /** + * Unregister Telephone Bearer Service and destroy all the associated data. + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + public void unregisterBearer() { + if (DBG) { + Log.d(TAG, "unregisterBearer"); + } + if (mCcid == 0) { + return; + } + + int ccid = mCcid; + mCcid = 0; + mCallback = null; + + final IBluetoothLeCallControl service = getService(); + if (service != null) { + try { + service.unregisterBearer(mToken); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + } + + /** + * Get the Content Control ID (CCID) value. + * + * @return ccid Content Control ID value + * @hide + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + public int getContentControlId() { + return mCcid; + } + + /** + * Notify about the newly added call. + * + * <p> + * This shall be called as early as possible after the call has been added. + * + * <p> + * Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param call Newly added call + * @hide + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + public void onCallAdded(@NonNull BluetoothLeCall call) { + if (DBG) { + Log.d(TAG, "onCallAdded: call=" + call); + } + if (mCcid == 0) { + return; + } + + final IBluetoothLeCallControl service = getService(); + if (service != null) { + try { + service.callAdded(mCcid, call); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + } + + /** + * Notify about the removed call. + * + * <p> + * This shall be called as early as possible after the call has been removed. + * + * <p> + * Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param callId The Id of a call that has been removed + * @param reason Call termination reason + * @hide + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + public void onCallRemoved(@NonNull UUID callId, @TerminationReason int reason) { + if (DBG) { + Log.d(TAG, "callRemoved: callId=" + callId); + } + if (mCcid == 0) { + return; + } + + final IBluetoothLeCallControl service = getService(); + if (service != null) { + try { + service.callRemoved(mCcid, new ParcelUuid(callId), reason); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + } + + /** + * Notify the call state change + * + * <p> + * This shall be called as early as possible after the state of the call has + * changed. + * + * <p> + * Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param callId The call Id that state has been changed + * @param state Call state + * @hide + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + public void onCallStateChanged(@NonNull UUID callId, @BluetoothLeCall.State int state) { + if (DBG) { + Log.d(TAG, "callStateChanged: callId=" + callId + " state=" + state); + } + if (mCcid == 0) { + return; + } + + final IBluetoothLeCallControl service = getService(); + if (service != null) { + try { + service.callStateChanged(mCcid, new ParcelUuid(callId), state); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + } + + /** + * Provide the current calls list + * + * <p> + * This function must be invoked after registration if application has any + * calls. + * + * @param calls current calls list + * @hide + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + public void currentCallsList(@NonNull List<BluetoothLeCall> calls) { + final IBluetoothLeCallControl service = getService(); + if (service != null) { + try { + service.currentCallsList(mCcid, calls); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + } + } + + /** + * Provide the network current status + * + * <p> + * This function must be invoked on change of network state. + * + * <p> + * Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * <!-- The Technology is an integer value. The possible values are defined at + * https://www.bluetooth.com/specifications/assigned-numbers (login required). + * --> + * + * @param provider Network provider name + * @param technology Network technology + * @hide + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + public void networkStateChanged(@NonNull String provider, int technology) { + if (DBG) { + Log.d(TAG, "networkStateChanged: provider=" + provider + ", technology=" + technology); + } + if (mCcid == 0) { + return; + } + + final IBluetoothLeCallControl service = getService(); + if (service != null) { + try { + service.networkStateChanged(mCcid, provider, technology); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + } + if (service == null) { + Log.w(TAG, "Proxy not attached to service"); + } + } + + /** + * Send a response to a call control request to a remote device. + * + * <p> + * This function must be invoked in when a request is received by one of these + * callback methods: + * + * <ul> + * <li>{@link Callback#onAcceptCall} + * <li>{@link Callback#onTerminateCall} + * <li>{@link Callback#onHoldCall} + * <li>{@link Callback#onUnholdCall} + * <li>{@link Callback#onPlaceCall} + * <li>{@link Callback#onJoinCalls} + * </ul> + * + * @param requestId The ID of the request that was received with the callback + * @param result The result of the request to be sent to the remote devices + */ + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + public void requestResult(int requestId, @Result int result) { + if (DBG) { + Log.d(TAG, "requestResult: requestId=" + requestId + " result=" + result); + } + if (mCcid == 0) { + return; + } + + final IBluetoothLeCallControl service = getService(); + if (service != null) { + try { + service.requestResult(mCcid, requestId, result); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + } + } + + @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) + private static boolean isValidDevice(@Nullable BluetoothDevice device) { + return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); + } + + private static void log(String msg) { + Log.d(TAG, msg); + } + + private final IBluetoothProfileServiceConnection mConnection = + new IBluetoothProfileServiceConnection.Stub() { + @Override + public void onServiceConnected(ComponentName className, IBinder service) { + if (DBG) { + Log.d(TAG, "Proxy object connected"); + } + mService = IBluetoothLeCallControl.Stub.asInterface(Binder.allowBlocking(service)); + mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_TBS_SERVICE_CONNECTED)); + } + + @Override + public void onServiceDisconnected(ComponentName className) { + if (DBG) { + Log.d(TAG, "Proxy object disconnected"); + } + doUnbind(); + mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_TBS_SERVICE_DISCONNECTED)); + } + }; + + private final Handler mHandler = new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MESSAGE_TBS_SERVICE_CONNECTED: { + if (mServiceListener != null) { + mServiceListener.onServiceConnected(BluetoothProfile.LE_CALL_CONTROL, + BluetoothLeCallControl.this); + } + break; + } + case MESSAGE_TBS_SERVICE_DISCONNECTED: { + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(BluetoothProfile.LE_CALL_CONTROL); + } + break; + } + } + } + }; +} diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java index e047e5d81a9d..d0f74e985729 100644 --- a/core/java/android/bluetooth/BluetoothProfile.java +++ b/core/java/android/bluetooth/BluetoothProfile.java @@ -240,12 +240,19 @@ public interface BluetoothProfile { int LE_AUDIO_BROADCAST = 26; /** + * @hide + * Telephone Bearer Service from Call Control Profile + * + */ + int LE_CALL_CONTROL = 27; + + /** * Max profile ID. This value should be updated whenever a new profile is added to match * the largest value assigned to a profile. * * @hide */ - int MAX_PROFILE_ID = 26; + int MAX_PROFILE_ID = 27; /** * Default priority for devices that we try to auto-connect to and diff --git a/core/java/android/bluetooth/BluetoothStatusCodes.java b/core/java/android/bluetooth/BluetoothStatusCodes.java index fff32fffd8a7..a8ce4b411881 100644 --- a/core/java/android/bluetooth/BluetoothStatusCodes.java +++ b/core/java/android/bluetooth/BluetoothStatusCodes.java @@ -20,7 +20,7 @@ import android.annotation.SystemApi; /** * A class with constants representing possible return values for Bluetooth APIs. General return - * values occupy the range 0 to 99. Profile-specific return values occupy the range 100-999. + * values occupy the range 0 to 199. Profile-specific return values occupy the range 200-999. * API-specific return values start at 1000. The exception to this is the "UNKNOWN" error code which * occupies the max integer value. */ @@ -29,28 +29,28 @@ public final class BluetoothStatusCodes { private BluetoothStatusCodes() {} /** - * Indicates that the API call was successful + * Indicates that the API call was successful. */ public static final int SUCCESS = 0; /** - * Error code indicating that Bluetooth is not enabled + * Error code indicating that Bluetooth is not enabled. */ public static final int ERROR_BLUETOOTH_NOT_ENABLED = 1; /** * Error code indicating that the API call was initiated by neither the system nor the active - * user + * user. */ public static final int ERROR_BLUETOOTH_NOT_ALLOWED = 2; /** - * Error code indicating that the Bluetooth Device specified is not bonded + * Error code indicating that the Bluetooth Device specified is not bonded. */ public static final int ERROR_DEVICE_NOT_BONDED = 3; /** - * Error code indicating that the Bluetooth Device specified is not connected, but is bonded + * Error code indicating that the Bluetooth Device specified is not connected, but is bonded. * * @hide */ @@ -58,7 +58,7 @@ public final class BluetoothStatusCodes { /** * Error code indicating that the caller does not have the - * {@link android.Manifest.permission#BLUETOOTH_ADVERTISE} permission + * {@link android.Manifest.permission#BLUETOOTH_ADVERTISE} permission. * * @hide */ @@ -66,13 +66,13 @@ public final class BluetoothStatusCodes { /** * Error code indicating that the caller does not have the - * {@link android.Manifest.permission#BLUETOOTH_CONNECT} permission + * {@link android.Manifest.permission#BLUETOOTH_CONNECT} permission. */ public static final int ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION = 6; /** * Error code indicating that the caller does not have the - * {@link android.Manifest.permission#BLUETOOTH_SCAN} permission + * {@link android.Manifest.permission#BLUETOOTH_SCAN} permission. * * @hide */ @@ -80,30 +80,67 @@ public final class BluetoothStatusCodes { /** * Error code indicating that the caller does not have the - * {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission + * {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission. */ public static final int ERROR_MISSING_BLUETOOTH_PRIVILEGED_PERMISSION = 8; /** * Error code indicating that the profile service is not bound. You can bind a profile service - * by calling {@link BluetoothAdapter#getProfileProxy} + * by calling {@link BluetoothAdapter#getProfileProxy}. */ public static final int ERROR_PROFILE_SERVICE_NOT_BOUND = 9; /** - * Error code indicating that the feature is not supported. + * Indicates that the feature is supported. */ - public static final int ERROR_FEATURE_NOT_SUPPORTED = 10; + public static final int FEATURE_SUPPORTED = 10; + + /** + * Indicates that the feature is not supported. + */ + public static final int FEATURE_NOT_SUPPORTED = 11; + + /** + * Error code indicating that the device is not the active device for this profile. + * + * @hide + */ + @SystemApi + public static final int ERROR_NOT_ACTIVE_DEVICE = 12; + + /** + * Error code indicating that there are no active devices for the profile. + * + * @hide + */ + @SystemApi + public static final int ERROR_NO_ACTIVE_DEVICES = 13; + + /** + * Indicates that the Bluetooth profile is not connected to this device. + * + * @hide + */ + @SystemApi + public static final int ERROR_PROFILE_NOT_CONNECTED = 14; + + /** + * Error code indicating that the requested operation timed out. + * + * @hide + */ + @SystemApi + public static final int ERROR_TIMEOUT = 15; /** * A GATT writeCharacteristic request is not permitted on the remote device. */ - public static final int ERROR_GATT_WRITE_NOT_ALLOWED = 101; + public static final int ERROR_GATT_WRITE_NOT_ALLOWED = 200; /** * A GATT writeCharacteristic request is issued to a busy remote device. */ - public static final int ERROR_GATT_WRITE_REQUEST_BUSY = 102; + public static final int ERROR_GATT_WRITE_REQUEST_BUSY = 201; /** * If another application has already requested {@link OobData} then another fetch will be @@ -286,6 +323,38 @@ public final class BluetoothStatusCodes { public static final int ERROR_LE_AUDIO_BROADCAST_SOURCE_DISABLE_ENCRYPTION_FAILED = 1115; /** + * Indicates that there is already one device for which SCO audio is connected or connecting. + * + * @hide + */ + @SystemApi + public static final int ERROR_AUDIO_DEVICE_ALREADY_CONNECTED = 1116; + + /** + * Indicates that SCO audio was already not connected for this device. + * + * @hide + */ + @SystemApi + public static final int ERROR_AUDIO_DEVICE_ALREADY_DISCONNECTED = 1117; + + /** + * Indicates that there audio route is currently blocked by the system. + * + * @hide + */ + @SystemApi + public static final int ERROR_AUDIO_ROUTE_BLOCKED = 1118; + + /** + * Indicates that there is an active call preventing this operation from succeeding. + * + * @hide + */ + @SystemApi + public static final int ERROR_CALL_ACTIVE = 1119; + + /** * Indicates that an unknown error has occurred has occurred. */ public static final int ERROR_UNKNOWN = Integer.MAX_VALUE; diff --git a/core/java/android/bluetooth/BufferConstraints.java b/core/java/android/bluetooth/BufferConstraints.java index 06b45ee5c8b5..97d97232b7a6 100644 --- a/core/java/android/bluetooth/BufferConstraints.java +++ b/core/java/android/bluetooth/BufferConstraints.java @@ -55,7 +55,7 @@ public final class BufferConstraints implements Parcelable { BufferConstraints(Parcel in) { mBufferConstraintList = new ArrayList<BufferConstraint>(); mBufferConstraints = new HashMap<Integer, BufferConstraint>(); - in.readList(mBufferConstraintList, BufferConstraint.class.getClassLoader(), android.bluetooth.BufferConstraint.class); + in.readList(mBufferConstraintList, BufferConstraint.class.getClassLoader()); for (int i = 0; i < mBufferConstraintList.size(); i++) { mBufferConstraints.put(i, mBufferConstraintList.get(i)); } diff --git a/core/java/android/bluetooth/le/AdvertiseSettings.java b/core/java/android/bluetooth/le/AdvertiseSettings.java index 7129d762cd95..c52a6ee33989 100644 --- a/core/java/android/bluetooth/le/AdvertiseSettings.java +++ b/core/java/android/bluetooth/le/AdvertiseSettings.java @@ -16,6 +16,9 @@ package android.bluetooth.le; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.bluetooth.le.AdvertisingSetParameters.AddressTypeStatus; import android.os.Parcel; import android.os.Parcelable; @@ -70,17 +73,21 @@ public final class AdvertiseSettings implements Parcelable { */ private static final int LIMITED_ADVERTISING_MAX_MILLIS = 180 * 1000; + private final int mAdvertiseMode; private final int mAdvertiseTxPowerLevel; private final int mAdvertiseTimeoutMillis; private final boolean mAdvertiseConnectable; + private final int mOwnAddressType; private AdvertiseSettings(int advertiseMode, int advertiseTxPowerLevel, - boolean advertiseConnectable, int advertiseTimeout) { + boolean advertiseConnectable, int advertiseTimeout, + @AddressTypeStatus int ownAddressType) { mAdvertiseMode = advertiseMode; mAdvertiseTxPowerLevel = advertiseTxPowerLevel; mAdvertiseConnectable = advertiseConnectable; mAdvertiseTimeoutMillis = advertiseTimeout; + mOwnAddressType = ownAddressType; } private AdvertiseSettings(Parcel in) { @@ -88,6 +95,7 @@ public final class AdvertiseSettings implements Parcelable { mAdvertiseTxPowerLevel = in.readInt(); mAdvertiseConnectable = in.readInt() != 0; mAdvertiseTimeoutMillis = in.readInt(); + mOwnAddressType = in.readInt(); } /** @@ -118,12 +126,23 @@ public final class AdvertiseSettings implements Parcelable { return mAdvertiseTimeoutMillis; } + /** + * @return the own address type for advertising + * + * @hide + */ + @SystemApi + public @AddressTypeStatus int getOwnAddressType() { + return mOwnAddressType; + } + @Override public String toString() { return "Settings [mAdvertiseMode=" + mAdvertiseMode + ", mAdvertiseTxPowerLevel=" + mAdvertiseTxPowerLevel + ", mAdvertiseConnectable=" + mAdvertiseConnectable - + ", mAdvertiseTimeoutMillis=" + mAdvertiseTimeoutMillis + "]"; + + ", mAdvertiseTimeoutMillis=" + mAdvertiseTimeoutMillis + + ", mOwnAddressType=" + mOwnAddressType + "]"; } @Override @@ -137,6 +156,7 @@ public final class AdvertiseSettings implements Parcelable { dest.writeInt(mAdvertiseTxPowerLevel); dest.writeInt(mAdvertiseConnectable ? 1 : 0); dest.writeInt(mAdvertiseTimeoutMillis); + dest.writeInt(mOwnAddressType); } public static final @android.annotation.NonNull Parcelable.Creator<AdvertiseSettings> CREATOR = @@ -160,6 +180,7 @@ public final class AdvertiseSettings implements Parcelable { private int mTxPowerLevel = ADVERTISE_TX_POWER_MEDIUM; private int mTimeoutMillis = 0; private boolean mConnectable = true; + private int mOwnAddressType = AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT; /** * Set advertise mode to control the advertising power and latency. @@ -226,10 +247,31 @@ public final class AdvertiseSettings implements Parcelable { } /** + * Set own address type for advertising to control public or privacy mode. If used to set + * address type anything other than {@link AdvertisingSetParameters#ADDRESS_TYPE_DEFAULT}, + * then it will require BLUETOOTH_PRIVILEGED permission and will be checked at the + * time of starting advertising. + * + * @throws IllegalArgumentException If the {@code ownAddressType} is invalid + * + * @hide + */ + @SystemApi + public @NonNull Builder setOwnAddressType(@AddressTypeStatus int ownAddressType) { + if (ownAddressType < AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT + || ownAddressType > AdvertisingSetParameters.ADDRESS_TYPE_RANDOM) { + throw new IllegalArgumentException("unknown address type " + ownAddressType); + } + mOwnAddressType = ownAddressType; + return this; + } + + /** * Build the {@link AdvertiseSettings} object. */ public AdvertiseSettings build() { - return new AdvertiseSettings(mMode, mTxPowerLevel, mConnectable, mTimeoutMillis); + return new AdvertiseSettings(mMode, mTxPowerLevel, mConnectable, mTimeoutMillis, + mOwnAddressType); } } } diff --git a/core/java/android/bluetooth/le/AdvertisingSetParameters.java b/core/java/android/bluetooth/le/AdvertisingSetParameters.java index e39b198ae384..5c8fae65193d 100644 --- a/core/java/android/bluetooth/le/AdvertisingSetParameters.java +++ b/core/java/android/bluetooth/le/AdvertisingSetParameters.java @@ -16,11 +16,17 @@ package android.bluetooth.le; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.SystemApi; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.os.Parcel; import android.os.Parcelable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * The {@link AdvertisingSetParameters} provide a way to adjust advertising * preferences for each @@ -97,6 +103,39 @@ public final class AdvertisingSetParameters implements Parcelable { */ private static final int LIMITED_ADVERTISING_MAX_MILLIS = 180 * 1000; + /** @hide */ + @IntDef(prefix = "ADDRESS_TYPE_", value = { + ADDRESS_TYPE_DEFAULT, + ADDRESS_TYPE_PUBLIC, + ADDRESS_TYPE_RANDOM + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AddressTypeStatus {} + + /** + * Advertise own address type that corresponds privacy settings of the device. + * + * @hide + */ + @SystemApi + public static final int ADDRESS_TYPE_DEFAULT = -1; + + /** + * Advertise own public address type. + * + * @hide + */ + @SystemApi + public static final int ADDRESS_TYPE_PUBLIC = 0; + + /** + * Generate and adverise own resolvable private address. + * + * @hide + */ + @SystemApi + public static final int ADDRESS_TYPE_RANDOM = 1; + private final boolean mIsLegacy; private final boolean mIsAnonymous; private final boolean mIncludeTxPower; @@ -106,11 +145,12 @@ public final class AdvertisingSetParameters implements Parcelable { private final boolean mScannable; private final int mInterval; private final int mTxPowerLevel; + private final int mOwnAddressType; private AdvertisingSetParameters(boolean connectable, boolean scannable, boolean isLegacy, boolean isAnonymous, boolean includeTxPower, int primaryPhy, int secondaryPhy, - int interval, int txPowerLevel) { + int interval, int txPowerLevel, @AddressTypeStatus int ownAddressType) { mConnectable = connectable; mScannable = scannable; mIsLegacy = isLegacy; @@ -120,6 +160,7 @@ public final class AdvertisingSetParameters implements Parcelable { mSecondaryPhy = secondaryPhy; mInterval = interval; mTxPowerLevel = txPowerLevel; + mOwnAddressType = ownAddressType; } private AdvertisingSetParameters(Parcel in) { @@ -132,6 +173,7 @@ public final class AdvertisingSetParameters implements Parcelable { mSecondaryPhy = in.readInt(); mInterval = in.readInt(); mTxPowerLevel = in.readInt(); + mOwnAddressType = in.readInt(); } /** @@ -197,6 +239,16 @@ public final class AdvertisingSetParameters implements Parcelable { return mTxPowerLevel; } + /** + * @return the own address type for advertising + * + * @hide + */ + @SystemApi + public @AddressTypeStatus int getOwnAddressType() { + return mOwnAddressType; + } + @Override public String toString() { return "AdvertisingSetParameters [connectable=" + mConnectable @@ -206,7 +258,8 @@ public final class AdvertisingSetParameters implements Parcelable { + ", primaryPhy=" + mPrimaryPhy + ", secondaryPhy=" + mSecondaryPhy + ", interval=" + mInterval - + ", txPowerLevel=" + mTxPowerLevel + "]"; + + ", txPowerLevel=" + mTxPowerLevel + + ", ownAddressType=" + mOwnAddressType + "]"; } @Override @@ -225,6 +278,7 @@ public final class AdvertisingSetParameters implements Parcelable { dest.writeInt(mSecondaryPhy); dest.writeInt(mInterval); dest.writeInt(mTxPowerLevel); + dest.writeInt(mOwnAddressType); } public static final @android.annotation.NonNull Parcelable.Creator<AdvertisingSetParameters> CREATOR = @@ -253,6 +307,7 @@ public final class AdvertisingSetParameters implements Parcelable { private int mSecondaryPhy = BluetoothDevice.PHY_LE_1M; private int mInterval = INTERVAL_LOW; private int mTxPowerLevel = TX_POWER_MEDIUM; + private int mOwnAddressType = ADDRESS_TYPE_DEFAULT; /** * Set whether the advertisement type should be connectable or @@ -399,6 +454,26 @@ public final class AdvertisingSetParameters implements Parcelable { } /** + * Set own address type for advertising to control public or privacy mode. If used to set + * address type anything other than {@link AdvertisingSetParameters#ADDRESS_TYPE_DEFAULT}, + * then it will require BLUETOOTH_PRIVILEGED permission and will be checked at the + * time of starting advertising. + * + * @throws IllegalArgumentException If the {@code ownAddressType} is invalid + * + * @hide + */ + @SystemApi + public @NonNull Builder setOwnAddressType(@AddressTypeStatus int ownAddressType) { + if (ownAddressType < AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT + || ownAddressType > AdvertisingSetParameters.ADDRESS_TYPE_RANDOM) { + throw new IllegalArgumentException("unknown address type " + ownAddressType); + } + mOwnAddressType = ownAddressType; + return this; + } + + /** * Build the {@link AdvertisingSetParameters} object. * * @throws IllegalStateException if invalid combination of parameters is used. @@ -431,7 +506,8 @@ public final class AdvertisingSetParameters implements Parcelable { } return new AdvertisingSetParameters(mConnectable, mScannable, mIsLegacy, mIsAnonymous, - mIncludeTxPower, mPrimaryPhy, mSecondaryPhy, mInterval, mTxPowerLevel); + mIncludeTxPower, mPrimaryPhy, mSecondaryPhy, mInterval, mTxPowerLevel, + mOwnAddressType); } } } diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java index b9f8a57a75ea..879dceedaaec 100644 --- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -138,6 +138,7 @@ public final class BluetoothLeAdvertiser { parameters.setLegacyMode(true); parameters.setConnectable(isConnectable); parameters.setScannable(true); // legacy advertisements we support are always scannable + parameters.setOwnAddressType(settings.getOwnAddressType()); if (settings.getMode() == AdvertiseSettings.ADVERTISE_MODE_LOW_POWER) { parameters.setInterval(1600); // 1s } else if (settings.getMode() == AdvertiseSettings.ADVERTISE_MODE_BALANCED) { diff --git a/core/java/android/bluetooth/le/ScanFilter.java b/core/java/android/bluetooth/le/ScanFilter.java index 675fe05a7dec..b059193ae03f 100644 --- a/core/java/android/bluetooth/le/ScanFilter.java +++ b/core/java/android/bluetooth/le/ScanFilter.java @@ -200,28 +200,28 @@ public final class ScanFilter implements Parcelable { address = in.readString(); } if (in.readInt() == 1) { - ParcelUuid uuid = in.readParcelable(ParcelUuid.class.getClassLoader(), android.os.ParcelUuid.class); + ParcelUuid uuid = in.readParcelable(ParcelUuid.class.getClassLoader()); builder.setServiceUuid(uuid); if (in.readInt() == 1) { ParcelUuid uuidMask = in.readParcelable( - ParcelUuid.class.getClassLoader(), android.os.ParcelUuid.class); + ParcelUuid.class.getClassLoader()); builder.setServiceUuid(uuid, uuidMask); } } if (in.readInt() == 1) { ParcelUuid solicitationUuid = in.readParcelable( - ParcelUuid.class.getClassLoader(), android.os.ParcelUuid.class); + ParcelUuid.class.getClassLoader()); builder.setServiceSolicitationUuid(solicitationUuid); if (in.readInt() == 1) { ParcelUuid solicitationUuidMask = in.readParcelable( - ParcelUuid.class.getClassLoader(), android.os.ParcelUuid.class); + ParcelUuid.class.getClassLoader()); builder.setServiceSolicitationUuid(solicitationUuid, solicitationUuidMask); } } if (in.readInt() == 1) { ParcelUuid servcieDataUuid = - in.readParcelable(ParcelUuid.class.getClassLoader(), android.os.ParcelUuid.class); + in.readParcelable(ParcelUuid.class.getClassLoader()); if (in.readInt() == 1) { int serviceDataLength = in.readInt(); byte[] serviceData = new byte[serviceDataLength]; diff --git a/core/java/android/companion/AssociationInfo.java b/core/java/android/companion/AssociationInfo.java index 78d5137ebfba..f0566b856dbd 100644 --- a/core/java/android/companion/AssociationInfo.java +++ b/core/java/android/companion/AssociationInfo.java @@ -36,6 +36,10 @@ import java.util.Objects; */ public final class AssociationInfo implements Parcelable { /** + * A String indicates the selfManaged device is not connected. + */ + private static final String LAST_TIME_CONNECTED_NONE = "None"; + /** * A unique ID of this Association record. * Disclosed to the clients (ie. companion applications) for referring to this record (eg. in * {@code disassociate()} API call). @@ -52,6 +56,11 @@ public final class AssociationInfo implements Parcelable { private final boolean mSelfManaged; private boolean mNotifyOnDeviceNearby; private final long mTimeApprovedMs; + /** + * A long value indicates the last time connected reported by selfManaged devices + * Default value is Long.MAX_VALUE. + */ + private long mLastTimeConnectedMs; /** * Creates a new Association. @@ -62,7 +71,7 @@ public final class AssociationInfo implements Parcelable { public AssociationInfo(int id, @UserIdInt int userId, @NonNull String packageName, @Nullable MacAddress macAddress, @Nullable CharSequence displayName, @Nullable String deviceProfile, boolean selfManaged, boolean notifyOnDeviceNearby, - long timeApprovedMs) { + long timeApprovedMs, long lastTimeConnectedMs) { if (id <= 0) { throw new IllegalArgumentException("Association ID should be greater than 0"); } @@ -83,6 +92,7 @@ public final class AssociationInfo implements Parcelable { mSelfManaged = selfManaged; mNotifyOnDeviceNearby = notifyOnDeviceNearby; mTimeApprovedMs = timeApprovedMs; + mLastTimeConnectedMs = lastTimeConnectedMs; } /** @@ -151,13 +161,21 @@ public final class AssociationInfo implements Parcelable { } /** - * Should only be used by the CdmService. + * Should only be used by the CompanionDeviceManagerService. * @hide */ public void setNotifyOnDeviceNearby(boolean notifyOnDeviceNearby) { mNotifyOnDeviceNearby = notifyOnDeviceNearby; } + /** + * Should only be used by the CompanionDeviceManagerService. + * @hide + */ + public void setLastTimeConnected(long lastTimeConnectedMs) { + mLastTimeConnectedMs = lastTimeConnectedMs; + } + /** @hide */ public boolean isNotifyOnDeviceNearby() { return mNotifyOnDeviceNearby; @@ -174,6 +192,14 @@ public final class AssociationInfo implements Parcelable { } /** + * @return the last time self reported disconnected for selfManaged only. + * @hide + */ + public Long getLastTimeConnectedMs() { + return mLastTimeConnectedMs; + } + + /** * Utility method for checking if the association represents a device with the given MAC * address. * @@ -223,6 +249,9 @@ public final class AssociationInfo implements Parcelable { + ", mSelfManaged=" + mSelfManaged + ", mNotifyOnDeviceNearby=" + mNotifyOnDeviceNearby + ", mTimeApprovedMs=" + new Date(mTimeApprovedMs) + + ", mLastTimeConnectedMs=" + ( + mLastTimeConnectedMs == Long.MAX_VALUE + ? LAST_TIME_CONNECTED_NONE : new Date(mLastTimeConnectedMs)) + '}'; } @@ -236,6 +265,7 @@ public final class AssociationInfo implements Parcelable { && mSelfManaged == that.mSelfManaged && mNotifyOnDeviceNearby == that.mNotifyOnDeviceNearby && mTimeApprovedMs == that.mTimeApprovedMs + && mLastTimeConnectedMs == that.mLastTimeConnectedMs && Objects.equals(mPackageName, that.mPackageName) && Objects.equals(mDeviceMacAddress, that.mDeviceMacAddress) && Objects.equals(mDisplayName, that.mDisplayName) @@ -245,7 +275,8 @@ public final class AssociationInfo implements Parcelable { @Override public int hashCode() { return Objects.hash(mId, mUserId, mPackageName, mDeviceMacAddress, mDisplayName, - mDeviceProfile, mSelfManaged, mNotifyOnDeviceNearby, mTimeApprovedMs); + mDeviceProfile, mSelfManaged, mNotifyOnDeviceNearby, mTimeApprovedMs, + mLastTimeConnectedMs); } @Override @@ -267,6 +298,7 @@ public final class AssociationInfo implements Parcelable { dest.writeBoolean(mSelfManaged); dest.writeBoolean(mNotifyOnDeviceNearby); dest.writeLong(mTimeApprovedMs); + dest.writeLong(mLastTimeConnectedMs); } private AssociationInfo(@NonNull Parcel in) { @@ -282,6 +314,7 @@ public final class AssociationInfo implements Parcelable { mSelfManaged = in.readBoolean(); mNotifyOnDeviceNearby = in.readBoolean(); mTimeApprovedMs = in.readLong(); + mLastTimeConnectedMs = in.readLong(); } @NonNull diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java index 1d2f06d34c8c..18a59d863c46 100644 --- a/core/java/android/companion/AssociationRequest.java +++ b/core/java/android/companion/AssociationRequest.java @@ -595,7 +595,7 @@ public final class AssociationRequest implements Parcelable { boolean forceConfirmation = (flg & 0x20) != 0; boolean skipPrompt = (flg & 0x400) != 0; List<DeviceFilter<?>> deviceFilters = new ArrayList<>(); - in.readParcelableList(deviceFilters, DeviceFilter.class.getClassLoader(), (Class<android.companion.DeviceFilter<?>>) (Class<?>) android.companion.DeviceFilter.class); + in.readParcelableList(deviceFilters, DeviceFilter.class.getClassLoader()); String deviceProfile = (flg & 0x4) == 0 ? null : in.readString(); CharSequence displayName = (flg & 0x8) == 0 ? null : (CharSequence) in.readCharSequence(); String packageName = (flg & 0x40) == 0 ? null : in.readString(); diff --git a/core/java/android/companion/BluetoothDeviceFilter.java b/core/java/android/companion/BluetoothDeviceFilter.java index e0018f4bad42..be663f7bdc1d 100644 --- a/core/java/android/companion/BluetoothDeviceFilter.java +++ b/core/java/android/companion/BluetoothDeviceFilter.java @@ -70,7 +70,7 @@ public final class BluetoothDeviceFilter implements DeviceFilter<BluetoothDevice } private static List<ParcelUuid> readUuids(Parcel in) { - return in.readParcelableList(new ArrayList<>(), ParcelUuid.class.getClassLoader(), android.os.ParcelUuid.class); + return in.readParcelableList(new ArrayList<>(), ParcelUuid.class.getClassLoader()); } /** @hide */ diff --git a/core/java/android/companion/BluetoothLeDeviceFilter.java b/core/java/android/companion/BluetoothLeDeviceFilter.java index e6091f04a72a..58898cc095be 100644 --- a/core/java/android/companion/BluetoothLeDeviceFilter.java +++ b/core/java/android/companion/BluetoothLeDeviceFilter.java @@ -252,7 +252,7 @@ public final class BluetoothLeDeviceFilter implements DeviceFilter<ScanResult> { public BluetoothLeDeviceFilter createFromParcel(Parcel in) { Builder builder = new Builder() .setNamePattern(patternFromString(in.readString())) - .setScanFilter(in.readParcelable(null, android.bluetooth.le.ScanFilter.class)); + .setScanFilter(in.readParcelable(null)); byte[] rawDataFilter = in.createByteArray(); byte[] rawDataFilterMask = in.createByteArray(); if (rawDataFilter != null) { diff --git a/core/java/android/companion/CompanionDeviceService.java b/core/java/android/companion/CompanionDeviceService.java index 3237f7ca340a..12ced96a0ffb 100644 --- a/core/java/android/companion/CompanionDeviceService.java +++ b/core/java/android/companion/CompanionDeviceService.java @@ -28,9 +28,6 @@ import android.os.Handler; import android.os.IBinder; import android.util.Log; - -import com.android.internal.util.function.pooled.PooledLambda; - import java.util.Objects; /** @@ -185,31 +182,24 @@ public abstract class CompanionDeviceService extends Service { public void onBindCompanionDeviceService(@NonNull Intent intent) { } - class Stub extends ICompanionDeviceService.Stub { + private class Stub extends ICompanionDeviceService.Stub { + final Handler mMainHandler = Handler.getMain(); + final CompanionDeviceService mService = CompanionDeviceService.this; @Override public void onDeviceAppeared(AssociationInfo associationInfo) { - Handler.getMain().post( - () -> CompanionDeviceService.this.onDeviceAppeared(associationInfo)); + mMainHandler.postAtFrontOfQueue(() -> mService.onDeviceAppeared(associationInfo)); } @Override public void onDeviceDisappeared(AssociationInfo associationInfo) { - Handler.getMain().post( - () -> CompanionDeviceService.this.onDeviceDisappeared(associationInfo)); + mMainHandler.postAtFrontOfQueue(() -> mService.onDeviceDisappeared(associationInfo)); } + @Override public void onDispatchMessage(int messageId, int associationId, @NonNull byte[] message) { - Handler.getMain().sendMessage(PooledLambda.obtainMessage( - CompanionDeviceService::onDispatchMessage, - CompanionDeviceService.this, messageId, associationId, message)); - } - - public final void dispatchMessage(int messageId, int associationId, - @NonNull byte[] message) { - Handler.getMain().sendMessage(PooledLambda.obtainMessage( - CompanionDeviceService::dispatchMessage, - CompanionDeviceService.this, messageId, associationId, message)); + mMainHandler.postAtFrontOfQueue( + () -> mService.onDispatchMessage(messageId, associationId, message)); } } } diff --git a/core/java/android/app/communal/ICommunalModeListener.aidl b/core/java/android/companion/SystemDataTransferRequest.aidl index 006e782c52ee..19ae60effa7a 100644 --- a/core/java/android/app/communal/ICommunalModeListener.aidl +++ b/core/java/android/companion/SystemDataTransferRequest.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,13 +14,6 @@ * limitations under the License. */ -package android.app.communal; +package android.companion; -/** - * System private API to be notified about communal mode changes. - * - * @hide - */ -oneway interface ICommunalModeListener { - void onCommunalModeChanged(boolean isCommunalMode); -}
\ No newline at end of file +parcelable SystemDataTransferRequest; diff --git a/core/java/android/companion/SystemDataTransferRequest.java b/core/java/android/companion/SystemDataTransferRequest.java new file mode 100644 index 000000000000..e3b0369e203d --- /dev/null +++ b/core/java/android/companion/SystemDataTransferRequest.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2022 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.companion; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; +import android.provider.OneTimeUseBuilder; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * A request for users to allow the companion app to transfer system data to the companion devices. + * + * @hide + */ +public final class SystemDataTransferRequest implements Parcelable { + + private final int mAssociationId; + private final boolean mPermissionSyncAllPackages; + private final List<String> mPermissionSyncPackages; + + /** + * @hide + */ + public SystemDataTransferRequest(int associationId, boolean syncAllPackages, + @Nullable List<String> permissionSyncPackages) { + mAssociationId = associationId; + mPermissionSyncAllPackages = syncAllPackages; + mPermissionSyncPackages = permissionSyncPackages; + } + + public int getAssociationId() { + return mAssociationId; + } + + @NonNull + public boolean isPermissionSyncAllPackages() { + return mPermissionSyncAllPackages; + } + + @NonNull + public List<String> getPermissionSyncPackages() { + return mPermissionSyncPackages; + } + + /** + * A builder for {@link SystemDataTransferRequest}. + * + * <p>You have to call one of the below methods to create a valid request</p> + * <br>1. {@link #setPermissionSyncAllPackages()} + * <br>2. {@link #setPermissionSyncPackages(List)} + */ + public static final class Builder extends OneTimeUseBuilder<SystemDataTransferRequest> { + + private final int mAssociationId; + private boolean mPermissionSyncAllPackages; + private List<String> mPermissionSyncPackages = new ArrayList<>(); + + public Builder(int associationId) { + mAssociationId = associationId; + } + + /** + * Call to sync permissions for all the packages. You can optionally call + * {@link #setPermissionSyncPackages(List)} to specify the packages to sync permissions. + * + * <p>The system will only sync permissions that are explicitly granted by the user.</p> + * + * <p>If a permission is granted or revoked by the system or a policy, even if the user has + * explicitly granted or revoked the permission earlier, the permission will be ignored.</p> + * + * <p>If a system or policy granted or revoked permission is granted or revoked by the user + * later, the permission will be ignored.</p> + * + * @see #setPermissionSyncPackages(List) + * + * @return the builder + */ + @NonNull + public Builder setPermissionSyncAllPackages() { + mPermissionSyncAllPackages = true; + return this; + } + + /** + * Set a list of packages to sync permissions. You can optionally call + * {@link #setPermissionSyncAllPackages()} to sync permissions for all the packages. + * + * @see #setPermissionSyncAllPackages() + * + * @param permissionSyncPackages packages to sync permissions + * @return builder + */ + @NonNull + public Builder setPermissionSyncPackages(@NonNull List<String> permissionSyncPackages) { + mPermissionSyncPackages = permissionSyncPackages; + return this; + } + + @Override + @NonNull + public SystemDataTransferRequest build() { + return new SystemDataTransferRequest(mAssociationId, mPermissionSyncAllPackages, + mPermissionSyncPackages); + } + } + + SystemDataTransferRequest(Parcel in) { + mAssociationId = in.readInt(); + mPermissionSyncAllPackages = in.readBoolean(); + mPermissionSyncPackages = Arrays.asList(in.createString8Array()); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mAssociationId); + dest.writeBoolean(mPermissionSyncAllPackages); + dest.writeString8Array(mPermissionSyncPackages.toArray(new String[0])); + } + + @Override + public int describeContents() { + return 0; + } + + @NonNull + public static final Creator<SystemDataTransferRequest> CREATOR = + new Creator<SystemDataTransferRequest>() { + @Override + public SystemDataTransferRequest createFromParcel(Parcel in) { + return new SystemDataTransferRequest(in); + } + + @Override + public SystemDataTransferRequest[] newArray(int size) { + return new SystemDataTransferRequest[size]; + } + }; +} diff --git a/core/java/android/content/ContentProviderOperation.java b/core/java/android/content/ContentProviderOperation.java index 0c065d9bd402..30775b19ab00 100644 --- a/core/java/android/content/ContentProviderOperation.java +++ b/core/java/android/content/ContentProviderOperation.java @@ -108,7 +108,7 @@ public class ContentProviderOperation implements Parcelable { mExtras = null; } mSelection = source.readInt() != 0 ? source.readString8() : null; - mSelectionArgs = source.readSparseArray(null, java.lang.Object.class); + mSelectionArgs = source.readSparseArray(null); mExpectedCount = source.readInt() != 0 ? source.readInt() : null; mYieldAllowed = source.readInt() != 0; mExceptionAllowed = source.readInt() != 0; diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index bccfacf70842..2df6f9ae2bd6 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -94,8 +94,11 @@ import java.io.IOException; import java.io.InputStream; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.concurrent.Executor; +import java.util.function.Consumer; /** * Interface to global information about an application environment. This is @@ -6407,6 +6410,43 @@ public abstract class Context { @Nullable String writePermission, int pid, int uid, @Intent.AccessUriMode int modeFlags, @Nullable String message); + + /** + * Triggers the asynchronous revocation of a permission. + * + * @param permName The name of the permission to be revoked. + * @see #selfRevokePermissions(Collection) + */ + public void selfRevokePermission(@NonNull String permName) { + selfRevokePermissions(Collections.singletonList(permName)); + } + + /** + * Triggers the revocation of one or more permissions for the calling package. A package is only + * able to revoke a permission under the following conditions: + * <ul> + * <li>Each permission in {@code permissions} must be granted to the calling package. + * <li>Each permission in {@code permissions} must be a runtime permission. + * </ul> + * <p> + * For every permission in {@code permissions}, the entire permission group it belongs to will + * be revoked. The revocation happens asynchronously and kills all processes running in the + * calling UID. It will be triggered once it is safe to do so. In particular, it will not be + * triggered as long as the package remains in the foreground, or has any active manifest + * components (e.g. when another app is accessing a content provider in the package). + * <p> + * If you want to revoke the permissions right away, you could call {@code System.exit()}, but + * this could affect other apps that are accessing your app at the moment. For example, apps + * accessing a content provider in your app will all crash. + * + * @param permissions Collection of permissions to be revoked. + * @see PackageManager#getGroupOfPlatformPermission(String, Executor, Consumer) + * @see PackageManager#getPlatformPermissionsForGroup(String, Executor, Consumer) + */ + public void selfRevokePermissions(@NonNull Collection<String> permissions) { + throw new AbstractMethodError("Must be overridden in implementing class"); + } + /** @hide */ @IntDef(flag = true, prefix = { "CONTEXT_" }, value = { CONTEXT_INCLUDE_CODE, diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index 3a02004edb1f..805e499bba46 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -53,6 +53,7 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.util.Collection; import java.util.List; import java.util.concurrent.Executor; @@ -1015,6 +1016,11 @@ public class ContextWrapper extends Context { } @Override + public void selfRevokePermissions(@NonNull Collection<String> permissions) { + mBase.selfRevokePermissions(permissions); + } + + @Override public Context createPackageContext(String packageName, int flags) throws PackageManager.NameNotFoundException { return mBase.createPackageContext(packageName, flags); diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index af84392b5169..74c326d9386e 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -4391,9 +4391,9 @@ public class Intent implements Parcelable, Cloneable { * restored from (corresponds to {@link android.os.Build.VERSION#SDK_INT}). The first three * values are represented as strings, the fourth one as int. * - * <p>This broadcast is sent only for settings provider entries known to require special handling - * around restore time. These entries are found in the BROADCAST_ON_RESTORE table within - * the provider's backup agent implementation. + * <p>This broadcast is sent only for settings provider entries known to require special + * handling around restore time to specific receivers. These entries are found in the + * BROADCAST_ON_RESTORE table within the provider's backup agent implementation. * * @see #EXTRA_SETTING_NAME * @see #EXTRA_SETTING_PREVIOUS_VALUE @@ -4401,15 +4401,46 @@ public class Intent implements Parcelable, Cloneable { * @see #EXTRA_SETTING_RESTORED_FROM_SDK_INT * {@hide} */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @SuppressLint("ActionValue") public static final String ACTION_SETTING_RESTORED = "android.os.action.SETTING_RESTORED"; - /** {@hide} */ + /** + * String intent extra to be used with {@link ACTION_SETTING_RESTORED}. + * Contain the name of the restored setting. + * {@hide} + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @SuppressLint("ActionValue") public static final String EXTRA_SETTING_NAME = "setting_name"; - /** {@hide} */ + + /** + * String intent extra to be used with {@link ACTION_SETTING_RESTORED}. + * Contain the value of the {@link EXTRA_SETTING_NAME} settings entry prior to the restore + * operation. + * {@hide} + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @SuppressLint("ActionValue") public static final String EXTRA_SETTING_PREVIOUS_VALUE = "previous_value"; - /** {@hide} */ + + /** + * String intent extra to be used with {@link ACTION_SETTING_RESTORED}. + * Contain the value of the {@link EXTRA_SETTING_NAME} settings entry being restored. + * {@hide} + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @SuppressLint("ActionValue") public static final String EXTRA_SETTING_NEW_VALUE = "new_value"; - /** {@hide} */ + + /** + * Int intent extra to be used with {@link ACTION_SETTING_RESTORED}. + * Contain the version of the SDK that the setting has been restored from (corresponds to + * {@link android.os.Build.VERSION#SDK_INT}). + * {@hide} + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @SuppressLint("ActionValue") public static final String EXTRA_SETTING_RESTORED_FROM_SDK_INT = "restored_from_sdk_int"; /** diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java index 32827ae11e0b..b3435b1180c2 100644 --- a/core/java/android/content/IntentFilter.java +++ b/core/java/android/content/IntentFilter.java @@ -1182,7 +1182,7 @@ public class IntentFilter implements Parcelable { public int match(Uri data, boolean wildcardSupported) { String host = data.getHost(); if (host == null) { - if (wildcardSupported && mWild) { + if (wildcardSupported && mWild && mHost.isEmpty()) { // special case, if no host is provided, but the Authority is wildcard, match return MATCH_CATEGORY_HOST; } else { diff --git a/core/java/android/content/PeriodicSync.java b/core/java/android/content/PeriodicSync.java index 6830f5f34e75..432e81bad019 100644 --- a/core/java/android/content/PeriodicSync.java +++ b/core/java/android/content/PeriodicSync.java @@ -84,7 +84,7 @@ public class PeriodicSync implements Parcelable { } private PeriodicSync(Parcel in) { - this.account = in.readParcelable(null, android.accounts.Account.class); + this.account = in.readParcelable(null); this.authority = in.readString(); this.extras = in.readBundle(); this.period = in.readLong(); diff --git a/core/java/android/content/RestrictionEntry.java b/core/java/android/content/RestrictionEntry.java index 63fcb49fff1b..8fd41f2c9e05 100644 --- a/core/java/android/content/RestrictionEntry.java +++ b/core/java/android/content/RestrictionEntry.java @@ -505,7 +505,7 @@ public class RestrictionEntry implements Parcelable { mChoiceValues = in.readStringArray(); mCurrentValue = in.readString(); mCurrentValues = in.readStringArray(); - Parcelable[] parcelables = in.readParcelableArray(null); + Parcelable[] parcelables = in.readParcelableArray(null, RestrictionEntry.class); if (parcelables != null) { mRestrictions = new RestrictionEntry[parcelables.length]; for (int i = 0; i < parcelables.length; i++) { diff --git a/core/java/android/content/SyncInfo.java b/core/java/android/content/SyncInfo.java index 57101be6507e..017a92b1e8bb 100644 --- a/core/java/android/content/SyncInfo.java +++ b/core/java/android/content/SyncInfo.java @@ -99,7 +99,7 @@ public class SyncInfo implements Parcelable { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) SyncInfo(Parcel parcel) { authorityId = parcel.readInt(); - account = parcel.readParcelable(Account.class.getClassLoader(), android.accounts.Account.class); + account = parcel.readParcelable(Account.class.getClassLoader()); authority = parcel.readString(); startTime = parcel.readLong(); } diff --git a/core/java/android/content/SyncRequest.java b/core/java/android/content/SyncRequest.java index 83ce84e7a5cb..e1e6f75d152f 100644 --- a/core/java/android/content/SyncRequest.java +++ b/core/java/android/content/SyncRequest.java @@ -174,7 +174,7 @@ public class SyncRequest implements Parcelable { mIsAuthority = (in.readInt() != 0); mIsExpedited = (in.readInt() != 0); mIsScheduledAsExpeditedJob = (in.readInt() != 0); - mAccountToSync = in.readParcelable(null, android.accounts.Account.class); + mAccountToSync = in.readParcelable(null); mAuthority = in.readString(); } diff --git a/core/java/android/content/UndoManager.java b/core/java/android/content/UndoManager.java index b2979f36e01b..87afbf874b37 100644 --- a/core/java/android/content/UndoManager.java +++ b/core/java/android/content/UndoManager.java @@ -777,7 +777,7 @@ public class UndoManager { final int N = p.readInt(); for (int i=0; i<N; i++) { UndoOwner owner = mManager.restoreOwner(p); - UndoOperation op = (UndoOperation)p.readParcelable(loader, android.content.UndoOperation.class); + UndoOperation op = (UndoOperation)p.readParcelable(loader); op.mOwner = owner; mOperations.add(op); } diff --git a/core/java/android/content/UriPermission.java b/core/java/android/content/UriPermission.java index 73477612985c..d3a9cb812539 100644 --- a/core/java/android/content/UriPermission.java +++ b/core/java/android/content/UriPermission.java @@ -47,7 +47,7 @@ public final class UriPermission implements Parcelable { /** {@hide} */ public UriPermission(Parcel in) { - mUri = in.readParcelable(null, android.net.Uri.class); + mUri = in.readParcelable(null); mModeFlags = in.readInt(); mPersistedTime = in.readLong(); } diff --git a/core/java/android/content/om/OverlayManagerTransaction.java b/core/java/android/content/om/OverlayManagerTransaction.java index 868dab298108..73be0ffbf467 100644 --- a/core/java/android/content/om/OverlayManagerTransaction.java +++ b/core/java/android/content/om/OverlayManagerTransaction.java @@ -67,7 +67,7 @@ public class OverlayManagerTransaction mRequests = new ArrayList<>(size); for (int i = 0; i < size; i++) { final int request = source.readInt(); - final OverlayIdentifier overlay = source.readParcelable(null, android.content.om.OverlayIdentifier.class); + final OverlayIdentifier overlay = source.readParcelable(null); final int userId = source.readInt(); final Bundle extras = source.readBundle(null); mRequests.add(new Request(request, overlay, userId, extras)); diff --git a/core/java/android/content/pm/BaseParceledListSlice.java b/core/java/android/content/pm/BaseParceledListSlice.java index 7bade740c9be..1e0deffbf8cd 100644 --- a/core/java/android/content/pm/BaseParceledListSlice.java +++ b/core/java/android/content/pm/BaseParceledListSlice.java @@ -50,10 +50,12 @@ abstract class BaseParceledListSlice<T> implements Parcelable { */ private static final int MAX_IPC_SIZE = IBinder.getSuggestedMaxIpcSizeBytes(); - private final List<T> mList; + private List<T> mList; private int mInlineCountLimit = Integer.MAX_VALUE; + private boolean mHasBeenParceled = false; + public BaseParceledListSlice(List<T> list) { mList = list; } @@ -151,9 +153,17 @@ abstract class BaseParceledListSlice<T> implements Parcelable { * Write this to another Parcel. Note that this discards the internal Parcel * and should not be used anymore. This is so we can pass this to a Binder * where we won't have a chance to call recycle on this. + * + * This method can only be called once per BaseParceledListSlice to ensure that + * the referenced list can be cleaned up before the recipient cleans up the + * Binder reference. */ @Override public void writeToParcel(Parcel dest, int flags) { + if (mHasBeenParceled) { + throw new IllegalStateException("Can't Parcel a ParceledListSlice more than once"); + } + mHasBeenParceled = true; final int N = mList.size(); final int callFlags = flags; dest.writeInt(N); @@ -180,9 +190,17 @@ abstract class BaseParceledListSlice<T> implements Parcelable { throws RemoteException { if (code != FIRST_CALL_TRANSACTION) { return super.onTransact(code, data, reply, flags); + } else if (mList == null) { + throw new IllegalArgumentException("Attempt to transfer null list, " + + "did transfer finish?"); } int i = data.readInt(); - if (DEBUG) Log.d(TAG, "Writing more @" + i + " of " + N); + + if (DEBUG) { + Log.d(TAG, "Writing more @" + i + " of " + N + " to " + + Binder.getCallingPid() + ", sender=" + this); + } + while (i < N && reply.dataSize() < MAX_IPC_SIZE) { reply.writeInt(1); @@ -196,6 +214,9 @@ abstract class BaseParceledListSlice<T> implements Parcelable { if (i < N) { if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N); reply.writeInt(0); + } else { + if (DEBUG) Log.d(TAG, "Transfer complete, clearing mList reference"); + mList = null; } return true; } diff --git a/core/java/android/content/pm/InstallSourceInfo.java b/core/java/android/content/pm/InstallSourceInfo.java index 84d2ca389611..a45bf7930509 100644 --- a/core/java/android/content/pm/InstallSourceInfo.java +++ b/core/java/android/content/pm/InstallSourceInfo.java @@ -61,7 +61,7 @@ public final class InstallSourceInfo implements Parcelable { private InstallSourceInfo(Parcel source) { mInitiatingPackageName = source.readString(); - mInitiatingPackageSigningInfo = source.readParcelable(SigningInfo.class.getClassLoader(), android.content.pm.SigningInfo.class); + mInitiatingPackageSigningInfo = source.readParcelable(SigningInfo.class.getClassLoader()); mOriginatingPackageName = source.readString(); mInstallingPackageName = source.readString(); } diff --git a/core/java/android/content/pm/InstantAppInfo.java b/core/java/android/content/pm/InstantAppInfo.java index d6cfb0e70693..24d6a07ec4e8 100644 --- a/core/java/android/content/pm/InstantAppInfo.java +++ b/core/java/android/content/pm/InstantAppInfo.java @@ -65,7 +65,7 @@ public final class InstantAppInfo implements Parcelable { mLabelText = parcel.readCharSequence(); mRequestedPermissions = parcel.readStringArray(); mGrantedPermissions = parcel.createStringArray(); - mApplicationInfo = parcel.readParcelable(null, android.content.pm.ApplicationInfo.class); + mApplicationInfo = parcel.readParcelable(null); } /** diff --git a/core/java/android/content/pm/InstantAppIntentFilter.java b/core/java/android/content/pm/InstantAppIntentFilter.java index 721b2616fbfd..123d2ba5aa8d 100644 --- a/core/java/android/content/pm/InstantAppIntentFilter.java +++ b/core/java/android/content/pm/InstantAppIntentFilter.java @@ -46,7 +46,7 @@ public final class InstantAppIntentFilter implements Parcelable { InstantAppIntentFilter(Parcel in) { mSplitName = in.readString(); - in.readList(mFilters, getClass().getClassLoader(), android.content.IntentFilter.class); + in.readList(mFilters, getClass().getClassLoader()); } public String getSplitName() { diff --git a/core/java/android/content/pm/InstantAppResolveInfo.java b/core/java/android/content/pm/InstantAppResolveInfo.java index 6124638ccbcb..98815647f0c3 100644 --- a/core/java/android/content/pm/InstantAppResolveInfo.java +++ b/core/java/android/content/pm/InstantAppResolveInfo.java @@ -140,7 +140,7 @@ public final class InstantAppResolveInfo implements Parcelable { mFilters = Collections.emptyList(); mVersionCode = -1; } else { - mDigest = in.readParcelable(null /*loader*/, android.content.pm.InstantAppResolveInfo.InstantAppDigest.class); + mDigest = in.readParcelable(null /*loader*/); mPackageName = in.readString(); mFilters = new ArrayList<>(); in.readTypedList(mFilters, InstantAppIntentFilter.CREATOR); diff --git a/core/java/android/content/pm/LauncherActivityInfoInternal.java b/core/java/android/content/pm/LauncherActivityInfoInternal.java index 46c415df7525..417f168940b6 100644 --- a/core/java/android/content/pm/LauncherActivityInfoInternal.java +++ b/core/java/android/content/pm/LauncherActivityInfoInternal.java @@ -43,10 +43,10 @@ public class LauncherActivityInfoInternal implements Parcelable { } public LauncherActivityInfoInternal(Parcel source) { - mActivityInfo = source.readParcelable(ActivityInfo.class.getClassLoader(), android.content.pm.ActivityInfo.class); + mActivityInfo = source.readParcelable(ActivityInfo.class.getClassLoader()); mComponentName = new ComponentName(mActivityInfo.packageName, mActivityInfo.name); mIncrementalStatesInfo = source.readParcelable( - IncrementalStatesInfo.class.getClassLoader(), android.content.pm.IncrementalStatesInfo.class); + IncrementalStatesInfo.class.getClassLoader()); } public ComponentName getComponentName() { diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 495100b0ae52..d76dc782c367 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -1754,11 +1754,11 @@ public class PackageInstaller { installScenario = source.readInt(); sizeBytes = source.readLong(); appPackageName = source.readString(); - appIcon = source.readParcelable(null, android.graphics.Bitmap.class); + appIcon = source.readParcelable(null); appLabel = source.readString(); - originatingUri = source.readParcelable(null, android.net.Uri.class); + originatingUri = source.readParcelable(null); originatingUid = source.readInt(); - referrerUri = source.readParcelable(null, android.net.Uri.class); + referrerUri = source.readParcelable(null); abiOverride = source.readString(); volumeUuid = source.readString(); grantedRuntimePermissions = source.readStringArray(); @@ -1770,7 +1770,7 @@ public class PackageInstaller { forceQueryableOverride = source.readBoolean(); requiredInstalledVersionCode = source.readLong(); DataLoaderParamsParcel dataLoaderParamsParcel = source.readParcelable( - DataLoaderParamsParcel.class.getClassLoader(), android.content.pm.DataLoaderParamsParcel.class); + DataLoaderParamsParcel.class.getClassLoader()); if (dataLoaderParamsParcel != null) { dataLoaderParams = new DataLoaderParams(dataLoaderParamsParcel); } @@ -2367,43 +2367,73 @@ public class PackageInstaller { private static final int[] NO_SESSIONS = {}; /** @hide */ - @IntDef(prefix = { "STAGED_SESSION_" }, value = { - STAGED_SESSION_NO_ERROR, - STAGED_SESSION_VERIFICATION_FAILED, - STAGED_SESSION_ACTIVATION_FAILED, - STAGED_SESSION_UNKNOWN, - STAGED_SESSION_CONFLICT}) + @IntDef(prefix = { "SESSION_" }, value = { + SESSION_NO_ERROR, + SESSION_VERIFICATION_FAILED, + SESSION_ACTIVATION_FAILED, + SESSION_UNKNOWN_ERROR, + SESSION_CONFLICT}) @Retention(RetentionPolicy.SOURCE) public @interface SessionErrorCode {} /** - * Constant indicating that no error occurred during the preparation or the activation of - * this staged session. + * @deprecated use {@link #SESSION_NO_ERROR}. */ + @Deprecated public static final int STAGED_SESSION_NO_ERROR = 0; /** - * Constant indicating that an error occurred during the verification phase (pre-reboot) of - * this staged session. + * @deprecated use {@link #SESSION_VERIFICATION_FAILED}. */ + @Deprecated public static final int STAGED_SESSION_VERIFICATION_FAILED = 1; /** - * Constant indicating that an error occurred during the activation phase (post-reboot) of - * this staged session. + * @deprecated use {@link #SESSION_ACTIVATION_FAILED}. */ + @Deprecated public static final int STAGED_SESSION_ACTIVATION_FAILED = 2; /** - * Constant indicating that an unknown error occurred while processing this staged session. + * @deprecated use {@link #SESSION_UNKNOWN_ERROR}. */ + @Deprecated public static final int STAGED_SESSION_UNKNOWN = 3; /** - * Constant indicating that the session was in conflict with another staged session and had - * to be sacrificed for resolution. + * @deprecated use {@link #SESSION_CONFLICT}. */ + @Deprecated public static final int STAGED_SESSION_CONFLICT = 4; + /** + * Constant indicating that no error occurred during the preparation or the activation of + * this session. + */ + public static final int SESSION_NO_ERROR = 0; + + /** + * Constant indicating that an error occurred during the verification phase of + * this session. + */ + public static final int SESSION_VERIFICATION_FAILED = 1; + + /** + * Constant indicating that an error occurred during the activation phase of + * this session. + */ + public static final int SESSION_ACTIVATION_FAILED = 2; + + /** + * Constant indicating that an unknown error occurred while processing this session. + */ + public static final int SESSION_UNKNOWN_ERROR = 3; + + /** + * Constant indicating that the session was in conflict with another session and had + * to be sacrificed for resolution. + */ + public static final int SESSION_CONFLICT = 4; + private static String userActionToString(int requireUserAction) { switch(requireUserAction) { case SessionParams.USER_ACTION_REQUIRED: @@ -2533,13 +2563,13 @@ public class PackageInstaller { installScenario = source.readInt(); sizeBytes = source.readLong(); appPackageName = source.readString(); - appIcon = source.readParcelable(null, android.graphics.Bitmap.class); + appIcon = source.readParcelable(null); appLabel = source.readString(); installLocation = source.readInt(); - originatingUri = source.readParcelable(null, android.net.Uri.class); + originatingUri = source.readParcelable(null); originatingUid = source.readInt(); - referrerUri = source.readParcelable(null, android.net.Uri.class); + referrerUri = source.readParcelable(null); grantedRuntimePermissions = source.readStringArray(); whitelistedRestrictedPermissions = source.createStringArrayList(); autoRevokePermissionsMode = source.readInt(); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 819cbb0f347d..a6d846b43396 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -4064,6 +4064,15 @@ public abstract class PackageManager { @TestApi public static final String FEATURE_COMMUNAL_MODE = "android.software.communal_mode"; + /** + * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device + * supports dream overlay feature, which is an informational layer shown on top of dreams. + * + * @hide + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_DREAM_OVERLAY = "android.software.dream_overlay"; + /** @hide */ public static final boolean APP_ENUMERATION_ENABLED_BY_DEFAULT = true; @@ -4221,6 +4230,17 @@ public abstract class PackageManager { "android.content.pm.action.REQUEST_PERMISSIONS"; /** + * The action used to request that the user approve a permission request + * from the application. Sent from an application other than the one whose permissions + * will be granted. Can only be used by the system server. + * + * @hide + */ + @SystemApi + public static final String ACTION_REQUEST_PERMISSIONS_FOR_OTHER = + "android.content.pm.action.REQUEST_PERMISSIONS_FOR_OTHER"; + + /** * The names of the requested permissions. * <p> * <strong>Type:</strong> String[] @@ -4328,8 +4348,9 @@ public abstract class PackageManager { public static final int FLAG_PERMISSION_GRANTED_BY_DEFAULT = 1 << 5; /** - * Permission flag: The permission has to be reviewed before any of - * the app components can run. + * Permission flag: If app targetSDK < M, then the permission has to be reviewed before any of + * the app components can run. If app targetSDK >= M, then the system might need to show a + * request dialog for this permission on behalf of an app. * * @hide */ diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index f31f78fb81f3..e2c91a4b1bea 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -7297,7 +7297,7 @@ public class PackageParser { splitFlags = dest.createIntArray(); splitPrivateFlags = dest.createIntArray(); baseHardwareAccelerated = (dest.readInt() == 1); - applicationInfo = dest.readParcelable(boot, android.content.pm.ApplicationInfo.class); + applicationInfo = dest.readParcelable(boot); if (applicationInfo.permission != null) { applicationInfo.permission = applicationInfo.permission.intern(); } @@ -7305,19 +7305,19 @@ public class PackageParser { // We don't serialize the "owner" package and the application info object for each of // these components, in order to save space and to avoid circular dependencies while // serialization. We need to fix them all up here. - dest.readParcelableList(permissions, boot, android.content.pm.PackageParser.Permission.class); + dest.readParcelableList(permissions, boot); fixupOwner(permissions); - dest.readParcelableList(permissionGroups, boot, android.content.pm.PackageParser.PermissionGroup.class); + dest.readParcelableList(permissionGroups, boot); fixupOwner(permissionGroups); - dest.readParcelableList(activities, boot, android.content.pm.PackageParser.Activity.class); + dest.readParcelableList(activities, boot); fixupOwner(activities); - dest.readParcelableList(receivers, boot, android.content.pm.PackageParser.Activity.class); + dest.readParcelableList(receivers, boot); fixupOwner(receivers); - dest.readParcelableList(providers, boot, android.content.pm.PackageParser.Provider.class); + dest.readParcelableList(providers, boot); fixupOwner(providers); - dest.readParcelableList(services, boot, android.content.pm.PackageParser.Service.class); + dest.readParcelableList(services, boot); fixupOwner(services); - dest.readParcelableList(instrumentation, boot, android.content.pm.PackageParser.Instrumentation.class); + dest.readParcelableList(instrumentation, boot); fixupOwner(instrumentation); dest.readStringList(requestedPermissions); @@ -7327,10 +7327,10 @@ public class PackageParser { protectedBroadcasts = dest.createStringArrayList(); internStringArrayList(protectedBroadcasts); - parentPackage = dest.readParcelable(boot, android.content.pm.PackageParser.Package.class); + parentPackage = dest.readParcelable(boot); childPackages = new ArrayList<>(); - dest.readParcelableList(childPackages, boot, android.content.pm.PackageParser.Package.class); + dest.readParcelableList(childPackages, boot); if (childPackages.size() == 0) { childPackages = null; } @@ -7364,7 +7364,7 @@ public class PackageParser { } preferredActivityFilters = new ArrayList<>(); - dest.readParcelableList(preferredActivityFilters, boot, android.content.pm.PackageParser.ActivityIntentInfo.class); + dest.readParcelableList(preferredActivityFilters, boot); if (preferredActivityFilters.size() == 0) { preferredActivityFilters = null; } @@ -7385,7 +7385,7 @@ public class PackageParser { } mSharedUserLabel = dest.readInt(); - mSigningDetails = dest.readParcelable(boot, android.content.pm.PackageParser.SigningDetails.class); + mSigningDetails = dest.readParcelable(boot); mPreferredOrder = dest.readInt(); @@ -7397,19 +7397,19 @@ public class PackageParser { configPreferences = new ArrayList<>(); - dest.readParcelableList(configPreferences, boot, android.content.pm.ConfigurationInfo.class); + dest.readParcelableList(configPreferences, boot); if (configPreferences.size() == 0) { configPreferences = null; } reqFeatures = new ArrayList<>(); - dest.readParcelableList(reqFeatures, boot, android.content.pm.FeatureInfo.class); + dest.readParcelableList(reqFeatures, boot); if (reqFeatures.size() == 0) { reqFeatures = null; } featureGroups = new ArrayList<>(); - dest.readParcelableList(featureGroups, boot, android.content.pm.FeatureGroupInfo.class); + dest.readParcelableList(featureGroups, boot); if (featureGroups.size() == 0) { featureGroups = null; } @@ -7806,13 +7806,13 @@ public class PackageParser { private Permission(Parcel in) { super(in); final ClassLoader boot = Object.class.getClassLoader(); - info = in.readParcelable(boot, android.content.pm.PermissionInfo.class); + info = in.readParcelable(boot); if (info.group != null) { info.group = info.group.intern(); } tree = (in.readInt() == 1); - group = in.readParcelable(boot, android.content.pm.PackageParser.PermissionGroup.class); + group = in.readParcelable(boot); } public static final Parcelable.Creator CREATOR = new Parcelable.Creator<Permission>() { @@ -7867,7 +7867,7 @@ public class PackageParser { private PermissionGroup(Parcel in) { super(in); - info = in.readParcelable(Object.class.getClassLoader(), android.content.pm.PermissionGroupInfo.class); + info = in.readParcelable(Object.class.getClassLoader()); } public static final Parcelable.Creator CREATOR = new Parcelable.Creator<PermissionGroup>() { @@ -8160,7 +8160,7 @@ public class PackageParser { private Activity(Parcel in) { super(in); - info = in.readParcelable(Object.class.getClassLoader(), android.content.pm.ActivityInfo.class); + info = in.readParcelable(Object.class.getClassLoader()); mHasMaxAspectRatio = in.readBoolean(); mHasMinAspectRatio = in.readBoolean(); @@ -8254,7 +8254,7 @@ public class PackageParser { private Service(Parcel in) { super(in); - info = in.readParcelable(Object.class.getClassLoader(), android.content.pm.ServiceInfo.class); + info = in.readParcelable(Object.class.getClassLoader()); for (ServiceIntentInfo aii : intents) { aii.service = this; @@ -8344,7 +8344,7 @@ public class PackageParser { private Provider(Parcel in) { super(in); - info = in.readParcelable(Object.class.getClassLoader(), android.content.pm.ProviderInfo.class); + info = in.readParcelable(Object.class.getClassLoader()); syncable = (in.readInt() == 1); for (ProviderIntentInfo aii : intents) { @@ -8436,7 +8436,7 @@ public class PackageParser { private Instrumentation(Parcel in) { super(in); - info = in.readParcelable(Object.class.getClassLoader(), android.content.pm.InstrumentationInfo.class); + info = in.readParcelable(Object.class.getClassLoader()); if (info.targetPackage != null) { info.targetPackage = info.targetPackage.intern(); diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java index 7696cbe0b631..78984bda24a7 100644 --- a/core/java/android/content/pm/RegisteredServicesCache.java +++ b/core/java/android/content/pm/RegisteredServicesCache.java @@ -174,7 +174,7 @@ public abstract class RegisteredServicesCache<V> { // Register for user-related events IntentFilter userFilter = new IntentFilter(); - sdFilter.addAction(Intent.ACTION_USER_REMOVED); + userFilter.addAction(Intent.ACTION_USER_REMOVED); mContext.registerReceiver(mUserRemovedReceiver, userFilter, null, handler); } diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java index 3443c757e6bc..f153566bf61a 100644 --- a/core/java/android/content/pm/SharedLibraryInfo.java +++ b/core/java/android/content/pm/SharedLibraryInfo.java @@ -34,9 +34,10 @@ import java.util.Objects; /** * This class provides information for a shared library. There are - * three types of shared libraries: builtin - non-updatable part of + * four types of shared libraries: builtin - non-updatable part of * the OS; dynamic - updatable backwards-compatible dynamically linked; - * static - non backwards-compatible emulating static linking. + * static - non backwards-compatible emulating static linking; + * SDK - updatable backwards-incompatible dynamically loaded. */ public final class SharedLibraryInfo implements Parcelable { @@ -45,6 +46,7 @@ public final class SharedLibraryInfo implements Parcelable { TYPE_BUILTIN, TYPE_DYNAMIC, TYPE_STATIC, + TYPE_SDK, }) @Retention(RetentionPolicy.SOURCE) @interface Type{} @@ -134,8 +136,8 @@ public final class SharedLibraryInfo implements Parcelable { mName = parcel.readString8(); mVersion = parcel.readLong(); mType = parcel.readInt(); - mDeclaringPackage = parcel.readParcelable(null, android.content.pm.VersionedPackage.class); - mDependentPackages = parcel.readArrayList(null, android.content.pm.VersionedPackage.class); + mDeclaringPackage = parcel.readParcelable(null); + mDependentPackages = parcel.readArrayList(null); mDependencies = parcel.createTypedArrayList(SharedLibraryInfo.CREATOR); mIsNative = parcel.readBoolean(); } diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java index 7d4f7ecef29c..613fb84812f8 100644 --- a/core/java/android/content/pm/ShortcutInfo.java +++ b/core/java/android/content/pm/ShortcutInfo.java @@ -2182,7 +2182,7 @@ public final class ShortcutInfo implements Parcelable { mUserId = source.readInt(); mId = source.readString8(); mPackageName = source.readString8(); - mActivity = source.readParcelable(cl, android.content.ComponentName.class); + mActivity = source.readParcelable(cl); mFlags = source.readInt(); mIconResId = source.readInt(); mLastChangedTimestamp = source.readLong(); @@ -2192,7 +2192,7 @@ public final class ShortcutInfo implements Parcelable { return; // key information only. } - mIcon = source.readParcelable(cl, android.graphics.drawable.Icon.class); + mIcon = source.readParcelable(cl); mTitle = source.readCharSequence(); mTitleResId = source.readInt(); mText = source.readCharSequence(); @@ -2202,7 +2202,7 @@ public final class ShortcutInfo implements Parcelable { mIntents = source.readParcelableArray(cl, Intent.class); mIntentPersistableExtrases = source.readParcelableArray(cl, PersistableBundle.class); mRank = source.readInt(); - mExtras = source.readParcelable(cl, android.os.PersistableBundle.class); + mExtras = source.readParcelable(cl); mBitmapPath = source.readString8(); mIconResName = source.readString8(); @@ -2221,7 +2221,7 @@ public final class ShortcutInfo implements Parcelable { } mPersons = source.readParcelableArray(cl, Person.class); - mLocusId = source.readParcelable(cl, android.content.LocusId.class); + mLocusId = source.readParcelable(cl); mIconUri = source.readString8(); mStartingThemeResName = source.readString8(); mExcludedSurfaces = source.readInt(); diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java index 7dbfd08310be..be0d934f5133 100644 --- a/core/java/android/content/pm/ShortcutManager.java +++ b/core/java/android/content/pm/ShortcutManager.java @@ -704,8 +704,8 @@ public class ShortcutManager { } private ShareShortcutInfo(@NonNull Parcel in) { - mShortcutInfo = in.readParcelable(ShortcutInfo.class.getClassLoader(), android.content.pm.ShortcutInfo.class); - mTargetComponent = in.readParcelable(ComponentName.class.getClassLoader(), android.content.ComponentName.class); + mShortcutInfo = in.readParcelable(ShortcutInfo.class.getClassLoader()); + mTargetComponent = in.readParcelable(ComponentName.class.getClassLoader()); } @NonNull diff --git a/core/java/android/content/pm/ShortcutQueryWrapper.java b/core/java/android/content/pm/ShortcutQueryWrapper.java index 64337d86f7ec..c6134416adbc 100644 --- a/core/java/android/content/pm/ShortcutQueryWrapper.java +++ b/core/java/android/content/pm/ShortcutQueryWrapper.java @@ -143,7 +143,7 @@ public final class ShortcutQueryWrapper extends LauncherApps.ShortcutQuery imple List<LocusId> locusIds = null; if ((flg & 0x8) != 0) { locusIds = new ArrayList<>(); - in.readParcelableList(locusIds, LocusId.class.getClassLoader(), android.content.LocusId.class); + in.readParcelableList(locusIds, LocusId.class.getClassLoader()); } ComponentName activity = (flg & 0x10) == 0 ? null : (ComponentName) in.readTypedObject(ComponentName.CREATOR); diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING index 77b8be351bd1..d04c97c1e915 100644 --- a/core/java/android/content/pm/TEST_MAPPING +++ b/core/java/android/content/pm/TEST_MAPPING @@ -37,6 +37,9 @@ "name": "CarrierAppIntegrationTestCases" }, { + "name": "ApkVerityTest" + }, + { "name": "CtsIncrementalInstallHostTestCases", "options": [ { @@ -135,6 +138,9 @@ "include-filter": "android.appsecurity.cts.AppSecurityTests#testPermissionDiffCert" } ] + }, + { + "name": "CtsInstallHostTestCases" } ] } diff --git a/core/java/android/content/pm/VerifierInfo.java b/core/java/android/content/pm/VerifierInfo.java index 868bb9cb995c..3e69ff555946 100644 --- a/core/java/android/content/pm/VerifierInfo.java +++ b/core/java/android/content/pm/VerifierInfo.java @@ -59,7 +59,7 @@ public class VerifierInfo implements Parcelable { private VerifierInfo(Parcel source) { packageName = source.readString(); - publicKey = (PublicKey) source.readSerializable(java.security.PublicKey.class.getClassLoader(), java.security.PublicKey.class); + publicKey = (PublicKey) source.readSerializable(); } @Override diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java index 23cae4c04467..ddab207437c2 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java +++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java @@ -1408,7 +1408,7 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden, this.processes = in.readHashMap(boot); this.metaData = in.readBundle(boot); this.volumeUuid = sForInternedString.unparcel(in); - this.signingDetails = in.readParcelable(boot, android.content.pm.SigningDetails.class); + this.signingDetails = in.readParcelable(boot); this.mPath = in.readString(); this.queriesIntents = in.createTypedArrayList(Intent.CREATOR); this.queriesPackages = sForInternedStringList.unparcel(in); diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java index f336672ffefa..e02eb7cca090 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java @@ -3519,7 +3519,7 @@ public class ParsingPackageUtils { ArraySet<PublicKey> keys = new ArraySet<>(M); for (int j = 0; j < M; ++j) { - PublicKey pk = (PublicKey) in.readSerializable(java.security.PublicKey.class.getClassLoader(), java.security.PublicKey.class); + PublicKey pk = (PublicKey) in.readSerializable(); keys.add(pk); } diff --git a/core/java/android/content/pm/parsing/ParsingUtils.java b/core/java/android/content/pm/parsing/ParsingUtils.java index 6dfb268c3d64..cce984eb93a7 100644 --- a/core/java/android/content/pm/parsing/ParsingUtils.java +++ b/core/java/android/content/pm/parsing/ParsingUtils.java @@ -138,7 +138,7 @@ public class ParsingUtils { final List<Pair<String, ParsedIntentInfo>> list = new ArrayList<>(size); for (int i = 0; i < size; ++i) { list.add(Pair.create(source.readString(), source.readParcelable( - ParsedIntentInfoImpl.class.getClassLoader(), android.content.pm.parsing.component.ParsedIntentInfo.class))); + ParsedIntentInfoImpl.class.getClassLoader()))); } return list; diff --git a/core/java/android/content/pm/parsing/component/ParsedPermissionImpl.java b/core/java/android/content/pm/parsing/component/ParsedPermissionImpl.java index 45038cf225fb..2145e441553a 100644 --- a/core/java/android/content/pm/parsing/component/ParsedPermissionImpl.java +++ b/core/java/android/content/pm/parsing/component/ParsedPermissionImpl.java @@ -116,7 +116,7 @@ public class ParsedPermissionImpl extends ParsedComponentImpl implements ParsedP this.requestRes = in.readInt(); this.protectionLevel = in.readInt(); this.tree = in.readBoolean(); - this.parsedPermissionGroup = in.readParcelable(boot, android.content.pm.parsing.component.ParsedPermissionGroup.class); + this.parsedPermissionGroup = in.readParcelable(boot); this.knownCerts = sForStringSet.unparcel(in); } diff --git a/core/java/android/content/res/TEST_MAPPING b/core/java/android/content/res/TEST_MAPPING index 535afd361f01..3703f2e2152b 100644 --- a/core/java/android/content/res/TEST_MAPPING +++ b/core/java/android/content/res/TEST_MAPPING @@ -1,4 +1,12 @@ { + "imports": [ + { + "path": "frameworks/base/core/tests/coretests/src/android/content/res" + }, + { + "path": "frameworks/base/core/tests/coretests/src/com/android/internal/content/res" + } + ], "presubmit": [ { "name": "CtsResourcesLoaderTests" diff --git a/core/java/android/debug/AdbManager.java b/core/java/android/debug/AdbManager.java index 7714dd80f910..243f80187185 100644 --- a/core/java/android/debug/AdbManager.java +++ b/core/java/android/debug/AdbManager.java @@ -38,6 +38,7 @@ public class AdbManager { * * @hide */ + @RequiresPermission(android.Manifest.permission.MANAGE_DEBUGGING) public static final String WIRELESS_DEBUG_STATE_CHANGED_ACTION = "com.android.server.adb.WIRELESS_DEBUG_STATUS"; @@ -46,6 +47,7 @@ public class AdbManager { * * @hide */ + @RequiresPermission(android.Manifest.permission.MANAGE_DEBUGGING) public static final String WIRELESS_DEBUG_PAIRED_DEVICES_ACTION = "com.android.server.adb.WIRELESS_DEBUG_PAIRED_DEVICES"; @@ -59,6 +61,7 @@ public class AdbManager { * * @hide */ + @RequiresPermission(android.Manifest.permission.MANAGE_DEBUGGING) public static final String WIRELESS_DEBUG_PAIRING_RESULT_ACTION = "com.android.server.adb.WIRELESS_DEBUG_PAIRING_RESULT"; diff --git a/core/java/android/graphics/fonts/FontUpdateRequest.java b/core/java/android/graphics/fonts/FontUpdateRequest.java index dae09f0977a4..cda1638a24dc 100644 --- a/core/java/android/graphics/fonts/FontUpdateRequest.java +++ b/core/java/android/graphics/fonts/FontUpdateRequest.java @@ -235,7 +235,7 @@ public final class FontUpdateRequest implements Parcelable { public Family createFromParcel(Parcel source) { String familyName = source.readString8(); List<Font> fonts = source.readParcelableList( - new ArrayList<>(), Font.class.getClassLoader(), android.graphics.fonts.FontUpdateRequest.Font.class); + new ArrayList<>(), Font.class.getClassLoader()); return new Family(familyName, fonts); } @@ -379,9 +379,9 @@ public final class FontUpdateRequest implements Parcelable { protected FontUpdateRequest(Parcel in) { mType = in.readInt(); - mFd = in.readParcelable(ParcelFileDescriptor.class.getClassLoader(), android.os.ParcelFileDescriptor.class); + mFd = in.readParcelable(ParcelFileDescriptor.class.getClassLoader()); mSignature = in.readBlob(); - mFontFamily = in.readParcelable(FontConfig.FontFamily.class.getClassLoader(), android.graphics.fonts.FontUpdateRequest.Family.class); + mFontFamily = in.readParcelable(FontConfig.FontFamily.class.getClassLoader()); } public @Type int getType() { diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java index 0c03948e5368..e6b762a64384 100644 --- a/core/java/android/hardware/biometrics/PromptInfo.java +++ b/core/java/android/hardware/biometrics/PromptInfo.java @@ -65,7 +65,7 @@ public class PromptInfo implements Parcelable { mAuthenticators = in.readInt(); mDisallowBiometricsIfPolicyExists = in.readBoolean(); mReceiveSystemEvents = in.readBoolean(); - mAllowedSensorIds = in.readArrayList(Integer.class.getClassLoader(), java.lang.Integer.class); + mAllowedSensorIds = in.readArrayList(Integer.class.getClassLoader()); mAllowBackgroundAuthentication = in.readBoolean(); mIgnoreEnrollmentState = in.readBoolean(); } diff --git a/core/java/android/hardware/biometrics/SensorPropertiesInternal.java b/core/java/android/hardware/biometrics/SensorPropertiesInternal.java index 1490ea1592a5..f365ee6066d0 100644 --- a/core/java/android/hardware/biometrics/SensorPropertiesInternal.java +++ b/core/java/android/hardware/biometrics/SensorPropertiesInternal.java @@ -60,7 +60,7 @@ public class SensorPropertiesInternal implements Parcelable { sensorStrength = in.readInt(); maxEnrollmentsPerUser = in.readInt(); componentInfo = new ArrayList<>(); - in.readList(componentInfo, ComponentInfoInternal.class.getClassLoader(), android.hardware.biometrics.ComponentInfoInternal.class); + in.readList(componentInfo, ComponentInfoInternal.class.getClassLoader()); resetLockoutRequiresHardwareAuthToken = in.readBoolean(); resetLockoutRequiresChallenge = in.readBoolean(); } diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index 86ae3a311c9b..5df64e3cca9e 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -599,7 +599,8 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> synchronized (mSurfacesLock) { mSurfaceSet.clear(); - Parcelable[] parcelableArray = in.readParcelableArray(Surface.class.getClassLoader()); + Parcelable[] parcelableArray = in.readParcelableArray(Surface.class.getClassLoader(), + Surface.class); if (parcelableArray != null) { for (Parcelable p : parcelableArray) { Surface s = (Surface) p; diff --git a/core/java/android/hardware/face/FaceAuthenticationFrame.java b/core/java/android/hardware/face/FaceAuthenticationFrame.java index a53aad74d4e0..f39d63411825 100644 --- a/core/java/android/hardware/face/FaceAuthenticationFrame.java +++ b/core/java/android/hardware/face/FaceAuthenticationFrame.java @@ -46,7 +46,7 @@ public final class FaceAuthenticationFrame implements Parcelable { } private FaceAuthenticationFrame(@NonNull Parcel source) { - mData = source.readParcelable(FaceDataFrame.class.getClassLoader(), android.hardware.face.FaceDataFrame.class); + mData = source.readParcelable(FaceDataFrame.class.getClassLoader()); } @Override diff --git a/core/java/android/hardware/face/FaceEnrollFrame.java b/core/java/android/hardware/face/FaceEnrollFrame.java index bbccee2e2c3d..822a57944449 100644 --- a/core/java/android/hardware/face/FaceEnrollFrame.java +++ b/core/java/android/hardware/face/FaceEnrollFrame.java @@ -73,9 +73,9 @@ public final class FaceEnrollFrame implements Parcelable { } private FaceEnrollFrame(@NonNull Parcel source) { - mCell = source.readParcelable(FaceEnrollCell.class.getClassLoader(), android.hardware.face.FaceEnrollCell.class); + mCell = source.readParcelable(FaceEnrollCell.class.getClassLoader()); mStage = source.readInt(); - mData = source.readParcelable(FaceDataFrame.class.getClassLoader(), android.hardware.face.FaceDataFrame.class); + mData = source.readParcelable(FaceDataFrame.class.getClassLoader()); } @Override diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java index b97055976e3e..56f81423db4e 100644 --- a/core/java/android/hardware/face/FaceManager.java +++ b/core/java/android/hardware/face/FaceManager.java @@ -306,21 +306,22 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan throw new IllegalArgumentException("Must supply an enrollment callback"); } - if (cancel != null && cancel.isCanceled()) { - Slog.w(TAG, "enrollment already canceled"); - return; + if (cancel != null) { + if (cancel.isCanceled()) { + Slog.w(TAG, "enrollment already canceled"); + return; + } else { + cancel.setOnCancelListener(new OnEnrollCancelListener()); + } } if (mService != null) { try { mEnrollmentCallback = callback; Trace.beginSection("FaceManager#enroll"); - final long enrollId = mService.enroll(userId, mToken, hardwareAuthToken, - mServiceReceiver, mContext.getOpPackageName(), disabledFeatures, - previewSurface, debugConsent); - if (cancel != null) { - cancel.setOnCancelListener(new OnEnrollCancelListener(enrollId)); - } + mService.enroll(userId, mToken, hardwareAuthToken, mServiceReceiver, + mContext.getOpPackageName(), disabledFeatures, previewSurface, + debugConsent); } catch (RemoteException e) { Slog.w(TAG, "Remote exception in enroll: ", e); // Though this may not be a hardware issue, it will cause apps to give up or @@ -358,20 +359,21 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan throw new IllegalArgumentException("Must supply an enrollment callback"); } - if (cancel != null && cancel.isCanceled()) { - Slog.w(TAG, "enrollRemotely is already canceled."); - return; + if (cancel != null) { + if (cancel.isCanceled()) { + Slog.w(TAG, "enrollRemotely is already canceled."); + return; + } else { + cancel.setOnCancelListener(new OnEnrollCancelListener()); + } } if (mService != null) { try { mEnrollmentCallback = callback; Trace.beginSection("FaceManager#enrollRemotely"); - final long enrolId = mService.enrollRemotely(userId, mToken, hardwareAuthToken, - mServiceReceiver, mContext.getOpPackageName(), disabledFeatures); - if (cancel != null) { - cancel.setOnCancelListener(new OnEnrollCancelListener(enrolId)); - } + mService.enrollRemotely(userId, mToken, hardwareAuthToken, mServiceReceiver, + mContext.getOpPackageName(), disabledFeatures); } catch (RemoteException e) { Slog.w(TAG, "Remote exception in enrollRemotely: ", e); // Though this may not be a hardware issue, it will cause apps to give up or @@ -711,10 +713,10 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan } } - private void cancelEnrollment(long requestId) { + private void cancelEnrollment() { if (mService != null) { try { - mService.cancelEnrollment(mToken, requestId); + mService.cancelEnrollment(mToken); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1098,16 +1100,9 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan } private class OnEnrollCancelListener implements OnCancelListener { - private final long mAuthRequestId; - - private OnEnrollCancelListener(long id) { - mAuthRequestId = id; - } - @Override public void onCancel() { - Slog.d(TAG, "Cancel face enrollment requested for: " + mAuthRequestId); - cancelEnrollment(mAuthRequestId); + cancelEnrollment(); } } diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl index 989b001ca8bf..e9198246dee3 100644 --- a/core/java/android/hardware/face/IFaceService.aidl +++ b/core/java/android/hardware/face/IFaceService.aidl @@ -76,16 +76,15 @@ interface IFaceService { void cancelAuthenticationFromService(int sensorId, IBinder token, String opPackageName, long requestId); // Start face enrollment - long enroll(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver, - String opPackageName, in int [] disabledFeatures, - in Surface previewSurface, boolean debugConsent); + void enroll(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver, + String opPackageName, in int [] disabledFeatures, in Surface previewSurface, boolean debugConsent); // Start remote face enrollment - long enrollRemotely(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver, + void enrollRemotely(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver, String opPackageName, in int [] disabledFeatures); // Cancel enrollment in progress - void cancelEnrollment(IBinder token, long requestId); + void cancelEnrollment(IBinder token); // Removes the specified face enrollment for the specified userId. void remove(IBinder token, int faceId, int userId, IFaceServiceReceiver receiver, diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index acf9427b1241..fe04e5d35784 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -183,16 +183,9 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing } private class OnEnrollCancelListener implements OnCancelListener { - private final long mAuthRequestId; - - private OnEnrollCancelListener(long id) { - mAuthRequestId = id; - } - @Override public void onCancel() { - Slog.d(TAG, "Cancel fingerprint enrollment requested for: " + mAuthRequestId); - cancelEnrollment(mAuthRequestId); + cancelEnrollment(); } } @@ -653,19 +646,20 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing throw new IllegalArgumentException("Must supply an enrollment callback"); } - if (cancel != null && cancel.isCanceled()) { - Slog.w(TAG, "enrollment already canceled"); - return; + if (cancel != null) { + if (cancel.isCanceled()) { + Slog.w(TAG, "enrollment already canceled"); + return; + } else { + cancel.setOnCancelListener(new OnEnrollCancelListener()); + } } if (mService != null) { try { mEnrollmentCallback = callback; - final long enrollId = mService.enroll(mToken, hardwareAuthToken, userId, - mServiceReceiver, mContext.getOpPackageName(), enrollReason); - if (cancel != null) { - cancel.setOnCancelListener(new OnEnrollCancelListener(enrollId)); - } + mService.enroll(mToken, hardwareAuthToken, userId, mServiceReceiver, + mContext.getOpPackageName(), enrollReason); } catch (RemoteException e) { Slog.w(TAG, "Remote exception in enroll: ", e); // Though this may not be a hardware issue, it will cause apps to give up or try @@ -1308,9 +1302,9 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing return allSensors.isEmpty() ? null : allSensors.get(0); } - private void cancelEnrollment(long requestId) { + private void cancelEnrollment() { if (mService != null) try { - mService.cancelEnrollment(mToken, requestId); + mService.cancelEnrollment(mToken); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index cbff8b11a72a..ba1dc6da62a6 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -84,11 +84,11 @@ interface IFingerprintService { void cancelAuthenticationFromService(int sensorId, IBinder token, String opPackageName, long requestId); // Start fingerprint enrollment - long enroll(IBinder token, in byte [] hardwareAuthToken, int userId, IFingerprintServiceReceiver receiver, + void enroll(IBinder token, in byte [] hardwareAuthToken, int userId, IFingerprintServiceReceiver receiver, String opPackageName, int enrollReason); // Cancel enrollment in progress - void cancelEnrollment(IBinder token, long requestId); + void cancelEnrollment(IBinder token); // Any errors resulting from this call will be returned to the listener void remove(IBinder token, int fingerId, int userId, IFingerprintServiceReceiver receiver, diff --git a/core/java/android/hardware/location/GeofenceHardwareMonitorEvent.java b/core/java/android/hardware/location/GeofenceHardwareMonitorEvent.java index 310ebe9ac093..78cca9601a2d 100644 --- a/core/java/android/hardware/location/GeofenceHardwareMonitorEvent.java +++ b/core/java/android/hardware/location/GeofenceHardwareMonitorEvent.java @@ -81,7 +81,7 @@ public class GeofenceHardwareMonitorEvent implements Parcelable { int monitoringType = source.readInt(); int monitoringStatus = source.readInt(); int sourceTechnologies = source.readInt(); - Location location = source.readParcelable(classLoader, android.location.Location.class); + Location location = source.readParcelable(classLoader); return new GeofenceHardwareMonitorEvent( monitoringType, diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java index 6ea2ac414704..4cc001a40146 100644 --- a/core/java/android/hardware/radio/RadioManager.java +++ b/core/java/android/hardware/radio/RadioManager.java @@ -436,7 +436,8 @@ public class RadioManager { mNumAudioSources = in.readInt(); mIsInitializationRequired = in.readInt() == 1; mIsCaptureSupported = in.readInt() == 1; - Parcelable[] tmp = in.readParcelableArray(BandDescriptor.class.getClassLoader()); + Parcelable[] tmp = in.readParcelableArray(BandDescriptor.class.getClassLoader(), + BandDescriptor.class); mBands = new BandDescriptor[tmp.length]; for (int i = 0; i < tmp.length; i++) { mBands[i] = (BandDescriptor) tmp[i]; diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index afaa085c7cbd..afcb6fccb137 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -54,6 +54,7 @@ import static android.view.WindowInsets.Type.statusBars; import static java.lang.annotation.RetentionPolicy.SOURCE; +import android.annotation.AnyThread; import android.annotation.CallSuper; import android.annotation.DrawableRes; import android.annotation.IntDef; @@ -83,6 +84,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.ResultReceiver; import android.os.SystemClock; +import android.os.SystemProperties; import android.os.Trace; import android.provider.Settings; import android.text.InputType; @@ -302,6 +304,44 @@ public class InputMethodService extends AbstractInputMethodService { static final boolean DEBUG = false; /** + * Key for a boolean value that tells whether {@link InputMethodService} is responsible for + * rendering the back button and the IME switcher button or not when the gestural navigation is + * enabled. + * + * <p>This sysprop is just ignored when the gestural navigation mode is not enabled.</p> + * + * <p> + * To avoid complexity that is not necessary for production, you always need to reboot the + * device after modifying this flag as follows: + * <pre> + * $ adb root + * $ adb shell setprop persist.sys.ime.can_render_gestural_nav_buttons true + * $ adb reboot + * </pre> + * </p> + */ + private static final String PROP_CAN_RENDER_GESTURAL_NAV_BUTTONS = + "persist.sys.ime.can_render_gestural_nav_buttons"; + + /** + * Returns whether {@link InputMethodService} is responsible for rendering the back button and + * the IME switcher button or not when the gestural navigation is enabled. + * + * <p>This method is supposed to be used with an assumption that the same value is returned in + * other processes. It is developers' responsibility for rebooting the device when the sysprop + * is modified.</p> + * + * @return {@code true} if {@link InputMethodService} is responsible for rendering the back + * button and the IME switcher button when the gestural navigation is enabled. + * + * @hide + */ + @AnyThread + public static boolean canImeRenderGesturalNavButtons() { + return SystemProperties.getBoolean(PROP_CAN_RENDER_GESTURAL_NAV_BUTTONS, false); + } + + /** * Allows the system to optimize the back button affordance based on the presence of software * keyboard. * @@ -523,6 +563,7 @@ public class InputMethodService extends AbstractInputMethodService { private Handler mHandler; private boolean mImeSurfaceScheduledForRemoval; private ImsConfigurationTracker mConfigTracker = new ImsConfigurationTracker(); + private boolean mDestroyed; /** * An opaque {@link Binder} token of window requesting {@link InputMethodImpl#showSoftInput} @@ -599,6 +640,11 @@ public class InputMethodService extends AbstractInputMethodService { @Override public final void initializeInternal(@NonNull IBinder token, IInputMethodPrivilegedOperations privilegedOperations, int configChanges) { + if (mDestroyed) { + Log.i(TAG, "The InputMethodService has already onDestroyed()." + + "Ignore the initialization."); + return; + } Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initializeInternal"); mConfigTracker.onInitialize(configChanges); mPrivOps.set(privilegedOperations); @@ -1435,6 +1481,7 @@ public class InputMethodService extends AbstractInputMethodService { } @Override public void onDestroy() { + mDestroyed = true; super.onDestroy(); mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener( mInsetsComputer); @@ -1811,15 +1858,19 @@ public class InputMethodService extends AbstractInputMethodService { void updateExtractFrameVisibility() { final int vis; + updateCandidatesVisibility(mCandidatesVisibility == View.VISIBLE); + if (isFullscreenMode()) { vis = mExtractViewHidden ? View.INVISIBLE : View.VISIBLE; // "vis" should be applied for the extract frame as well in the fullscreen mode. mExtractFrame.setVisibility(vis); } else { - vis = View.VISIBLE; + // mFullscreenArea visibility will according the candidate frame visibility once the + // extract frame is gone. + vis = mCandidatesVisibility; mExtractFrame.setVisibility(View.GONE); } - updateCandidatesVisibility(mCandidatesVisibility == View.VISIBLE); + if (mDecorViewWasVisible && mFullscreenArea.getVisibility() != vis) { int animRes = mThemeAttrs.getResourceId(vis == View.VISIBLE ? com.android.internal.R.styleable.InputMethodService_imeExtractEnterAnimation diff --git a/core/java/android/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java index fab692cba2f6..55541377a0bf 100644 --- a/core/java/android/net/Ikev2VpnProfile.java +++ b/core/java/android/net/Ikev2VpnProfile.java @@ -35,6 +35,7 @@ import android.annotation.Nullable; import android.annotation.RequiresFeature; import android.content.pm.PackageManager; import android.security.Credentials; +import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.net.VpnProfile; @@ -70,6 +71,7 @@ import java.util.Objects; * Exchange, Version 2 (IKEv2)</a> */ public final class Ikev2VpnProfile extends PlatformVpnProfile { + private static final String TAG = Ikev2VpnProfile.class.getSimpleName(); /** Prefix for when a Private Key is an alias to look for in KeyStore @hide */ public static final String PREFIX_KEYSTORE_ALIAS = "KEYSTORE_ALIAS:"; /** Prefix for when a Private Key is stored directly in the profile @hide */ @@ -163,6 +165,10 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { // UnmodifiableList doesn't make a defensive copy by default. mAllowedAlgorithms = Collections.unmodifiableList(new ArrayList<>(allowedAlgorithms)); + if (excludeLocalRoutes && !isBypassable) { + throw new IllegalArgumentException( + "Vpn should be byassable if excludeLocalRoutes is set"); + } mIsBypassable = isBypassable; mIsMetered = isMetered; @@ -520,7 +526,10 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { throw new IllegalArgumentException("Invalid auth method set"); } - builder.setExcludeLocalRoutes(profile.excludeLocalRoutes); + if (profile.excludeLocalRoutes && !profile.isBypassable) { + Log.w(TAG, "ExcludeLocalRoutes should only be set in the bypassable VPN"); + } + builder.setExcludeLocalRoutes(profile.excludeLocalRoutes && profile.isBypassable); return builder.build(); } @@ -907,9 +916,23 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { } /** - * Sets whether the local traffic is exempted from the VPN. + * Sets whether the local traffic is exempted from the VPN. + * + * When this is set, the system will not use the VPN network when an app + * tries to send traffic for an IP address that is on a local network. + * + * Note that there are important security implications. In particular, the + * networks that the device connects to typically decides what IP addresses + * are part of the local network. This means that for VPNs setting this + * flag, it is possible for anybody to set up a public network in such a + * way that traffic to arbitrary IP addresses will bypass the VPN, including + * traffic to services like DNS. When using this API, please consider the + * security implications for your particular case. + * + * Note that because the local traffic will always bypass the VPN, + * it is not possible to set this flag on a non-bypassable VPN. * - * @hide TODO(184750836): unhide once the implementation is completed + * @hide TODO(184750836): unhide once the implementation is completed */ @NonNull @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS) diff --git a/core/java/android/net/InterfaceConfiguration.java b/core/java/android/net/InterfaceConfiguration.java index 1c4089c0f366..37425ffc18aa 100644 --- a/core/java/android/net/InterfaceConfiguration.java +++ b/core/java/android/net/InterfaceConfiguration.java @@ -160,7 +160,6 @@ public class InterfaceConfiguration implements Parcelable { } } - @SuppressWarnings("UnsafeParcelApi") public static final @android.annotation.NonNull Creator<InterfaceConfiguration> CREATOR = new Creator< InterfaceConfiguration>() { public InterfaceConfiguration createFromParcel(Parcel in) { diff --git a/core/java/android/net/InternalNetworkUpdateRequest.java b/core/java/android/net/InternalNetworkUpdateRequest.java index 6f093835fb08..f42c4b7c420d 100644 --- a/core/java/android/net/InternalNetworkUpdateRequest.java +++ b/core/java/android/net/InternalNetworkUpdateRequest.java @@ -17,7 +17,6 @@ package android.net; import android.annotation.NonNull; -import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; @@ -27,7 +26,7 @@ import java.util.Objects; public final class InternalNetworkUpdateRequest implements Parcelable { @NonNull private final StaticIpConfiguration mIpConfig; - @Nullable + @NonNull private final NetworkCapabilities mNetworkCapabilities; @NonNull @@ -37,20 +36,16 @@ public final class InternalNetworkUpdateRequest implements Parcelable { @NonNull public NetworkCapabilities getNetworkCapabilities() { - return mNetworkCapabilities == null - ? null : new NetworkCapabilities(mNetworkCapabilities); + return new NetworkCapabilities(mNetworkCapabilities); } /** @hide */ public InternalNetworkUpdateRequest(@NonNull final StaticIpConfiguration ipConfig, - @Nullable final NetworkCapabilities networkCapabilities) { + @NonNull final NetworkCapabilities networkCapabilities) { Objects.requireNonNull(ipConfig); + Objects.requireNonNull(networkCapabilities); mIpConfig = new StaticIpConfiguration(ipConfig); - if (null == networkCapabilities) { - mNetworkCapabilities = null; - } else { - mNetworkCapabilities = new NetworkCapabilities(networkCapabilities); - } + mNetworkCapabilities = new NetworkCapabilities(networkCapabilities); } private InternalNetworkUpdateRequest(@NonNull final Parcel source) { diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java index 596f4317dce3..ab1f5420fb3f 100644 --- a/core/java/android/net/NetworkPolicy.java +++ b/core/java/android/net/NetworkPolicy.java @@ -142,8 +142,8 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> { } private NetworkPolicy(Parcel source) { - template = source.readParcelable(null, android.net.NetworkTemplate.class); - cycleRule = source.readParcelable(null, android.util.RecurrenceRule.class); + template = source.readParcelable(null); + cycleRule = source.readParcelable(null); warningBytes = source.readLong(); limitBytes = source.readLong(); lastWarningSnooze = source.readLong(); diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java index 2ced05693755..1ae1b050d32f 100644 --- a/core/java/android/net/VpnService.java +++ b/core/java/android/net/VpnService.java @@ -41,6 +41,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.net.NetworkUtilsInternal; import com.android.internal.net.VpnConfig; @@ -50,6 +51,7 @@ import java.net.Inet6Address; import java.net.InetAddress; import java.net.Socket; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Set; @@ -471,6 +473,13 @@ public class VpnService extends Service { } } + private static void checkNonPrefixBytes(@NonNull InetAddress address, int prefixLength) { + final IpPrefix prefix = new IpPrefix(address, prefixLength); + if (!prefix.getAddress().equals(address)) { + throw new IllegalArgumentException("Bad address"); + } + } + /** * Helper class to create a VPN interface. This class should be always * used within the scope of the outer {@link VpnService}. @@ -481,9 +490,9 @@ public class VpnService extends Service { private final VpnConfig mConfig = new VpnConfig(); @UnsupportedAppUsage - private final List<LinkAddress> mAddresses = new ArrayList<LinkAddress>(); + private final List<LinkAddress> mAddresses = new ArrayList<>(); @UnsupportedAppUsage - private final List<RouteInfo> mRoutes = new ArrayList<RouteInfo>(); + private final List<RouteInfo> mRoutes = new ArrayList<>(); public Builder() { mConfig.user = VpnService.this.getClass().getName(); @@ -555,7 +564,6 @@ public class VpnService extends Service { throw new IllegalArgumentException("Bad address"); } mAddresses.add(new LinkAddress(address, prefixLength)); - mConfig.updateAllowedFamilies(address); return this; } @@ -579,28 +587,68 @@ public class VpnService extends Service { * Add a network route to the VPN interface. Both IPv4 and IPv6 * routes are supported. * + * If a route with the same destination is already present, its type will be updated. + * + * @throws IllegalArgumentException if the route is invalid. + */ + @NonNull + private Builder addRoute(@NonNull IpPrefix prefix, int type) { + check(prefix.getAddress(), prefix.getPrefixLength()); + + final RouteInfo newRoute = new RouteInfo(prefix, /* gateway */ + null, /* interface */ null, type); + + final int index = findRouteIndexByDestination(newRoute); + + if (index == -1) { + mRoutes.add(newRoute); + } else { + mRoutes.set(index, newRoute); + } + + return this; + } + + /** + * Add a network route to the VPN interface. Both IPv4 and IPv6 + * routes are supported. + * * Adding a route implicitly allows traffic from that address family * (i.e., IPv4 or IPv6) to be routed over the VPN. @see #allowFamily * + * Calling this method overrides previous calls to {@link #excludeRoute} for the same + * destination. + * + * If multiple routes match the packet destination, route with the longest prefix takes + * precedence. + * * @throws IllegalArgumentException if the route is invalid. */ @NonNull public Builder addRoute(@NonNull InetAddress address, int prefixLength) { - check(address, prefixLength); + checkNonPrefixBytes(address, prefixLength); - int offset = prefixLength / 8; - byte[] bytes = address.getAddress(); - if (offset < bytes.length) { - for (bytes[offset] <<= prefixLength % 8; offset < bytes.length; ++offset) { - if (bytes[offset] != 0) { - throw new IllegalArgumentException("Bad address"); - } - } - } - mRoutes.add(new RouteInfo(new IpPrefix(address, prefixLength), null, null, - RouteInfo.RTN_UNICAST)); - mConfig.updateAllowedFamilies(address); - return this; + return addRoute(new IpPrefix(address, prefixLength), RouteInfo.RTN_UNICAST); + } + + /** + * Add a network route to the VPN interface. Both IPv4 and IPv6 + * routes are supported. + * + * Adding a route implicitly allows traffic from that address family + * (i.e., IPv4 or IPv6) to be routed over the VPN. @see #allowFamily + * + * Calling this method overrides previous calls to {@link #excludeRoute} for the same + * destination. + * + * If multiple routes match the packet destination, route with the longest prefix takes + * precedence. + * + * @throws IllegalArgumentException if the route is invalid. + */ + @NonNull + public Builder addRoute(@NonNull IpPrefix prefix) { + return addRoute(prefix, RouteInfo.RTN_UNICAST); } /** @@ -611,6 +659,12 @@ public class VpnService extends Service { * Adding a route implicitly allows traffic from that address family * (i.e., IPv4 or IPv6) to be routed over the VPN. @see #allowFamily * + * Calling this method overrides previous calls to {@link #excludeRoute} for the same + * destination. + * + * If multiple routes match the packet destination, route with the longest prefix takes + * precedence. + * * @throws IllegalArgumentException if the route is invalid. * @see #addRoute(InetAddress, int) */ @@ -620,6 +674,23 @@ public class VpnService extends Service { } /** + * Exclude a network route from the VPN interface. Both IPv4 and IPv6 + * routes are supported. + * + * Calling this method overrides previous calls to {@link #addRoute} for the same + * destination. + * + * If multiple routes match the packet destination, route with the longest prefix takes + * precedence. + * + * @throws IllegalArgumentException if the route is invalid. + */ + @NonNull + public Builder excludeRoute(@NonNull IpPrefix prefix) { + return addRoute(prefix, RouteInfo.RTN_THROW); + } + + /** * Add a DNS server to the VPN connection. Both IPv4 and IPv6 * addresses are supported. If none is set, the DNS servers of * the default network will be used. @@ -900,5 +971,23 @@ public class VpnService extends Service { throw new IllegalStateException(e); } } + + private int findRouteIndexByDestination(RouteInfo route) { + for (int i = 0; i < mRoutes.size(); i++) { + if (mRoutes.get(i).getDestination().equals(route.getDestination())) { + return i; + } + } + return -1; + } + + /** + * Method for testing, to observe mRoutes while builder is being used. + * @hide + */ + @VisibleForTesting + public List<RouteInfo> routes() { + return Collections.unmodifiableList(mRoutes); + } } } diff --git a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java index 1ac3f0a6d7d1..125b5730b2ed 100644 --- a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java +++ b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java @@ -15,6 +15,9 @@ */ package android.net.vcn; +import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY; +import static android.net.vcn.VcnUnderlyingNetworkTemplate.getMatchCriteriaString; + import static com.android.internal.annotations.VisibleForTesting.Visibility; import static com.android.server.vcn.util.PersistableBundleUtils.INTEGER_DESERIALIZER; import static com.android.server.vcn.util.PersistableBundleUtils.INTEGER_SERIALIZER; @@ -23,8 +26,12 @@ import static com.android.server.vcn.util.PersistableBundleUtils.STRING_SERIALIZ import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; +import android.net.NetworkCapabilities; +import android.net.vcn.VcnUnderlyingNetworkTemplate.MatchCriteria; import android.os.PersistableBundle; import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.util.ArraySet; @@ -37,32 +44,36 @@ import java.util.Collections; import java.util.Objects; import java.util.Set; -// TODO: Add documents -/** @hide */ +/** + * This class represents a configuration for a network template class of underlying cellular + * networks. + * + * <p>See {@link VcnUnderlyingNetworkTemplate} + */ public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetworkTemplate { private static final String ALLOWED_NETWORK_PLMN_IDS_KEY = "mAllowedNetworkPlmnIds"; @NonNull private final Set<String> mAllowedNetworkPlmnIds; private static final String ALLOWED_SPECIFIC_CARRIER_IDS_KEY = "mAllowedSpecificCarrierIds"; @NonNull private final Set<Integer> mAllowedSpecificCarrierIds; - private static final String ALLOW_ROAMING_KEY = "mAllowRoaming"; - private final boolean mAllowRoaming; + private static final String ROAMING_MATCH_KEY = "mRoamingMatchCriteria"; + private final int mRoamingMatchCriteria; - private static final String REQUIRE_OPPORTUNISTIC_KEY = "mRequireOpportunistic"; - private final boolean mRequireOpportunistic; + private static final String OPPORTUNISTIC_MATCH_KEY = "mOpportunisticMatchCriteria"; + private final int mOpportunisticMatchCriteria; private VcnCellUnderlyingNetworkTemplate( int networkQuality, - boolean allowMetered, + int meteredMatchCriteria, Set<String> allowedNetworkPlmnIds, Set<Integer> allowedSpecificCarrierIds, - boolean allowRoaming, - boolean requireOpportunistic) { - super(NETWORK_PRIORITY_TYPE_CELL, networkQuality, allowMetered); + int roamingMatchCriteria, + int opportunisticMatchCriteria) { + super(NETWORK_PRIORITY_TYPE_CELL, networkQuality, meteredMatchCriteria); mAllowedNetworkPlmnIds = new ArraySet<>(allowedNetworkPlmnIds); mAllowedSpecificCarrierIds = new ArraySet<>(allowedSpecificCarrierIds); - mAllowRoaming = allowRoaming; - mRequireOpportunistic = requireOpportunistic; + mRoamingMatchCriteria = roamingMatchCriteria; + mOpportunisticMatchCriteria = opportunisticMatchCriteria; validate(); } @@ -72,15 +83,17 @@ public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetwork protected void validate() { super.validate(); validatePlmnIds(mAllowedNetworkPlmnIds); - Objects.requireNonNull(mAllowedSpecificCarrierIds, "allowedCarrierIds is null"); + Objects.requireNonNull(mAllowedSpecificCarrierIds, "matchingCarrierIds is null"); + validateMatchCriteria(mRoamingMatchCriteria, "mRoamingMatchCriteria"); + validateMatchCriteria(mOpportunisticMatchCriteria, "mOpportunisticMatchCriteria"); } - private static void validatePlmnIds(Set<String> allowedNetworkPlmnIds) { - Objects.requireNonNull(allowedNetworkPlmnIds, "allowedNetworkPlmnIds is null"); + private static void validatePlmnIds(Set<String> matchingOperatorPlmnIds) { + Objects.requireNonNull(matchingOperatorPlmnIds, "matchingOperatorPlmnIds is null"); // A valid PLMN is a concatenation of MNC and MCC, and thus consists of 5 or 6 decimal // digits. - for (String id : allowedNetworkPlmnIds) { + for (String id : matchingOperatorPlmnIds) { if ((id.length() == 5 || id.length() == 6) && id.matches("[0-9]+")) { continue; } else { @@ -97,7 +110,7 @@ public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetwork Objects.requireNonNull(in, "PersistableBundle is null"); final int networkQuality = in.getInt(NETWORK_QUALITY_KEY); - final boolean allowMetered = in.getBoolean(ALLOW_METERED_KEY); + final int meteredMatchCriteria = in.getInt(METERED_MATCH_KEY); final PersistableBundle plmnIdsBundle = in.getPersistableBundle(ALLOWED_NETWORK_PLMN_IDS_KEY); @@ -114,16 +127,16 @@ public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetwork PersistableBundleUtils.toList( specificCarrierIdsBundle, INTEGER_DESERIALIZER)); - final boolean allowRoaming = in.getBoolean(ALLOW_ROAMING_KEY); - final boolean requireOpportunistic = in.getBoolean(REQUIRE_OPPORTUNISTIC_KEY); + final int roamingMatchCriteria = in.getInt(ROAMING_MATCH_KEY); + final int opportunisticMatchCriteria = in.getInt(OPPORTUNISTIC_MATCH_KEY); return new VcnCellUnderlyingNetworkTemplate( networkQuality, - allowMetered, + meteredMatchCriteria, allowedNetworkPlmnIds, allowedSpecificCarrierIds, - allowRoaming, - requireOpportunistic); + roamingMatchCriteria, + opportunisticMatchCriteria); } /** @hide */ @@ -143,35 +156,51 @@ public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetwork new ArrayList<>(mAllowedSpecificCarrierIds), INTEGER_SERIALIZER); result.putPersistableBundle(ALLOWED_SPECIFIC_CARRIER_IDS_KEY, specificCarrierIdsBundle); - result.putBoolean(ALLOW_ROAMING_KEY, mAllowRoaming); - result.putBoolean(REQUIRE_OPPORTUNISTIC_KEY, mRequireOpportunistic); + result.putInt(ROAMING_MATCH_KEY, mRoamingMatchCriteria); + result.putInt(OPPORTUNISTIC_MATCH_KEY, mOpportunisticMatchCriteria); return result; } - /** Retrieve the allowed PLMN IDs, or an empty set if any PLMN ID is acceptable. */ + /** + * Retrieve the matching operator PLMN IDs, or an empty set if any PLMN ID is acceptable. + * + * @see Builder#setOperatorPlmnIds(Set) + */ @NonNull - public Set<String> getAllowedOperatorPlmnIds() { + public Set<String> getOperatorPlmnIds() { return Collections.unmodifiableSet(mAllowedNetworkPlmnIds); } /** - * Retrieve the allowed specific carrier IDs, or an empty set if any specific carrier ID is - * acceptable. + * Retrieve the matching sim specific carrier IDs, or an empty set if any sim specific carrier + * ID is acceptable. + * + * @see Builder#setSimSpecificCarrierIds(Set) */ @NonNull - public Set<Integer> getAllowedSpecificCarrierIds() { + public Set<Integer> getSimSpecificCarrierIds() { return Collections.unmodifiableSet(mAllowedSpecificCarrierIds); } - /** Return if roaming is allowed. */ - public boolean allowRoaming() { - return mAllowRoaming; + /** + * Return the matching criteria for roaming networks. + * + * @see Builder#setRoaming(int) + */ + @MatchCriteria + public int getRoaming() { + return mRoamingMatchCriteria; } - /** Return if requiring an opportunistic network. */ - public boolean requireOpportunistic() { - return mRequireOpportunistic; + /** + * Return the matching criteria for opportunistic cellular subscriptions. + * + * @see Builder#setOpportunistic(int) + */ + @MatchCriteria + public int getOpportunistic() { + return mOpportunisticMatchCriteria; } @Override @@ -180,8 +209,8 @@ public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetwork super.hashCode(), mAllowedNetworkPlmnIds, mAllowedSpecificCarrierIds, - mAllowRoaming, - mRequireOpportunistic); + mRoamingMatchCriteria, + mOpportunisticMatchCriteria); } @Override @@ -197,8 +226,8 @@ public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetwork final VcnCellUnderlyingNetworkTemplate rhs = (VcnCellUnderlyingNetworkTemplate) other; return Objects.equals(mAllowedNetworkPlmnIds, rhs.mAllowedNetworkPlmnIds) && Objects.equals(mAllowedSpecificCarrierIds, rhs.mAllowedSpecificCarrierIds) - && mAllowRoaming == rhs.mAllowRoaming - && mRequireOpportunistic == rhs.mRequireOpportunistic; + && mRoamingMatchCriteria == rhs.mRoamingMatchCriteria + && mOpportunisticMatchCriteria == rhs.mOpportunisticMatchCriteria; } /** @hide */ @@ -206,77 +235,137 @@ public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetwork void dumpTransportSpecificFields(IndentingPrintWriter pw) { pw.println("mAllowedNetworkPlmnIds: " + mAllowedNetworkPlmnIds.toString()); pw.println("mAllowedSpecificCarrierIds: " + mAllowedSpecificCarrierIds.toString()); - pw.println("mAllowRoaming: " + mAllowRoaming); - pw.println("mRequireOpportunistic: " + mRequireOpportunistic); + pw.println("mRoamingMatchCriteria: " + getMatchCriteriaString(mRoamingMatchCriteria)); + pw.println( + "mOpportunisticMatchCriteria: " + + getMatchCriteriaString(mOpportunisticMatchCriteria)); } - /** This class is used to incrementally build WifiNetworkPriority objects. */ - public static final class Builder extends VcnUnderlyingNetworkTemplate.Builder<Builder> { + /** This class is used to incrementally build VcnCellUnderlyingNetworkTemplate objects. */ + public static final class Builder { + private int mNetworkQuality = NETWORK_QUALITY_ANY; + private int mMeteredMatchCriteria = MATCH_ANY; + @NonNull private final Set<String> mAllowedNetworkPlmnIds = new ArraySet<>(); @NonNull private final Set<Integer> mAllowedSpecificCarrierIds = new ArraySet<>(); - private boolean mAllowRoaming = false; - private boolean mRequireOpportunistic = false; + private int mRoamingMatchCriteria = MATCH_ANY; + private int mOpportunisticMatchCriteria = MATCH_ANY; /** Construct a Builder object. */ public Builder() {} /** - * Set allowed operator PLMN IDs. + * Set the required network quality to match this template. + * + * <p>Network quality is a aggregation of multiple signals that reflect the network link + * metrics. For example, the network validation bit (see {@link + * NetworkCapabilities#NET_CAPABILITY_VALIDATED}), estimated first hop transport bandwidth + * and signal strength. + * + * @param networkQuality the required network quality. Defaults to NETWORK_QUALITY_ANY + * @hide + */ + @NonNull + public Builder setNetworkQuality(@NetworkQuality int networkQuality) { + validateNetworkQuality(networkQuality); + + mNetworkQuality = networkQuality; + return this; + } + + /** + * Set the matching criteria for metered networks. + * + * <p>A template where setMetered(MATCH_REQUIRED) will only match metered networks (one + * without NET_CAPABILITY_NOT_METERED). A template where setMetered(MATCH_FORBIDDEN) will + * only match a network that is not metered (one with NET_CAPABILITY_NOT_METERED). + * + * @param matchCriteria the matching criteria for metered networks. Defaults to {@link + * #MATCH_ANY}. + * @see NetworkCapabilities#NET_CAPABILITY_NOT_METERED + */ + // The matching getter is defined in the super class. Please see {@link + // VcnUnderlyingNetworkTemplate#getMetered()} + @SuppressLint("MissingGetterMatchingBuilder") + @NonNull + public Builder setMetered(@MatchCriteria int matchCriteria) { + validateMatchCriteria(matchCriteria, "setMetered"); + + mMeteredMatchCriteria = matchCriteria; + return this; + } + + /** + * Set operator PLMN IDs with which a network can match this template. * * <p>This is used to distinguish cases where roaming agreements may dictate a different * priority from a partner's networks. * - * @param allowedNetworkPlmnIds the allowed operator PLMN IDs in String. Defaults to an - * empty set, allowing ANY PLMN ID. A valid PLMN is a concatenation of MNC and MCC, and - * thus consists of 5 or 6 decimal digits. See {@link SubscriptionInfo#getMccString()} - * and {@link SubscriptionInfo#getMncString()}. + * @param operatorPlmnIds the matching operator PLMN IDs in String. Network with one of the + * matching PLMN IDs can match this template. If the set is empty, any PLMN ID will + * match. The default is an empty set. A valid PLMN is a concatenation of MNC and MCC, + * and thus consists of 5 or 6 decimal digits. + * @see SubscriptionInfo#getMccString() + * @see SubscriptionInfo#getMncString() */ @NonNull - public Builder setAllowedOperatorPlmnIds(@NonNull Set<String> allowedNetworkPlmnIds) { - validatePlmnIds(allowedNetworkPlmnIds); + public Builder setOperatorPlmnIds(@NonNull Set<String> operatorPlmnIds) { + validatePlmnIds(operatorPlmnIds); mAllowedNetworkPlmnIds.clear(); - mAllowedNetworkPlmnIds.addAll(allowedNetworkPlmnIds); + mAllowedNetworkPlmnIds.addAll(operatorPlmnIds); return this; } /** - * Set allowed specific carrier IDs. + * Set sim specific carrier IDs with which a network can match this template. * - * @param allowedSpecificCarrierIds the allowed specific carrier IDs. Defaults to an empty - * set, allowing ANY carrier ID. See {@link TelephonyManager#getSimSpecificCarrierId()}. + * @param simSpecificCarrierIds the matching sim specific carrier IDs. Network with one of + * the sim specific carrier IDs can match this template. If the set is empty, any + * carrier ID will match. The default is an empty set. + * @see TelephonyManager#getSimSpecificCarrierId() */ @NonNull - public Builder setAllowedSpecificCarrierIds( - @NonNull Set<Integer> allowedSpecificCarrierIds) { - Objects.requireNonNull(allowedSpecificCarrierIds, "allowedCarrierIds is null"); + public Builder setSimSpecificCarrierIds(@NonNull Set<Integer> simSpecificCarrierIds) { + Objects.requireNonNull(simSpecificCarrierIds, "simSpecificCarrierIds is null"); + mAllowedSpecificCarrierIds.clear(); - mAllowedSpecificCarrierIds.addAll(allowedSpecificCarrierIds); + mAllowedSpecificCarrierIds.addAll(simSpecificCarrierIds); return this; } /** - * Set if roaming is allowed. + * Set the matching criteria for roaming networks. * - * @param allowRoaming the flag to indicate if roaming is allowed. Defaults to {@code - * false}. + * <p>A template where setRoaming(MATCH_REQUIRED) will only match roaming networks (one + * without NET_CAPABILITY_NOT_ROAMING). A template where setRoaming(MATCH_FORBIDDEN) will + * only match a network that is not roaming (one with NET_CAPABILITY_NOT_ROAMING). + * + * @param matchCriteria the matching criteria for roaming networks. Defaults to {@link + * #MATCH_ANY}. + * @see NetworkCapabilities#NET_CAPABILITY_NOT_ROAMING */ @NonNull - public Builder setAllowRoaming(boolean allowRoaming) { - mAllowRoaming = allowRoaming; + public Builder setRoaming(@MatchCriteria int matchCriteria) { + validateMatchCriteria(matchCriteria, "setRoaming"); + + mRoamingMatchCriteria = matchCriteria; return this; } /** - * Set if requiring an opportunistic network. + * Set the matching criteria for opportunistic cellular subscriptions. * - * @param requireOpportunistic the flag to indicate if caller requires an opportunistic - * network. Defaults to {@code false}. + * @param matchCriteria the matching criteria for opportunistic cellular subscriptions. + * Defaults to {@link #MATCH_ANY}. + * @see SubscriptionManager#setOpportunistic(boolean, int) */ @NonNull - public Builder setRequireOpportunistic(boolean requireOpportunistic) { - mRequireOpportunistic = requireOpportunistic; + public Builder setOpportunistic(@MatchCriteria int matchCriteria) { + validateMatchCriteria(matchCriteria, "setOpportunistic"); + + mOpportunisticMatchCriteria = matchCriteria; return this; } @@ -285,17 +374,11 @@ public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetwork public VcnCellUnderlyingNetworkTemplate build() { return new VcnCellUnderlyingNetworkTemplate( mNetworkQuality, - mAllowMetered, + mMeteredMatchCriteria, mAllowedNetworkPlmnIds, mAllowedSpecificCarrierIds, - mAllowRoaming, - mRequireOpportunistic); - } - - /** @hide */ - @Override - Builder self() { - return this; + mRoamingMatchCriteria, + mOpportunisticMatchCriteria); } } } diff --git a/core/java/android/net/vcn/VcnConfig.java b/core/java/android/net/vcn/VcnConfig.java index fd3fe3731b74..caab15251f58 100644 --- a/core/java/android/net/vcn/VcnConfig.java +++ b/core/java/android/net/vcn/VcnConfig.java @@ -173,7 +173,7 @@ public final class VcnConfig implements Parcelable { new Parcelable.Creator<VcnConfig>() { @NonNull public VcnConfig createFromParcel(Parcel in) { - return new VcnConfig((PersistableBundle) in.readParcelable(null, android.os.PersistableBundle.class)); + return new VcnConfig((PersistableBundle) in.readParcelable(null)); } @NonNull diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java index d07c24a6529c..92956e859fc7 100644 --- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java +++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java @@ -16,6 +16,7 @@ package android.net.vcn; import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE; +import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED; import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK; import static com.android.internal.annotations.VisibleForTesting.Visibility; @@ -42,7 +43,7 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.LinkedHashSet; +import java.util.List; import java.util.Objects; import java.util.Set; import java.util.SortedSet; @@ -162,30 +163,24 @@ public final class VcnGatewayConnectionConfig { /** @hide */ @VisibleForTesting(visibility = Visibility.PRIVATE) - public static final LinkedHashSet<VcnUnderlyingNetworkTemplate> - DEFAULT_UNDERLYING_NETWORK_PRIORITIES = new LinkedHashSet<>(); + public static final List<VcnUnderlyingNetworkTemplate> DEFAULT_UNDERLYING_NETWORK_TEMPLATES = + new ArrayList<>(); static { - DEFAULT_UNDERLYING_NETWORK_PRIORITIES.add( + DEFAULT_UNDERLYING_NETWORK_TEMPLATES.add( new VcnCellUnderlyingNetworkTemplate.Builder() .setNetworkQuality(NETWORK_QUALITY_OK) - .setAllowMetered(true /* allowMetered */) - .setAllowRoaming(true /* allowRoaming */) - .setRequireOpportunistic(true /* requireOpportunistic */) + .setOpportunistic(MATCH_REQUIRED) .build()); - DEFAULT_UNDERLYING_NETWORK_PRIORITIES.add( + DEFAULT_UNDERLYING_NETWORK_TEMPLATES.add( new VcnWifiUnderlyingNetworkTemplate.Builder() .setNetworkQuality(NETWORK_QUALITY_OK) - .setAllowMetered(true /* allowMetered */) .build()); - DEFAULT_UNDERLYING_NETWORK_PRIORITIES.add( + DEFAULT_UNDERLYING_NETWORK_TEMPLATES.add( new VcnCellUnderlyingNetworkTemplate.Builder() .setNetworkQuality(NETWORK_QUALITY_OK) - .setAllowMetered(true /* allowMetered */) - .setAllowRoaming(true /* allowRoaming */) - .setRequireOpportunistic(false /* requireOpportunistic */) .build()); } @@ -200,9 +195,9 @@ public final class VcnGatewayConnectionConfig { /** @hide */ @VisibleForTesting(visibility = Visibility.PRIVATE) - public static final String UNDERLYING_NETWORK_PRIORITIES_KEY = "mUnderlyingNetworkPriorities"; + public static final String UNDERLYING_NETWORK_TEMPLATES_KEY = "mUnderlyingNetworkTemplates"; - @NonNull private final LinkedHashSet<VcnUnderlyingNetworkTemplate> mUnderlyingNetworkPriorities; + @NonNull private final List<VcnUnderlyingNetworkTemplate> mUnderlyingNetworkTemplates; private static final String MAX_MTU_KEY = "mMaxMtu"; private final int mMaxMtu; @@ -215,7 +210,7 @@ public final class VcnGatewayConnectionConfig { @NonNull String gatewayConnectionName, @NonNull IkeTunnelConnectionParams tunnelConnectionParams, @NonNull Set<Integer> exposedCapabilities, - @NonNull LinkedHashSet<VcnUnderlyingNetworkTemplate> underlyingNetworkPriorities, + @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, @NonNull long[] retryIntervalsMs, @IntRange(from = MIN_MTU_V6) int maxMtu) { mGatewayConnectionName = gatewayConnectionName; @@ -224,9 +219,9 @@ public final class VcnGatewayConnectionConfig { mRetryIntervalsMs = retryIntervalsMs; mMaxMtu = maxMtu; - mUnderlyingNetworkPriorities = new LinkedHashSet<>(underlyingNetworkPriorities); - if (mUnderlyingNetworkPriorities.isEmpty()) { - mUnderlyingNetworkPriorities.addAll(DEFAULT_UNDERLYING_NETWORK_PRIORITIES); + mUnderlyingNetworkTemplates = new ArrayList<>(underlyingNetworkTemplates); + if (mUnderlyingNetworkTemplates.isEmpty()) { + mUnderlyingNetworkTemplates.addAll(DEFAULT_UNDERLYING_NETWORK_TEMPLATES); } validate(); @@ -250,22 +245,19 @@ public final class VcnGatewayConnectionConfig { mExposedCapabilities = new TreeSet<>(PersistableBundleUtils.toList( exposedCapsBundle, PersistableBundleUtils.INTEGER_DESERIALIZER)); - final PersistableBundle networkPrioritiesBundle = - in.getPersistableBundle(UNDERLYING_NETWORK_PRIORITIES_KEY); + final PersistableBundle networkTemplatesBundle = + in.getPersistableBundle(UNDERLYING_NETWORK_TEMPLATES_KEY); - if (networkPrioritiesBundle == null) { - // UNDERLYING_NETWORK_PRIORITIES_KEY was added in Android T. Thus + if (networkTemplatesBundle == null) { + // UNDERLYING_NETWORK_TEMPLATES_KEY was added in Android T. Thus // VcnGatewayConnectionConfig created on old platforms will not have this data and will // be assigned with the default value - mUnderlyingNetworkPriorities = - new LinkedHashSet<>(DEFAULT_UNDERLYING_NETWORK_PRIORITIES); - + mUnderlyingNetworkTemplates = new ArrayList<>(DEFAULT_UNDERLYING_NETWORK_TEMPLATES); } else { - mUnderlyingNetworkPriorities = - new LinkedHashSet<>( - PersistableBundleUtils.toList( - networkPrioritiesBundle, - VcnUnderlyingNetworkTemplate::fromPersistableBundle)); + mUnderlyingNetworkTemplates = + PersistableBundleUtils.toList( + networkTemplatesBundle, + VcnUnderlyingNetworkTemplate::fromPersistableBundle); } mRetryIntervalsMs = in.getLongArray(RETRY_INTERVAL_MS_KEY); @@ -285,7 +277,7 @@ public final class VcnGatewayConnectionConfig { checkValidCapability(cap); } - Objects.requireNonNull(mUnderlyingNetworkPriorities, "underlyingNetworkPriorities is null"); + validateNetworkTemplateList(mUnderlyingNetworkTemplates); Objects.requireNonNull(mRetryIntervalsMs, "retryIntervalsMs was null"); validateRetryInterval(mRetryIntervalsMs); @@ -314,6 +306,19 @@ public final class VcnGatewayConnectionConfig { } } + private static void validateNetworkTemplateList( + List<VcnUnderlyingNetworkTemplate> networkPriorityRules) { + Objects.requireNonNull(networkPriorityRules, "networkPriorityRules is null"); + + Set<VcnUnderlyingNetworkTemplate> existingRules = new ArraySet<>(); + for (VcnUnderlyingNetworkTemplate rule : networkPriorityRules) { + Objects.requireNonNull(rule, "Found null value VcnUnderlyingNetworkTemplate"); + if (!existingRules.add(rule)) { + throw new IllegalArgumentException("Found duplicate VcnUnderlyingNetworkTemplate"); + } + } + } + /** * Returns the configured Gateway Connection name. * @@ -368,15 +373,13 @@ public final class VcnGatewayConnectionConfig { } /** - * Retrieve the configured VcnUnderlyingNetworkTemplate list, or a default list if it is not - * configured. + * Retrieve the VcnUnderlyingNetworkTemplate list, or a default list if it is not configured. * - * @see Builder#setVcnUnderlyingNetworkPriorities(LinkedHashSet<VcnUnderlyingNetworkTemplate>) - * @hide + * @see Builder#setVcnUnderlyingNetworkPriorities(List) */ @NonNull - public LinkedHashSet<VcnUnderlyingNetworkTemplate> getVcnUnderlyingNetworkPriorities() { - return new LinkedHashSet<>(mUnderlyingNetworkPriorities); + public List<VcnUnderlyingNetworkTemplate> getVcnUnderlyingNetworkPriorities() { + return new ArrayList<>(mUnderlyingNetworkTemplates); } /** @@ -415,15 +418,15 @@ public final class VcnGatewayConnectionConfig { PersistableBundleUtils.fromList( new ArrayList<>(mExposedCapabilities), PersistableBundleUtils.INTEGER_SERIALIZER); - final PersistableBundle networkPrioritiesBundle = + final PersistableBundle networkTemplatesBundle = PersistableBundleUtils.fromList( - new ArrayList<>(mUnderlyingNetworkPriorities), + mUnderlyingNetworkTemplates, VcnUnderlyingNetworkTemplate::toPersistableBundle); result.putString(GATEWAY_CONNECTION_NAME_KEY, mGatewayConnectionName); result.putPersistableBundle(TUNNEL_CONNECTION_PARAMS_KEY, tunnelConnectionParamsBundle); result.putPersistableBundle(EXPOSED_CAPABILITIES_KEY, exposedCapsBundle); - result.putPersistableBundle(UNDERLYING_NETWORK_PRIORITIES_KEY, networkPrioritiesBundle); + result.putPersistableBundle(UNDERLYING_NETWORK_TEMPLATES_KEY, networkTemplatesBundle); result.putLongArray(RETRY_INTERVAL_MS_KEY, mRetryIntervalsMs); result.putInt(MAX_MTU_KEY, mMaxMtu); @@ -436,7 +439,7 @@ public final class VcnGatewayConnectionConfig { mGatewayConnectionName, mTunnelConnectionParams, mExposedCapabilities, - mUnderlyingNetworkPriorities, + mUnderlyingNetworkTemplates, Arrays.hashCode(mRetryIntervalsMs), mMaxMtu); } @@ -451,7 +454,7 @@ public final class VcnGatewayConnectionConfig { return mGatewayConnectionName.equals(rhs.mGatewayConnectionName) && mTunnelConnectionParams.equals(rhs.mTunnelConnectionParams) && mExposedCapabilities.equals(rhs.mExposedCapabilities) - && mUnderlyingNetworkPriorities.equals(rhs.mUnderlyingNetworkPriorities) + && mUnderlyingNetworkTemplates.equals(rhs.mUnderlyingNetworkTemplates) && Arrays.equals(mRetryIntervalsMs, rhs.mRetryIntervalsMs) && mMaxMtu == rhs.mMaxMtu; } @@ -465,8 +468,8 @@ public final class VcnGatewayConnectionConfig { @NonNull private final Set<Integer> mExposedCapabilities = new ArraySet(); @NonNull - private final LinkedHashSet<VcnUnderlyingNetworkTemplate> mUnderlyingNetworkPriorities = - new LinkedHashSet<>(DEFAULT_UNDERLYING_NETWORK_PRIORITIES); + private final List<VcnUnderlyingNetworkTemplate> mUnderlyingNetworkTemplates = + new ArrayList<>(DEFAULT_UNDERLYING_NETWORK_TEMPLATES); @NonNull private long[] mRetryIntervalsMs = DEFAULT_RETRY_INTERVALS_MS; private int mMaxMtu = DEFAULT_MAX_MTU; @@ -539,27 +542,37 @@ public final class VcnGatewayConnectionConfig { } /** - * Set the VcnUnderlyingNetworkTemplate list. + * Set the list of templates to match underlying networks against, in high-to-low priority + * order. + * + * <p>To select the VCN underlying network, the VCN connection will go through all the + * network candidates and return a network matching the highest priority rule. + * + * <p>If multiple networks match the same rule, the VCN will prefer an already-selected + * network as opposed to a new/unselected network. However, if both are new/unselected + * networks, a network will be chosen arbitrarily amongst the networks matching the highest + * priority rule. * - * @param underlyingNetworkPriorities a list of unique VcnUnderlyingNetworkPriorities that - * are ordered from most to least preferred, or an empty list to use the default - * prioritization. The default network prioritization is Opportunistic cellular, Carrier - * WiFi and Macro cellular - * @return + * <p>If all networks fail to match the rules provided, an underlying network will still be + * selected (at random if necessary). + * + * @param underlyingNetworkTemplates a list of unique VcnUnderlyingNetworkTemplates that are + * ordered from most to least preferred, or an empty list to use the default + * prioritization. The default network prioritization order is Opportunistic cellular, + * Carrier WiFi and then Macro cellular. + * @return this {@link Builder} instance, for chaining */ - /** @hide */ @NonNull public Builder setVcnUnderlyingNetworkPriorities( - @NonNull LinkedHashSet<VcnUnderlyingNetworkTemplate> underlyingNetworkPriorities) { - Objects.requireNonNull( - mUnderlyingNetworkPriorities, "underlyingNetworkPriorities is null"); + @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates) { + validateNetworkTemplateList(underlyingNetworkTemplates); - mUnderlyingNetworkPriorities.clear(); + mUnderlyingNetworkTemplates.clear(); - if (underlyingNetworkPriorities.isEmpty()) { - mUnderlyingNetworkPriorities.addAll(DEFAULT_UNDERLYING_NETWORK_PRIORITIES); + if (underlyingNetworkTemplates.isEmpty()) { + mUnderlyingNetworkTemplates.addAll(DEFAULT_UNDERLYING_NETWORK_TEMPLATES); } else { - mUnderlyingNetworkPriorities.addAll(underlyingNetworkPriorities); + mUnderlyingNetworkTemplates.addAll(underlyingNetworkTemplates); } return this; @@ -629,7 +642,7 @@ public final class VcnGatewayConnectionConfig { mGatewayConnectionName, mTunnelConnectionParams, mExposedCapabilities, - mUnderlyingNetworkPriorities, + mUnderlyingNetworkTemplates, mRetryIntervalsMs, mMaxMtu); } diff --git a/core/java/android/net/vcn/VcnNetworkPolicyResult.java b/core/java/android/net/vcn/VcnNetworkPolicyResult.java index fca084a00a79..14e70cfeb18a 100644 --- a/core/java/android/net/vcn/VcnNetworkPolicyResult.java +++ b/core/java/android/net/vcn/VcnNetworkPolicyResult.java @@ -114,7 +114,7 @@ public final class VcnNetworkPolicyResult implements Parcelable { public static final @NonNull Creator<VcnNetworkPolicyResult> CREATOR = new Creator<VcnNetworkPolicyResult>() { public VcnNetworkPolicyResult createFromParcel(Parcel in) { - return new VcnNetworkPolicyResult(in.readBoolean(), in.readParcelable(null, android.net.NetworkCapabilities.class)); + return new VcnNetworkPolicyResult(in.readBoolean(), in.readParcelable(null)); } public VcnNetworkPolicyResult[] newArray(int size) { diff --git a/core/java/android/net/vcn/VcnTransportInfo.java b/core/java/android/net/vcn/VcnTransportInfo.java index 5c47b28a7c74..25a257423ce2 100644 --- a/core/java/android/net/vcn/VcnTransportInfo.java +++ b/core/java/android/net/vcn/VcnTransportInfo.java @@ -146,7 +146,7 @@ public class VcnTransportInfo implements TransportInfo, Parcelable { new Creator<VcnTransportInfo>() { public VcnTransportInfo createFromParcel(Parcel in) { final int subId = in.readInt(); - final WifiInfo wifiInfo = in.readParcelable(null, android.net.wifi.WifiInfo.class); + final WifiInfo wifiInfo = in.readParcelable(null); // If all fields are their null values, return null TransportInfo to avoid // leaking information about this being a VCN Network (instead of macro diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java b/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java index 2b5305d05dcd..b0d4f3be248f 100644 --- a/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java +++ b/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java @@ -106,7 +106,7 @@ public final class VcnUnderlyingNetworkPolicy implements Parcelable { public static final @NonNull Creator<VcnUnderlyingNetworkPolicy> CREATOR = new Creator<VcnUnderlyingNetworkPolicy>() { public VcnUnderlyingNetworkPolicy createFromParcel(Parcel in) { - return new VcnUnderlyingNetworkPolicy(in.readParcelable(null, android.net.vcn.VcnNetworkPolicyResult.class)); + return new VcnUnderlyingNetworkPolicy(in.readParcelable(null)); } public VcnUnderlyingNetworkPolicy[] newArray(int size) { diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java index d306d5cb6826..60fc936072fb 100644 --- a/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java +++ b/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java @@ -15,6 +15,8 @@ */ package android.net.vcn; +import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY; + import static com.android.internal.annotations.VisibleForTesting.Visibility; import android.annotation.IntDef; @@ -31,17 +33,24 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Objects; -// TODO: Add documents -/** @hide */ +/** + * This class represents a template containing set of underlying network requirements for doing + * route selection. + * + * <p>Apps provisioning a VCN can configure the underlying network priority for each Gateway + * Connection by setting a list (in priority order, most to least preferred) of the appropriate + * subclasses in the VcnGatewayConnectionConfig. See {@link + * VcnGatewayConnectionConfig.Builder#setVcnUnderlyingNetworkPriorities} + */ public abstract class VcnUnderlyingNetworkTemplate { /** @hide */ - protected static final int NETWORK_PRIORITY_TYPE_WIFI = 1; + static final int NETWORK_PRIORITY_TYPE_WIFI = 1; /** @hide */ - protected static final int NETWORK_PRIORITY_TYPE_CELL = 2; + static final int NETWORK_PRIORITY_TYPE_CELL = 2; - /** Denotes that any network quality is acceptable */ + /** Denotes that any network quality is acceptable. @hide */ public static final int NETWORK_QUALITY_ANY = 0; - /** Denotes that network quality needs to be OK */ + /** Denotes that network quality needs to be OK. @hide */ public static final int NETWORK_QUALITY_OK = 100000; private static final SparseArray<String> NETWORK_QUALITY_TO_STRING_MAP = new SparseArray<>(); @@ -56,34 +65,82 @@ public abstract class VcnUnderlyingNetworkTemplate { @IntDef({NETWORK_QUALITY_OK, NETWORK_QUALITY_ANY}) public @interface NetworkQuality {} + /** + * Used to configure the matching criteria of a network characteristic. This may include network + * capabilities, or cellular subscription information. Denotes that networks with or without the + * characteristic are both acceptable to match this template. + */ + public static final int MATCH_ANY = 0; + + /** + * Used to configure the matching criteria of a network characteristic. This may include network + * capabilities, or cellular subscription information. Denotes that a network MUST have the + * capability in order to match this template. + */ + public static final int MATCH_REQUIRED = 1; + + /** + * Used to configure the matching criteria of a network characteristic. This may include network + * capabilities, or cellular subscription information. Denotes that a network MUST NOT have the + * capability in order to match this template. + */ + public static final int MATCH_FORBIDDEN = 2; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({MATCH_ANY, MATCH_REQUIRED, MATCH_FORBIDDEN}) + public @interface MatchCriteria {} + + private static final SparseArray<String> MATCH_CRITERIA_TO_STRING_MAP = new SparseArray<>(); + + static { + MATCH_CRITERIA_TO_STRING_MAP.put(MATCH_ANY, "MATCH_ANY"); + MATCH_CRITERIA_TO_STRING_MAP.put(MATCH_REQUIRED, "MATCH_REQUIRED"); + MATCH_CRITERIA_TO_STRING_MAP.put(MATCH_FORBIDDEN, "MATCH_FORBIDDEN"); + } + private static final String NETWORK_PRIORITY_TYPE_KEY = "mNetworkPriorityType"; private final int mNetworkPriorityType; /** @hide */ - protected static final String NETWORK_QUALITY_KEY = "mNetworkQuality"; + static final String NETWORK_QUALITY_KEY = "mNetworkQuality"; + private final int mNetworkQuality; /** @hide */ - protected static final String ALLOW_METERED_KEY = "mAllowMetered"; - private final boolean mAllowMetered; + static final String METERED_MATCH_KEY = "mMeteredMatchCriteria"; + + private final int mMeteredMatchCriteria; /** @hide */ - protected VcnUnderlyingNetworkTemplate( - int networkPriorityType, int networkQuality, boolean allowMetered) { + VcnUnderlyingNetworkTemplate( + int networkPriorityType, int networkQuality, int meteredMatchCriteria) { mNetworkPriorityType = networkPriorityType; mNetworkQuality = networkQuality; - mAllowMetered = allowMetered; + mMeteredMatchCriteria = meteredMatchCriteria; } - private static void validateNetworkQuality(int networkQuality) { + /** @hide */ + static void validateNetworkQuality(int networkQuality) { Preconditions.checkArgument( networkQuality == NETWORK_QUALITY_ANY || networkQuality == NETWORK_QUALITY_OK, "Invalid networkQuality:" + networkQuality); } /** @hide */ + static void validateMatchCriteria(int meteredMatchCriteria, String matchingCapability) { + Preconditions.checkArgument( + MATCH_CRITERIA_TO_STRING_MAP.contains(meteredMatchCriteria), + "Invalid matching criteria: " + + meteredMatchCriteria + + " for " + + matchingCapability); + } + + /** @hide */ protected void validate() { validateNetworkQuality(mNetworkQuality); + validateMatchCriteria(mMeteredMatchCriteria, "mMeteredMatchCriteria"); } /** @hide */ @@ -112,14 +169,14 @@ public abstract class VcnUnderlyingNetworkTemplate { result.putInt(NETWORK_PRIORITY_TYPE_KEY, mNetworkPriorityType); result.putInt(NETWORK_QUALITY_KEY, mNetworkQuality); - result.putBoolean(ALLOW_METERED_KEY, mAllowMetered); + result.putInt(METERED_MATCH_KEY, mMeteredMatchCriteria); return result; } @Override public int hashCode() { - return Objects.hash(mNetworkPriorityType, mNetworkQuality, mAllowMetered); + return Objects.hash(mNetworkPriorityType, mNetworkQuality, mMeteredMatchCriteria); } @Override @@ -131,7 +188,17 @@ public abstract class VcnUnderlyingNetworkTemplate { final VcnUnderlyingNetworkTemplate rhs = (VcnUnderlyingNetworkTemplate) other; return mNetworkPriorityType == rhs.mNetworkPriorityType && mNetworkQuality == rhs.mNetworkQuality - && mAllowMetered == rhs.mAllowMetered; + && mMeteredMatchCriteria == rhs.mMeteredMatchCriteria; + } + + /** @hide */ + static String getNameString(SparseArray<String> toStringMap, int key) { + return toStringMap.get(key, "Invalid value " + key); + } + + /** @hide */ + static String getMatchCriteriaString(int meteredMatchCriteria) { + return getNameString(MATCH_CRITERIA_TO_STRING_MAP, meteredMatchCriteria); } /** @hide */ @@ -148,65 +215,32 @@ public abstract class VcnUnderlyingNetworkTemplate { pw.println( "mNetworkQuality: " - + NETWORK_QUALITY_TO_STRING_MAP.get( - mNetworkQuality, "Invalid value " + mNetworkQuality)); - pw.println("mAllowMetered: " + mAllowMetered); + + getNameString(NETWORK_QUALITY_TO_STRING_MAP, mNetworkQuality)); + pw.println("mMeteredMatchCriteria: " + getMatchCriteriaString(mMeteredMatchCriteria)); dumpTransportSpecificFields(pw); pw.decreaseIndent(); } - /** Retrieve the required network quality. */ + /** + * Retrieve the required network quality to match this template. + * + * @see Builder#setNetworkQuality(int) + * @hide + */ @NetworkQuality public int getNetworkQuality() { return mNetworkQuality; } - /** Return if a metered network is allowed. */ - public boolean allowMetered() { - return mAllowMetered; - } - /** - * This class is used to incrementally build VcnUnderlyingNetworkTemplate objects. + * Return the matching criteria for metered networks. * - * @param <T> The subclass to be built. + * @see VcnWifiUnderlyingNetworkTemplate.Builder#setMetered(int) + * @see VcnCellUnderlyingNetworkTemplate.Builder#setMetered(int) */ - public abstract static class Builder<T extends Builder<T>> { - /** @hide */ - protected int mNetworkQuality = NETWORK_QUALITY_ANY; - /** @hide */ - protected boolean mAllowMetered = false; - - /** @hide */ - protected Builder() {} - - /** - * Set the required network quality. - * - * @param networkQuality the required network quality. Defaults to NETWORK_QUALITY_ANY - */ - @NonNull - public T setNetworkQuality(@NetworkQuality int networkQuality) { - validateNetworkQuality(networkQuality); - - mNetworkQuality = networkQuality; - return self(); - } - - /** - * Set if a metered network is allowed. - * - * @param allowMetered the flag to indicate if a metered network is allowed, defaults to - * {@code false} - */ - @NonNull - public T setAllowMetered(boolean allowMetered) { - mAllowMetered = allowMetered; - return self(); - } - - /** @hide */ - abstract T self(); + @MatchCriteria + public int getMetered() { + return mMeteredMatchCriteria; } } diff --git a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java index 6bbb2bfecda4..272ca9dd7583 100644 --- a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java +++ b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java @@ -16,31 +16,59 @@ package android.net.vcn; import static com.android.internal.annotations.VisibleForTesting.Visibility; +import static com.android.server.vcn.util.PersistableBundleUtils.STRING_DESERIALIZER; +import static com.android.server.vcn.util.PersistableBundleUtils.STRING_SERIALIZER; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; +import android.net.NetworkCapabilities; import android.os.PersistableBundle; +import android.util.ArraySet; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; +import com.android.server.vcn.util.PersistableBundleUtils; +import java.util.ArrayList; +import java.util.Collections; import java.util.Objects; +import java.util.Set; -// TODO: Add documents -/** @hide */ +/** + * This class represents a configuration for a network template class of underlying Carrier WiFi + * networks. + * + * <p>See {@link VcnUnderlyingNetworkTemplate} + */ public final class VcnWifiUnderlyingNetworkTemplate extends VcnUnderlyingNetworkTemplate { - private static final String SSID_KEY = "mSsid"; - @Nullable private final String mSsid; + private static final String SSIDS_KEY = "mSsids"; + @Nullable private final Set<String> mSsids; private VcnWifiUnderlyingNetworkTemplate( - int networkQuality, boolean allowMetered, String ssid) { - super(NETWORK_PRIORITY_TYPE_WIFI, networkQuality, allowMetered); - mSsid = ssid; + int networkQuality, int meteredMatchCriteria, Set<String> ssids) { + super(NETWORK_PRIORITY_TYPE_WIFI, networkQuality, meteredMatchCriteria); + mSsids = new ArraySet<>(ssids); validate(); } /** @hide */ + @Override + protected void validate() { + super.validate(); + validateSsids(mSsids); + } + + private static void validateSsids(Set<String> ssids) { + Objects.requireNonNull(ssids, "ssids is null"); + + for (String ssid : ssids) { + Objects.requireNonNull(ssid, "found null value ssid"); + } + } + + /** @hide */ @NonNull @VisibleForTesting(visibility = Visibility.PROTECTED) public static VcnWifiUnderlyingNetworkTemplate fromPersistableBundle( @@ -48,9 +76,14 @@ public final class VcnWifiUnderlyingNetworkTemplate extends VcnUnderlyingNetwork Objects.requireNonNull(in, "PersistableBundle is null"); final int networkQuality = in.getInt(NETWORK_QUALITY_KEY); - final boolean allowMetered = in.getBoolean(ALLOW_METERED_KEY); - final String ssid = in.getString(SSID_KEY); - return new VcnWifiUnderlyingNetworkTemplate(networkQuality, allowMetered, ssid); + final int meteredMatchCriteria = in.getInt(METERED_MATCH_KEY); + + final PersistableBundle ssidsBundle = in.getPersistableBundle(SSIDS_KEY); + Objects.requireNonNull(ssidsBundle, "ssidsBundle is null"); + final Set<String> ssids = + new ArraySet<String>( + PersistableBundleUtils.toList(ssidsBundle, STRING_DESERIALIZER)); + return new VcnWifiUnderlyingNetworkTemplate(networkQuality, meteredMatchCriteria, ssids); } /** @hide */ @@ -59,13 +92,17 @@ public final class VcnWifiUnderlyingNetworkTemplate extends VcnUnderlyingNetwork @VisibleForTesting(visibility = Visibility.PROTECTED) public PersistableBundle toPersistableBundle() { final PersistableBundle result = super.toPersistableBundle(); - result.putString(SSID_KEY, mSsid); + + final PersistableBundle ssidsBundle = + PersistableBundleUtils.fromList(new ArrayList<>(mSsids), STRING_SERIALIZER); + result.putPersistableBundle(SSIDS_KEY, ssidsBundle); + return result; } @Override public int hashCode() { - return Objects.hash(super.hashCode(), mSsid); + return Objects.hash(super.hashCode(), mSsids); } @Override @@ -79,49 +116,95 @@ public final class VcnWifiUnderlyingNetworkTemplate extends VcnUnderlyingNetwork } final VcnWifiUnderlyingNetworkTemplate rhs = (VcnWifiUnderlyingNetworkTemplate) other; - return mSsid.equals(rhs.mSsid); + return mSsids.equals(rhs.mSsids); } /** @hide */ @Override void dumpTransportSpecificFields(IndentingPrintWriter pw) { - pw.println("mSsid: " + mSsid); + pw.println("mSsids: " + mSsids); } - /** Retrieve the required SSID, or {@code null} if there is no requirement on SSID. */ - @Nullable - public String getSsid() { - return mSsid; + /** + * Retrieve the matching SSIDs, or an empty set if any SSID is acceptable. + * + * @see Builder#setSsids(Set) + */ + @NonNull + public Set<String> getSsids() { + return Collections.unmodifiableSet(mSsids); } /** This class is used to incrementally build VcnWifiUnderlyingNetworkTemplate objects. */ - public static class Builder extends VcnUnderlyingNetworkTemplate.Builder<Builder> { - @Nullable private String mSsid; + public static final class Builder { + private int mNetworkQuality = NETWORK_QUALITY_ANY; + private int mMeteredMatchCriteria = MATCH_ANY; + @NonNull private final Set<String> mSsids = new ArraySet<>(); /** Construct a Builder object. */ public Builder() {} /** - * Set the required SSID. + * Set the required network quality to match this template. * - * @param ssid the required SSID, or {@code null} if any SSID is acceptable. + * <p>Network quality is a aggregation of multiple signals that reflect the network link + * metrics. For example, the network validation bit (see {@link + * NetworkCapabilities#NET_CAPABILITY_VALIDATED}), estimated first hop transport bandwidth + * and signal strength. + * + * @param networkQuality the required network quality. Defaults to NETWORK_QUALITY_ANY + * @hide */ @NonNull - public Builder setSsid(@Nullable String ssid) { - mSsid = ssid; + public Builder setNetworkQuality(@NetworkQuality int networkQuality) { + validateNetworkQuality(networkQuality); + + mNetworkQuality = networkQuality; return this; } - /** Build the VcnWifiUnderlyingNetworkTemplate. */ + /** + * Set the matching criteria for metered networks. + * + * <p>A template where setMetered(MATCH_REQUIRED) will only match metered networks (one + * without NET_CAPABILITY_NOT_METERED). A template where setMetered(MATCH_FORBIDDEN) will + * only match a network that is not metered (one with NET_CAPABILITY_NOT_METERED). + * + * @param matchCriteria the matching criteria for metered networks. Defaults to {@link + * #MATCH_ANY}. + * @see NetworkCapabilities#NET_CAPABILITY_NOT_METERED + */ + // The matching getter is defined in the super class. Please see {@link + // VcnUnderlyingNetworkTemplate#getMetered()} + @SuppressLint("MissingGetterMatchingBuilder") @NonNull - public VcnWifiUnderlyingNetworkTemplate build() { - return new VcnWifiUnderlyingNetworkTemplate(mNetworkQuality, mAllowMetered, mSsid); + public Builder setMetered(@MatchCriteria int matchCriteria) { + validateMatchCriteria(matchCriteria, "setMetered"); + + mMeteredMatchCriteria = matchCriteria; + return this; } - /** @hide */ - @Override - Builder self() { + /** + * Set the SSIDs with which a network can match this priority rule. + * + * @param ssids the matching SSIDs. Network with one of the matching SSIDs can match this + * priority rule. If the set is empty, any SSID will match. The default is an empty set. + */ + @NonNull + public Builder setSsids(@NonNull Set<String> ssids) { + validateSsids(ssids); + + mSsids.clear(); + mSsids.addAll(ssids); return this; } + + /** Build the VcnWifiUnderlyingNetworkTemplate. */ + @NonNull + public VcnWifiUnderlyingNetworkTemplate build() { + return new VcnWifiUnderlyingNetworkTemplate( + mNetworkQuality, mMeteredMatchCriteria, mSsids); + } } } diff --git a/core/java/android/nfc/BeamShareData.java b/core/java/android/nfc/BeamShareData.java index 6a40f98fe21c..ed3b74ab6308 100644 --- a/core/java/android/nfc/BeamShareData.java +++ b/core/java/android/nfc/BeamShareData.java @@ -47,13 +47,13 @@ public final class BeamShareData implements Parcelable { @Override public BeamShareData createFromParcel(Parcel source) { Uri[] uris = null; - NdefMessage msg = source.readParcelable(NdefMessage.class.getClassLoader(), android.nfc.NdefMessage.class); + NdefMessage msg = source.readParcelable(NdefMessage.class.getClassLoader()); int numUris = source.readInt(); if (numUris > 0) { uris = new Uri[numUris]; source.readTypedArray(uris, Uri.CREATOR); } - UserHandle userHandle = source.readParcelable(UserHandle.class.getClassLoader(), android.os.UserHandle.class); + UserHandle userHandle = source.readParcelable(UserHandle.class.getClassLoader()); int flags = source.readInt(); return new BeamShareData(msg, uris, userHandle, flags); diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java index 72fb4ae03a63..c62df407ca77 100644 --- a/core/java/android/os/Message.java +++ b/core/java/android/os/Message.java @@ -654,7 +654,7 @@ public final class Message implements Parcelable { arg1 = source.readInt(); arg2 = source.readInt(); if (source.readInt() != 0) { - obj = source.readParcelable(getClass().getClassLoader(), java.lang.Object.class); + obj = source.readParcelable(getClass().getClassLoader()); } when = source.readLong(); data = source.readBundle(); diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 92d652df35d9..d4a338bef02b 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -335,6 +335,12 @@ public final class PowerManager { public static final int USER_ACTIVITY_EVENT_FACE_DOWN = 5; /** + * User activity event type: There is a change in the device state. + * @hide + */ + public static final int USER_ACTIVITY_EVENT_DEVICE_STATE = 6; + + /** * User activity flag: If already dimmed, extend the dim timeout * but do not brighten. This flag is useful for keeping the screen on * a little longer without causing a visible change such as when diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java index 70aaa5e52c44..ebbfe47c4417 100644 --- a/core/java/android/os/StrictMode.java +++ b/core/java/android/os/StrictMode.java @@ -2993,7 +2993,7 @@ public final class StrictMode { * should be removed. */ public ViolationInfo(Parcel in, boolean unsetGatheringBit) { - mViolation = (Violation) in.readSerializable(android.os.strictmode.Violation.class.getClassLoader(), android.os.strictmode.Violation.class); + mViolation = (Violation) in.readSerializable(); int binderStackSize = in.readInt(); for (int i = 0; i < binderStackSize; i++) { StackTraceElement[] traceElements = new StackTraceElement[in.readInt()]; diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index b4f3fa0ae80c..edf628004728 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -1657,41 +1657,49 @@ public class UserManager { public @interface UserSwitchabilityResult {} /** - * A response code from {@link #removeUserOrSetEphemeral(int)} indicating that the specified - * user has been successfully removed. + * A response code from {@link #removeUserWhenPossible(UserHandle, boolean)} indicating that + * the specified user has been successfully removed. + * * @hide */ + @SystemApi public static final int REMOVE_RESULT_REMOVED = 0; /** - * A response code from {@link #removeUserOrSetEphemeral(int)} indicating that the specified - * user has had its {@link UserInfo#FLAG_EPHEMERAL} flag set to {@code true}, so that it will be - * removed when the user is stopped or on boot. + * A response code from {@link #removeUserWhenPossible(UserHandle, boolean)} indicating that + * the specified user is marked so that it will be removed when the user is stopped or on boot. + * * @hide */ - public static final int REMOVE_RESULT_SET_EPHEMERAL = 1; + @SystemApi + public static final int REMOVE_RESULT_DEFERRED = 1; /** - * A response code from {@link #removeUserOrSetEphemeral(int)} indicating that the specified - * user is already in the process of being removed. + * A response code from {@link #removeUserWhenPossible(UserHandle, boolean)} indicating that + * the specified user is already in the process of being removed. + * * @hide */ + @SystemApi public static final int REMOVE_RESULT_ALREADY_BEING_REMOVED = 2; /** - * A response code from {@link #removeUserOrSetEphemeral(int)} indicating that an error occurred - * that prevented the user from being removed or set as ephemeral. + * A response code from {@link #removeUserWhenPossible(UserHandle, boolean)} indicating that + * an error occurred that prevented the user from being removed or set as ephemeral. + * * @hide */ + @SystemApi public static final int REMOVE_RESULT_ERROR = 3; /** - * Possible response codes from {@link #removeUserOrSetEphemeral(int)}. + * Possible response codes from {@link #removeUserWhenPossible(UserHandle, boolean)}. + * * @hide */ @IntDef(prefix = { "REMOVE_RESULT_" }, value = { REMOVE_RESULT_REMOVED, - REMOVE_RESULT_SET_EPHEMERAL, + REMOVE_RESULT_DEFERRED, REMOVE_RESULT_ALREADY_BEING_REMOVED, REMOVE_RESULT_ERROR, }) @@ -4728,12 +4736,42 @@ public class UserManager { * the current user, then set the user as ephemeral so that it will be removed when it is * stopped. * - * @param evenWhenDisallowed when {@code true}, user is removed even if the caller user has the + * @param overrideDevicePolicy when {@code true}, user is removed even if the caller has + * the {@link #DISALLOW_REMOVE_USER} or {@link #DISALLOW_REMOVE_MANAGED_PROFILE} restriction + * + * @return the result code {@link #REMOVE_RESULT_REMOVED}, {@link #REMOVE_RESULT_DEFERRED}, + * {@link #REMOVE_RESULT_ALREADY_BEING_REMOVED}, or {@link #REMOVE_RESULT_ERROR}. + * + * @hide + */ + @SystemApi + @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS, + Manifest.permission.CREATE_USERS}) + public int removeUserWhenPossible(@NonNull UserHandle user, + boolean overrideDevicePolicy) { + try { + return mService.removeUserOrSetEphemeral(user.getIdentifier(), overrideDevicePolicy); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** + * Immediately removes the user or, if the user cannot be removed, such as when the user is + * the current user, then set the user as ephemeral so that it will be removed when it is + * stopped. + * + * @param evenWhenDisallowed when {@code true}, user is removed even if the caller has the * {@link #DISALLOW_REMOVE_USER} or {@link #DISALLOW_REMOVE_MANAGED_PROFILE} restriction * * @return the {@link RemoveResult} code + * + * @deprecated TODO(b/199446770): remove this call after converting all calls to + * removeUserWhenPossible(UserHandle, boolean) + * * @hide */ + @Deprecated @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS, Manifest.permission.CREATE_USERS}) public @RemoveResult int removeUserOrSetEphemeral(@UserIdInt int userId, diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java index 58315736f37f..8834725cc3e9 100644 --- a/core/java/android/os/VibrationAttributes.java +++ b/core/java/android/os/VibrationAttributes.java @@ -32,32 +32,30 @@ import java.util.Objects; public final class VibrationAttributes implements Parcelable { private static final String TAG = "VibrationAttributes"; - /** - * @hide - */ + /** @hide */ @IntDef(prefix = { "USAGE_CLASS_" }, value = { USAGE_CLASS_UNKNOWN, USAGE_CLASS_ALARM, USAGE_CLASS_FEEDBACK, }) @Retention(RetentionPolicy.SOURCE) - public @interface UsageClass{} + public @interface UsageClass {} - /** - * @hide - */ + /** @hide */ @IntDef(prefix = { "USAGE_" }, value = { USAGE_UNKNOWN, + USAGE_ACCESSIBILITY, USAGE_ALARM, - USAGE_RINGTONE, - USAGE_NOTIFICATION, USAGE_COMMUNICATION_REQUEST, - USAGE_TOUCH, - USAGE_PHYSICAL_EMULATION, USAGE_HARDWARE_FEEDBACK, + USAGE_MEDIA, + USAGE_NOTIFICATION, + USAGE_PHYSICAL_EMULATION, + USAGE_RINGTONE, + USAGE_TOUCH, }) @Retention(RetentionPolicy.SOURCE) - public @interface Usage{} + public @interface Usage {} /** * Vibration usage filter value to match all usages. diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java index 41ab062ee822..5de455695c01 100644 --- a/core/java/android/os/VibrationEffect.java +++ b/core/java/android/os/VibrationEffect.java @@ -260,7 +260,7 @@ public abstract class VibrationEffect implements Parcelable { for (int i = 0; i < timings.length; i++) { float parsedAmplitude = amplitudes[i] == DEFAULT_AMPLITUDE ? DEFAULT_AMPLITUDE : (float) amplitudes[i] / MAX_AMPLITUDE; - segments.add(new StepSegment(parsedAmplitude, /* frequency= */ 0, (int) timings[i])); + segments.add(new StepSegment(parsedAmplitude, /* frequencyHz= */ 0, (int) timings[i])); } VibrationEffect effect = new Composed(segments, repeat); effect.validate(); @@ -576,7 +576,7 @@ public abstract class VibrationEffect implements Parcelable { private final int mRepeatIndex; Composed(@NonNull Parcel in) { - this(in.readArrayList(VibrationEffectSegment.class.getClassLoader(), android.os.vibrator.VibrationEffectSegment.class), in.readInt()); + this(in.readArrayList(VibrationEffectSegment.class.getClassLoader()), in.readInt()); } Composed(@NonNull VibrationEffectSegment segment) { @@ -866,7 +866,7 @@ public abstract class VibrationEffect implements Parcelable { Preconditions.checkArgumentNonnegative(delay); if (delay > 0) { // Created a segment sustaining the zero amplitude to represent the delay. - addSegment(new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, + addSegment(new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ delay)); } return addSegments(effect); @@ -1033,26 +1033,27 @@ public abstract class VibrationEffect implements Parcelable { @NonNull public WaveformBuilder addStep(@FloatRange(from = 0f, to = 1f) float amplitude, @IntRange(from = 0) int duration) { - return addStep(amplitude, getPreviousFrequency(), duration); + mSegments.add(new StepSegment(amplitude, getPreviousFrequencyHz(), duration)); + return this; } /** - * Vibrate with given amplitude for the given duration, in millis, keeping the previous - * vibration frequency the same. + * Vibrate with given amplitude and frequency for the given duration, in millis. * * <p>If the duration is zero the vibrator will jump to new amplitude. * * @param amplitude The amplitude for this step - * @param frequency The frequency for this step + * @param frequencyHz The frequency for this step, in hertz * @param duration The duration of this step in milliseconds * @return The {@link WaveformBuilder} object to enable adding multiple steps in chain. */ @SuppressLint("MissingGetterMatchingBuilder") @NonNull public WaveformBuilder addStep(@FloatRange(from = 0f, to = 1f) float amplitude, - @FloatRange(from = -1f, to = 1f) float frequency, + @FloatRange(from = 1f) float frequencyHz, @IntRange(from = 0) int duration) { - mSegments.add(new StepSegment(amplitude, frequency, duration)); + Preconditions.checkArgument(frequencyHz >= 1, "Frequency must be >= 1"); + mSegments.add(new StepSegment(amplitude, frequencyHz, duration)); return this; } @@ -1070,7 +1071,9 @@ public abstract class VibrationEffect implements Parcelable { @NonNull public WaveformBuilder addRamp(@FloatRange(from = 0f, to = 1f) float amplitude, @IntRange(from = 0) int duration) { - return addRamp(amplitude, getPreviousFrequency(), duration); + mSegments.add(new RampSegment(getPreviousAmplitude(), amplitude, + getPreviousFrequencyHz(), getPreviousFrequencyHz(), duration)); + return this; } /** @@ -1080,22 +1083,23 @@ public abstract class VibrationEffect implements Parcelable { * <p>If the duration is zero the vibrator will jump to new amplitude and frequency. * * @param amplitude The final amplitude this ramp should reach - * @param frequency The final frequency this ramp should reach + * @param frequencyHz The final frequency this ramp should reach, in hertz * @param duration The duration of this ramp in milliseconds * @return The {@link WaveformBuilder} object to enable adding multiple steps in chain. */ @SuppressLint("MissingGetterMatchingBuilder") @NonNull public WaveformBuilder addRamp(@FloatRange(from = 0f, to = 1f) float amplitude, - @FloatRange(from = -1f, to = 1f) float frequency, + @FloatRange(from = 1f) float frequencyHz, @IntRange(from = 0) int duration) { - mSegments.add(new RampSegment(getPreviousAmplitude(), amplitude, getPreviousFrequency(), - frequency, duration)); + Preconditions.checkArgument(frequencyHz >= 1, "Frequency must be >= 1"); + mSegments.add(new RampSegment(getPreviousAmplitude(), amplitude, + getPreviousFrequencyHz(), frequencyHz, duration)); return this; } /** - * Compose all of the steps together into a single {@link VibrationEffect}. + * Compose all the steps together into a single {@link VibrationEffect}. * * The {@link WaveformBuilder} object is still valid after this call, so you can * continue adding more primitives to it and generating more {@link VibrationEffect}s by @@ -1109,7 +1113,7 @@ public abstract class VibrationEffect implements Parcelable { } /** - * Compose all of the steps together into a single {@link VibrationEffect}. + * Compose all the steps together into a single {@link VibrationEffect}. * * <p>To cause the pattern to repeat, pass the index at which to start the repetition * (starting at 0), or -1 to disable repeating. @@ -1131,13 +1135,13 @@ public abstract class VibrationEffect implements Parcelable { return effect; } - private float getPreviousFrequency() { + private float getPreviousFrequencyHz() { if (!mSegments.isEmpty()) { VibrationEffectSegment segment = mSegments.get(mSegments.size() - 1); if (segment instanceof StepSegment) { - return ((StepSegment) segment).getFrequency(); + return ((StepSegment) segment).getFrequencyHz(); } else if (segment instanceof RampSegment) { - return ((RampSegment) segment).getEndFrequency(); + return ((RampSegment) segment).getEndFrequencyHz(); } } return 0; diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java index c67c82e37cd2..eba9ff1bb4f8 100644 --- a/core/java/android/os/Vibrator.java +++ b/core/java/android/os/Vibrator.java @@ -17,20 +17,21 @@ package android.os; import android.annotation.CallbackExecutor; -import android.annotation.FloatRange; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; +import android.annotation.TestApi; import android.app.ActivityThread; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; +import android.content.res.Resources; import android.hardware.vibrator.IVibrator; import android.media.AudioAttributes; +import android.os.vibrator.VibrationConfig; import android.util.Log; -import android.util.Range; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -51,6 +52,7 @@ public abstract class Vibrator { * * @hide */ + @TestApi public static final int VIBRATION_INTENSITY_OFF = 0; /** @@ -58,6 +60,7 @@ public abstract class Vibrator { * * @hide */ + @TestApi public static final int VIBRATION_INTENSITY_LOW = 1; /** @@ -65,6 +68,7 @@ public abstract class Vibrator { * * @hide */ + @TestApi public static final int VIBRATION_INTENSITY_MEDIUM = 2; /** @@ -72,6 +76,7 @@ public abstract class Vibrator { * * @hide */ + @TestApi public static final int VIBRATION_INTENSITY_HIGH = 3; /** @@ -117,16 +122,12 @@ public abstract class Vibrator { } private final String mPackageName; - // The default vibration intensity level for haptic feedback. - @VibrationIntensity - private int mDefaultHapticFeedbackIntensity; - // The default vibration intensity level for notifications. - @VibrationIntensity - private int mDefaultNotificationVibrationIntensity; - // The default vibration intensity level for ringtones. - @VibrationIntensity - private int mDefaultRingVibrationIntensity; - private float mHapticChannelMaxVibrationAmplitude; + @Nullable + private final Resources mResources; + + // This is lazily loaded only for the few clients that need this (e. Settings app). + @Nullable + private volatile VibrationConfig mVibrationConfig; /** * @hide to prevent subclassing from outside of the framework @@ -134,8 +135,7 @@ public abstract class Vibrator { @UnsupportedAppUsage public Vibrator() { mPackageName = ActivityThread.currentPackageName(); - final Context ctx = ActivityThread.currentActivityThread().getSystemContext(); - loadVibrationConfig(ctx); + mResources = null; } /** @@ -143,26 +143,7 @@ public abstract class Vibrator { */ protected Vibrator(Context context) { mPackageName = context.getOpPackageName(); - loadVibrationConfig(context); - } - - private void loadVibrationConfig(Context context) { - mDefaultHapticFeedbackIntensity = loadDefaultIntensity(context, - com.android.internal.R.integer.config_defaultHapticFeedbackIntensity); - mDefaultNotificationVibrationIntensity = loadDefaultIntensity(context, - com.android.internal.R.integer.config_defaultNotificationVibrationIntensity); - mDefaultRingVibrationIntensity = loadDefaultIntensity(context, - com.android.internal.R.integer.config_defaultRingVibrationIntensity); - mHapticChannelMaxVibrationAmplitude = loadFloat(context, - com.android.internal.R.dimen.config_hapticChannelMaxVibrationAmplitude, 0); - } - - private int loadDefaultIntensity(Context ctx, int resId) { - return ctx != null ? ctx.getResources().getInteger(resId) : VIBRATION_INTENSITY_MEDIUM; - } - - private float loadFloat(Context ctx, int resId, float defaultValue) { - return ctx != null ? ctx.getResources().getFloat(resId) : defaultValue; + mResources = context.getResources(); } /** @@ -174,31 +155,30 @@ public abstract class Vibrator { return VibratorInfo.EMPTY_VIBRATOR_INFO; } - /** - * Get the default vibration intensity for haptic feedback. - * - * @hide - */ - public int getDefaultHapticFeedbackIntensity() { - return mDefaultHapticFeedbackIntensity; - } - - /** - * Get the default vibration intensity for notifications. - * - * @hide - */ - public int getDefaultNotificationVibrationIntensity() { - return mDefaultNotificationVibrationIntensity; + /** Get the static vibrator configuration from config.xml. */ + private VibrationConfig getConfig() { + if (mVibrationConfig == null) { + Resources resources = mResources; + if (resources == null) { + final Context ctx = ActivityThread.currentActivityThread().getSystemContext(); + resources = ctx != null ? ctx.getResources() : null; + } + // This might be constructed more than once, but it only loads static config data from a + // xml file, so it would be ok. + mVibrationConfig = new VibrationConfig(resources); + } + return mVibrationConfig; } /** - * Get the default vibration intensity for ringtones. + * Get the default vibration intensity for given usage. * * @hide */ - public int getDefaultRingVibrationIntensity() { - return mDefaultRingVibrationIntensity; + @TestApi + @VibrationIntensity + public int getDefaultVibrationIntensity(@VibrationAttributes.Usage int usage) { + return getConfig().getDefaultVibrationIntensity(usage); } /** @@ -271,43 +251,6 @@ public abstract class Vibrator { } /** - * Return a range of relative frequency values supported by the vibrator. - * - * <p>These values can be used to create waveforms that controls the vibration frequency via - * {@link VibrationEffect.WaveformBuilder}. - * - * @return A range of relative frequency values supported. The range will always contain the - * value 0, representing the device resonant frequency. Devices without frequency control will - * return the range [0,0]. Devices with frequency control will always return a range containing - * the safe range [-1, 1]. - * @hide - */ - public Range<Float> getRelativeFrequencyRange() { - return getInfo().getFrequencyRange(); - } - - /** - * Return the maximum amplitude the vibrator can play at given relative frequency. - * - * <p>Devices without frequency control will return 1 for the input zero (resonant frequency), - * and 0 to any other input. - * - * <p>Devices with frequency control will return the supported value, for input in - * {@link #getRelativeFrequencyRange()}, and 0 for any other input. - * - * <p>These values can be used to create waveforms that plays vibrations outside the resonant - * frequency via {@link VibrationEffect.WaveformBuilder}. - * - * @return a value in [0,1] representing the maximum amplitude the device can play at given - * relative frequency. - * @hide - */ - @FloatRange(from = 0, to = 1) - public float getMaximumAmplitude(float relativeFrequency) { - return getInfo().getMaxAmplitude(relativeFrequency); - } - - /** * Return the maximum amplitude the vibrator can play using the audio haptic channels. * * <p>This is a positive value, or {@link Float#NaN NaN} if it's unknown. If this returns a @@ -319,10 +262,7 @@ public abstract class Vibrator { * @hide */ public float getHapticChannelMaximumAmplitude() { - if (mHapticChannelMaxVibrationAmplitude <= 0) { - return Float.NaN; - } - return mHapticChannelMaxVibrationAmplitude; + return getConfig().getHapticChannelMaximumAmplitude(); } /** diff --git a/core/java/android/os/VibratorInfo.java b/core/java/android/os/VibratorInfo.java index 0a0e3c82f4ad..189e454f1488 100644 --- a/core/java/android/os/VibratorInfo.java +++ b/core/java/android/os/VibratorInfo.java @@ -21,7 +21,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.hardware.vibrator.Braking; import android.hardware.vibrator.IVibrator; -import android.util.Log; import android.util.MathUtils; import android.util.Range; import android.util.SparseBooleanArray; @@ -70,7 +69,7 @@ public class VibratorInfo implements Parcelable { mPwlePrimitiveDurationMax = in.readInt(); mPwleSizeMax = in.readInt(); mQFactor = in.readFloat(); - mFrequencyMapping = in.readParcelable(VibratorInfo.class.getClassLoader(), android.os.VibratorInfo.FrequencyMapping.class); + mFrequencyMapping = in.readParcelable(VibratorInfo.class.getClassLoader()); } /** @@ -345,49 +344,31 @@ public class VibratorInfo implements Parcelable { } /** - * Return a range of relative frequency values supported by the vibrator. + * Return a range of frequency values supported by the vibrator. * - * @return A range of relative frequency values supported. The range will always contain the - * value 0, representing the device resonant frequency. Devices without frequency control will - * return the range [0,0]. Devices with frequency control will always return a range containing - * the safe range [-1, 1]. + * @return A range of frequency values supported, in hertz. The range will always contain the + * device resonant frequency. Devices without frequency control will return null. * @hide */ - public Range<Float> getFrequencyRange() { - return mFrequencyMapping.mRelativeFrequencyRange; + @Nullable + public Range<Float> getFrequencyRangeHz() { + return mFrequencyMapping.mFrequencyRangeHz; } /** - * Return the maximum amplitude the vibrator can play at given relative frequency. + * Return the maximum amplitude the vibrator can play at given frequency. * + * @param frequencyHz The frequency, in hertz, for query. + * @return a value in [0,1] representing the maximum amplitude the device can play at given - * relative frequency. Devices without frequency control will return 1 for the input zero - * (resonant frequency), and 0 to any other input. Devices with frequency control will return - * the supported value, for input in {@code #getFrequencyRange()}, and 0 for any other input. + * frequency. Devices without frequency control will return 0 to any input. Devices with + * frequency control will return the supported value, for input in + * {@link #getFrequencyRangeHz()}, and 0 for any other input. * @hide */ @FloatRange(from = 0, to = 1) - public float getMaxAmplitude(float relativeFrequency) { - if (mFrequencyMapping.isEmpty()) { - // The vibrator has not provided values for frequency mapping. - // Return the expected behavior for devices without frequency control. - return Float.compare(relativeFrequency, 0) == 0 ? 1 : 0; - } - return mFrequencyMapping.getMaxAmplitude(relativeFrequency); - } - - /** - * Return absolute frequency value for this vibrator, in hertz, that corresponds to given - * relative frequency. - * - * @retur a value in hertz that corresponds to given relative frequency. Input values outside - * {@link #getFrequencyRange()} will return {@link Float#NaN}. Devices without frequency control - * will return {@link Float#NaN} for any input. - * @hide - */ - @FloatRange(from = 0) - public float getAbsoluteFrequency(float relativeFrequency) { - return mFrequencyMapping.toHertz(relativeFrequency); + public float getMaxAmplitude(float frequencyHz) { + return mFrequencyMapping.getMaxAmplitude(frequencyHz); } protected long getCapabilities() { @@ -468,134 +449,96 @@ public class VibratorInfo implements Parcelable { } /** - * Describes how frequency should be mapped to absolute values for a specific {@link Vibrator}. + * Describes the maximum relative output acceleration that can be achieved for each supported + * frequency in a specific vibrator. * * <p>This mapping is defined by the following parameters: * * <ol> - * <li>{@code minFrequency}, {@code resonantFrequency} and {@code frequencyResolution}, in - * hertz, provided by the vibrator. + * <li>{@code minFrequencyHz}, {@code resonantFrequencyHz} and {@code frequencyResolutionHz} + * provided by the vibrator in hertz. * <li>{@code maxAmplitudes} a list of values in [0,1] provided by the vibrator, where * {@code maxAmplitudes[i]} represents max supported amplitude at frequency - * {@code minFrequency + frequencyResolution * i}. - * <li>{@code maxFrequency = minFrequency + frequencyResolution * (maxAmplitudes.length-1)} - * <li>{@code suggestedSafeRangeHz} is the suggested frequency range in hertz that should be - * mapped to relative values -1 and 1, where 0 maps to {@code resonantFrequency}. - * </ol> - * - * <p>The mapping is defined linearly by the following points: - * - * <ol> - * <li>{@code toHertz(relativeMinFrequency) = minFrequency} - * <li>{@code toHertz(-1) = resonantFrequency - safeRange / 2} - * <li>{@code toHertz(0) = resonantFrequency} - * <li>{@code toHertz(1) = resonantFrequency + safeRange / 2} - * <li>{@code toHertz(relativeMaxFrequency) = maxFrequency} + * {@code minFrequencyHz + frequencyResolutionHz * i}. + * <li>{@code maxFrequencyHz = minFrequencyHz + * + frequencyResolutionHz * (maxAmplitudes.length-1)} * </ol> * * @hide */ public static final class FrequencyMapping implements Parcelable { + @Nullable + private final Range<Float> mFrequencyRangeHz; private final float mMinFrequencyHz; private final float mResonantFrequencyHz; private final float mFrequencyResolutionHz; - private final float mSuggestedSafeRangeHz; private final float[] mMaxAmplitudes; - // Relative fields calculated from input values: - private final Range<Float> mRelativeFrequencyRange; - FrequencyMapping(Parcel in) { - this(in.readFloat(), in.readFloat(), in.readFloat(), in.readFloat(), - in.createFloatArray()); + this(in.readFloat(), in.readFloat(), in.readFloat(), in.createFloatArray()); } /** * Default constructor. * - * @param minFrequencyHz Minimum supported frequency, in hertz. * @param resonantFrequencyHz The vibrator resonant frequency, in hertz. + * @param minFrequencyHz Minimum supported frequency, in hertz. * @param frequencyResolutionHz The frequency resolution, in hertz, used by the max * amplitudes mapping. - * @param suggestedSafeRangeHz The suggested range, in hertz, for the safe relative - * frequency range represented by [-1, 1]. * @param maxAmplitudes The max amplitude supported by each supported frequency, * starting at minimum frequency with jumps of frequency * resolution. * @hide */ - public FrequencyMapping(float minFrequencyHz, float resonantFrequencyHz, - float frequencyResolutionHz, float suggestedSafeRangeHz, float[] maxAmplitudes) { + public FrequencyMapping(float resonantFrequencyHz, float minFrequencyHz, + float frequencyResolutionHz, float[] maxAmplitudes) { mMinFrequencyHz = minFrequencyHz; mResonantFrequencyHz = resonantFrequencyHz; mFrequencyResolutionHz = frequencyResolutionHz; - mSuggestedSafeRangeHz = suggestedSafeRangeHz; mMaxAmplitudes = new float[maxAmplitudes == null ? 0 : maxAmplitudes.length]; if (maxAmplitudes != null) { System.arraycopy(maxAmplitudes, 0, mMaxAmplitudes, 0, maxAmplitudes.length); } - float maxFrequencyHz = - minFrequencyHz + frequencyResolutionHz * (mMaxAmplitudes.length - 1); - if (Float.isNaN(resonantFrequencyHz) || Float.isNaN(minFrequencyHz) - || Float.isNaN(frequencyResolutionHz) || Float.isNaN(suggestedSafeRangeHz) - || resonantFrequencyHz < minFrequencyHz - || resonantFrequencyHz > maxFrequencyHz) { - // Some required fields are undefined or have bad values. - // Leave this mapping empty. - mRelativeFrequencyRange = Range.create(0f, 0f); - return; - } + // If any required field is undefined then leave this mapping empty. + boolean isValid = !Float.isNaN(resonantFrequencyHz) + && !Float.isNaN(minFrequencyHz) + && !Float.isNaN(frequencyResolutionHz) + && (mMaxAmplitudes.length > 0); - // Calculate actual safe range, limiting the suggested one by the device supported range - float safeDelta = MathUtils.min( - suggestedSafeRangeHz / 2, - resonantFrequencyHz - minFrequencyHz, - maxFrequencyHz - resonantFrequencyHz); - mRelativeFrequencyRange = Range.create( - (minFrequencyHz - resonantFrequencyHz) / safeDelta, - (maxFrequencyHz - resonantFrequencyHz) / safeDelta); - } + float maxFrequencyHz = isValid + ? minFrequencyHz + frequencyResolutionHz * (mMaxAmplitudes.length - 1) + : Float.NaN; - /** - * Returns true if this frequency mapping is empty, i.e. the only supported relative - * frequency is 0 (resonant frequency). - */ - public boolean isEmpty() { - return Float.compare(mRelativeFrequencyRange.getLower(), - mRelativeFrequencyRange.getUpper()) == 0; + // If the non-empty mapping does not have min < resonant < max frequency respected + // then leave this mapping empty. + isValid &= !Float.isNaN(maxFrequencyHz) + && (resonantFrequencyHz >= minFrequencyHz) + && (resonantFrequencyHz <= maxFrequencyHz) + && (minFrequencyHz < maxFrequencyHz); + + mFrequencyRangeHz = isValid ? Range.create(minFrequencyHz, maxFrequencyHz) : null; } /** - * Returns the frequency value in hertz that is mapped to the given relative frequency. - * - * @return The mapped frequency, in hertz, or {@link Float#NaN} is value outside the device - * supported range. + * Returns true if this frequency mapping is empty, i.e. the only supported is the resonant + * frequency. */ - public float toHertz(float relativeFrequency) { - if (!mRelativeFrequencyRange.contains(relativeFrequency)) { - return Float.NaN; - } - float relativeMinFrequency = mRelativeFrequencyRange.getLower(); - if (Float.compare(relativeMinFrequency, 0) == 0) { - // relative supported range is [0,0], so toHertz(0) should be the resonant frequency - return mResonantFrequencyHz; - } - float shift = (mMinFrequencyHz - mResonantFrequencyHz) / relativeMinFrequency; - return mResonantFrequencyHz + relativeFrequency * shift; + public boolean isEmpty() { + return mFrequencyRangeHz == null; } /** - * Returns the maximum amplitude the vibrator can reach while playing at given relative - * frequency. + * Returns the maximum relative amplitude the vibrator can reach while playing at the + * given frequency. * - * @return A value in [0,1] representing the max amplitude supported at given relative - * frequency. This will return 0 if frequency is outside supported range, or if max - * amplitude mapping is empty. + * @param frequencyHz frequency, in hertz, for query. + * @return A value in [0,1] representing the max relative amplitude supported at the given + * frequency. This will return 0 if the frequency is outside the supported range, or if the + * mapping is empty. */ - public float getMaxAmplitude(float relativeFrequency) { - float frequencyHz = toHertz(relativeFrequency); - if (Float.isNaN(frequencyHz)) { + public float getMaxAmplitude(float frequencyHz) { + if (isEmpty() || Float.isNaN(frequencyHz)) { // Unsupported frequency requested, vibrator cannot play at this frequency. return 0; } @@ -603,13 +546,6 @@ public class VibratorInfo implements Parcelable { int floorIndex = (int) Math.floor(position); int ceilIndex = (int) Math.ceil(position); if (floorIndex < 0 || floorIndex >= mMaxAmplitudes.length) { - if (mMaxAmplitudes.length > 0) { - // This should never happen if the setup of relative frequencies was correct. - Log.w(TAG, "Max amplitudes has " + mMaxAmplitudes.length - + " entries and was expected to cover the frequency " + frequencyHz - + " Hz when starting at min frequency of " + mMinFrequencyHz - + " Hz with resolution of " + mFrequencyResolutionHz + " Hz."); - } return 0; } if (floorIndex != ceilIndex && ceilIndex < mMaxAmplitudes.length) { @@ -621,10 +557,9 @@ public class VibratorInfo implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeFloat(mMinFrequencyHz); dest.writeFloat(mResonantFrequencyHz); + dest.writeFloat(mMinFrequencyHz); dest.writeFloat(mFrequencyResolutionHz); - dest.writeFloat(mSuggestedSafeRangeHz); dest.writeFloatArray(mMaxAmplitudes); } @@ -645,14 +580,13 @@ public class VibratorInfo implements Parcelable { return Float.compare(mMinFrequencyHz, that.mMinFrequencyHz) == 0 && Float.compare(mResonantFrequencyHz, that.mResonantFrequencyHz) == 0 && Float.compare(mFrequencyResolutionHz, that.mFrequencyResolutionHz) == 0 - && Float.compare(mSuggestedSafeRangeHz, that.mSuggestedSafeRangeHz) == 0 && Arrays.equals(mMaxAmplitudes, that.mMaxAmplitudes); } @Override public int hashCode() { int hashCode = Objects.hash(mMinFrequencyHz, mFrequencyResolutionHz, - mFrequencyResolutionHz, mSuggestedSafeRangeHz); + mFrequencyResolutionHz); hashCode = 31 * hashCode + Arrays.hashCode(mMaxAmplitudes); return hashCode; } @@ -660,13 +594,10 @@ public class VibratorInfo implements Parcelable { @Override public String toString() { return "FrequencyMapping{" - + "mRelativeFrequencyRange=" + mRelativeFrequencyRange + + "mFrequencyRange=" + mFrequencyRangeHz + ", mMinFrequency=" + mMinFrequencyHz + ", mResonantFrequency=" + mResonantFrequencyHz - + ", mMaxFrequency=" - + (mMinFrequencyHz + mFrequencyResolutionHz * (mMaxAmplitudes.length - 1)) + ", mFrequencyResolution=" + mFrequencyResolutionHz - + ", mSuggestedSafeRange=" + mSuggestedSafeRangeHz + ", mMaxAmplitudes count=" + mMaxAmplitudes.length + '}'; } @@ -699,7 +630,7 @@ public class VibratorInfo implements Parcelable { private int mPwleSizeMax; private float mQFactor = Float.NaN; private FrequencyMapping mFrequencyMapping = - new FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, Float.NaN, null); + new FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, null); /** A builder class for a {@link VibratorInfo}. */ public Builder(int id) { diff --git a/core/java/android/os/WorkSource.java b/core/java/android/os/WorkSource.java index e899f7729efa..6588b5748d09 100644 --- a/core/java/android/os/WorkSource.java +++ b/core/java/android/os/WorkSource.java @@ -130,7 +130,7 @@ public class WorkSource implements Parcelable { int numChains = in.readInt(); if (numChains > 0) { mChains = new ArrayList<>(numChains); - in.readParcelableList(mChains, WorkChain.class.getClassLoader(), android.os.WorkSource.WorkChain.class); + in.readParcelableList(mChains, WorkChain.class.getClassLoader()); } else { mChains = null; } diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java index 8ee52c21e869..b78bb253bcf7 100644 --- a/core/java/android/os/storage/StorageVolume.java +++ b/core/java/android/os/storage/StorageVolume.java @@ -168,7 +168,7 @@ public final class StorageVolume implements Parcelable { mExternallyManaged = in.readInt() != 0; mAllowMassStorage = in.readInt() != 0; mMaxFileSize = in.readLong(); - mOwner = in.readParcelable(null, android.os.UserHandle.class); + mOwner = in.readParcelable(null); if (in.readInt() != 0) { mUuid = StorageManager.convert(in.readString8()); } else { diff --git a/core/java/android/os/vibrator/RampSegment.java b/core/java/android/os/vibrator/RampSegment.java index 3ec56366d921..9e1f6360b090 100644 --- a/core/java/android/os/vibrator/RampSegment.java +++ b/core/java/android/os/vibrator/RampSegment.java @@ -29,14 +29,20 @@ import java.util.Objects; * Representation of {@link VibrationEffectSegment} that ramps vibration amplitude and/or frequency * for a specified duration. * + * <p>The amplitudes are expressed by float values in the range [0, 1], representing the relative + * output acceleration for the vibrator. The frequencies are expressed in hertz by positive finite + * float values. The special value zero is used here for an unspecified frequency, and will be + * automatically mapped to the device's default vibration frequency (usually the resonant + * frequency). + * * @hide */ @TestApi public final class RampSegment extends VibrationEffectSegment { private final float mStartAmplitude; - private final float mStartFrequency; + private final float mStartFrequencyHz; private final float mEndAmplitude; - private final float mEndFrequency; + private final float mEndFrequencyHz; private final int mDuration; RampSegment(@NonNull Parcel in) { @@ -44,12 +50,12 @@ public final class RampSegment extends VibrationEffectSegment { } /** @hide */ - public RampSegment(float startAmplitude, float endAmplitude, float startFrequency, - float endFrequency, int duration) { + public RampSegment(float startAmplitude, float endAmplitude, float startFrequencyHz, + float endFrequencyHz, int duration) { mStartAmplitude = startAmplitude; mEndAmplitude = endAmplitude; - mStartFrequency = startFrequency; - mEndFrequency = endFrequency; + mStartFrequencyHz = startFrequencyHz; + mEndFrequencyHz = endFrequencyHz; mDuration = duration; } @@ -61,8 +67,8 @@ public final class RampSegment extends VibrationEffectSegment { RampSegment other = (RampSegment) o; return Float.compare(mStartAmplitude, other.mStartAmplitude) == 0 && Float.compare(mEndAmplitude, other.mEndAmplitude) == 0 - && Float.compare(mStartFrequency, other.mStartFrequency) == 0 - && Float.compare(mEndFrequency, other.mEndFrequency) == 0 + && Float.compare(mStartFrequencyHz, other.mStartFrequencyHz) == 0 + && Float.compare(mEndFrequencyHz, other.mEndFrequencyHz) == 0 && mDuration == other.mDuration; } @@ -74,12 +80,12 @@ public final class RampSegment extends VibrationEffectSegment { return mEndAmplitude; } - public float getStartFrequency() { - return mStartFrequency; + public float getStartFrequencyHz() { + return mStartFrequencyHz; } - public float getEndFrequency() { - return mEndFrequency; + public float getEndFrequencyHz() { + return mEndFrequencyHz; } @Override @@ -102,6 +108,12 @@ public final class RampSegment extends VibrationEffectSegment { /** @hide */ @Override public void validate() { + Preconditions.checkArgumentNonNegative(mStartFrequencyHz, + "Frequencies must all be >= 0, got start frequency of " + mStartFrequencyHz); + Preconditions.checkArgumentFinite(mStartFrequencyHz, "startFrequencyHz"); + Preconditions.checkArgumentNonNegative(mEndFrequencyHz, + "Frequencies must all be >= 0, got end frequency of " + mEndFrequencyHz); + Preconditions.checkArgumentFinite(mEndFrequencyHz, "endFrequencyHz"); Preconditions.checkArgumentNonnegative(mDuration, "Durations must all be >= 0, got " + mDuration); Preconditions.checkArgumentInRange(mStartAmplitude, 0f, 1f, "startAmplitude"); @@ -126,7 +138,8 @@ public final class RampSegment extends VibrationEffectSegment { && Float.compare(mEndAmplitude, newEndAmplitude) == 0) { return this; } - return new RampSegment(newStartAmplitude, newEndAmplitude, mStartFrequency, mEndFrequency, + return new RampSegment(newStartAmplitude, newEndAmplitude, mStartFrequencyHz, + mEndFrequencyHz, mDuration); } @@ -139,7 +152,7 @@ public final class RampSegment extends VibrationEffectSegment { @Override public int hashCode() { - return Objects.hash(mStartAmplitude, mEndAmplitude, mStartFrequency, mEndFrequency, + return Objects.hash(mStartAmplitude, mEndAmplitude, mStartFrequencyHz, mEndFrequencyHz, mDuration); } @@ -147,8 +160,8 @@ public final class RampSegment extends VibrationEffectSegment { public String toString() { return "Ramp{startAmplitude=" + mStartAmplitude + ", endAmplitude=" + mEndAmplitude - + ", startFrequency=" + mStartFrequency - + ", endFrequency=" + mEndFrequency + + ", startFrequencyHz=" + mStartFrequencyHz + + ", endFrequencyHz=" + mEndFrequencyHz + ", duration=" + mDuration + "}"; } @@ -163,8 +176,8 @@ public final class RampSegment extends VibrationEffectSegment { out.writeInt(PARCEL_TOKEN_RAMP); out.writeFloat(mStartAmplitude); out.writeFloat(mEndAmplitude); - out.writeFloat(mStartFrequency); - out.writeFloat(mEndFrequency); + out.writeFloat(mStartFrequencyHz); + out.writeFloat(mEndFrequencyHz); out.writeInt(mDuration); } diff --git a/core/java/android/os/vibrator/StepSegment.java b/core/java/android/os/vibrator/StepSegment.java index 69a381f5c558..c6795111d496 100644 --- a/core/java/android/os/vibrator/StepSegment.java +++ b/core/java/android/os/vibrator/StepSegment.java @@ -30,12 +30,18 @@ import java.util.Objects; * Representation of {@link VibrationEffectSegment} that holds a fixed vibration amplitude and * frequency for a specified duration. * + * <p>The amplitude is expressed by a float value in the range [0, 1], representing the relative + * output acceleration for the vibrator. The frequency is expressed in hertz by a positive finite + * float value. The special value zero is used here for an unspecified frequency, and will be + * automatically mapped to the device's default vibration frequency (usually the resonant + * frequency). + * * @hide */ @TestApi public final class StepSegment extends VibrationEffectSegment { private final float mAmplitude; - private final float mFrequency; + private final float mFrequencyHz; private final int mDuration; StepSegment(@NonNull Parcel in) { @@ -43,9 +49,9 @@ public final class StepSegment extends VibrationEffectSegment { } /** @hide */ - public StepSegment(float amplitude, float frequency, int duration) { + public StepSegment(float amplitude, float frequencyHz, int duration) { mAmplitude = amplitude; - mFrequency = frequency; + mFrequencyHz = frequencyHz; mDuration = duration; } @@ -56,7 +62,7 @@ public final class StepSegment extends VibrationEffectSegment { } StepSegment other = (StepSegment) o; return Float.compare(mAmplitude, other.mAmplitude) == 0 - && Float.compare(mFrequency, other.mFrequency) == 0 + && Float.compare(mFrequencyHz, other.mFrequencyHz) == 0 && mDuration == other.mDuration; } @@ -64,8 +70,8 @@ public final class StepSegment extends VibrationEffectSegment { return mAmplitude; } - public float getFrequency() { - return mFrequency; + public float getFrequencyHz() { + return mFrequencyHz; } @Override @@ -89,6 +95,9 @@ public final class StepSegment extends VibrationEffectSegment { /** @hide */ @Override public void validate() { + Preconditions.checkArgumentNonNegative(mFrequencyHz, + "Frequencies must all be >= 0, got " + mFrequencyHz); + Preconditions.checkArgumentFinite(mFrequencyHz, "frequencyHz"); Preconditions.checkArgumentNonnegative(mDuration, "Durations must all be >= 0, got " + mDuration); if (Float.compare(mAmplitude, VibrationEffect.DEFAULT_AMPLITUDE) != 0) { @@ -108,7 +117,8 @@ public final class StepSegment extends VibrationEffectSegment { if (Float.compare(mAmplitude, VibrationEffect.DEFAULT_AMPLITUDE) != 0) { return this; } - return new StepSegment((float) defaultAmplitude / VibrationEffect.MAX_AMPLITUDE, mFrequency, + return new StepSegment((float) defaultAmplitude / VibrationEffect.MAX_AMPLITUDE, + mFrequencyHz, mDuration); } @@ -119,7 +129,7 @@ public final class StepSegment extends VibrationEffectSegment { if (Float.compare(mAmplitude, VibrationEffect.DEFAULT_AMPLITUDE) == 0) { return this; } - return new StepSegment(VibrationEffect.scale(mAmplitude, scaleFactor), mFrequency, + return new StepSegment(VibrationEffect.scale(mAmplitude, scaleFactor), mFrequencyHz, mDuration); } @@ -132,13 +142,13 @@ public final class StepSegment extends VibrationEffectSegment { @Override public int hashCode() { - return Objects.hash(mAmplitude, mFrequency, mDuration); + return Objects.hash(mAmplitude, mFrequencyHz, mDuration); } @Override public String toString() { return "Step{amplitude=" + mAmplitude - + ", frequency=" + mFrequency + + ", frequencyHz=" + mFrequencyHz + ", duration=" + mDuration + "}"; } @@ -152,7 +162,7 @@ public final class StepSegment extends VibrationEffectSegment { public void writeToParcel(@NonNull Parcel out, int flags) { out.writeInt(PARCEL_TOKEN_STEP); out.writeFloat(mAmplitude); - out.writeFloat(mFrequency); + out.writeFloat(mFrequencyHz); out.writeInt(mDuration); } diff --git a/core/java/android/os/vibrator/VibrationConfig.java b/core/java/android/os/vibrator/VibrationConfig.java new file mode 100644 index 000000000000..4a61472e6205 --- /dev/null +++ b/core/java/android/os/vibrator/VibrationConfig.java @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os.vibrator; + +import static android.os.VibrationAttributes.USAGE_ACCESSIBILITY; +import static android.os.VibrationAttributes.USAGE_ALARM; +import static android.os.VibrationAttributes.USAGE_COMMUNICATION_REQUEST; +import static android.os.VibrationAttributes.USAGE_HARDWARE_FEEDBACK; +import static android.os.VibrationAttributes.USAGE_MEDIA; +import static android.os.VibrationAttributes.USAGE_NOTIFICATION; +import static android.os.VibrationAttributes.USAGE_PHYSICAL_EMULATION; +import static android.os.VibrationAttributes.USAGE_RINGTONE; +import static android.os.VibrationAttributes.USAGE_TOUCH; +import static android.os.VibrationAttributes.USAGE_UNKNOWN; + +import android.annotation.Nullable; +import android.content.res.Resources; +import android.os.VibrationAttributes; +import android.os.Vibrator; +import android.os.Vibrator.VibrationIntensity; + +/** + * List of device-specific internal vibration configuration loaded from platform config.xml. + * + * <p>This should not be public, but some individual values are exposed by {@link Vibrator} by + * hidden methods, made available to Settings, SysUI and other platform client code. They can also + * be individually exposed with the necessary permissions by the {@link Vibrator} service. + * + * @hide + */ +public class VibrationConfig { + + // TODO(b/191150049): move these to vibrator static config file + private final float mHapticChannelMaxVibrationAmplitude; + private final int mRampStepDurationMs; + private final int mRampDownDurationMs; + + @VibrationIntensity + private final int mDefaultAlarmVibrationIntensity; + @VibrationIntensity + private final int mDefaultHapticFeedbackIntensity; + @VibrationIntensity + private final int mDefaultMediaVibrationIntensity; + @VibrationIntensity + private final int mDefaultNotificationVibrationIntensity; + @VibrationIntensity + private final int mDefaultRingVibrationIntensity; + + /** @hide */ + public VibrationConfig(@Nullable Resources resources) { + mHapticChannelMaxVibrationAmplitude = loadFloat(resources, + com.android.internal.R.dimen.config_hapticChannelMaxVibrationAmplitude, 0); + mRampDownDurationMs = loadInteger(resources, + com.android.internal.R.integer.config_vibrationWaveformRampDownDuration, 0); + mRampStepDurationMs = loadInteger(resources, + com.android.internal.R.integer.config_vibrationWaveformRampStepDuration, 0); + + mDefaultAlarmVibrationIntensity = loadDefaultIntensity(resources, + com.android.internal.R.integer.config_defaultAlarmVibrationIntensity); + mDefaultHapticFeedbackIntensity = loadDefaultIntensity(resources, + com.android.internal.R.integer.config_defaultHapticFeedbackIntensity); + mDefaultMediaVibrationIntensity = loadDefaultIntensity(resources, + com.android.internal.R.integer.config_defaultMediaVibrationIntensity); + mDefaultNotificationVibrationIntensity = loadDefaultIntensity(resources, + com.android.internal.R.integer.config_defaultNotificationVibrationIntensity); + mDefaultRingVibrationIntensity = loadDefaultIntensity(resources, + com.android.internal.R.integer.config_defaultRingVibrationIntensity); + } + + @VibrationIntensity + private static int loadDefaultIntensity(@Nullable Resources res, int resId) { + int defaultIntensity = Vibrator.VIBRATION_INTENSITY_MEDIUM; + int value = loadInteger(res, resId, defaultIntensity); + if (value < Vibrator.VIBRATION_INTENSITY_OFF || value > Vibrator.VIBRATION_INTENSITY_HIGH) { + return defaultIntensity; + } + return value; + } + + private static float loadFloat(@Nullable Resources res, int resId, float defaultValue) { + return res != null ? res.getFloat(resId) : defaultValue; + } + + private static int loadInteger(@Nullable Resources res, int resId, int defaultValue) { + return res != null ? res.getInteger(resId) : defaultValue; + } + + /** + * Return the maximum amplitude the vibrator can play using the audio haptic channels. + * + * @return a positive value representing the maximum absolute value the device can play signals + * from audio haptic channels, or {@link Float#NaN NaN} if it's unknown. + */ + public float getHapticChannelMaximumAmplitude() { + if (mHapticChannelMaxVibrationAmplitude <= 0) { + return Float.NaN; + } + return mHapticChannelMaxVibrationAmplitude; + } + + /** + * The duration, in milliseconds, that should be applied to the ramp to turn off the vibrator + * when a vibration is cancelled or finished at non-zero amplitude. + */ + public int getRampDownDurationMs() { + if (mRampDownDurationMs < 0) { + return 0; + } + return mRampDownDurationMs; + } + + /** + * The duration, in milliseconds, that should be applied to convert vibration effect's + * {@link android.os.vibrator.RampSegment} to a {@link android.os.vibrator.StepSegment} on + * devices without PWLE support. + */ + public int getRampStepDurationMs() { + if (mRampStepDurationMs < 0) { + return 0; + } + return mRampStepDurationMs; + } + + /** Get the default vibration intensity for given usage. */ + @VibrationIntensity + public int getDefaultVibrationIntensity(@VibrationAttributes.Usage int usage) { + switch (usage) { + case USAGE_ALARM: + return mDefaultAlarmVibrationIntensity; + case USAGE_NOTIFICATION: + case USAGE_COMMUNICATION_REQUEST: + return mDefaultNotificationVibrationIntensity; + case USAGE_RINGTONE: + return mDefaultRingVibrationIntensity; + case USAGE_TOUCH: + case USAGE_HARDWARE_FEEDBACK: + case USAGE_PHYSICAL_EMULATION: + case USAGE_ACCESSIBILITY: + return mDefaultHapticFeedbackIntensity; + case USAGE_MEDIA: + case USAGE_UNKNOWN: + // fall through + default: + return mDefaultMediaVibrationIntensity; + } + } + + @Override + public String toString() { + return "VibrationConfig{" + + "mHapticChannelMaxVibrationAmplitude=" + mHapticChannelMaxVibrationAmplitude + + ", mRampStepDurationMs=" + mRampStepDurationMs + + ", mRampDownDurationMs=" + mRampDownDurationMs + + ", mDefaultAlarmIntensity=" + mDefaultAlarmVibrationIntensity + + ", mDefaultHapticFeedbackIntensity=" + mDefaultHapticFeedbackIntensity + + ", mDefaultMediaIntensity=" + mDefaultMediaVibrationIntensity + + ", mDefaultNotificationIntensity=" + mDefaultNotificationVibrationIntensity + + ", mDefaultRingIntensity=" + mDefaultRingVibrationIntensity + + "}"; + } +} diff --git a/core/java/android/permission/IPermissionController.aidl b/core/java/android/permission/IPermissionController.aidl index 0e32a78411dd..5814bac06f59 100644 --- a/core/java/android/permission/IPermissionController.aidl +++ b/core/java/android/permission/IPermissionController.aidl @@ -56,4 +56,6 @@ oneway interface IPermissionController { in AndroidFuture<String> callback); void getUnusedAppCount( in AndroidFuture callback); + void selfRevokePermissions(in String packageName, in List<String> permissions, + in AndroidFuture callback); } diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl index 4a94c32501a1..8e5581b1b80e 100644 --- a/core/java/android/permission/IPermissionManager.aidl +++ b/core/java/android/permission/IPermissionManager.aidl @@ -67,6 +67,8 @@ interface IPermissionManager { void revokeRuntimePermission(String packageName, String permissionName, int userId, String reason); + void revokePostNotificationPermissionWithoutKillForTest(String packageName, int userId); + boolean shouldShowRequestPermissionRationale(String packageName, String permissionName, int userId); @@ -74,6 +76,8 @@ interface IPermissionManager { List<SplitPermissionInfoParcelable> getSplitPermissions(); + void selfRevokePermissions(String packageName, in List<String> permissions); + void startOneTimePermissionSession(String packageName, int userId, long timeout, int importanceToResetTimer, int importanceToKeepSessionAlive); diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java index a0788e70b247..47cd10765da0 100644 --- a/core/java/android/permission/PermissionControllerManager.java +++ b/core/java/android/permission/PermissionControllerManager.java @@ -817,4 +817,40 @@ public final class PermissionControllerManager { } }); } + + /** + * Triggers the revocation of one or more permissions for a package, under the following + * conditions: + * <ul> + * <li>The package {@code packageName} must be under the same UID as the calling process + * (typically, the target package is the calling package). + * <li>Each permission in {@code permissions} must be granted to the package + * {@code packageName}. + * <li>Each permission in {@code permissions} must be a runtime permission. + * </ul> + * <p> + * For every permission in {@code permissions}, the entire permission group it belongs to will + * be revoked. This revocation happens asynchronously and kills all processes running in the + * same UID as {@code packageName}. It will be triggered once it is safe to do so. + * + * @param packageName The name of the package for which the permissions will be revoked. + * @param permissions List of permissions to be revoked. + * + * @see Context#selfRevokePermissions(Collection) + * + * @hide + */ + public void selfRevokePermissions(@NonNull String packageName, + @NonNull List<String> permissions) { + mRemoteService.postAsync(service -> { + AndroidFuture<Void> future = new AndroidFuture<>(); + service.selfRevokePermissions(packageName, permissions, future); + return future; + }).whenComplete((result, err) -> { + if (err != null) { + Log.e(TAG, "Failed to self revoke " + String.join(",", permissions) + + " for package " + packageName, err); + } + }); + } } diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java index c9793032c7eb..dcbab62530b1 100644 --- a/core/java/android/permission/PermissionControllerService.java +++ b/core/java/android/permission/PermissionControllerService.java @@ -324,6 +324,27 @@ public abstract class PermissionControllerService extends Service { @NonNull Consumer<String> callback) { throw new AbstractMethodError("Must be overridden in implementing class"); } + + /** + * Triggers the revocation of one or more permissions for a package. This should only be called + * at the request of {@code packageName}. + * <p> + * For every permission in {@code permissions}, the entire permission group it belongs to will + * be revoked. This revocation happens asynchronously and kills all processes running in the + * same UID as {@code packageName}. It will be triggered once it is safe to do so. + * + * @param packageName The name of the package for which the permissions will be revoked. + * @param permissions List of permissions to be revoked. + * @param callback Callback waiting for operation to be complete. + * + * @see PermissionManager#selfRevokePermissions(java.util.Collection) + */ + @BinderThread + public void onSelfRevokePermissions(@NonNull String packageName, + @NonNull List<String> permissions, @NonNull Runnable callback) { + throw new AbstractMethodError("Must be overridden in implementing class"); + } + /** * Get a user-readable sentence, describing the set of privileges that are to be granted to a * companion app managing a device of the given profile. @@ -646,6 +667,20 @@ public abstract class PermissionControllerService extends Service { callback.completeExceptionally(t); } } + + @Override + public void selfRevokePermissions(@NonNull String packageName, + @NonNull List<String> permissions, @NonNull AndroidFuture callback) { + try { + enforceSomePermissionsGrantedToCaller( + Manifest.permission.REVOKE_RUNTIME_PERMISSIONS); + Objects.requireNonNull(callback); + onSelfRevokePermissions(packageName, permissions, + () -> callback.complete(null)); + } catch (Throwable t) { + callback.completeExceptionally(t); + } + } }; } } diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java index 3ea50e98879b..13941dc5ef82 100644 --- a/core/java/android/permission/PermissionManager.java +++ b/core/java/android/permission/PermissionManager.java @@ -68,6 +68,7 @@ import com.android.internal.annotations.Immutable; import com.android.internal.util.CollectionUtils; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Objects; @@ -561,6 +562,19 @@ public final class PermissionManager { } /** + * @see Context#selfRevokePermissions(Collection) + * @hide + */ + public void selfRevokePermissions(@NonNull Collection<String> permissions) { + try { + mPermissionManager.selfRevokePermissions(mContext.getPackageName(), + new ArrayList<String>(permissions)); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Gets the state flags associated with a permission. * * @param packageName the package name for which to get the flags @@ -1362,6 +1376,26 @@ public final class PermissionManager { return false; } + /** + * Revoke the POST_NOTIFICATIONS permission, without killing the app. This method must ONLY BE + * USED in CTS or local tests. + * + * @param packageName The package to be revoked + * @param userId The user for which to revoke + * + * @hide + */ + @TestApi + public void revokePostNotificationPermissionWithoutKillForTest(@NonNull String packageName, + int userId) { + try { + mPermissionManager.revokePostNotificationPermissionWithoutKillForTest(packageName, + userId); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + /* @hide */ private static int checkPermissionUncached(@Nullable String permission, int pid, int uid) { final IActivityManager am = ActivityManager.getService(); diff --git a/core/java/android/print/PrintJobInfo.java b/core/java/android/print/PrintJobInfo.java index 9bdfd8e69c00..67249be2b806 100644 --- a/core/java/android/print/PrintJobInfo.java +++ b/core/java/android/print/PrintJobInfo.java @@ -231,24 +231,24 @@ public final class PrintJobInfo implements Parcelable { } private PrintJobInfo(@NonNull Parcel parcel) { - mId = parcel.readParcelable(null, android.print.PrintJobId.class); + mId = parcel.readParcelable(null); mLabel = parcel.readString(); - mPrinterId = parcel.readParcelable(null, android.print.PrinterId.class); + mPrinterId = parcel.readParcelable(null); mPrinterName = parcel.readString(); mState = parcel.readInt(); mAppId = parcel.readInt(); mTag = parcel.readString(); mCreationTime = parcel.readLong(); mCopies = parcel.readInt(); - Parcelable[] parcelables = parcel.readParcelableArray(null); + Parcelable[] parcelables = parcel.readParcelableArray(null, PageRange.class); if (parcelables != null) { mPageRanges = new PageRange[parcelables.length]; for (int i = 0; i < parcelables.length; i++) { mPageRanges[i] = (PageRange) parcelables[i]; } } - mAttributes = (PrintAttributes) parcel.readParcelable(null, android.print.PrintAttributes.class); - mDocumentInfo = (PrintDocumentInfo) parcel.readParcelable(null, android.print.PrintDocumentInfo.class); + mAttributes = (PrintAttributes) parcel.readParcelable(null); + mDocumentInfo = (PrintDocumentInfo) parcel.readParcelable(null); mProgress = parcel.readFloat(); mStatus = parcel.readCharSequence(); mStatusRes = parcel.readInt(); diff --git a/core/java/android/print/PrinterId.java b/core/java/android/print/PrinterId.java index 284e122fc103..25260c473709 100644 --- a/core/java/android/print/PrinterId.java +++ b/core/java/android/print/PrinterId.java @@ -48,7 +48,7 @@ public final class PrinterId implements Parcelable { } private PrinterId(@NonNull Parcel parcel) { - mServiceName = Preconditions.checkNotNull((ComponentName) parcel.readParcelable(null, android.content.ComponentName.class)); + mServiceName = Preconditions.checkNotNull((ComponentName) parcel.readParcelable(null)); mLocalId = Preconditions.checkNotNull(parcel.readString()); } diff --git a/core/java/android/print/PrinterInfo.java b/core/java/android/print/PrinterInfo.java index 2f93e404a211..8e03e3eb3f22 100644 --- a/core/java/android/print/PrinterInfo.java +++ b/core/java/android/print/PrinterInfo.java @@ -270,15 +270,15 @@ public final class PrinterInfo implements Parcelable { private PrinterInfo(Parcel parcel) { // mName can be null due to unchecked set in Builder.setName and status can be invalid // due to unchecked set in Builder.setStatus, hence we can only check mId for a valid state - mId = checkPrinterId((PrinterId) parcel.readParcelable(null, android.print.PrinterId.class)); + mId = checkPrinterId((PrinterId) parcel.readParcelable(null)); mName = checkName(parcel.readString()); mStatus = checkStatus(parcel.readInt()); mDescription = parcel.readString(); - mCapabilities = parcel.readParcelable(null, android.print.PrinterCapabilitiesInfo.class); + mCapabilities = parcel.readParcelable(null); mIconResourceId = parcel.readInt(); mHasCustomPrinterIcon = parcel.readByte() != 0; mCustomPrinterIconGen = parcel.readInt(); - mInfoIntent = parcel.readParcelable(null, android.app.PendingIntent.class); + mInfoIntent = parcel.readParcelable(null); } @Override diff --git a/core/java/android/printservice/PrintServiceInfo.java b/core/java/android/printservice/PrintServiceInfo.java index 347955718f78..0c1b61d583b3 100644 --- a/core/java/android/printservice/PrintServiceInfo.java +++ b/core/java/android/printservice/PrintServiceInfo.java @@ -76,7 +76,7 @@ public final class PrintServiceInfo implements Parcelable { public PrintServiceInfo(Parcel parcel) { mId = parcel.readString(); mIsEnabled = parcel.readByte() != 0; - mResolveInfo = parcel.readParcelable(null, android.content.pm.ResolveInfo.class); + mResolveInfo = parcel.readParcelable(null); mSettingsActivityName = parcel.readString(); mAddPrintersActivityName = parcel.readString(); mAdvancedPrintOptionsActivityName = parcel.readString(); diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index a97d4609539b..72e28630da40 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -4603,6 +4603,43 @@ public final class Settings { public static final String VIBRATE_INPUT_DEVICES = "vibrate_input_devices"; /** + * The intensity of alarm vibrations, if configurable. + * + * Not all devices are capable of changing their vibration intensity; on these devices + * there will likely be no difference between the various vibration intensities except for + * intensity 0 (off) and the rest. + * + * <b>Values:</b><br/> + * 0 - Vibration is disabled<br/> + * 1 - Weak vibrations<br/> + * 2 - Medium vibrations<br/> + * 3 - Strong vibrations + * @hide + */ + public static final String ALARM_VIBRATION_INTENSITY = + "alarm_vibration_intensity"; + + /** + * The intensity of media vibrations, if configurable. + * + * This includes any vibration that is part of media, such as music, movie, soundtrack, + * game or animations. + * + * Not all devices are capable of changing their vibration intensity; on these devices + * there will likely be no difference between the various vibration intensities except for + * intensity 0 (off) and the rest. + * + * <b>Values:</b><br/> + * 0 - Vibration is disabled<br/> + * 1 - Weak vibrations<br/> + * 2 - Medium vibrations<br/> + * 3 - Strong vibrations + * @hide + */ + public static final String MEDIA_VIBRATION_INTENSITY = + "media_vibration_intensity"; + + /** * The intensity of notification vibrations, if configurable. * * Not all devices are capable of changing their vibration intensity; on these devices @@ -4619,6 +4656,7 @@ public final class Settings { @Readable public static final String NOTIFICATION_VIBRATION_INTENSITY = "notification_vibration_intensity"; + /** * The intensity of ringtone vibrations, if configurable. * @@ -4670,7 +4708,6 @@ public final class Settings { * 3 - Strong vibrations * @hide */ - @Readable public static final String HARDWARE_HAPTIC_FEEDBACK_INTENSITY = "hardware_haptic_feedback_intensity"; @@ -9949,6 +9986,7 @@ public final class Settings { * * @hide */ + @Readable public static final String NOTIFICATION_PERMISSION_ENABLED = "notification_permission_enabled"; diff --git a/core/java/android/service/autofill/BatchUpdates.java b/core/java/android/service/autofill/BatchUpdates.java index c996cc088d66..8eeecc293104 100644 --- a/core/java/android/service/autofill/BatchUpdates.java +++ b/core/java/android/service/autofill/BatchUpdates.java @@ -205,7 +205,7 @@ public final class BatchUpdates implements Parcelable { builder.transformChild(ids[i], values[i]); } } - final RemoteViews updates = parcel.readParcelable(null, android.widget.RemoteViews.class); + final RemoteViews updates = parcel.readParcelable(null); if (updates != null) { builder.updateTemplate(updates); } diff --git a/core/java/android/service/autofill/CompositeUserData.java b/core/java/android/service/autofill/CompositeUserData.java index 55ac5a5e92f0..92952cb7dc24 100644 --- a/core/java/android/service/autofill/CompositeUserData.java +++ b/core/java/android/service/autofill/CompositeUserData.java @@ -197,8 +197,8 @@ public final class CompositeUserData implements FieldClassificationUserData, Par // Always go through the builder to ensure the data ingested by // the system obeys the contract of the builder to avoid attacks // using specially crafted parcels. - final UserData genericUserData = parcel.readParcelable(null, android.service.autofill.UserData.class); - final UserData packageUserData = parcel.readParcelable(null, android.service.autofill.UserData.class); + final UserData genericUserData = parcel.readParcelable(null); + final UserData packageUserData = parcel.readParcelable(null); return new CompositeUserData(genericUserData, packageUserData); } diff --git a/core/java/android/service/autofill/CustomDescription.java b/core/java/android/service/autofill/CustomDescription.java index 690cd0691631..f3f912bb3a5b 100644 --- a/core/java/android/service/autofill/CustomDescription.java +++ b/core/java/android/service/autofill/CustomDescription.java @@ -437,7 +437,7 @@ public final class CustomDescription implements Parcelable { // Always go through the builder to ensure the data ingested by // the system obeys the contract of the builder to avoid attacks // using specially crafted parcels. - final RemoteViews parentPresentation = parcel.readParcelable(null, android.widget.RemoteViews.class); + final RemoteViews parentPresentation = parcel.readParcelable(null); if (parentPresentation == null) return null; final Builder builder = new Builder(parentPresentation); diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java index 86341a908ad7..8539bf58da27 100644 --- a/core/java/android/service/autofill/Dataset.java +++ b/core/java/android/service/autofill/Dataset.java @@ -913,10 +913,10 @@ public final class Dataset implements Parcelable { public static final @NonNull Creator<Dataset> CREATOR = new Creator<Dataset>() { @Override public Dataset createFromParcel(Parcel parcel) { - final RemoteViews presentation = parcel.readParcelable(null, android.widget.RemoteViews.class); - final InlinePresentation inlinePresentation = parcel.readParcelable(null, android.service.autofill.InlinePresentation.class); + final RemoteViews presentation = parcel.readParcelable(null); + final InlinePresentation inlinePresentation = parcel.readParcelable(null); final InlinePresentation inlineTooltipPresentation = - parcel.readParcelable(null, android.service.autofill.InlinePresentation.class); + parcel.readParcelable(null); final ArrayList<AutofillId> ids = parcel.createTypedArrayList(AutofillId.CREATOR); final ArrayList<AutofillValue> values = @@ -929,8 +929,8 @@ public final class Dataset implements Parcelable { parcel.createTypedArrayList(InlinePresentation.CREATOR); final ArrayList<DatasetFieldFilter> filters = parcel.createTypedArrayList(DatasetFieldFilter.CREATOR); - final ClipData fieldContent = parcel.readParcelable(null, android.content.ClipData.class); - final IntentSender authentication = parcel.readParcelable(null, android.content.IntentSender.class); + final ClipData fieldContent = parcel.readParcelable(null); + final IntentSender authentication = parcel.readParcelable(null); final String datasetId = parcel.readString(); // Always go through the builder to ensure the data ingested by @@ -1014,7 +1014,7 @@ public final class Dataset implements Parcelable { @Override public DatasetFieldFilter createFromParcel(Parcel parcel) { - return new DatasetFieldFilter((Pattern) parcel.readSerializable(java.util.regex.Pattern.class.getClassLoader(), java.util.regex.Pattern.class)); + return new DatasetFieldFilter((Pattern) parcel.readSerializable()); } @Override diff --git a/core/java/android/service/autofill/DateTransformation.java b/core/java/android/service/autofill/DateTransformation.java index df5ed4dace55..734085737159 100644 --- a/core/java/android/service/autofill/DateTransformation.java +++ b/core/java/android/service/autofill/DateTransformation.java @@ -114,8 +114,8 @@ public final class DateTransformation extends InternalTransformation implements new Parcelable.Creator<DateTransformation>() { @Override public DateTransformation createFromParcel(Parcel parcel) { - return new DateTransformation(parcel.readParcelable(null, android.view.autofill.AutofillId.class), - (DateFormat) parcel.readSerializable(android.icu.text.DateFormat.class.getClassLoader(), android.icu.text.DateFormat.class)); + return new DateTransformation(parcel.readParcelable(null), + (DateFormat) parcel.readSerializable()); } @Override diff --git a/core/java/android/service/autofill/DateValueSanitizer.java b/core/java/android/service/autofill/DateValueSanitizer.java index c7d5b79ae484..6f7808ee181a 100644 --- a/core/java/android/service/autofill/DateValueSanitizer.java +++ b/core/java/android/service/autofill/DateValueSanitizer.java @@ -111,7 +111,7 @@ public final class DateValueSanitizer extends InternalSanitizer implements Sanit new Parcelable.Creator<DateValueSanitizer>() { @Override public DateValueSanitizer createFromParcel(Parcel parcel) { - return new DateValueSanitizer((DateFormat) parcel.readSerializable(android.icu.text.DateFormat.class.getClassLoader(), android.icu.text.DateFormat.class)); + return new DateValueSanitizer((DateFormat) parcel.readSerializable()); } @Override diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java index 43bd4102ffb5..af846b62ae2c 100644 --- a/core/java/android/service/autofill/FillRequest.java +++ b/core/java/android/service/autofill/FillRequest.java @@ -384,7 +384,7 @@ public final class FillRequest implements Parcelable { byte flg = in.readByte(); int id = in.readInt(); List<FillContext> fillContexts = new ArrayList<>(); - in.readParcelableList(fillContexts, FillContext.class.getClassLoader(), android.service.autofill.FillContext.class); + in.readParcelableList(fillContexts, FillContext.class.getClassLoader()); Bundle clientState = (flg & 0x4) == 0 ? null : in.readBundle(); int flags = in.readInt(); InlineSuggestionsRequest inlineSuggestionsRequest = (flg & 0x10) == 0 ? null : (InlineSuggestionsRequest) in.readTypedObject(InlineSuggestionsRequest.CREATOR); diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java index d94988ebea66..970cb1888317 100644 --- a/core/java/android/service/autofill/FillResponse.java +++ b/core/java/android/service/autofill/FillResponse.java @@ -834,35 +834,35 @@ public final class FillResponse implements Parcelable { // the system obeys the contract of the builder to avoid attacks // using specially crafted parcels. final Builder builder = new Builder(); - final ParceledListSlice<Dataset> datasetSlice = parcel.readParcelable(null, android.content.pm.ParceledListSlice.class); + final ParceledListSlice<Dataset> datasetSlice = parcel.readParcelable(null); final List<Dataset> datasets = (datasetSlice != null) ? datasetSlice.getList() : null; final int datasetCount = (datasets != null) ? datasets.size() : 0; for (int i = 0; i < datasetCount; i++) { builder.addDataset(datasets.get(i)); } - builder.setSaveInfo(parcel.readParcelable(null, android.service.autofill.SaveInfo.class)); - builder.setClientState(parcel.readParcelable(null, android.os.Bundle.class)); + builder.setSaveInfo(parcel.readParcelable(null)); + builder.setClientState(parcel.readParcelable(null)); // Sets authentication state. final AutofillId[] authenticationIds = parcel.readParcelableArray(null, AutofillId.class); - final IntentSender authentication = parcel.readParcelable(null, android.content.IntentSender.class); - final RemoteViews presentation = parcel.readParcelable(null, android.widget.RemoteViews.class); - final InlinePresentation inlinePresentation = parcel.readParcelable(null, android.service.autofill.InlinePresentation.class); - final InlinePresentation inlineTooltipPresentation = parcel.readParcelable(null, android.service.autofill.InlinePresentation.class); + final IntentSender authentication = parcel.readParcelable(null); + final RemoteViews presentation = parcel.readParcelable(null); + final InlinePresentation inlinePresentation = parcel.readParcelable(null); + final InlinePresentation inlineTooltipPresentation = parcel.readParcelable(null); if (authenticationIds != null) { builder.setAuthentication(authenticationIds, authentication, presentation, inlinePresentation, inlineTooltipPresentation); } - final RemoteViews header = parcel.readParcelable(null, android.widget.RemoteViews.class); + final RemoteViews header = parcel.readParcelable(null); if (header != null) { builder.setHeader(header); } - final RemoteViews footer = parcel.readParcelable(null, android.widget.RemoteViews.class); + final RemoteViews footer = parcel.readParcelable(null); if (footer != null) { builder.setFooter(footer); } - final UserData userData = parcel.readParcelable(null, android.service.autofill.UserData.class); + final UserData userData = parcel.readParcelable(null); if (userData != null) { builder.setUserData(userData); } diff --git a/core/java/android/service/autofill/ImageTransformation.java b/core/java/android/service/autofill/ImageTransformation.java index af82205b77b9..e3171594c39e 100644 --- a/core/java/android/service/autofill/ImageTransformation.java +++ b/core/java/android/service/autofill/ImageTransformation.java @@ -247,7 +247,7 @@ public final class ImageTransformation extends InternalTransformation implements new Parcelable.Creator<ImageTransformation>() { @Override public ImageTransformation createFromParcel(Parcel parcel) { - final AutofillId id = parcel.readParcelable(null, android.view.autofill.AutofillId.class); + final AutofillId id = parcel.readParcelable(null); final Pattern[] regexs = (Pattern[]) parcel.readSerializable(); final int[] resIds = parcel.createIntArray(); diff --git a/core/java/android/service/autofill/NegationValidator.java b/core/java/android/service/autofill/NegationValidator.java index 85cd981e3152..d626845b3b3e 100644 --- a/core/java/android/service/autofill/NegationValidator.java +++ b/core/java/android/service/autofill/NegationValidator.java @@ -68,7 +68,7 @@ final class NegationValidator extends InternalValidator { new Parcelable.Creator<NegationValidator>() { @Override public NegationValidator createFromParcel(Parcel parcel) { - return new NegationValidator(parcel.readParcelable(null, android.service.autofill.InternalValidator.class)); + return new NegationValidator(parcel.readParcelable(null)); } @Override diff --git a/core/java/android/service/autofill/RegexValidator.java b/core/java/android/service/autofill/RegexValidator.java index 4c58590ab7cf..00c43473ce7f 100644 --- a/core/java/android/service/autofill/RegexValidator.java +++ b/core/java/android/service/autofill/RegexValidator.java @@ -96,8 +96,8 @@ public final class RegexValidator extends InternalValidator implements Validator new Parcelable.Creator<RegexValidator>() { @Override public RegexValidator createFromParcel(Parcel parcel) { - return new RegexValidator(parcel.readParcelable(null, android.view.autofill.AutofillId.class), - (Pattern) parcel.readSerializable(java.util.regex.Pattern.class.getClassLoader(), java.util.regex.Pattern.class)); + return new RegexValidator(parcel.readParcelable(null), + (Pattern) parcel.readSerializable()); } @Override diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java index 5fe1d4f5ca5f..8edfde8c3914 100644 --- a/core/java/android/service/autofill/SaveInfo.java +++ b/core/java/android/service/autofill/SaveInfo.java @@ -888,14 +888,14 @@ public final class SaveInfo implements Parcelable { builder.setOptionalIds(optionalIds); } - builder.setNegativeAction(parcel.readInt(), parcel.readParcelable(null, android.content.IntentSender.class)); + builder.setNegativeAction(parcel.readInt(), parcel.readParcelable(null)); builder.setPositiveAction(parcel.readInt()); builder.setDescription(parcel.readCharSequence()); - final CustomDescription customDescripton = parcel.readParcelable(null, android.service.autofill.CustomDescription.class); + final CustomDescription customDescripton = parcel.readParcelable(null); if (customDescripton != null) { builder.setCustomDescription(customDescripton); } - final InternalValidator validator = parcel.readParcelable(null, android.service.autofill.InternalValidator.class); + final InternalValidator validator = parcel.readParcelable(null); if (validator != null) { builder.setValidator(validator); } @@ -909,7 +909,7 @@ public final class SaveInfo implements Parcelable { builder.addSanitizer(sanitizers[i], autofillIds); } } - final AutofillId triggerId = parcel.readParcelable(null, android.view.autofill.AutofillId.class); + final AutofillId triggerId = parcel.readParcelable(null); if (triggerId != null) { builder.setTriggerId(triggerId); } diff --git a/core/java/android/service/autofill/TextValueSanitizer.java b/core/java/android/service/autofill/TextValueSanitizer.java index 46c18b23a74d..5bafa7a1ff54 100644 --- a/core/java/android/service/autofill/TextValueSanitizer.java +++ b/core/java/android/service/autofill/TextValueSanitizer.java @@ -119,7 +119,7 @@ public final class TextValueSanitizer extends InternalSanitizer implements new Parcelable.Creator<TextValueSanitizer>() { @Override public TextValueSanitizer createFromParcel(Parcel parcel) { - return new TextValueSanitizer((Pattern) parcel.readSerializable(java.util.regex.Pattern.class.getClassLoader(), java.util.regex.Pattern.class), parcel.readString()); + return new TextValueSanitizer((Pattern) parcel.readSerializable(), parcel.readString()); } @Override diff --git a/core/java/android/service/contentcapture/ActivityEvent.java b/core/java/android/service/contentcapture/ActivityEvent.java index d286942c74fa..74a735518120 100644 --- a/core/java/android/service/contentcapture/ActivityEvent.java +++ b/core/java/android/service/contentcapture/ActivityEvent.java @@ -149,7 +149,7 @@ public final class ActivityEvent implements Parcelable { @Override @NonNull public ActivityEvent createFromParcel(@NonNull Parcel parcel) { - final ComponentName componentName = parcel.readParcelable(null, android.content.ComponentName.class); + final ComponentName componentName = parcel.readParcelable(null); final int eventType = parcel.readInt(); return new ActivityEvent(componentName, eventType); } diff --git a/core/java/android/service/contentcapture/SnapshotData.java b/core/java/android/service/contentcapture/SnapshotData.java index f72624d00061..bf469b4b3ad8 100644 --- a/core/java/android/service/contentcapture/SnapshotData.java +++ b/core/java/android/service/contentcapture/SnapshotData.java @@ -51,8 +51,8 @@ public final class SnapshotData implements Parcelable { SnapshotData(@NonNull Parcel parcel) { mAssistData = parcel.readBundle(); - mAssistStructure = parcel.readParcelable(null, android.app.assist.AssistStructure.class); - mAssistContent = parcel.readParcelable(null, android.app.assist.AssistContent.class); + mAssistStructure = parcel.readParcelable(null); + mAssistContent = parcel.readParcelable(null); } /** diff --git a/core/java/android/service/dreams/DreamOverlayService.java b/core/java/android/service/dreams/DreamOverlayService.java index 50f9d8ac2958..163d6ed4b18b 100644 --- a/core/java/android/service/dreams/DreamOverlayService.java +++ b/core/java/android/service/dreams/DreamOverlayService.java @@ -35,6 +35,7 @@ import android.view.WindowManager; public abstract class DreamOverlayService extends Service { private static final String TAG = "DreamOverlayService"; private static final boolean DEBUG = false; + private boolean mShowComplications; private IDreamOverlay mDreamOverlay = new IDreamOverlay.Stub() { @Override @@ -53,6 +54,8 @@ public abstract class DreamOverlayService extends Service { @Nullable @Override public final IBinder onBind(@NonNull Intent intent) { + mShowComplications = intent.getBooleanExtra(DreamService.EXTRA_SHOW_COMPLICATIONS, + DreamService.DEFAULT_SHOW_COMPLICATIONS); return mDreamOverlay.asBinder(); } @@ -74,4 +77,11 @@ public abstract class DreamOverlayService extends Service { Log.e(TAG, "Could not request exit:" + e); } } + + /** + * Returns whether to show complications on the dream overlay. + */ + public final boolean shouldShowComplications() { + return mShowComplications; + } } diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index 4ae3d7de26c2..133e384dfa8f 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -189,6 +189,19 @@ public class DreamService extends Service implements Window.Callback { */ public static final String DREAM_META_DATA = "android.service.dream"; + /** + * Extra containing a boolean for whether to show complications on the overlay. + * @hide + */ + public static final String EXTRA_SHOW_COMPLICATIONS = + "android.service.dreams.SHOW_COMPLICATIONS"; + + /** + * The default value for whether to show complications on the overlay. + * @hide + */ + public static final boolean DEFAULT_SHOW_COMPLICATIONS = true; + private final IDreamManager mDreamManager; private final Handler mHandler = new Handler(Looper.getMainLooper()); private IBinder mDreamToken; diff --git a/core/java/android/service/games/GameService.java b/core/java/android/service/games/GameService.java index b79c0553460f..105c2aa53374 100644 --- a/core/java/android/service/games/GameService.java +++ b/core/java/android/service/games/GameService.java @@ -38,10 +38,23 @@ import java.util.Objects; * when a game session should begin. It is always kept running by the system. * Because of this it should be kept as lightweight as possible. * - * Heavy weight operations (such as showing UI) should be implemented in the + * <p>Heavyweight operations (such as showing UI) should be implemented in the * associated {@link GameSessionService} when a game session is taking place. Its * implementation should run in a separate process from the {@link GameService}. * + * <p>To extend this class, you must declare the service in your manifest file with + * the {@link android.Manifest.permission#BIND_GAME_SERVICE} permission + * and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example: + * <pre> + * <service android:name=".GameService" + * android:label="@string/service_name" + * android:permission="android.permission.BIND_GAME_SERVICE"> + * <intent-filter> + * <action android:name="android.service.games.GameService" /> + * </intent-filter> + * </service> + * </pre> + * * @hide */ @SystemApi @@ -90,7 +103,7 @@ public class GameService extends Service { @Override @Nullable - public IBinder onBind(@Nullable Intent intent) { + public final IBinder onBind(@Nullable Intent intent) { if (ACTION_GAME_SERVICE.equals(intent.getAction())) { return mInterface.asBinder(); } diff --git a/core/java/android/service/games/GameSessionService.java b/core/java/android/service/games/GameSessionService.java index a2c88709b62d..c1a3eb5286c4 100644 --- a/core/java/android/service/games/GameSessionService.java +++ b/core/java/android/service/games/GameSessionService.java @@ -73,7 +73,7 @@ public abstract class GameSessionService extends Service { @Override @Nullable - public IBinder onBind(@Nullable Intent intent) { + public final IBinder onBind(@Nullable Intent intent) { if (intent == null) { return null; } diff --git a/core/java/android/service/notification/Condition.java b/core/java/android/service/notification/Condition.java index 267b2ff818a6..4f324f9e35bf 100644 --- a/core/java/android/service/notification/Condition.java +++ b/core/java/android/service/notification/Condition.java @@ -114,7 +114,7 @@ public final class Condition implements Parcelable { } public Condition(Parcel source) { - this((Uri)source.readParcelable(Condition.class.getClassLoader(), android.net.Uri.class), + this((Uri)source.readParcelable(Condition.class.getClassLoader()), source.readString(), source.readString(), source.readString(), diff --git a/core/java/android/service/notification/ConversationChannelWrapper.java b/core/java/android/service/notification/ConversationChannelWrapper.java index 35b6bad4e40b..3d0984ca80ee 100644 --- a/core/java/android/service/notification/ConversationChannelWrapper.java +++ b/core/java/android/service/notification/ConversationChannelWrapper.java @@ -40,10 +40,10 @@ public final class ConversationChannelWrapper implements Parcelable { public ConversationChannelWrapper() {} protected ConversationChannelWrapper(Parcel in) { - mNotificationChannel = in.readParcelable(NotificationChannel.class.getClassLoader(), android.app.NotificationChannel.class); + mNotificationChannel = in.readParcelable(NotificationChannel.class.getClassLoader()); mGroupLabel = in.readCharSequence(); mParentChannelLabel = in.readCharSequence(); - mShortcutInfo = in.readParcelable(ShortcutInfo.class.getClassLoader(), android.content.pm.ShortcutInfo.class); + mShortcutInfo = in.readParcelable(ShortcutInfo.class.getClassLoader()); mPkg = in.readStringNoHelper(); mUid = in.readInt(); } diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index ae39d3d3c2da..c94595468aec 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -1763,7 +1763,7 @@ public abstract class NotificationListenerService extends Service { mImportanceExplanation = in.readCharSequence(); // may be null mRankingScore = in.readFloat(); mOverrideGroupKey = in.readString(); // may be null - mChannel = in.readParcelable(cl, android.app.NotificationChannel.class); // may be null + mChannel = in.readParcelable(cl); // may be null mOverridePeople = in.createStringArrayList(); mSnoozeCriteria = in.createTypedArrayList(SnoozeCriterion.CREATOR); mShowBadge = in.readBoolean(); @@ -1776,7 +1776,7 @@ public abstract class NotificationListenerService extends Service { mCanBubble = in.readBoolean(); mIsTextChanged = in.readBoolean(); mIsConversation = in.readBoolean(); - mShortcutInfo = in.readParcelable(cl, android.content.pm.ShortcutInfo.class); + mShortcutInfo = in.readParcelable(cl); mRankingAdjustment = in.readInt(); mIsBubble = in.readBoolean(); } diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java index a853714c0e9d..c64f4c46a769 100644 --- a/core/java/android/service/notification/NotificationRankingUpdate.java +++ b/core/java/android/service/notification/NotificationRankingUpdate.java @@ -30,7 +30,7 @@ public class NotificationRankingUpdate implements Parcelable { } public NotificationRankingUpdate(Parcel in) { - mRankingMap = in.readParcelable(getClass().getClassLoader(), android.service.notification.NotificationListenerService.RankingMap.class); + mRankingMap = in.readParcelable(getClass().getClassLoader()); } public NotificationListenerService.RankingMap getRankingMap() { diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index 8834ceea7453..c1d5a28aa349 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -211,7 +211,7 @@ public class ZenModeConfig implements Parcelable { allowCallsFrom = source.readInt(); allowMessagesFrom = source.readInt(); user = source.readInt(); - manualRule = source.readParcelable(null, android.service.notification.ZenModeConfig.ZenRule.class); + manualRule = source.readParcelable(null); final int len = source.readInt(); if (len > 0) { final String[] ids = new String[len]; @@ -1800,10 +1800,10 @@ public class ZenModeConfig implements Parcelable { name = source.readString(); } zenMode = source.readInt(); - conditionId = source.readParcelable(null, android.net.Uri.class); - condition = source.readParcelable(null, android.service.notification.Condition.class); - component = source.readParcelable(null, android.content.ComponentName.class); - configurationActivity = source.readParcelable(null, android.content.ComponentName.class); + conditionId = source.readParcelable(null); + condition = source.readParcelable(null); + component = source.readParcelable(null); + configurationActivity = source.readParcelable(null); if (source.readInt() == 1) { id = source.readString(); } @@ -1811,7 +1811,7 @@ public class ZenModeConfig implements Parcelable { if (source.readInt() == 1) { enabler = source.readString(); } - zenPolicy = source.readParcelable(null, android.service.notification.ZenPolicy.class); + zenPolicy = source.readParcelable(null); modified = source.readInt() == 1; pkg = source.readString(); } diff --git a/core/java/android/service/notification/ZenPolicy.java b/core/java/android/service/notification/ZenPolicy.java index a04f07380ce8..ed3a9ac33738 100644 --- a/core/java/android/service/notification/ZenPolicy.java +++ b/core/java/android/service/notification/ZenPolicy.java @@ -804,8 +804,8 @@ public final class ZenPolicy implements Parcelable { @Override public ZenPolicy createFromParcel(Parcel source) { ZenPolicy policy = new ZenPolicy(); - policy.mPriorityCategories = source.readArrayList(Integer.class.getClassLoader(), java.lang.Integer.class); - policy.mVisualEffects = source.readArrayList(Integer.class.getClassLoader(), java.lang.Integer.class); + policy.mPriorityCategories = source.readArrayList(Integer.class.getClassLoader()); + policy.mVisualEffects = source.readArrayList(Integer.class.getClassLoader()); policy.mPriorityCalls = source.readInt(); policy.mPriorityMessages = source.readInt(); policy.mConversationSenders = source.readInt(); diff --git a/core/java/android/service/quickaccesswallet/GetWalletCardsResponse.java b/core/java/android/service/quickaccesswallet/GetWalletCardsResponse.java index 7471a4f399a5..0551e2709de6 100644 --- a/core/java/android/service/quickaccesswallet/GetWalletCardsResponse.java +++ b/core/java/android/service/quickaccesswallet/GetWalletCardsResponse.java @@ -63,7 +63,7 @@ public final class GetWalletCardsResponse implements Parcelable { private static GetWalletCardsResponse readFromParcel(Parcel source) { int size = source.readInt(); List<WalletCard> walletCards = - source.readParcelableList(new ArrayList<>(size), WalletCard.class.getClassLoader(), android.service.quickaccesswallet.WalletCard.class); + source.readParcelableList(new ArrayList<>(size), WalletCard.class.getClassLoader()); int selectedIndex = source.readInt(); return new GetWalletCardsResponse(walletCards, selectedIndex); } diff --git a/core/java/android/service/settings/suggestions/Suggestion.java b/core/java/android/service/settings/suggestions/Suggestion.java index 16622d70065f..3e63efbda9c0 100644 --- a/core/java/android/service/settings/suggestions/Suggestion.java +++ b/core/java/android/service/settings/suggestions/Suggestion.java @@ -120,9 +120,9 @@ public final class Suggestion implements Parcelable { mId = in.readString(); mTitle = in.readCharSequence(); mSummary = in.readCharSequence(); - mIcon = in.readParcelable(Icon.class.getClassLoader(), android.graphics.drawable.Icon.class); + mIcon = in.readParcelable(Icon.class.getClassLoader()); mFlags = in.readInt(); - mPendingIntent = in.readParcelable(PendingIntent.class.getClassLoader(), android.app.PendingIntent.class); + mPendingIntent = in.readParcelable(PendingIntent.class.getClassLoader()); } public static final @android.annotation.NonNull Creator<Suggestion> CREATOR = new Creator<Suggestion>() { diff --git a/core/java/android/service/timezone/TimeZoneProviderEvent.java b/core/java/android/service/timezone/TimeZoneProviderEvent.java index f6433b7f371e..700528116a8f 100644 --- a/core/java/android/service/timezone/TimeZoneProviderEvent.java +++ b/core/java/android/service/timezone/TimeZoneProviderEvent.java @@ -141,7 +141,7 @@ public final class TimeZoneProviderEvent implements Parcelable { int type = in.readInt(); long creationElapsedMillis = in.readLong(); TimeZoneProviderSuggestion suggestion = - in.readParcelable(getClass().getClassLoader(), android.service.timezone.TimeZoneProviderSuggestion.class); + in.readParcelable(getClass().getClassLoader()); String failureCause = in.readString8(); return new TimeZoneProviderEvent( type, creationElapsedMillis, suggestion, failureCause); diff --git a/core/java/android/service/timezone/TimeZoneProviderSuggestion.java b/core/java/android/service/timezone/TimeZoneProviderSuggestion.java index 4841ac189034..229fa268a47c 100644 --- a/core/java/android/service/timezone/TimeZoneProviderSuggestion.java +++ b/core/java/android/service/timezone/TimeZoneProviderSuggestion.java @@ -100,7 +100,7 @@ public final class TimeZoneProviderSuggestion implements Parcelable { public TimeZoneProviderSuggestion createFromParcel(Parcel in) { @SuppressWarnings("unchecked") ArrayList<String> timeZoneIds = - (ArrayList<String>) in.readArrayList(null /* classLoader */, java.lang.String.class); + (ArrayList<String>) in.readArrayList(null /* classLoader */); long elapsedRealtimeMillis = in.readLong(); return new TimeZoneProviderSuggestion(timeZoneIds, elapsedRealtimeMillis); } diff --git a/core/java/android/speech/tts/Voice.java b/core/java/android/speech/tts/Voice.java index 0d98a6ca5f14..7ffe5eb7893d 100644 --- a/core/java/android/speech/tts/Voice.java +++ b/core/java/android/speech/tts/Voice.java @@ -84,7 +84,7 @@ public class Voice implements Parcelable { private Voice(Parcel in) { this.mName = in.readString(); - this.mLocale = (Locale)in.readSerializable(java.util.Locale.class.getClassLoader(), java.util.Locale.class); + this.mLocale = (Locale)in.readSerializable(); this.mQuality = in.readInt(); this.mLatency = in.readInt(); this.mRequiresNetworkConnection = (in.readByte() == 1); diff --git a/core/java/android/telephony/SubscriptionPlan.java b/core/java/android/telephony/SubscriptionPlan.java index fb2d7714d402..d5ac4368aa97 100644 --- a/core/java/android/telephony/SubscriptionPlan.java +++ b/core/java/android/telephony/SubscriptionPlan.java @@ -99,7 +99,7 @@ public final class SubscriptionPlan implements Parcelable { } private SubscriptionPlan(Parcel source) { - cycleRule = source.readParcelable(null, android.util.RecurrenceRule.class); + cycleRule = source.readParcelable(null); title = source.readCharSequence(); summary = source.readCharSequence(); dataLimitBytes = source.readLong(); diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java index 9eaaa91532d0..542de3fad8b0 100644 --- a/core/java/android/telephony/TelephonyRegistryManager.java +++ b/core/java/android/telephony/TelephonyRegistryManager.java @@ -131,6 +131,7 @@ public class TelephonyRegistryManager { mContext.getAttributionTag(), callback); } catch (RemoteException ex) { // system server crash + throw ex.rethrowFromSystemServer(); } } @@ -152,6 +153,7 @@ public class TelephonyRegistryManager { mSubscriptionChangedListenerMap.remove(listener); } catch (RemoteException ex) { // system server crash + throw ex.rethrowFromSystemServer(); } } @@ -194,6 +196,7 @@ public class TelephonyRegistryManager { mContext.getAttributionTag(), callback); } catch (RemoteException ex) { // system server crash + throw ex.rethrowFromSystemServer(); } } @@ -216,6 +219,7 @@ public class TelephonyRegistryManager { mOpportunisticSubscriptionChangedListenerMap.remove(listener); } catch (RemoteException ex) { // system server crash + throw ex.rethrowFromSystemServer(); } } @@ -304,6 +308,7 @@ public class TelephonyRegistryManager { sRegistry.notifyCarrierNetworkChange(active); } catch (RemoteException ex) { // system server crash + throw ex.rethrowFromSystemServer(); } } @@ -329,6 +334,7 @@ public class TelephonyRegistryManager { sRegistry.notifyCarrierNetworkChangeWithSubId(subscriptionId, active); } catch (RemoteException ex) { // system server crash + throw ex.rethrowFromSystemServer(); } } @@ -347,6 +353,7 @@ public class TelephonyRegistryManager { sRegistry.notifyCallState(slotIndex, subId, state, incomingNumber); } catch (RemoteException ex) { // system server crash + throw ex.rethrowFromSystemServer(); } } @@ -364,6 +371,7 @@ public class TelephonyRegistryManager { sRegistry.notifyCallStateForAllSubs(state, incomingNumber); } catch (RemoteException ex) { // system server crash + throw ex.rethrowFromSystemServer(); } } @@ -376,6 +384,7 @@ public class TelephonyRegistryManager { sRegistry.notifySubscriptionInfoChanged(); } catch (RemoteException ex) { // system server crash + throw ex.rethrowFromSystemServer(); } } @@ -388,6 +397,7 @@ public class TelephonyRegistryManager { sRegistry.notifyOpportunisticSubscriptionInfoChanged(); } catch (RemoteException ex) { // system server crash + throw ex.rethrowFromSystemServer(); } } @@ -404,6 +414,7 @@ public class TelephonyRegistryManager { sRegistry.notifyServiceStateForPhoneId(slotIndex, subId, state); } catch (RemoteException ex) { // system server crash + throw ex.rethrowFromSystemServer(); } } @@ -421,6 +432,7 @@ public class TelephonyRegistryManager { sRegistry.notifySignalStrengthForPhoneId(slotIndex, subId, signalStrength); } catch (RemoteException ex) { // system server crash + throw ex.rethrowFromSystemServer(); } } @@ -439,6 +451,7 @@ public class TelephonyRegistryManager { sRegistry.notifyMessageWaitingChangedForPhoneId(slotIndex, subId, msgWaitingInd); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -454,6 +467,7 @@ public class TelephonyRegistryManager { sRegistry.notifyCallForwardingChangedForSubscriber(subId, callForwardInd); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -469,6 +483,7 @@ public class TelephonyRegistryManager { sRegistry.notifyDataActivityForSubscriber(subId, dataActivityType); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -490,6 +505,7 @@ public class TelephonyRegistryManager { slotIndex, subId, preciseState); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -508,6 +524,7 @@ public class TelephonyRegistryManager { sRegistry.notifyCallQualityChanged(callQuality, slotIndex, subId, networkType); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -523,6 +540,7 @@ public class TelephonyRegistryManager { sRegistry.notifyEmergencyNumberList(slotIndex, subId); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -538,6 +556,7 @@ public class TelephonyRegistryManager { sRegistry.notifyOutgoingEmergencyCall(phoneId, subId, emergencyNumber); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -553,6 +572,7 @@ public class TelephonyRegistryManager { sRegistry.notifyOutgoingEmergencySms(phoneId, subId, emergencyNumber); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -570,6 +590,7 @@ public class TelephonyRegistryManager { sRegistry.notifyRadioPowerStateChanged(slotIndex, subId, radioPowerState); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -583,6 +604,7 @@ public class TelephonyRegistryManager { sRegistry.notifyPhoneCapabilityChanged(phoneCapability); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -615,6 +637,7 @@ public class TelephonyRegistryManager { SIM_ACTIVATION_TYPE_DATA, activationState); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -634,6 +657,7 @@ public class TelephonyRegistryManager { SIM_ACTIVATION_TYPE_VOICE, activationState); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -651,6 +675,7 @@ public class TelephonyRegistryManager { sRegistry.notifyUserMobileDataStateChangedForPhoneId(slotIndex, subId, state); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -669,6 +694,7 @@ public class TelephonyRegistryManager { sRegistry.notifyDisplayInfoChanged(slotIndex, subscriptionId, telephonyDisplayInfo); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -683,6 +709,7 @@ public class TelephonyRegistryManager { sRegistry.notifyImsDisconnectCause(subId, imsReasonInfo); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -698,6 +725,7 @@ public class TelephonyRegistryManager { sRegistry.notifySrvccStateChanged(subId, state); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -721,6 +749,7 @@ public class TelephonyRegistryManager { foregroundCallPreciseState, backgroundCallPreciseState); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -741,6 +770,7 @@ public class TelephonyRegistryManager { sRegistry.notifyDisconnectCause(slotIndex, subId, cause, preciseCause); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -755,6 +785,7 @@ public class TelephonyRegistryManager { sRegistry.notifyCellLocationForSubscriber(subId, cellLocation); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -769,7 +800,7 @@ public class TelephonyRegistryManager { try { sRegistry.notifyCellInfoForSubscriber(subId, cellInfo); } catch (RemoteException ex) { - + throw ex.rethrowFromSystemServer(); } } @@ -781,7 +812,7 @@ public class TelephonyRegistryManager { try { sRegistry.notifyActiveDataSubIdChanged(activeDataSubId); } catch (RemoteException ex) { - + throw ex.rethrowFromSystemServer(); } } @@ -814,6 +845,7 @@ public class TelephonyRegistryManager { sRegistry.notifyRegistrationFailed(slotIndex, subId, cellIdentity, chosenPlmn, domain, causeCode, additionalCauseCode); } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -830,6 +862,7 @@ public class TelephonyRegistryManager { sRegistry.notifyBarringInfoChanged(slotIndex, subId, barringInfo); } catch (RemoteException ex) { // system server crash + throw ex.rethrowFromSystemServer(); } } @@ -846,6 +879,7 @@ public class TelephonyRegistryManager { sRegistry.notifyPhysicalChannelConfigForSubscriber(slotIndex, subId, configs); } catch (RemoteException ex) { // system server crash + throw ex.rethrowFromSystemServer(); } } @@ -862,6 +896,7 @@ public class TelephonyRegistryManager { sRegistry.notifyDataEnabled(slotIndex, subId, enabled, reason); } catch (RemoteException ex) { // system server crash + throw ex.rethrowFromSystemServer(); } } @@ -880,6 +915,7 @@ public class TelephonyRegistryManager { allowedNetworkType); } catch (RemoteException ex) { // system process is dead + throw ex.rethrowFromSystemServer(); } } @@ -895,6 +931,7 @@ public class TelephonyRegistryManager { sRegistry.notifyLinkCapacityEstimateChanged(slotIndex, subId, linkCapacityEstimateList); } catch (RemoteException ex) { // system server crash + throw ex.rethrowFromSystemServer(); } } diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java index 4ee02f01f330..fee23f4b0be7 100644 --- a/core/java/android/text/BoringLayout.java +++ b/core/java/android/text/BoringLayout.java @@ -107,6 +107,10 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback * @param ellipsizedWidth the width to which this Layout is ellipsizing. If {@code ellipsize} is * {@code null}, or is {@link TextUtils.TruncateAt#MARQUEE} this value is * not used, {@code outerWidth} is used instead + * @param useFallbackLineSpacing True for adjusting the line spacing based on fallback fonts. + * False for keeping the first font's line height. If some glyphs + * requires larger vertical spaces, by passing true to this + * argument, the layout increase the line height to fit all glyphs. */ public static @NonNull BoringLayout make( @NonNull CharSequence source, @NonNull TextPaint paint, @@ -168,7 +172,11 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback * requested width * @param ellipsizedWidth the width to which this Layout is ellipsizing. If {@code ellipsize} is * {@code null}, or is {@link TextUtils.TruncateAt#MARQUEE} this value is - * not used, {@code outerwidth} is used instead + * not used, {@code outerWidth} is used instead + * @param useFallbackLineSpacing True for adjusting the line spacing based on fallback fonts. + * False for keeping the first font's line height. If some glyphs + * requires larger vertical spaces, by passing true to this + * argument, the layout increase the line height to fit all glyphs. */ public @NonNull BoringLayout replaceOrMake(@NonNull CharSequence source, @NonNull TextPaint paint, @IntRange(from = 0) int outerWidth, @@ -216,7 +224,7 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback * requested width * @param ellipsizedWidth the width to which this Layout is ellipsizing. If {@code ellipsize} is * {@code null}, or is {@link TextUtils.TruncateAt#MARQUEE} this value is - * not used, {@code outerwidth} is used instead + * not used, {@code outerWidth} is used instead */ public BoringLayout replaceOrMake(CharSequence source, TextPaint paint, int outerWidth, Alignment align, float spacingMult, float spacingAdd, BoringLayout.Metrics metrics, @@ -261,10 +269,10 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback * @param includePad set whether to include extra space beyond font ascent and descent which is * needed to avoid clipping in some scripts * @param ellipsize whether to ellipsize the text if width of the text is longer than the - * requested {@code outerwidth} + * requested {@code outerWidth} * @param ellipsizedWidth the width to which this Layout is ellipsizing. If {@code ellipsize} is * {@code null}, or is {@link TextUtils.TruncateAt#MARQUEE} this value is - * not used, {@code outerwidth} is used instead + * not used, {@code outerWidth} is used instead */ public BoringLayout(CharSequence source, TextPaint paint, int outerWidth, Alignment align, float spacingMult, float spacingAdd, BoringLayout.Metrics metrics, boolean includePad, @@ -286,10 +294,14 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback * @param includePad set whether to include extra space beyond font ascent and descent which is * needed to avoid clipping in some scripts * @param ellipsize whether to ellipsize the text if width of the text is longer than the - * requested {@code outerwidth} + * requested {@code outerWidth} * @param ellipsizedWidth the width to which this Layout is ellipsizing. If {@code ellipsize} is * {@code null}, or is {@link TextUtils.TruncateAt#MARQUEE} this value is - * not used, {@code outerwidth} is used instead + * not used, {@code outerWidth} is used instead + * @param useFallbackLineSpacing True for adjusting the line spacing based on fallback fonts. + * False for keeping the first font's line height. If some glyphs + * requires larger vertical spaces, by passing true to this + * argument, the layout increase the line height to fit all glyphs. */ public BoringLayout( @NonNull CharSequence source, @NonNull TextPaint paint, @@ -443,7 +455,10 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback * @param text a text to be calculated text layout. * @param paint a paint object used for styling. * @param textDir a text direction. - * @param useFallbackLineSpacing true if use fallback line spacing, otherwise false. + * @param useFallbackLineSpacing True for adjusting the line spacing based on fallback fonts. + * False for keeping the first font's line height. If some glyphs + * requires larger vertical spaces, by passing true to this + * argument, the layout increase the line height to fit all glyphs. * @param metrics the out metrics. * @return metrics on success. null if text cannot be rendered by BoringLayout. */ diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java index 32b3bc62a8cd..2f7fb2f0ab9d 100644 --- a/core/java/android/text/FontConfig.java +++ b/core/java/android/text/FontConfig.java @@ -143,9 +143,9 @@ public final class FontConfig implements Parcelable { @Override public FontConfig createFromParcel(Parcel source) { List<FontFamily> families = source.readParcelableList(new ArrayList<>(), - FontFamily.class.getClassLoader(), android.text.FontConfig.FontFamily.class); + FontFamily.class.getClassLoader()); List<Alias> aliases = source.readParcelableList(new ArrayList<>(), - Alias.class.getClassLoader(), android.text.FontConfig.Alias.class); + Alias.class.getClassLoader()); long lastModifiedDate = source.readLong(); int configVersion = source.readInt(); return new FontConfig(families, aliases, lastModifiedDate, configVersion); @@ -617,7 +617,7 @@ public final class FontConfig implements Parcelable { @Override public FontFamily createFromParcel(Parcel source) { List<Font> fonts = source.readParcelableList( - new ArrayList<>(), Font.class.getClassLoader(), android.text.FontConfig.Font.class); + new ArrayList<>(), Font.class.getClassLoader()); String name = source.readString8(); String langTags = source.readString8(); int variant = source.readInt(); diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java index 2b396612cf3c..49e21110d679 100644 --- a/core/java/android/text/TextLine.java +++ b/core/java/android/text/TextLine.java @@ -860,15 +860,14 @@ public class TextLine { final int previousBottom = fmi.bottom; final int previousLeading = fmi.leading; + int count = end - start; + int contextCount = contextEnd - contextStart; if (mCharsValid) { - int count = end - start; - int contextCount = contextEnd - contextStart; wp.getFontMetricsInt(mChars, start, count, contextStart, contextCount, runIsRtl, fmi); } else { - int delta = mStart; - wp.getFontMetricsInt(mText, delta + start, delta + end, - delta + contextStart, delta + contextEnd, runIsRtl, fmi); + wp.getFontMetricsInt(mText, mStart + start, count, mStart + contextStart, contextCount, + runIsRtl, fmi); } updateMetrics(fmi, previousTop, previousAscent, previousDescent, previousBottom, diff --git a/core/java/android/text/style/EasyEditSpan.java b/core/java/android/text/style/EasyEditSpan.java index 3da83332d62e..ccccdcf88b69 100644 --- a/core/java/android/text/style/EasyEditSpan.java +++ b/core/java/android/text/style/EasyEditSpan.java @@ -82,7 +82,7 @@ public class EasyEditSpan implements ParcelableSpan { * Constructor called from {@link TextUtils} to restore the span. */ public EasyEditSpan(@NonNull Parcel source) { - mPendingIntent = source.readParcelable(null, android.app.PendingIntent.class); + mPendingIntent = source.readParcelable(null); mDeleteEnabled = (source.readByte() == 1); } diff --git a/core/java/android/text/style/TextAppearanceSpan.java b/core/java/android/text/style/TextAppearanceSpan.java index adb379a397b7..23557694a48d 100644 --- a/core/java/android/text/style/TextAppearanceSpan.java +++ b/core/java/android/text/style/TextAppearanceSpan.java @@ -249,7 +249,7 @@ public class TextAppearanceSpan extends MetricAffectingSpan implements Parcelabl mTypeface = LeakyTypefaceStorage.readTypefaceFromParcel(src); mTextFontWeight = src.readInt(); - mTextLocales = src.readParcelable(LocaleList.class.getClassLoader(), android.os.LocaleList.class); + mTextLocales = src.readParcelable(LocaleList.class.getClassLoader()); mShadowRadius = src.readFloat(); mShadowDx = src.readFloat(); diff --git a/core/java/android/util/MemoryIntArray.java b/core/java/android/util/MemoryIntArray.java index 5cbbbef2cf88..42181c3c1722 100644 --- a/core/java/android/util/MemoryIntArray.java +++ b/core/java/android/util/MemoryIntArray.java @@ -80,7 +80,7 @@ public final class MemoryIntArray implements Parcelable, Closeable { private MemoryIntArray(Parcel parcel) throws IOException { mIsOwner = false; - ParcelFileDescriptor pfd = parcel.readParcelable(null, android.os.ParcelFileDescriptor.class); + ParcelFileDescriptor pfd = parcel.readParcelable(null); if (pfd == null) { throw new IOException("No backing file descriptor"); } diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index 678c80a2094b..b8614ccde6fd 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -449,8 +449,8 @@ public final class DisplayInfo implements Parcelable { type = source.readInt(); displayId = source.readInt(); displayGroupId = source.readInt(); - address = source.readParcelable(null, android.view.DisplayAddress.class); - deviceProductInfo = source.readParcelable(null, android.hardware.display.DeviceProductInfo.class); + address = source.readParcelable(null); + deviceProductInfo = source.readParcelable(null); name = source.readString8(); appWidth = source.readInt(); appHeight = source.readInt(); @@ -475,7 +475,7 @@ public final class DisplayInfo implements Parcelable { for (int i = 0; i < nColorModes; i++) { supportedColorModes[i] = source.readInt(); } - hdrCapabilities = source.readParcelable(null, android.view.Display.HdrCapabilities.class); + hdrCapabilities = source.readParcelable(null); minimalPostProcessingSupported = source.readBoolean(); logicalDensityDpi = source.readInt(); physicalXDpi = source.readFloat(); diff --git a/core/java/android/view/KeyboardShortcutInfo.java b/core/java/android/view/KeyboardShortcutInfo.java index 118b03ce5504..2660e74dcb20 100644 --- a/core/java/android/view/KeyboardShortcutInfo.java +++ b/core/java/android/view/KeyboardShortcutInfo.java @@ -91,7 +91,7 @@ public final class KeyboardShortcutInfo implements Parcelable { private KeyboardShortcutInfo(Parcel source) { mLabel = source.readCharSequence(); - mIcon = source.readParcelable(null, android.graphics.drawable.Icon.class); + mIcon = source.readParcelable(null); mBaseCharacter = (char) source.readInt(); mKeycode = source.readInt(); mModifiers = source.readInt(); diff --git a/core/java/android/view/OWNERS b/core/java/android/view/OWNERS index 2b5e286e89e4..d160be59cca3 100644 --- a/core/java/android/view/OWNERS +++ b/core/java/android/view/OWNERS @@ -29,6 +29,13 @@ per-file KeyEvent.java = file:/services/core/java/com/android/server/input/OWNER per-file MotionEvent.java = file:/services/core/java/com/android/server/input/OWNERS per-file PointerIcon.java = file:/services/core/java/com/android/server/input/OWNERS per-file SimulatedDpad.java = file:/services/core/java/com/android/server/input/OWNERS +per-file BatchedInputEventReceiver.java = file:/services/core/java/com/android/server/input/OWNERS +per-file DragEvent.java = file:/services/core/java/com/android/server/input/OWNERS +per-file DragEvent.aidl = file:/services/core/java/com/android/server/input/OWNERS +per-file GestureDetector.java = file:/services/core/java/com/android/server/input/OWNERS +per-file ScaleGestureDetector.java = file:/services/core/java/com/android/server/input/OWNERS +per-file KeyboardShortcut*.java = file:/services/core/java/com/android/server/input/OWNERS +per-file KeyCharacterMap.java = file:/services/core/java/com/android/server/input/OWNERS # InputWindowHandle per-file InputWindowHandle.java = file:/services/core/java/com/android/server/input/OWNERS diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 258359e98264..2307a039bfa5 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -15012,8 +15012,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * @return true if this view and all ancestors are visible as of the last * {@link #onVisibilityAggregated(boolean)} call. + * + * @hide */ - boolean isAggregatedVisible() { + public boolean isAggregatedVisible() { return (mPrivateFlags3 & PFLAG3_AGGREGATED_VISIBLE) != 0; } diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java index 07d5fc533a3a..25e0eca12bf3 100644 --- a/core/java/android/view/ViewDebug.java +++ b/core/java/android/view/ViewDebug.java @@ -1896,7 +1896,7 @@ public class ViewDebug { private Canvas mCanvas; private Bitmap mBitmap; - private boolean mEnabledHwBitmapsInSwMode; + private boolean mEnabledHwFeaturesInSwMode; @Override public Canvas getCanvas(View view, int width, int height) { @@ -1913,7 +1913,7 @@ public class ViewDebug { if (mCanvas == null) { mCanvas = new Canvas(); } - mEnabledHwBitmapsInSwMode = mCanvas.isHwBitmapsInSwModeEnabled(); + mEnabledHwFeaturesInSwMode = mCanvas.isHwFeaturesInSwModeEnabled(); mCanvas.setBitmap(mBitmap); return mCanvas; } @@ -1921,7 +1921,7 @@ public class ViewDebug { @Override public Bitmap createBitmap() { mCanvas.setBitmap(null); - mCanvas.setHwBitmapsInSwModeEnabled(mEnabledHwBitmapsInSwMode); + mCanvas.setHwFeaturesInSwModeEnabled(mEnabledHwFeaturesInSwMode); return mBitmap; } } diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java index a427ab8fe837..6ad2d9a7adb1 100644 --- a/core/java/android/view/accessibility/AccessibilityEvent.java +++ b/core/java/android/view/accessibility/AccessibilityEvent.java @@ -1315,7 +1315,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par record.mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); record.mBeforeText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); record.mParcelableData = parcel.readParcelable(null); - parcel.readList(record.mText, null, java.lang.CharSequence.class); + parcel.readList(record.mText, null); record.mSourceWindowId = parcel.readInt(); record.mSourceNodeId = parcel.readLong(); record.mSourceDisplayId = parcel.readInt(); diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java index 36e779a44e70..67e6d3f2aec3 100644 --- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java +++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java @@ -84,6 +84,12 @@ public final class AccessibilityWindowInfo implements Parcelable { */ public static final int TYPE_SPLIT_SCREEN_DIVIDER = 5; + /** + * Window type: A system window used to show the UI for the interaction with + * window-based magnification, which includes the magnified content and the option menu. + */ + public static final int TYPE_MAGNIFICATION_OVERLAY = 6; + /* Special values for window IDs */ /** @hide */ public static final int ACTIVE_WINDOW_ID = Integer.MAX_VALUE; @@ -801,6 +807,9 @@ public final class AccessibilityWindowInfo implements Parcelable { case TYPE_SPLIT_SCREEN_DIVIDER: { return "TYPE_SPLIT_SCREEN_DIVIDER"; } + case TYPE_MAGNIFICATION_OVERLAY: { + return "TYPE_MAGNIFICATION_OVERLAY"; + } default: return "<UNKNOWN:" + type + ">"; } @@ -908,7 +917,7 @@ public final class AccessibilityWindowInfo implements Parcelable { final int count = source.readInt(); for (int i = 0; i < count; i++) { List<AccessibilityWindowInfo> windows = new ArrayList<>(); - source.readParcelableList(windows, loader, android.view.accessibility.AccessibilityWindowInfo.class); + source.readParcelableList(windows, loader); array.put(source.readInt(), windows); } return array; diff --git a/core/java/android/view/autofill/ParcelableMap.java b/core/java/android/view/autofill/ParcelableMap.java index 3fa7734e56fc..d8459aa22fa1 100644 --- a/core/java/android/view/autofill/ParcelableMap.java +++ b/core/java/android/view/autofill/ParcelableMap.java @@ -56,8 +56,8 @@ class ParcelableMap extends HashMap<AutofillId, AutofillValue> implements Parcel ParcelableMap map = new ParcelableMap(size); for (int i = 0; i < size; i++) { - AutofillId key = source.readParcelable(null, android.view.autofill.AutofillId.class); - AutofillValue value = source.readParcelable(null, android.view.autofill.AutofillValue.class); + AutofillId key = source.readParcelable(null); + AutofillValue value = source.readParcelable(null); map.put(key, value); } diff --git a/core/java/android/view/contentcapture/ContentCaptureCondition.java b/core/java/android/view/contentcapture/ContentCaptureCondition.java index 685ea1aeaba8..027c8d20ccc6 100644 --- a/core/java/android/view/contentcapture/ContentCaptureCondition.java +++ b/core/java/android/view/contentcapture/ContentCaptureCondition.java @@ -133,7 +133,7 @@ public final class ContentCaptureCondition implements Parcelable { @Override public ContentCaptureCondition createFromParcel(@NonNull Parcel parcel) { - return new ContentCaptureCondition(parcel.readParcelable(null, android.content.LocusId.class), + return new ContentCaptureCondition(parcel.readParcelable(null), parcel.readInt()); } diff --git a/core/java/android/view/contentcapture/ContentCaptureContext.java b/core/java/android/view/contentcapture/ContentCaptureContext.java index 59b5286f6fc5..3bc9a967ea20 100644 --- a/core/java/android/view/contentcapture/ContentCaptureContext.java +++ b/core/java/android/view/contentcapture/ContentCaptureContext.java @@ -419,7 +419,7 @@ public final class ContentCaptureContext implements Parcelable { final ContentCaptureContext clientContext; if (hasClientContext) { // Must reconstruct the client context using the Builder API - final LocusId id = parcel.readParcelable(null, android.content.LocusId.class); + final LocusId id = parcel.readParcelable(null); final Bundle extras = parcel.readBundle(); final Builder builder = new Builder(id); if (extras != null) builder.setExtras(extras); @@ -427,7 +427,7 @@ public final class ContentCaptureContext implements Parcelable { } else { clientContext = null; } - final ComponentName componentName = parcel.readParcelable(null, android.content.ComponentName.class); + final ComponentName componentName = parcel.readParcelable(null); if (componentName == null) { // Client-state only return clientContext; diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java index ba4176faa283..0f4bc191fe4e 100644 --- a/core/java/android/view/contentcapture/ContentCaptureEvent.java +++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java @@ -620,7 +620,7 @@ public final class ContentCaptureEvent implements Parcelable { final int type = parcel.readInt(); final long eventTime = parcel.readLong(); final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, type, eventTime); - final AutofillId id = parcel.readParcelable(null, android.view.autofill.AutofillId.class); + final AutofillId id = parcel.readParcelable(null); if (id != null) { event.setAutofillId(id); } @@ -637,13 +637,13 @@ public final class ContentCaptureEvent implements Parcelable { event.setParentSessionId(parcel.readInt()); } if (type == TYPE_SESSION_STARTED || type == TYPE_CONTEXT_UPDATED) { - event.setClientContext(parcel.readParcelable(null, android.view.contentcapture.ContentCaptureContext.class)); + event.setClientContext(parcel.readParcelable(null)); } if (type == TYPE_VIEW_INSETS_CHANGED) { - event.setInsets(parcel.readParcelable(null, android.graphics.Insets.class)); + event.setInsets(parcel.readParcelable(null)); } if (type == TYPE_WINDOW_BOUNDS_CHANGED) { - event.setBounds(parcel.readParcelable(null, android.graphics.Rect.class)); + event.setBounds(parcel.readParcelable(null)); } if (type == TYPE_VIEW_TEXT_CHANGED) { event.setComposingIndex(parcel.readInt(), parcel.readInt()); diff --git a/core/java/android/view/contentcapture/ViewNode.java b/core/java/android/view/contentcapture/ViewNode.java index 1762a5817aaf..1b4a00f81e44 100644 --- a/core/java/android/view/contentcapture/ViewNode.java +++ b/core/java/android/view/contentcapture/ViewNode.java @@ -124,10 +124,10 @@ public final class ViewNode extends AssistStructure.ViewNode { mFlags = nodeFlags; if ((nodeFlags & FLAGS_HAS_AUTOFILL_ID) != 0) { - mAutofillId = parcel.readParcelable(null, android.view.autofill.AutofillId.class); + mAutofillId = parcel.readParcelable(null); } if ((nodeFlags & FLAGS_HAS_AUTOFILL_PARENT_ID) != 0) { - mParentAutofillId = parcel.readParcelable(null, android.view.autofill.AutofillId.class); + mParentAutofillId = parcel.readParcelable(null); } if ((nodeFlags & FLAGS_HAS_TEXT) != 0) { mText = new ViewNodeText(parcel, (nodeFlags & FLAGS_HAS_COMPLEX_TEXT) == 0); @@ -169,7 +169,7 @@ public final class ViewNode extends AssistStructure.ViewNode { mExtras = parcel.readBundle(); } if ((nodeFlags & FLAGS_HAS_LOCALE_LIST) != 0) { - mLocaleList = parcel.readParcelable(null, android.os.LocaleList.class); + mLocaleList = parcel.readParcelable(null); } if ((nodeFlags & FLAGS_HAS_MIME_TYPES) != 0) { mReceiveContentMimeTypes = parcel.readStringArray(); @@ -196,7 +196,7 @@ public final class ViewNode extends AssistStructure.ViewNode { mAutofillHints = parcel.readStringArray(); } if ((nodeFlags & FLAGS_HAS_AUTOFILL_VALUE) != 0) { - mAutofillValue = parcel.readParcelable(null, android.view.autofill.AutofillValue.class); + mAutofillValue = parcel.readParcelable(null); } if ((nodeFlags & FLAGS_HAS_AUTOFILL_OPTIONS) != 0) { mAutofillOptions = parcel.readCharSequenceArray(); diff --git a/core/java/android/view/inputmethod/CursorAnchorInfo.java b/core/java/android/view/inputmethod/CursorAnchorInfo.java index 437e54f635ee..fbc947071c99 100644 --- a/core/java/android/view/inputmethod/CursorAnchorInfo.java +++ b/core/java/android/view/inputmethod/CursorAnchorInfo.java @@ -140,7 +140,7 @@ public final class CursorAnchorInfo implements Parcelable { mInsertionMarkerTop = source.readFloat(); mInsertionMarkerBaseline = source.readFloat(); mInsertionMarkerBottom = source.readFloat(); - mCharacterBoundsArray = source.readParcelable(SparseRectFArray.class.getClassLoader(), android.view.inputmethod.SparseRectFArray.class); + mCharacterBoundsArray = source.readParcelable(SparseRectFArray.class.getClassLoader()); mMatrixValues = source.createFloatArray(); } diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java index 09a14484095e..4cbd477d807a 100644 --- a/core/java/android/view/inputmethod/EditorInfo.java +++ b/core/java/android/view/inputmethod/EditorInfo.java @@ -1067,7 +1067,7 @@ public class EditorInfo implements InputType, Parcelable { res.hintText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); res.label = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); res.packageName = source.readString(); - res.autofillId = source.readParcelable(AutofillId.class.getClassLoader(), android.view.autofill.AutofillId.class); + res.autofillId = source.readParcelable(AutofillId.class.getClassLoader()); res.fieldId = source.readInt(); res.fieldName = source.readString(); res.extras = source.readBundle(); diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java index 70279cc8e845..e1e175512edc 100644 --- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java +++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java @@ -490,7 +490,7 @@ public final class InlineSuggestionsRequest implements Parcelable { boolean clientSupported = (flg & 0x200) != 0; int maxSuggestionCount = in.readInt(); List<InlinePresentationSpec> inlinePresentationSpecs = new ArrayList<>(); - in.readParcelableList(inlinePresentationSpecs, InlinePresentationSpec.class.getClassLoader(), android.widget.inline.InlinePresentationSpec.class); + in.readParcelableList(inlinePresentationSpecs, InlinePresentationSpec.class.getClassLoader()); String hostPackageName = in.readString(); LocaleList supportedLocales = (LocaleList) in.readTypedObject(LocaleList.CREATOR); Bundle extras = in.readBundle(); diff --git a/core/java/android/view/inputmethod/InlineSuggestionsResponse.java b/core/java/android/view/inputmethod/InlineSuggestionsResponse.java index 532fc85dcc44..b393c67d7876 100644 --- a/core/java/android/view/inputmethod/InlineSuggestionsResponse.java +++ b/core/java/android/view/inputmethod/InlineSuggestionsResponse.java @@ -170,7 +170,7 @@ public final class InlineSuggestionsResponse implements Parcelable { // static FieldType unparcelFieldName(Parcel in) { ... } List<InlineSuggestion> inlineSuggestions = new ArrayList<>(); - in.readParcelableList(inlineSuggestions, InlineSuggestion.class.getClassLoader(), android.view.inputmethod.InlineSuggestion.class); + in.readParcelableList(inlineSuggestions, InlineSuggestion.class.getClassLoader()); this.mInlineSuggestions = inlineSuggestions; com.android.internal.util.AnnotationValidations.validate( diff --git a/core/java/android/view/textclassifier/ConversationAction.java b/core/java/android/view/textclassifier/ConversationAction.java index a4a5a1ed0ac9..bf0409dfc919 100644 --- a/core/java/android/view/textclassifier/ConversationAction.java +++ b/core/java/android/view/textclassifier/ConversationAction.java @@ -141,7 +141,7 @@ public final class ConversationAction implements Parcelable { private ConversationAction(Parcel in) { mType = in.readString(); - mAction = in.readParcelable(null, android.app.RemoteAction.class); + mAction = in.readParcelable(null); mTextReply = in.readCharSequence(); mScore = in.readFloat(); mExtras = in.readBundle(); diff --git a/core/java/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java index 7a6a3cd026fd..6ad5cb913553 100644 --- a/core/java/android/view/textclassifier/ConversationActions.java +++ b/core/java/android/view/textclassifier/ConversationActions.java @@ -149,7 +149,7 @@ public final class ConversationActions implements Parcelable { } private Message(Parcel in) { - mAuthor = in.readParcelable(null, android.app.Person.class); + mAuthor = in.readParcelable(null); mReferenceTime = in.readInt() == 0 ? null @@ -331,13 +331,13 @@ public final class ConversationActions implements Parcelable { private static Request readFromParcel(Parcel in) { List<Message> conversation = new ArrayList<>(); - in.readParcelableList(conversation, null, android.view.textclassifier.ConversationActions.Message.class); - TextClassifier.EntityConfig typeConfig = in.readParcelable(null, android.view.textclassifier.TextClassifier.EntityConfig.class); + in.readParcelableList(conversation, null); + TextClassifier.EntityConfig typeConfig = in.readParcelable(null); int maxSuggestions = in.readInt(); List<String> hints = new ArrayList<>(); in.readStringList(hints); Bundle extras = in.readBundle(); - SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null, android.view.textclassifier.SystemTextClassifierMetadata.class); + SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null); Request request = new Request( conversation, diff --git a/core/java/android/view/textclassifier/SelectionEvent.java b/core/java/android/view/textclassifier/SelectionEvent.java index b34701082b80..858825b1d5ac 100644 --- a/core/java/android/view/textclassifier/SelectionEvent.java +++ b/core/java/android/view/textclassifier/SelectionEvent.java @@ -172,7 +172,7 @@ public final class SelectionEvent implements Parcelable { mEnd = in.readInt(); mSmartStart = in.readInt(); mSmartEnd = in.readInt(); - mSystemTcMetadata = in.readParcelable(null, android.view.textclassifier.SystemTextClassifierMetadata.class); + mSystemTcMetadata = in.readParcelable(null); } @Override diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java index 8b04d35734ec..7db35d4bf8b5 100644 --- a/core/java/android/view/textclassifier/TextClassification.java +++ b/core/java/android/view/textclassifier/TextClassification.java @@ -713,12 +713,12 @@ public final class TextClassification implements Parcelable { final CharSequence text = in.readCharSequence(); final int startIndex = in.readInt(); final int endIndex = in.readInt(); - final LocaleList defaultLocales = in.readParcelable(null, android.os.LocaleList.class); + final LocaleList defaultLocales = in.readParcelable(null); final String referenceTimeString = in.readString(); final ZonedDateTime referenceTime = referenceTimeString == null ? null : ZonedDateTime.parse(referenceTimeString); final Bundle extras = in.readBundle(); - final SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null, android.view.textclassifier.SystemTextClassifierMetadata.class); + final SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null); final Request request = new Request(text, startIndex, endIndex, defaultLocales, referenceTime, extras); diff --git a/core/java/android/view/textclassifier/TextClassificationContext.java b/core/java/android/view/textclassifier/TextClassificationContext.java index 3a50809ea8b4..5d5683f7110e 100644 --- a/core/java/android/view/textclassifier/TextClassificationContext.java +++ b/core/java/android/view/textclassifier/TextClassificationContext.java @@ -159,7 +159,7 @@ public final class TextClassificationContext implements Parcelable { mPackageName = in.readString(); mWidgetType = in.readString(); mWidgetVersion = in.readString(); - mSystemTcMetadata = in.readParcelable(null, android.view.textclassifier.SystemTextClassifierMetadata.class); + mSystemTcMetadata = in.readParcelable(null); } public static final @android.annotation.NonNull Parcelable.Creator<TextClassificationContext> CREATOR = diff --git a/core/java/android/view/textclassifier/TextClassifierEvent.java b/core/java/android/view/textclassifier/TextClassifierEvent.java index 195565c5bc09..90667cf54f93 100644 --- a/core/java/android/view/textclassifier/TextClassifierEvent.java +++ b/core/java/android/view/textclassifier/TextClassifierEvent.java @@ -189,7 +189,7 @@ public abstract class TextClassifierEvent implements Parcelable { mEventCategory = in.readInt(); mEventType = in.readInt(); mEntityTypes = in.readStringArray(); - mEventContext = in.readParcelable(null, android.view.textclassifier.TextClassificationContext.class); + mEventContext = in.readParcelable(null); mResultId = in.readString(); mEventIndex = in.readInt(); int scoresLength = in.readInt(); diff --git a/core/java/android/view/textclassifier/TextLanguage.java b/core/java/android/view/textclassifier/TextLanguage.java index 67167c6d3e65..604979b1ac78 100644 --- a/core/java/android/view/textclassifier/TextLanguage.java +++ b/core/java/android/view/textclassifier/TextLanguage.java @@ -295,7 +295,7 @@ public final class TextLanguage implements Parcelable { private static Request readFromParcel(Parcel in) { final CharSequence text = in.readCharSequence(); final Bundle extra = in.readBundle(); - final SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null, android.view.textclassifier.SystemTextClassifierMetadata.class); + final SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null); final Request request = new Request(text, extra); request.setSystemTextClassifierMetadata(systemTcMetadata); diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java index 445e9ecff54f..dea3a9010b18 100644 --- a/core/java/android/view/textclassifier/TextLinks.java +++ b/core/java/android/view/textclassifier/TextLinks.java @@ -558,13 +558,13 @@ public final class TextLinks implements Parcelable { private static Request readFromParcel(Parcel in) { final String text = in.readString(); - final LocaleList defaultLocales = in.readParcelable(null, android.os.LocaleList.class); - final EntityConfig entityConfig = in.readParcelable(null, android.view.textclassifier.TextClassifier.EntityConfig.class); + final LocaleList defaultLocales = in.readParcelable(null); + final EntityConfig entityConfig = in.readParcelable(null); final Bundle extras = in.readBundle(); final String referenceTimeString = in.readString(); final ZonedDateTime referenceTime = referenceTimeString == null ? null : ZonedDateTime.parse(referenceTimeString); - final SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null, android.view.textclassifier.SystemTextClassifierMetadata.class); + final SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null); final Request request = new Request(text, defaultLocales, entityConfig, /* legacyFallback= */ true, referenceTime, extras); diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java index dda0fcdd44fd..c1913f69546c 100644 --- a/core/java/android/view/textclassifier/TextSelection.java +++ b/core/java/android/view/textclassifier/TextSelection.java @@ -489,9 +489,9 @@ public final class TextSelection implements Parcelable { final CharSequence text = in.readCharSequence(); final int startIndex = in.readInt(); final int endIndex = in.readInt(); - final LocaleList defaultLocales = in.readParcelable(null, android.os.LocaleList.class); + final LocaleList defaultLocales = in.readParcelable(null); final Bundle extras = in.readBundle(); - final SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null, android.view.textclassifier.SystemTextClassifierMetadata.class); + final SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null); final boolean includeTextClassification = in.readBoolean(); final Request request = new Request(text, startIndex, endIndex, defaultLocales, @@ -548,6 +548,6 @@ public final class TextSelection implements Parcelable { mEntityConfidence = EntityConfidence.CREATOR.createFromParcel(in); mId = in.readString(); mExtras = in.readBundle(); - mTextClassification = in.readParcelable(TextClassification.class.getClassLoader(), android.view.textclassifier.TextClassification.class); + mTextClassification = in.readParcelable(TextClassification.class.getClassLoader()); } } diff --git a/core/java/android/view/translation/TranslationRequest.java b/core/java/android/view/translation/TranslationRequest.java index 027edc21389f..0d41851ca704 100644 --- a/core/java/android/view/translation/TranslationRequest.java +++ b/core/java/android/view/translation/TranslationRequest.java @@ -255,9 +255,9 @@ public final class TranslationRequest implements Parcelable { int flags = in.readInt(); List<TranslationRequestValue> translationRequestValues = new ArrayList<>(); - in.readParcelableList(translationRequestValues, TranslationRequestValue.class.getClassLoader(), android.view.translation.TranslationRequestValue.class); + in.readParcelableList(translationRequestValues, TranslationRequestValue.class.getClassLoader()); List<ViewTranslationRequest> viewTranslationRequests = new ArrayList<>(); - in.readParcelableList(viewTranslationRequests, ViewTranslationRequest.class.getClassLoader(), android.view.translation.ViewTranslationRequest.class); + in.readParcelableList(viewTranslationRequests, ViewTranslationRequest.class.getClassLoader()); this.mFlags = flags; diff --git a/core/java/android/view/translation/TranslationSpec.java b/core/java/android/view/translation/TranslationSpec.java index 76dda5fbe83b..efc3d8ba8096 100644 --- a/core/java/android/view/translation/TranslationSpec.java +++ b/core/java/android/view/translation/TranslationSpec.java @@ -64,7 +64,7 @@ public final class TranslationSpec implements Parcelable { } static ULocale unparcelLocale(Parcel in) { - return (ULocale) in.readSerializable(android.icu.util.ULocale.class.getClassLoader(), android.icu.util.ULocale.class); + return (ULocale) in.readSerializable(); } /** diff --git a/core/java/android/widget/ExpandableListView.java b/core/java/android/widget/ExpandableListView.java index e243aae81da4..51869d4e04d5 100644 --- a/core/java/android/widget/ExpandableListView.java +++ b/core/java/android/widget/ExpandableListView.java @@ -1309,7 +1309,7 @@ public class ExpandableListView extends ListView { private SavedState(Parcel in) { super(in); expandedGroupMetadataList = new ArrayList<ExpandableListConnector.GroupMetadata>(); - in.readList(expandedGroupMetadataList, ExpandableListConnector.class.getClassLoader(), android.widget.ExpandableListConnector.GroupMetadata.class); + in.readList(expandedGroupMetadataList, ExpandableListConnector.class.getClassLoader()); } @Override diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 6d58ee22b159..e60f9a648730 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -1489,7 +1489,7 @@ public class RemoteViews implements Parcelable, Filter { SetRippleDrawableColor(Parcel parcel) { viewId = parcel.readInt(); - mColorStateList = parcel.readParcelable(null, android.content.res.ColorStateList.class); + mColorStateList = parcel.readParcelable(null); } public void writeToParcel(Parcel dest, int flags) { @@ -6628,6 +6628,7 @@ public class RemoteViews implements Parcelable, Filter { opts = ActivityOptions.makeBasic(); opts.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK); } + opts.setLaunchDisplayId(view.getDisplay().getDisplayId()); return Pair.create(intent, opts); } } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 7327214c5389..1a808b2e7c24 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -10689,8 +10689,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return; } - if ((mMarquee == null || mMarquee.isStopped()) && (isFocused() || isSelected()) - && getLineCount() == 1 && canMarquee()) { + if ((mMarquee == null || mMarquee.isStopped()) && isAggregatedVisible() + && (isFocused() || isSelected()) && getLineCount() == 1 && canMarquee()) { if (mMarqueeFadeMode == MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS) { mMarqueeFadeMode = MARQUEE_FADE_SWITCH_SHOW_FADE; @@ -11149,6 +11149,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } + @Override + public void onVisibilityAggregated(boolean isVisible) { + super.onVisibilityAggregated(isVisible); + startStopMarquee(isVisible); + } + /** * Use {@link BaseInputConnection#removeComposingSpans * BaseInputConnection.removeComposingSpans()} to remove any IME composing @@ -13802,7 +13808,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mChoreographer.removeFrameCallback(mTickCallback); final TextView textView = mView.get(); - if (textView != null && (textView.isFocused() || textView.isSelected())) { + if (textView != null && textView.isAggregatedVisible() + && (textView.isFocused() || textView.isSelected())) { long currentMs = mChoreographer.getFrameTime(); long deltaMs = currentMs - mLastAnimationMs; mLastAnimationMs = currentMs; diff --git a/core/java/android/window/DisplayAreaOrganizer.java b/core/java/android/window/DisplayAreaOrganizer.java index 974a1dd50cf5..88ece5c536f6 100644 --- a/core/java/android/window/DisplayAreaOrganizer.java +++ b/core/java/android/window/DisplayAreaOrganizer.java @@ -101,14 +101,6 @@ public class DisplayAreaOrganizer extends WindowOrganizer { public static final int FEATURE_IME_PLACEHOLDER = FEATURE_SYSTEM_FIRST + 7; /** - * Display area for one handed background layer, which preventing when user - * turning the Dark theme on, they can not clearly identify the screen has entered - * one handed mode. - * @hide - */ - public static final int FEATURE_ONE_HANDED_BACKGROUND_PANEL = FEATURE_SYSTEM_FIRST + 8; - - /** * Display area hosting IME window tokens (@see ImeContainer). By default, IMEs are parented * to FEATURE_IME_PLACEHOLDER but can be reparented under other RootDisplayArea. * @@ -118,7 +110,7 @@ public class DisplayAreaOrganizer extends WindowOrganizer { * app on another screen). * @hide */ - public static final int FEATURE_IME = FEATURE_SYSTEM_FIRST + 9; + public static final int FEATURE_IME = FEATURE_SYSTEM_FIRST + 8; /** * The last boundary of display area for system features diff --git a/core/java/com/android/ims/internal/uce/options/OptionsCapInfo.java b/core/java/com/android/ims/internal/uce/options/OptionsCapInfo.java index d709acfc2872..6f83bf3224a8 100644 --- a/core/java/com/android/ims/internal/uce/options/OptionsCapInfo.java +++ b/core/java/com/android/ims/internal/uce/options/OptionsCapInfo.java @@ -89,6 +89,6 @@ public class OptionsCapInfo implements Parcelable { public void readFromParcel(Parcel source) { mSdp = source.readString(); - mCapInfo = source.readParcelable(CapInfo.class.getClassLoader(), com.android.ims.internal.uce.common.CapInfo.class); + mCapInfo = source.readParcelable(CapInfo.class.getClassLoader()); } }
\ No newline at end of file diff --git a/core/java/com/android/ims/internal/uce/options/OptionsCmdStatus.java b/core/java/com/android/ims/internal/uce/options/OptionsCmdStatus.java index 559d61b20d8c..461f8bfb48c8 100644 --- a/core/java/com/android/ims/internal/uce/options/OptionsCmdStatus.java +++ b/core/java/com/android/ims/internal/uce/options/OptionsCmdStatus.java @@ -147,8 +147,8 @@ public class OptionsCmdStatus implements Parcelable { /** @hide */ public void readFromParcel(Parcel source) { mUserData = source.readInt(); - mCmdId = source.readParcelable(OptionsCmdId.class.getClassLoader(), com.android.ims.internal.uce.options.OptionsCmdId.class); - mStatus = source.readParcelable(StatusCode.class.getClassLoader(), com.android.ims.internal.uce.common.StatusCode.class); - mCapInfo = source.readParcelable(CapInfo.class.getClassLoader(), com.android.ims.internal.uce.common.CapInfo.class); + mCmdId = source.readParcelable(OptionsCmdId.class.getClassLoader()); + mStatus = source.readParcelable(StatusCode.class.getClassLoader()); + mCapInfo = source.readParcelable(CapInfo.class.getClassLoader()); } }
\ No newline at end of file diff --git a/core/java/com/android/ims/internal/uce/options/OptionsSipResponse.java b/core/java/com/android/ims/internal/uce/options/OptionsSipResponse.java index 160f9ebaebc8..32420816f5ab 100644 --- a/core/java/com/android/ims/internal/uce/options/OptionsSipResponse.java +++ b/core/java/com/android/ims/internal/uce/options/OptionsSipResponse.java @@ -180,7 +180,7 @@ public class OptionsSipResponse implements Parcelable { mRequestId = source.readInt(); mSipResponseCode = source.readInt(); mReasonPhrase = source.readString(); - mCmdId = source.readParcelable(OptionsCmdId.class.getClassLoader(), com.android.ims.internal.uce.options.OptionsCmdId.class); + mCmdId = source.readParcelable(OptionsCmdId.class.getClassLoader()); mRetryAfter = source.readInt(); mReasonHeader = source.readString(); } diff --git a/core/java/com/android/ims/internal/uce/presence/PresCapInfo.java b/core/java/com/android/ims/internal/uce/presence/PresCapInfo.java index f0ee5f3bb77d..ec8b6bfa4ef3 100644 --- a/core/java/com/android/ims/internal/uce/presence/PresCapInfo.java +++ b/core/java/com/android/ims/internal/uce/presence/PresCapInfo.java @@ -105,6 +105,6 @@ public class PresCapInfo implements Parcelable { /** @hide */ public void readFromParcel(Parcel source) { mContactUri = source.readString(); - mCapInfo = source.readParcelable(CapInfo.class.getClassLoader(), com.android.ims.internal.uce.common.CapInfo.class); + mCapInfo = source.readParcelable(CapInfo.class.getClassLoader()); } } diff --git a/core/java/com/android/ims/internal/uce/presence/PresCmdStatus.java b/core/java/com/android/ims/internal/uce/presence/PresCmdStatus.java index 8fbb000c20f5..7e22106f3be3 100644 --- a/core/java/com/android/ims/internal/uce/presence/PresCmdStatus.java +++ b/core/java/com/android/ims/internal/uce/presence/PresCmdStatus.java @@ -146,8 +146,8 @@ public class PresCmdStatus implements Parcelable{ public void readFromParcel(Parcel source) { mUserData = source.readInt(); mRequestId = source.readInt(); - mCmdId = source.readParcelable(PresCmdId.class.getClassLoader(), com.android.ims.internal.uce.presence.PresCmdId.class); - mStatus = source.readParcelable(StatusCode.class.getClassLoader(), com.android.ims.internal.uce.common.StatusCode.class); + mCmdId = source.readParcelable(PresCmdId.class.getClassLoader()); + mStatus = source.readParcelable(StatusCode.class.getClassLoader()); } }
\ No newline at end of file diff --git a/core/java/com/android/ims/internal/uce/presence/PresResInfo.java b/core/java/com/android/ims/internal/uce/presence/PresResInfo.java index 954c2b61c286..2f797b41b14f 100644 --- a/core/java/com/android/ims/internal/uce/presence/PresResInfo.java +++ b/core/java/com/android/ims/internal/uce/presence/PresResInfo.java @@ -122,6 +122,6 @@ public class PresResInfo implements Parcelable { public void readFromParcel(Parcel source) { mResUri = source.readString(); mDisplayName = source.readString(); - mInstanceInfo = source.readParcelable(PresResInstanceInfo.class.getClassLoader(), com.android.ims.internal.uce.presence.PresResInstanceInfo.class); + mInstanceInfo = source.readParcelable(PresResInstanceInfo.class.getClassLoader()); } }
\ No newline at end of file diff --git a/core/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.java b/core/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.java index 733c0afff367..0130ef47010a 100644 --- a/core/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.java +++ b/core/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.java @@ -190,7 +190,7 @@ public class PresResInstanceInfo implements Parcelable{ mResInstanceState = source.readInt(); mPresentityUri = source.readString(); Parcelable[] tempParcelableArray = source.readParcelableArray( - PresTupleInfo.class.getClassLoader()); + PresTupleInfo.class.getClassLoader(), PresTupleInfo.class); mTupleInfoArray = new PresTupleInfo[] {}; if(tempParcelableArray != null) { mTupleInfoArray = Arrays.copyOf(tempParcelableArray, tempParcelableArray.length, diff --git a/core/java/com/android/ims/internal/uce/presence/PresRlmiInfo.java b/core/java/com/android/ims/internal/uce/presence/PresRlmiInfo.java index 63247dbd8172..e33aa1303886 100644 --- a/core/java/com/android/ims/internal/uce/presence/PresRlmiInfo.java +++ b/core/java/com/android/ims/internal/uce/presence/PresRlmiInfo.java @@ -236,7 +236,7 @@ public class PresRlmiInfo implements Parcelable { mListName = source.readString(); mRequestId = source.readInt(); mPresSubscriptionState = source.readParcelable( - PresSubscriptionState.class.getClassLoader(), com.android.ims.internal.uce.presence.PresSubscriptionState.class); + PresSubscriptionState.class.getClassLoader()); mSubscriptionExpireTime = source.readInt(); mSubscriptionTerminatedReason = source.readString(); } diff --git a/core/java/com/android/ims/internal/uce/presence/PresSipResponse.java b/core/java/com/android/ims/internal/uce/presence/PresSipResponse.java index 8097a3797556..5e394efed294 100644 --- a/core/java/com/android/ims/internal/uce/presence/PresSipResponse.java +++ b/core/java/com/android/ims/internal/uce/presence/PresSipResponse.java @@ -185,7 +185,7 @@ public class PresSipResponse implements Parcelable { mRequestId = source.readInt(); mSipResponseCode = source.readInt(); mReasonPhrase = source.readString(); - mCmdId = source.readParcelable(PresCmdId.class.getClassLoader(), com.android.ims.internal.uce.presence.PresCmdId.class); + mCmdId = source.readParcelable(PresCmdId.class.getClassLoader()); mRetryAfter = source.readInt(); mReasonHeader = source.readString(); } diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 025f7118334c..be7388bfca13 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -759,11 +759,11 @@ public class ChooserActivity extends ResolverActivity implements } try { - IBinder permissionToken = ActivityTaskManager.getService() - .requestStartActivityPermissionToken(getActivityToken()); Intent delegationIntent = new Intent(); final ComponentName delegateActivity = ComponentName.unflattenFromString( Resources.getSystem().getString(R.string.config_chooserActivity)); + IBinder permissionToken = ActivityTaskManager.getService() + .requestStartActivityPermissionToken(delegateActivity); delegationIntent.setComponent(delegateActivity); delegationIntent.putExtra(Intent.EXTRA_INTENT, getIntent()); delegationIntent.putExtra(ActivityTaskManager.EXTRA_PERMISSION_TOKEN, permissionToken); diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index b273f6ded8d0..f9a8c7b58897 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -1437,11 +1437,11 @@ public class ResolverActivity extends Activity implements try { // TODO: Once this is a small springboard activity, it can move off the UI process // and we can move the request method to ActivityManagerInternal. - IBinder permissionToken = ActivityTaskManager.getService() - .requestStartActivityPermissionToken(getActivityToken()); final Intent chooserIntent = new Intent(); final ComponentName delegateActivity = ComponentName.unflattenFromString( Resources.getSystem().getString(R.string.config_chooserActivity)); + IBinder permissionToken = ActivityTaskManager.getService() + .requestStartActivityPermissionToken(delegateActivity); chooserIntent.setClassName(delegateActivity.getPackageName(), delegateActivity.getClassName()); chooserIntent.putExtra(ActivityTaskManager.EXTRA_PERMISSION_TOKEN, permissionToken); diff --git a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java index 289daee26f52..9c3c22451c5a 100644 --- a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java +++ b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java @@ -237,11 +237,12 @@ public class DisplayResolveInfo implements TargetInfo, Parcelable { private DisplayResolveInfo(Parcel in) { mDisplayLabel = in.readCharSequence(); mExtendedInfo = in.readCharSequence(); - mResolvedIntent = in.readParcelable(null /* ClassLoader */, android.content.Intent.class); + mResolvedIntent = in.readParcelable(null /* ClassLoader */); mSourceIntents.addAll( - Arrays.asList((Intent[]) in.readParcelableArray(null /* ClassLoader */))); + Arrays.asList((Intent[]) in.readParcelableArray(null /* ClassLoader */, + Intent.class))); mIsSuspended = in.readBoolean(); mPinned = in.readBoolean(); - mResolveInfo = in.readParcelable(null /* ClassLoader */, android.content.pm.ResolveInfo.class); + mResolveInfo = in.readParcelable(null /* ClassLoader */); } } diff --git a/core/java/com/android/internal/infra/AndroidFuture.java b/core/java/com/android/internal/infra/AndroidFuture.java index 84391c169941..0443ad03b6ea 100644 --- a/core/java/com/android/internal/infra/AndroidFuture.java +++ b/core/java/com/android/internal/infra/AndroidFuture.java @@ -24,7 +24,6 @@ import android.os.Looper; import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; -import android.util.EventLog; import android.util.Log; import com.android.internal.annotations.GuardedBy; @@ -585,6 +584,7 @@ public class AndroidFuture<T> extends CompletableFuture<T> implements Parcelable /** * @see #writeThrowable */ + @SuppressWarnings("UnsafeParcelApi") private static @Nullable Throwable readThrowable(@NonNull Parcel parcel) { final boolean hasThrowable = parcel.readBoolean(); if (!hasThrowable) { diff --git a/core/java/com/android/internal/net/LegacyVpnInfo.java b/core/java/com/android/internal/net/LegacyVpnInfo.java index b3bc93a058cf..43984b59378c 100644 --- a/core/java/com/android/internal/net/LegacyVpnInfo.java +++ b/core/java/com/android/internal/net/LegacyVpnInfo.java @@ -69,7 +69,7 @@ public class LegacyVpnInfo implements Parcelable { LegacyVpnInfo info = new LegacyVpnInfo(); info.key = in.readString(); info.state = in.readInt(); - info.intent = in.readParcelable(null, android.app.PendingIntent.class); + info.intent = in.readParcelable(null); return info; } diff --git a/core/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java index 2a203acebc72..2ae56f808972 100644 --- a/core/java/com/android/internal/net/VpnConfig.java +++ b/core/java/com/android/internal/net/VpnConfig.java @@ -34,8 +34,6 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.UserHandle; -import java.net.Inet4Address; -import java.net.InetAddress; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -93,8 +91,8 @@ public class VpnConfig implements Parcelable { public String interfaze; public String session; public int mtu = -1; - public List<LinkAddress> addresses = new ArrayList<LinkAddress>(); - public List<RouteInfo> routes = new ArrayList<RouteInfo>(); + public List<LinkAddress> addresses = new ArrayList<>(); + public List<RouteInfo> routes = new ArrayList<>(); public List<String> dnsServers; public List<String> searchDomains; public List<String> allowedApplications; @@ -114,12 +112,32 @@ public class VpnConfig implements Parcelable { public VpnConfig() { } - public void updateAllowedFamilies(InetAddress address) { - if (address instanceof Inet4Address) { - allowIPv4 = true; - } else { - allowIPv6 = true; - } + public VpnConfig(VpnConfig other) { + user = other.user; + interfaze = other.interfaze; + session = other.session; + mtu = other.mtu; + addresses = copyOf(other.addresses); + routes = copyOf(other.routes); + dnsServers = copyOf(other.dnsServers); + searchDomains = copyOf(other.searchDomains); + allowedApplications = copyOf(other.allowedApplications); + disallowedApplications = copyOf(other.disallowedApplications); + configureIntent = other.configureIntent; + startTime = other.startTime; + legacy = other.legacy; + blocking = other.blocking; + allowBypass = other.allowBypass; + allowIPv4 = other.allowIPv4; + allowIPv6 = other.allowIPv6; + isMetered = other.isMetered; + underlyingNetworks = other.underlyingNetworks != null ? Arrays.copyOf( + other.underlyingNetworks, other.underlyingNetworks.length) : null; + proxyInfo = other.proxyInfo; + } + + private static <T> List<T> copyOf(List<T> list) { + return list != null ? new ArrayList<>(list) : null; } public void addLegacyRoutes(String routesStr) { @@ -131,7 +149,6 @@ public class VpnConfig implements Parcelable { //each route is ip/prefix RouteInfo info = new RouteInfo(new IpPrefix(route), null, null, RouteInfo.RTN_UNICAST); this.routes.add(info); - updateAllowedFamilies(info.getDestination().getAddress()); } } @@ -144,7 +161,6 @@ public class VpnConfig implements Parcelable { //each address is ip/prefix LinkAddress addr = new LinkAddress(address); this.addresses.add(addr); - updateAllowedFamilies(addr.getAddress()); } } @@ -192,7 +208,7 @@ public class VpnConfig implements Parcelable { config.searchDomains = in.createStringArrayList(); config.allowedApplications = in.createStringArrayList(); config.disallowedApplications = in.createStringArrayList(); - config.configureIntent = in.readParcelable(null, android.app.PendingIntent.class); + config.configureIntent = in.readParcelable(null); config.startTime = in.readLong(); config.legacy = in.readInt() != 0; config.blocking = in.readInt() != 0; @@ -201,7 +217,7 @@ public class VpnConfig implements Parcelable { config.allowIPv6 = in.readInt() != 0; config.isMetered = in.readInt() != 0; config.underlyingNetworks = in.createTypedArray(Network.CREATOR); - config.proxyInfo = in.readParcelable(null, android.net.ProxyInfo.class); + config.proxyInfo = in.readParcelable(null); return config; } diff --git a/core/java/com/android/internal/net/VpnProfile.java b/core/java/com/android/internal/net/VpnProfile.java index 519faa8456cc..d8dc1436128e 100644 --- a/core/java/com/android/internal/net/VpnProfile.java +++ b/core/java/com/android/internal/net/VpnProfile.java @@ -182,9 +182,9 @@ public final class VpnProfile implements Cloneable, Parcelable { ipsecCaCert = in.readString(); ipsecServerCert = in.readString(); saveLogin = in.readInt() != 0; - proxy = in.readParcelable(null, android.net.ProxyInfo.class); + proxy = in.readParcelable(null); mAllowedAlgorithms = new ArrayList<>(); - in.readList(mAllowedAlgorithms, null, java.lang.String.class); + in.readList(mAllowedAlgorithms, null); isBypassable = in.readBoolean(); isMetered = in.readBoolean(); maxMtu = in.readInt(); diff --git a/core/java/com/android/internal/os/AppFuseMount.java b/core/java/com/android/internal/os/AppFuseMount.java index 5404fea68672..04d72117d28a 100644 --- a/core/java/com/android/internal/os/AppFuseMount.java +++ b/core/java/com/android/internal/os/AppFuseMount.java @@ -57,7 +57,7 @@ public class AppFuseMount implements Parcelable { new Parcelable.Creator<AppFuseMount>() { @Override public AppFuseMount createFromParcel(Parcel in) { - return new AppFuseMount(in.readInt(), in.readParcelable(null, android.os.ParcelFileDescriptor.class)); + return new AppFuseMount(in.readInt(), in.readParcelable(null)); } @Override diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 21f719c74aa8..9429c79697bf 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -26,6 +26,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; +import android.app.usage.NetworkStatsManager; import android.bluetooth.BluetoothActivityEnergyInfo; import android.bluetooth.UidTraffic; import android.compat.annotation.UnsupportedAppUsage; @@ -37,7 +38,6 @@ import android.content.IntentFilter; import android.database.ContentObserver; import android.hardware.usb.UsbManager; import android.location.GnssSignalQuality; -import android.net.INetworkStatsService; import android.net.NetworkStats; import android.net.Uri; import android.net.wifi.WifiManager; @@ -137,7 +137,9 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Queue; +import java.util.Set; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; @@ -12534,19 +12536,11 @@ public class BatteryStatsImpl extends BatteryStats { private NetworkStats mLastModemNetworkStats = new NetworkStats(0, -1); @VisibleForTesting - protected NetworkStats readNetworkStatsLocked(String[] ifaces) { - try { - if (!ArrayUtils.isEmpty(ifaces)) { - INetworkStatsService statsService = INetworkStatsService.Stub.asInterface( - ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); - if (statsService != null) { - return statsService.getDetailedUidStats(ifaces); - } else { - Slog.e(TAG, "Failed to get networkStatsService "); - } - } - } catch (RemoteException e) { - Slog.e(TAG, "failed to read network stats for ifaces: " + Arrays.toString(ifaces) + e); + protected NetworkStats readNetworkStatsLocked(@NonNull NetworkStatsManager networkStatsManager, + String[] ifaces) { + Objects.requireNonNull(networkStatsManager); + if (!ArrayUtils.isEmpty(ifaces)) { + return networkStatsManager.getDetailedUidStats(Set.of(ifaces)); } return null; } @@ -12557,7 +12551,8 @@ public class BatteryStatsImpl extends BatteryStats { */ @GuardedBy("this") public void updateWifiState(@Nullable final WifiActivityEnergyInfo info, - final long consumedChargeUC, long elapsedRealtimeMs, long uptimeMs) { + final long consumedChargeUC, long elapsedRealtimeMs, long uptimeMs, + @NonNull NetworkStatsManager networkStatsManager) { if (DEBUG_ENERGY) { synchronized (mWifiNetworkLock) { Slog.d(TAG, "Updating wifi stats: " + Arrays.toString(mWifiIfaces)); @@ -12567,7 +12562,8 @@ public class BatteryStatsImpl extends BatteryStats { // Grab a separate lock to acquire the network stats, which may do I/O. NetworkStats delta = null; synchronized (mWifiNetworkLock) { - final NetworkStats latestStats = readNetworkStatsLocked(mWifiIfaces); + final NetworkStats latestStats = readNetworkStatsLocked(networkStatsManager, + mWifiIfaces); if (latestStats != null) { delta = NetworkStats.subtract(latestStats, mLastWifiNetworkStats, null, null, mNetworkStatsPool.acquire()); @@ -12920,7 +12916,8 @@ public class BatteryStatsImpl extends BatteryStats { * Distribute Cell radio energy info and network traffic to apps. */ public void noteModemControllerActivity(@Nullable final ModemActivityInfo activityInfo, - final long consumedChargeUC, long elapsedRealtimeMs, long uptimeMs) { + final long consumedChargeUC, long elapsedRealtimeMs, long uptimeMs, + @NonNull NetworkStatsManager networkStatsManager) { if (DEBUG_ENERGY) { Slog.d(TAG, "Updating mobile radio stats with " + activityInfo); } @@ -12934,7 +12931,8 @@ public class BatteryStatsImpl extends BatteryStats { // Grab a separate lock to acquire the network stats, which may do I/O. NetworkStats delta = null; synchronized (mModemNetworkLock) { - final NetworkStats latestStats = readNetworkStatsLocked(mModemIfaces); + final NetworkStats latestStats = readNetworkStatsLocked(networkStatsManager, + mModemIfaces); if (latestStats != null) { delta = NetworkStats.subtract(latestStats, mLastModemNetworkStats, null, null, mNetworkStatsPool.acquire()); diff --git a/core/java/com/android/internal/statusbar/StatusBarIcon.java b/core/java/com/android/internal/statusbar/StatusBarIcon.java index 4f80afaab696..1d626235c4d2 100644 --- a/core/java/com/android/internal/statusbar/StatusBarIcon.java +++ b/core/java/com/android/internal/statusbar/StatusBarIcon.java @@ -81,9 +81,9 @@ public class StatusBarIcon implements Parcelable { } public void readFromParcel(Parcel in) { - this.icon = (Icon) in.readParcelable(null, android.graphics.drawable.Icon.class); + this.icon = (Icon) in.readParcelable(null); this.pkg = in.readString(); - this.user = (UserHandle) in.readParcelable(null, android.os.UserHandle.class); + this.user = (UserHandle) in.readParcelable(null); this.iconLevel = in.readInt(); this.visible = in.readInt() != 0; this.number = in.readInt(); diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java index d3c3917cd791..f46223ac8769 100644 --- a/core/java/com/android/internal/util/ScreenshotHelper.java +++ b/core/java/com/android/internal/util/ScreenshotHelper.java @@ -71,11 +71,11 @@ public class ScreenshotHelper { if (in.readInt() == 1) { mBitmapBundle = in.readBundle(getClass().getClassLoader()); - mBoundsInScreen = in.readParcelable(Rect.class.getClassLoader(), android.graphics.Rect.class); - mInsets = in.readParcelable(Insets.class.getClassLoader(), android.graphics.Insets.class); + mBoundsInScreen = in.readParcelable(Rect.class.getClassLoader()); + mInsets = in.readParcelable(Insets.class.getClassLoader()); mTaskId = in.readInt(); mUserId = in.readInt(); - mTopComponent = in.readParcelable(ComponentName.class.getClassLoader(), android.content.ComponentName.class); + mTopComponent = in.readParcelable(ComponentName.class.getClassLoader()); } } diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java index 627381c620c1..09ff4e0aa076 100644 --- a/core/java/com/android/internal/widget/PointerLocationView.java +++ b/core/java/com/android/internal/widget/PointerLocationView.java @@ -37,6 +37,7 @@ import android.view.InputDevice; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.MotionEvent.PointerCoords; +import android.view.RoundedCorner; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; @@ -229,13 +230,29 @@ public class PointerLocationView extends View implements InputDeviceListener, @Override public WindowInsets onApplyWindowInsets(WindowInsets insets) { + int headerPaddingTop = 0; + Insets waterfallInsets = Insets.NONE; + + final RoundedCorner topLeftRounded = + insets.getRoundedCorner(RoundedCorner.POSITION_TOP_LEFT); + if (topLeftRounded != null) { + headerPaddingTop = topLeftRounded.getRadius(); + } + + final RoundedCorner topRightRounded = + insets.getRoundedCorner(RoundedCorner.POSITION_TOP_RIGHT); + if (topRightRounded != null) { + headerPaddingTop = Math.max(headerPaddingTop, topRightRounded.getRadius()); + } + if (insets.getDisplayCutout() != null) { - mHeaderPaddingTop = insets.getDisplayCutout().getSafeInsetTop(); - mWaterfallInsets = insets.getDisplayCutout().getWaterfallInsets(); - } else { - mHeaderPaddingTop = 0; - mWaterfallInsets = Insets.NONE; + headerPaddingTop = + Math.max(headerPaddingTop, insets.getDisplayCutout().getSafeInsetTop()); + waterfallInsets = insets.getDisplayCutout().getWaterfallInsets(); } + + mHeaderPaddingTop = headerPaddingTop; + mWaterfallInsets = waterfallInsets; return super.onApplyWindowInsets(insets); } diff --git a/core/jni/android_text_Hyphenator.cpp b/core/jni/android_text_Hyphenator.cpp index 011e0514b82f..3651dbdb3fa3 100644 --- a/core/jni/android_text_Hyphenator.cpp +++ b/core/jni/android_text_Hyphenator.cpp @@ -130,6 +130,7 @@ static void init() { addHyphenator("sk", 2, 2); // Slovak addHyphenator("sl", 2, 2); // Slovenian addHyphenator("sq", 2, 2); // Albanian + addHyphenator("sv", 1, 2); // Swedish addHyphenator("ta", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Tamil addHyphenator("te", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX); // Telugu addHyphenator("tk", 2, 2); // Turkmen diff --git a/core/proto/android/providers/settings/system.proto b/core/proto/android/providers/settings/system.proto index 73d6a17799d9..e56d55e940e4 100644 --- a/core/proto/android/providers/settings/system.proto +++ b/core/proto/android/providers/settings/system.proto @@ -212,6 +212,12 @@ message SystemSettingsProto { // DatabaseHelper. optional SettingProto in_silent = 3 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto when_ringing = 4 [ (android.privacy).dest = DEST_AUTOMATIC ]; + + optional SettingProto alarm_intensity = 5 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto media_intensity = 6 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto ring_intensity = 7 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // notification_intensity is already logged at Notification.vibration_intensity + // haptic_feedback_intensity is already logged at HapticFeedback.intensity } optional Vibrate vibrate = 32; diff --git a/core/proto/android/server/vibrator/OWNERS b/core/proto/android/server/vibrator/OWNERS new file mode 100644 index 000000000000..b54d6bf07818 --- /dev/null +++ b/core/proto/android/server/vibrator/OWNERS @@ -0,0 +1 @@ +include platform/frameworks/base:/services/core/java/com/android/server/vibrator/OWNERS
\ No newline at end of file diff --git a/core/proto/android/server/vibrator/vibratormanagerservice.proto b/core/proto/android/server/vibrator/vibratormanagerservice.proto index 7b97524d0510..fbe2170ea51c 100644 --- a/core/proto/android/server/vibrator/vibratormanagerservice.proto +++ b/core/proto/android/server/vibrator/vibratormanagerservice.proto @@ -97,7 +97,7 @@ message VibrationProto { optional int32 status = 6; } -// Next id: 18 +// Next id: 24 message VibratorManagerServiceDumpProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; repeated int32 vibrator_ids = 1; @@ -106,8 +106,14 @@ message VibratorManagerServiceDumpProto { optional VibrationProto current_external_vibration = 4; optional bool vibrator_under_external_control = 5; optional bool low_power_mode = 6; + optional int32 alarm_intensity = 18; + optional int32 alarm_default_intensity = 19; optional int32 haptic_feedback_intensity = 7; optional int32 haptic_feedback_default_intensity = 8; + optional int32 hardware_feedback_intensity = 22; + optional int32 hardware_feedback_default_intensity = 23; + optional int32 media_intensity = 20; + optional int32 media_default_intensity = 21; optional int32 notification_intensity = 9; optional int32 notification_default_intensity = 10; optional int32 ring_intensity = 11; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index a005eb0d5f03..ae5414d4fa47 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -4438,6 +4438,12 @@ <permission android:name="android.permission.REVOKE_RUNTIME_PERMISSIONS" android:protectionLevel="signature|installer|verifier" /> + <!-- @TestApi Allows an application to revoke the POST_NOTIFICATIONS permission from an app + without killing the app. Only granted to the shell. + @hide --> + <permission android:name="android.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL" + android:protectionLevel="signature" /> + <!-- @SystemApi Allows the system to read runtime permission state. @hide --> <permission android:name="android.permission.GET_RUNTIME_PERMISSIONS" @@ -5580,13 +5586,6 @@ <permission android:name="android.permission.WRITE_COMMUNAL_STATE" android:protectionLevel="signature" /> - <!-- Allows an application to view information from the currently active - {@link com.android.server.communal.CommunalManagerService}. - @hide - @SystemApi --> - <permission android:name="android.permission.READ_COMMUNAL_STATE" - android:protectionLevel="signature|privileged"/> - <!-- Allows the holder to manage whether the system can bind to services provided by instant apps. This permission is intended to protect test/development fucntionality and should be used only in such cases. @@ -6101,7 +6100,7 @@ @hide --> <permission android:name="android.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES" android:protectionLevel="signature|role" /> - + <!-- @SystemApi Allows an app to read whether SafetyCenter is enabled/disabled. <p>Protection level: signature|privileged @hide @@ -6109,6 +6108,14 @@ <permission android:name="android.permission.READ_SAFETY_CENTER_STATUS" android:protectionLevel="signature|privileged" /> + <!-- @SystemApi Required to access the safety center internal APIs using the + {@link android.safetycenter.SafetyCenterManager}. + <p>Protection level: internal|installer|role + @hide + --> + <permission android:name="android.permission.MANAGE_SAFETY_CENTER" + android:protectionLevel="internal|installer|role" /> + <!-- Attribution for Geofencing service. --> <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/> <!-- Attribution for Country Detector. --> @@ -6603,6 +6610,10 @@ android:permission="android.permission.BIND_JOB_SERVICE"> </service> + <service android:name="com.android.server.companion.AssociationCleanUpService" + android:permission="android.permission.BIND_JOB_SERVICE"> + </service> + <service android:name="com.android.server.pm.PackageManagerShellCommandDataLoader" android:exported="false"> <intent-filter> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 1705371a7f4a..86c83c590b77 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -8442,6 +8442,9 @@ <attr name="settingsActivity" /> <!-- A preview, in a drawable resource id, of what the Dream will look like. --> <attr name="previewImage" format="reference" /> + <!-- Whether to show clock and other complications such as weather in the overlay. Default + to true. Note that the overlay on dreams is currently only supported on tablets. --> + <attr name="showClockAndComplications" format="boolean" /> </declare-styleable> <!-- Use <code>trust-agent</code> as the root tag of the XML resource that diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 7d8bceaf89e9..2836c9816886 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1179,10 +1179,18 @@ <string-array translatable="false" name="config_ringtoneEffectUris"> </string-array> + <!-- The default intensity level for alarm vibrations. See + Settings.System.ALARM_VIBRATION_INTENSITY more details on the constant values and + meanings. --> + <integer name="config_defaultAlarmVibrationIntensity">2</integer> <!-- The default intensity level for haptic feedback. See Settings.System.HAPTIC_FEEDBACK_INTENSITY more details on the constant values and meanings. --> <integer name="config_defaultHapticFeedbackIntensity">2</integer> + <!-- The default intensity level for media vibrations. See + Settings.System.MEDIA_VIBRATION_INTENSITY more details on the constant values and + meanings. --> + <integer name="config_defaultMediaVibrationIntensity">2</integer> <!-- The default intensity level for notification vibrations. See Settings.System.NOTIFICATION_VIBRATION_INTENSITY more details on the constant values and meanings. --> @@ -2125,6 +2133,8 @@ <string name="config_deviceManager" translatable="false"></string> <!-- The name of the package that will hold the app protection service role. --> <string name="config_systemAppProtectionService" translatable="false"></string> + <!-- The name of the package that will hold the system calendar sync manager role. --> + <string name="config_systemAutomotiveCalendarSyncManager" translatable="false"></string> <!-- The name of the package that will be allowed to change its components' label/icon. --> <string name="config_overrideComponentUiPackage" translatable="false">com.android.stk</string> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 9449f606b3aa..5fa7409150ab 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3250,6 +3250,7 @@ <public name="supportedTypes" /> <public name="resetEnabledSettingsOnAppDataCleared" /> <public name="supportsStylusHandwriting" /> + <public name="showClockAndComplications" /> <!-- @hide @SystemApi --> <public name="gameSessionService" /> </staging-public-group> @@ -3273,6 +3274,8 @@ <public name="config_deviceManager" /> <!-- @hide @SystemApi --> <public name="config_systemAppProtectionService" /> + <!-- @hide @SystemApi @TestApi --> + <public name="config_systemAutomotiveCalendarSyncManager" /> </staging-public-group> <staging-public-group type="dimen" first-id="0x01db0000"> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index ba4aa81766e0..a4b5d3caaabf 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3867,7 +3867,9 @@ <java-symbol type="drawable" name="ic_arrow_forward" /> <java-symbol type="drawable" name="ic_permission" /> + <java-symbol type="integer" name="config_defaultAlarmVibrationIntensity" /> <java-symbol type="integer" name="config_defaultHapticFeedbackIntensity" /> + <java-symbol type="integer" name="config_defaultMediaVibrationIntensity" /> <java-symbol type="integer" name="config_defaultNotificationVibrationIntensity" /> <java-symbol type="integer" name="config_defaultRingVibrationIntensity" /> diff --git a/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java b/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java index 47b14bbaa8fa..4f8b85554f5c 100644 --- a/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java +++ b/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java @@ -16,6 +16,7 @@ package android.content.res; +import android.platform.test.annotations.Presubmit; import android.test.ActivityInstrumentationTestCase2; import android.util.TypedValue; @@ -25,6 +26,7 @@ import com.android.frameworks.coretests.R; import java.lang.reflect.InvocationTargetException; +@Presubmit public class ConfigurationBoundResourceCacheTest extends ActivityInstrumentationTestCase2<ResourceCacheActivity> { diff --git a/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java b/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java index 57f01e988440..9aef2ca104bd 100644 --- a/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java +++ b/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java @@ -27,6 +27,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import android.app.Instrumentation; +import android.platform.test.annotations.Presubmit; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; @@ -45,6 +46,7 @@ import java.util.List; /** * Tests for {@link FontResourcesParser}. */ +@Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class FontResourcesParserTest { diff --git a/core/tests/coretests/src/android/content/res/ResourcesDrawableTest.java b/core/tests/coretests/src/android/content/res/ResourcesDrawableTest.java index c4df88b49935..f7f9569c413e 100644 --- a/core/tests/coretests/src/android/content/res/ResourcesDrawableTest.java +++ b/core/tests/coretests/src/android/content/res/ResourcesDrawableTest.java @@ -24,6 +24,7 @@ import android.content.Context; import android.graphics.drawable.ColorStateListDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; +import android.platform.test.annotations.Presubmit; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; @@ -34,6 +35,7 @@ import com.android.frameworks.coretests.R; import org.junit.Test; import org.junit.runner.RunWith; +@Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class ResourcesDrawableTest { diff --git a/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java b/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java index aa1a5341de57..25c3db5c6910 100644 --- a/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java +++ b/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java @@ -18,6 +18,7 @@ package android.content.res; import android.os.FileUtils; import android.os.LocaleList; +import android.platform.test.annotations.Presubmit; import android.test.AndroidTestCase; import android.util.DisplayMetrics; @@ -30,6 +31,7 @@ import java.io.InputStream; import java.util.Arrays; import java.util.Locale; +@Presubmit public class ResourcesLocaleTest extends AndroidTestCase { private String extractApkAndGetPath(int id) throws Exception { diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java index e7ee9dcf7557..34a8bdea5e8a 100644 --- a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java +++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.app.ResourcesManager; import android.os.Binder; import android.os.LocaleList; +import android.platform.test.annotations.Postsubmit; import android.util.DisplayMetrics; import android.util.TypedValue; import android.view.Display; @@ -32,6 +33,7 @@ import junit.framework.TestCase; import java.util.HashMap; import java.util.Map; +@Postsubmit public class ResourcesManagerTest extends TestCase { private static final int SECONDARY_DISPLAY_ID = 1; private static final String APP_ONE_RES_DIR = "app_one.apk"; diff --git a/core/tests/coretests/src/android/content/res/TEST_MAPPING b/core/tests/coretests/src/android/content/res/TEST_MAPPING new file mode 100644 index 000000000000..4ea6e40a7225 --- /dev/null +++ b/core/tests/coretests/src/android/content/res/TEST_MAPPING @@ -0,0 +1,43 @@ +{ + "presubmit": [ + { + "name": "FrameworksCoreTests", + "options": [ + { + "include-filter": "android.content.res." + }, + { + "include-annotation": "android.platform.test.annotations.Presubmit" + }, + { + "exclude-annotation": "android.platform.test.annotations.Postsubmit" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + } + ] + } + ], + "postsubmit": [ + { + "name": "FrameworksCoreTests", + "options": [ + { + "include-filter": "android.content.res." + }, + { + "include-annotation": "android.platform.test.annotations.Postsubmit" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + } + ] + } + ] +} diff --git a/core/tests/coretests/src/android/os/OWNERS b/core/tests/coretests/src/android/os/OWNERS index a42285eedbf4..f2d6ff87f7f2 100644 --- a/core/tests/coretests/src/android/os/OWNERS +++ b/core/tests/coretests/src/android/os/OWNERS @@ -2,11 +2,7 @@ per-file BrightnessLimit.java = michaelwr@google.com, santoscordon@google.com # Haptics -per-file CombinedVibrationEffectTest.java = file:/services/core/java/com/android/server/vibrator/OWNERS -per-file ExternalVibrationTest.java = file:/services/core/java/com/android/server/vibrator/OWNERS -per-file VibrationEffectTest.java = file:/services/core/java/com/android/server/vibrator/OWNERS -per-file VibratorInfoTest.java = file:/services/core/java/com/android/server/vibrator/OWNERS -per-file VibratorTest.java = file:/services/core/java/com/android/server/vibrator/OWNERS +per-file *Vibrat*.java = file:/services/core/java/com/android/server/vibrator/OWNERS # Power per-file PowerManager*.java = michaelwr@google.com, santoscordon@google.com
\ No newline at end of file diff --git a/core/tests/coretests/src/android/os/VibrationEffectTest.java b/core/tests/coretests/src/android/os/VibrationEffectTest.java index 781564b7be35..10cec8243b8e 100644 --- a/core/tests/coretests/src/android/os/VibrationEffectTest.java +++ b/core/tests/coretests/src/android/os/VibrationEffectTest.java @@ -125,8 +125,8 @@ public class VibrationEffectTest { VibrationEffect.startWaveform() .addStep(/* amplitude= */ 1, /* duration= */ 10) .addRamp(/* amplitude= */ 0, /* duration= */ 20) - .addStep(/* amplitude= */ 1, /* frequency*/ 1, /* duration= */ 100) - .addRamp(/* amplitude= */ 0.5f, /* frequency*/ -1, /* duration= */ 50) + .addStep(/* amplitude= */ 1, /* frequencyHz= */ 1, /* duration= */ 100) + .addRamp(/* amplitude= */ 0.5f, /* frequencyHz= */ 100, /* duration= */ 50) .build() .validate(); @@ -150,10 +150,22 @@ public class VibrationEffectTest { .addStep(/* amplitude= */ -2, 10).build().validate()); assertThrows(IllegalArgumentException.class, () -> VibrationEffect.startWaveform() + .addStep(1, /* frequencyHz= */ -1f, 10).build().validate()); + assertThrows(IllegalArgumentException.class, + () -> VibrationEffect.startWaveform() .addStep(1, /* duration= */ -1).build().validate()); assertThrows(IllegalArgumentException.class, () -> VibrationEffect.startWaveform() - .addStep(1, 0, /* duration= */ -1).build().validate()); + .addStep(1, 100f, /* duration= */ -1).build().validate()); + assertThrows(IllegalArgumentException.class, + () -> VibrationEffect.startWaveform() + .addRamp(/* amplitude= */ -3, 10).build().validate()); + assertThrows(IllegalArgumentException.class, + () -> VibrationEffect.startWaveform() + .addRamp(1, /* frequencyHz= */ 0, 10).build().validate()); + assertThrows(IllegalArgumentException.class, + () -> VibrationEffect.startWaveform() + .addRamp(1, 10f, /* duration= */ -3).build().validate()); } @Test diff --git a/core/tests/coretests/src/android/os/VibratorInfoTest.java b/core/tests/coretests/src/android/os/VibratorInfoTest.java index 6e07fa264c1c..d0e03a24427e 100644 --- a/core/tests/coretests/src/android/os/VibratorInfoTest.java +++ b/core/tests/coretests/src/android/os/VibratorInfoTest.java @@ -19,6 +19,7 @@ package android.os; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import android.hardware.vibrator.Braking; @@ -43,19 +44,17 @@ public class VibratorInfoTest { /* 50Hz= */ 0.1f, 0.2f, 0.4f, 0.8f, /* 150Hz= */ 1f, 0.9f, /* 200Hz= */ 0.8f}; private static final VibratorInfo.FrequencyMapping EMPTY_FREQUENCY_MAPPING = - new VibratorInfo.FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, Float.NaN, null); + new VibratorInfo.FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, null); private static final VibratorInfo.FrequencyMapping TEST_FREQUENCY_MAPPING = - new VibratorInfo.FrequencyMapping(TEST_MIN_FREQUENCY, - TEST_RESONANT_FREQUENCY, TEST_FREQUENCY_RESOLUTION, - /* suggestedSafeRangeHz= */ 50, TEST_AMPLITUDE_MAP); + new VibratorInfo.FrequencyMapping(TEST_RESONANT_FREQUENCY, TEST_MIN_FREQUENCY, + TEST_FREQUENCY_RESOLUTION, TEST_AMPLITUDE_MAP); @Test public void testHasAmplitudeControl() { VibratorInfo noCapabilities = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build(); assertFalse(noCapabilities.hasAmplitudeControl()); VibratorInfo composeAndAmplitudeControl = new VibratorInfo.Builder(TEST_VIBRATOR_ID) - .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS - | IVibrator.CAP_AMPLITUDE_CONTROL) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS | IVibrator.CAP_AMPLITUDE_CONTROL) .build(); assertTrue(composeAndAmplitudeControl.hasAmplitudeControl()); } @@ -143,138 +142,95 @@ public class VibratorInfoTest { } @Test - public void testGetFrequencyRange_invalidFrequencyMappingReturnsEmptyRange() { + public void testGetFrequencyRangeHz_invalidFrequencyMappingReturnsNull() { // Invalid, contains NaN values or empty array. - assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder( - TEST_VIBRATOR_ID).build().getFrequencyRange()); - assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(TEST_VIBRATOR_ID) + assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID).build().getFrequencyRangeHz()); + assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID) .setFrequencyMapping(new VibratorInfo.FrequencyMapping( - Float.NaN, 150, 25, 50, TEST_AMPLITUDE_MAP)) - .build().getFrequencyRange()); - assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(TEST_VIBRATOR_ID) + Float.NaN, 50, 25, TEST_AMPLITUDE_MAP)) + .build().getFrequencyRangeHz()); + assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID) .setFrequencyMapping(new VibratorInfo.FrequencyMapping( - 50, Float.NaN, 25, 50, TEST_AMPLITUDE_MAP)) - .build().getFrequencyRange()); - assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(TEST_VIBRATOR_ID) + 150, Float.NaN, 25, TEST_AMPLITUDE_MAP)) + .build().getFrequencyRangeHz()); + assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID) .setFrequencyMapping(new VibratorInfo.FrequencyMapping( - 50, 150, Float.NaN, 50, TEST_AMPLITUDE_MAP)) - .build().getFrequencyRange()); - assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(TEST_VIBRATOR_ID) - .setFrequencyMapping(new VibratorInfo.FrequencyMapping( - 50, 150, 25, Float.NaN, TEST_AMPLITUDE_MAP)) - .build().getFrequencyRange()); - assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(TEST_VIBRATOR_ID) - .setFrequencyMapping(new VibratorInfo.FrequencyMapping(50, 150, 25, 50, null)) - .build().getFrequencyRange()); + 150, 50, Float.NaN, TEST_AMPLITUDE_MAP)) + .build().getFrequencyRangeHz()); + assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID) + .setFrequencyMapping(new VibratorInfo.FrequencyMapping(150, 50, 25, null)) + .build().getFrequencyRangeHz()); // Invalid, minFrequency > resonantFrequency - assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(TEST_VIBRATOR_ID) + assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID) .setFrequencyMapping(new VibratorInfo.FrequencyMapping( - /* minFrequencyHz= */ 250, /* resonantFrequency= */ 150, 25, 50, null)) - .build().getFrequencyRange()); + /* resonantFrequencyHz= */ 150, /* minFrequencyHz= */ 250, 25, null)) + .build().getFrequencyRangeHz()); // Invalid, maxFrequency < resonantFrequency by changing resolution. - assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(TEST_VIBRATOR_ID) + assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID) .setFrequencyMapping(new VibratorInfo.FrequencyMapping( - 50, 150, /* frequencyResolutionHz= */10, 50, null)) - .build().getFrequencyRange()); + 150, 50, /* frequencyResolutionHz= */ 10, null)) + .build().getFrequencyRangeHz()); } @Test - public void testGetFrequencyRange_safeRangeLimitedByMaxFrequency() { + public void testGetFrequencyRangeHz_resultRangeDerivedFromHalMapping() { VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID) .setFrequencyMapping(new VibratorInfo.FrequencyMapping( - /* minFrequencyHz= */ 50, /* resonantFrequencyHz= */ 150, - /* frequencyResolutionHz= */ 25, /* suggestedSafeRangeHz= */ 200, - TEST_AMPLITUDE_MAP)) + /* resonantFrequencyHz= */ 150, + /* minFrequencyHz= */ 50, + /* frequencyResolutionHz= */ 25, + new float[]{ + /* 50Hz= */ 0.1f, 0.2f, 0.4f, 0.8f, /* 150Hz= */ 1f, 0.9f, + /* 200Hz= */ 0.8f})) .build(); - // Mapping should range from 50Hz = -2 to 200Hz = 1 - // Safe range [-1, 1] = [100Hz, 200Hz] defined by max - resonant = 50Hz - assertEquals(Range.create(-2f, 1f), info.getFrequencyRange()); + assertEquals(Range.create(50f, 200f), info.getFrequencyRangeHz()); } @Test - public void testGetFrequencyRange_safeRangeLimitedByMinFrequency() { - VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID) - .setFrequencyMapping(new VibratorInfo.FrequencyMapping( - /* minFrequencyHz= */ 50, /* resonantFrequencyHz= */ 150, - /* frequencyResolutionHz= */ 50, /* suggestedSafeRangeHz= */ 200, - TEST_AMPLITUDE_MAP)) - .build(); - - // Mapping should range from 50Hz = -1 to 350Hz = 2 - // Safe range [-1, 1] = [50Hz, 250Hz] defined by resonant - min = 100Hz - assertEquals(Range.create(-1f, 2f), info.getFrequencyRange()); - } + public void testGetMaxAmplitude_emptyMappingReturnsAlwaysZero() { + VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build(); + assertEquals(0f, info.getMaxAmplitude(Float.NaN), TEST_TOLERANCE); + assertEquals(0f, info.getMaxAmplitude(100f), TEST_TOLERANCE); + assertEquals(0f, info.getMaxAmplitude(200f), TEST_TOLERANCE); - @Test - public void testGetFrequencyRange_validMappingReturnsFullRelativeRange() { - VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID) + info = new VibratorInfo.Builder(TEST_VIBRATOR_ID) .setFrequencyMapping(new VibratorInfo.FrequencyMapping( - /* minFrequencyHz= */ 50, /* resonantFrequencyHz= */ 150, - /* frequencyResolutionHz= */ 50, /* suggestedSafeRangeHz= */ 100, - TEST_AMPLITUDE_MAP)) + /* resonantFrequencyHz= */ 150, + /* minFrequencyHz= */ Float.NaN, + /* frequencyResolutionHz= */ Float.NaN, + null)) .build(); - // Mapping should range from 50Hz = -2 to 350Hz = 4 - // Safe range [-1, 1] = [100Hz, 200Hz] defined by suggested safe range 100Hz - assertEquals(Range.create(-2f, 4f), info.getFrequencyRange()); - } - - @Test - public void testAbsoluteFrequency_emptyMappingReturnsNaN() { - VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build(); - assertTrue(Float.isNaN(info.getAbsoluteFrequency(-1))); - assertTrue(Float.isNaN(info.getAbsoluteFrequency(0))); - assertTrue(Float.isNaN(info.getAbsoluteFrequency(1))); - } - - @Test - public void testAbsoluteFrequency_validRangeReturnsOriginalValue() { - VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID).setFrequencyMapping( - TEST_FREQUENCY_MAPPING).build(); - assertEquals(TEST_RESONANT_FREQUENCY, info.getAbsoluteFrequency(0), TEST_TOLERANCE); - - // Safe range [-1, 1] = [125Hz, 175Hz] defined by suggested safe range 100Hz - assertEquals(125, info.getAbsoluteFrequency(-1), TEST_TOLERANCE); - assertEquals(175, info.getAbsoluteFrequency(1), TEST_TOLERANCE); - assertEquals(155, info.getAbsoluteFrequency(0.2f), TEST_TOLERANCE); - assertEquals(140, info.getAbsoluteFrequency(-0.4f), TEST_TOLERANCE); - - // Full range [-4, 2] = [50Hz, 200Hz] defined by min frequency and amplitude mapping size - assertEquals(50, info.getAbsoluteFrequency(info.getFrequencyRange().getLower()), - TEST_TOLERANCE); - assertEquals(200, info.getAbsoluteFrequency(info.getFrequencyRange().getUpper()), - TEST_TOLERANCE); - } - - @Test - public void testGetMaxAmplitude_emptyMappingReturnsOnlyResonantFrequency() { - VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build(); - assertEquals(1f, info.getMaxAmplitude(0), TEST_TOLERANCE); - assertEquals(0f, info.getMaxAmplitude(0.1f), TEST_TOLERANCE); - assertEquals(0f, info.getMaxAmplitude(-1), TEST_TOLERANCE); + assertEquals(0f, info.getMaxAmplitude(Float.NaN), TEST_TOLERANCE); + assertEquals(0f, info.getMaxAmplitude(100f), TEST_TOLERANCE); + assertEquals(0f, info.getMaxAmplitude(150f), TEST_TOLERANCE); } @Test public void testGetMaxAmplitude_validMappingReturnsMappedValues() { VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID) - .setFrequencyMapping(new VibratorInfo.FrequencyMapping(/* minFrequencyHz= */ 50, - /* resonantFrequencyHz= */ 150, /* frequencyResolutionHz= */ 25, - /* suggestedSafeRangeHz= */ 50, TEST_AMPLITUDE_MAP)) + .setFrequencyMapping(new VibratorInfo.FrequencyMapping( + /* resonantFrequencyHz= */ 150, + /* minFrequencyHz= */ 50, + /* frequencyResolutionHz= */ 25, + new float[]{ + /* 50Hz= */ 0.1f, 0.2f, 0.4f, 0.8f, /* 150Hz= */ 1f, 0.9f, + /* 200Hz= */ 0.8f})) .build(); - assertEquals(1f, info.getMaxAmplitude(0), TEST_TOLERANCE); // 150Hz - assertEquals(0.9f, info.getMaxAmplitude(1), TEST_TOLERANCE); // 175Hz - assertEquals(0.8f, info.getMaxAmplitude(-1), TEST_TOLERANCE); // 125Hz - assertEquals(0.8f, info.getMaxAmplitude(info.getFrequencyRange().getUpper()), + assertEquals(1f, info.getMaxAmplitude(150f), TEST_TOLERANCE); + assertEquals(0.9f, info.getMaxAmplitude(175f), TEST_TOLERANCE); + assertEquals(0.8f, info.getMaxAmplitude(125f), TEST_TOLERANCE); + assertEquals(0.8f, info.getMaxAmplitude(info.getFrequencyRangeHz().getUpper()), TEST_TOLERANCE); // 200Hz - assertEquals(0.1f, info.getMaxAmplitude(info.getFrequencyRange().getLower()), + assertEquals(0.1f, info.getMaxAmplitude(info.getFrequencyRangeHz().getLower()), TEST_TOLERANCE); // 50Hz - // Rounds 145Hz to the max amplitude for 125Hz, which is lower. - assertEquals(0.8f, info.getMaxAmplitude(-0.1f), TEST_TOLERANCE); // 145Hz - // Rounds 185Hz to the max amplitude for 200Hz, which is lower. - assertEquals(0.8f, info.getMaxAmplitude(1.2f), TEST_TOLERANCE); // 185Hz + // 145Hz maps to the max amplitude for 125Hz, which is lower. + assertEquals(0.8f, info.getMaxAmplitude(145f), TEST_TOLERANCE); // 145Hz + // 185Hz maps to the max amplitude for 200Hz, which is lower. + assertEquals(0.8f, info.getMaxAmplitude(185f), TEST_TOLERANCE); // 185Hz } @Test @@ -317,9 +273,11 @@ public class VibratorInfoTest { assertNotEquals(complete, completeWithDifferentPrimitiveDuration); VibratorInfo completeWithDifferentFrequencyMapping = completeBuilder - .setFrequencyMapping(new VibratorInfo.FrequencyMapping(TEST_MIN_FREQUENCY + 10, - TEST_RESONANT_FREQUENCY + 20, TEST_FREQUENCY_RESOLUTION + 5, - /* suggestedSafeRangeHz= */ 100, TEST_AMPLITUDE_MAP)) + .setFrequencyMapping(new VibratorInfo.FrequencyMapping( + TEST_RESONANT_FREQUENCY + 20, + TEST_MIN_FREQUENCY + 10, + TEST_FREQUENCY_RESOLUTION + 5, + TEST_AMPLITUDE_MAP)) .build(); assertNotEquals(complete, completeWithDifferentFrequencyMapping); diff --git a/core/tests/coretests/src/android/os/VibratorTest.java b/core/tests/coretests/src/android/os/VibratorTest.java index bdd76a5c162a..981086d6b152 100644 --- a/core/tests/coretests/src/android/os/VibratorTest.java +++ b/core/tests/coretests/src/android/os/VibratorTest.java @@ -27,14 +27,22 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import android.content.ContentResolver; +import android.content.Context; +import android.content.ContextWrapper; import android.hardware.vibrator.IVibrator; import android.media.AudioAttributes; import android.platform.test.annotations.Presubmit; import androidx.test.InstrumentationRegistry; +import com.android.internal.util.test.FakeSettingsProvider; +import com.android.internal.util.test.FakeSettingsProviderRule; + import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -50,11 +58,19 @@ import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class VibratorTest { + @Rule + public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule(); + + private Context mContextSpy; private Vibrator mVibratorSpy; @Before public void setUp() { - mVibratorSpy = spy(InstrumentationRegistry.getContext().getSystemService(Vibrator.class)); + mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext())); + + ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy); + when(mContextSpy.getContentResolver()).thenReturn(contentResolver); + mVibratorSpy = spy(new SystemVibrator(mContextSpy)); } @Test diff --git a/core/tests/coretests/src/android/os/vibrator/OWNERS b/core/tests/coretests/src/android/os/vibrator/OWNERS new file mode 100644 index 000000000000..b54d6bf07818 --- /dev/null +++ b/core/tests/coretests/src/android/os/vibrator/OWNERS @@ -0,0 +1 @@ +include platform/frameworks/base:/services/core/java/com/android/server/vibrator/OWNERS
\ No newline at end of file diff --git a/core/tests/coretests/src/android/os/vibrator/RampSegmentTest.java b/core/tests/coretests/src/android/os/vibrator/RampSegmentTest.java index 5f80d2a10515..3291b2d8edd9 100644 --- a/core/tests/coretests/src/android/os/vibrator/RampSegmentTest.java +++ b/core/tests/coretests/src/android/os/vibrator/RampSegmentTest.java @@ -39,19 +39,19 @@ public class RampSegmentTest { @Test public void testCreation() { RampSegment ramp = new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 0, - /* StartFrequency= */ -1, /* endFrequency= */ 1, /* duration= */ 100); + /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 200, /* duration= */ 100); assertEquals(100L, ramp.getDuration()); assertTrue(ramp.hasNonZeroAmplitude()); assertEquals(1f, ramp.getStartAmplitude()); assertEquals(0f, ramp.getEndAmplitude()); - assertEquals(-1f, ramp.getStartFrequency()); - assertEquals(1f, ramp.getEndFrequency()); + assertEquals(100f, ramp.getStartFrequencyHz()); + assertEquals(200f, ramp.getEndFrequencyHz()); } @Test public void testSerialization() { - RampSegment original = new RampSegment(0, 1, 0, 0.5f, 10); + RampSegment original = new RampSegment(0, 1, 10, 20.5f, 10); Parcel parcel = Parcel.obtain(); original.writeToParcel(parcel, 0); parcel.setDataPosition(0); @@ -61,7 +61,9 @@ public class RampSegmentTest { @Test public void testValidate() { new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 0, - /* StartFrequency= */ -1, /* endFrequency= */ 1, /* duration= */ 100).validate(); + /* startFrequencyHz= */ 2, /* endFrequencyHz= */ 1, /* duration= */ 100).validate(); + // Zero frequency is still used internally for unset frequency. + new RampSegment(0, 0, 0, 0, 0).validate(); assertThrows(IllegalArgumentException.class, () -> new RampSegment(VibrationEffect.DEFAULT_AMPLITUDE, 0, 0, 0, 0).validate()); @@ -70,7 +72,15 @@ public class RampSegmentTest { assertThrows(IllegalArgumentException.class, () -> new RampSegment(0, /* endAmplitude= */ 2, 0, 0, 0).validate()); assertThrows(IllegalArgumentException.class, + () -> new RampSegment(0, 0, /* startFrequencyHz= */ -1, 0, 0).validate()); + assertThrows(IllegalArgumentException.class, + () -> new RampSegment(0, 0, 0, /* endFrequencyHz= */ -3, 0).validate()); + assertThrows(IllegalArgumentException.class, () -> new RampSegment(0, 0, 0, 0, /* duration= */ -1).validate()); + assertThrows(IllegalArgumentException.class, + () -> new RampSegment(/* startAmplitude= */ Float.NaN, 0, 0, 0, 0).validate()); + assertThrows(IllegalArgumentException.class, + () -> new RampSegment(0, 0, /* startFrequencyHz= */ Float.NaN, 0, 0).validate()); } @Test diff --git a/core/tests/coretests/src/android/os/vibrator/StepSegmentTest.java b/core/tests/coretests/src/android/os/vibrator/StepSegmentTest.java index fdce86a27ac4..44241273d9e3 100644 --- a/core/tests/coretests/src/android/os/vibrator/StepSegmentTest.java +++ b/core/tests/coretests/src/android/os/vibrator/StepSegmentTest.java @@ -38,13 +38,13 @@ public class StepSegmentTest { @Test public void testCreation() { - StepSegment step = new StepSegment(/* amplitude= */ 1f, /* frequency= */ -1f, + StepSegment step = new StepSegment(/* amplitude= */ 1f, /* frequencyHz= */ 1f, /* duration= */ 100); assertEquals(100, step.getDuration()); assertTrue(step.hasNonZeroAmplitude()); assertEquals(1f, step.getAmplitude()); - assertEquals(-1f, step.getFrequency()); + assertEquals(1f, step.getFrequencyHz()); } @Test @@ -58,14 +58,22 @@ public class StepSegmentTest { @Test public void testValidate() { - new StepSegment(/* amplitude= */ 0f, /* frequency= */ -1f, /* duration= */ 100).validate(); + new StepSegment(/* amplitude= */ 0f, /* frequencyHz= */ 10f, /* duration= */ 10).validate(); + // Zero frequency is still used internally for unset frequency. + new StepSegment(0, 0, 0).validate(); assertThrows(IllegalArgumentException.class, () -> new StepSegment(/* amplitude= */ -2, 1f, 10).validate()); assertThrows(IllegalArgumentException.class, () -> new StepSegment(/* amplitude= */ 2, 1f, 10).validate()); assertThrows(IllegalArgumentException.class, + () -> new StepSegment(1, /* frequencyHz*/ -1f, 10).validate()); + assertThrows(IllegalArgumentException.class, () -> new StepSegment(2, 1f, /* duration= */ -1).validate()); + assertThrows(IllegalArgumentException.class, + () -> new StepSegment(/* amplitude= */ Float.NaN, 1f, 10).validate()); + assertThrows(IllegalArgumentException.class, + () -> new StepSegment(1, /* frequencyHz*/ Float.NaN, 10).validate()); } @Test diff --git a/core/tests/coretests/src/com/android/internal/content/res/TEST_MAPPING b/core/tests/coretests/src/com/android/internal/content/res/TEST_MAPPING new file mode 100644 index 000000000000..9aed8be4f10f --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/content/res/TEST_MAPPING @@ -0,0 +1,21 @@ +{ + "presubmit": [ + { + "name": "FrameworksCoreTests", + "options": [ + { + "include-filter": "com.android.internal.content." + }, + { + "include-annotation": "android.platform.test.annotations.Presubmit" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + } + ] + } + ] +} diff --git a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java index 8d9d79d0c4d9..ce2f76457422 100644 --- a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java @@ -23,6 +23,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import android.app.usage.NetworkStatsManager; import android.net.NetworkCapabilities; import android.net.NetworkStats; import android.os.BatteryConsumer; @@ -44,6 +45,7 @@ import com.google.common.collect.Range; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; @RunWith(AndroidJUnit4.class) @SmallTest @@ -51,6 +53,8 @@ import org.junit.runner.RunWith; public class MobileRadioPowerCalculatorTest { private static final double PRECISION = 0.00001; private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42; + @Mock + NetworkStatsManager mNetworkStatsManager; @Rule public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule() @@ -95,7 +99,8 @@ public class MobileRadioPowerCalculatorTest { ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000, new int[]{100, 200, 300, 400, 500}, 600); - stats.noteModemControllerActivity(mai, POWER_DATA_UNAVAILABLE, 10000, 10000); + stats.noteModemControllerActivity(mai, POWER_DATA_UNAVAILABLE, 10000, 10000, + mNetworkStatsManager); mStatsRule.setTime(12_000_000, 12_000_000); @@ -157,7 +162,8 @@ public class MobileRadioPowerCalculatorTest { mStatsRule.setNetworkStats(new NetworkStats(10000, 1) .insertEntry("cellular", APP_UID, 0, 0, 1000, 100, 2000, 20, 100)); - stats.noteModemControllerActivity(null, POWER_DATA_UNAVAILABLE, 10000, 10000); + stats.noteModemControllerActivity(null, POWER_DATA_UNAVAILABLE, 10000, 10000, + mNetworkStatsManager); uid.setProcessStateForTest( BatteryStats.Uid.PROCESS_STATE_BACKGROUND, 11000); @@ -165,7 +171,8 @@ public class MobileRadioPowerCalculatorTest { mStatsRule.setNetworkStats(new NetworkStats(12000, 1) .insertEntry("cellular", APP_UID, 0, 0, 1000, 250, 2000, 80, 200)); - stats.noteModemControllerActivity(null, POWER_DATA_UNAVAILABLE, 12000, 12000); + stats.noteModemControllerActivity(null, POWER_DATA_UNAVAILABLE, 12000, 12000, + mNetworkStatsManager); assertThat(uid.getMobileRadioMeasuredBatteryConsumptionUC()).isAtMost(0); // 12000-8000 = 4000 ms == 4_000_000 us @@ -239,7 +246,7 @@ public class MobileRadioPowerCalculatorTest { ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000, new int[]{100, 200, 300, 400, 500}, 600); - stats.noteModemControllerActivity(mai, 10_000_000, 10000, 10000); + stats.noteModemControllerActivity(mai, 10_000_000, 10000, 10000, mNetworkStatsManager); mStatsRule.setTime(12_000_000, 12_000_000); @@ -301,7 +308,7 @@ public class MobileRadioPowerCalculatorTest { mStatsRule.setNetworkStats(new NetworkStats(10000, 1) .insertEntry("cellular", APP_UID, 0, 0, 1000, 100, 2000, 20, 100)); - stats.noteModemControllerActivity(null, 10_000_000, 10000, 10000); + stats.noteModemControllerActivity(null, 10_000_000, 10000, 10000, mNetworkStatsManager); uid.setProcessStateForTest( BatteryStats.Uid.PROCESS_STATE_BACKGROUND, 11000); @@ -309,7 +316,7 @@ public class MobileRadioPowerCalculatorTest { mStatsRule.setNetworkStats(new NetworkStats(12000, 1) .insertEntry("cellular", APP_UID, 0, 0, 1000, 250, 2000, 80, 200)); - stats.noteModemControllerActivity(null, 15_000_000, 12000, 12000); + stats.noteModemControllerActivity(null, 15_000_000, 12000, 12000, mNetworkStatsManager); mStatsRule.setTime(20000, 20000); diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java index 4faf349aad7e..bddb3a1906fd 100644 --- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java +++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java @@ -20,6 +20,8 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import android.annotation.NonNull; +import android.app.usage.NetworkStatsManager; import android.net.NetworkStats; import android.os.Handler; import android.os.Looper; @@ -116,7 +118,8 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { } @Override - protected NetworkStats readNetworkStatsLocked(String[] ifaces) { + protected NetworkStats readNetworkStatsLocked(@NonNull NetworkStatsManager networkStatsManager, + String[] ifaces) { return mNetworkStats; } diff --git a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java index fc44ddc216b4..e7ce9a03f18a 100644 --- a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java @@ -21,6 +21,7 @@ import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE; import static com.google.common.truth.Truth.assertThat; +import android.app.usage.NetworkStatsManager; import android.net.NetworkCapabilities; import android.net.NetworkStats; import android.os.BatteryConsumer; @@ -35,6 +36,7 @@ import androidx.test.runner.AndroidJUnit4; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; @RunWith(AndroidJUnit4.class) @SmallTest @@ -43,6 +45,9 @@ public class WifiPowerCalculatorTest { private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42; + @Mock + NetworkStatsManager mNetworkStatsManager; + @Rule public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule() .setAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE, 360.0) @@ -80,7 +85,8 @@ public class WifiPowerCalculatorTest { final BatteryStatsImpl batteryStats = setupTestNetworkNumbers(); final WifiActivityEnergyInfo energyInfo = setupPowerControllerBasedModelEnergyNumbersInfo(); - batteryStats.updateWifiState(energyInfo, POWER_DATA_UNAVAILABLE, 1000, 1000); + batteryStats.updateWifiState(energyInfo, POWER_DATA_UNAVAILABLE, 1000, 1000, + mNetworkStatsManager); WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile()); mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator); @@ -113,7 +119,7 @@ public class WifiPowerCalculatorTest { final BatteryStatsImpl batteryStats = setupTestNetworkNumbers(); final WifiActivityEnergyInfo energyInfo = setupPowerControllerBasedModelEnergyNumbersInfo(); - batteryStats.updateWifiState(energyInfo, 1_000_000, 1000, 1000); + batteryStats.updateWifiState(energyInfo, 1_000_000, 1000, 1000, mNetworkStatsManager); WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile()); mStatsRule.apply(calculator); @@ -160,7 +166,8 @@ public class WifiPowerCalculatorTest { // Don't pass WifiActivityEnergyInfo, making WifiPowerCalculator rely exclusively // on the packet counts. - batteryStats.updateWifiState(/* energyInfo */ null, POWER_DATA_UNAVAILABLE, 1000, 1000); + batteryStats.updateWifiState(/* energyInfo */ null, POWER_DATA_UNAVAILABLE, 1000, 1000, + mNetworkStatsManager); WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile()); mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator); @@ -180,7 +187,8 @@ public class WifiPowerCalculatorTest { // Don't pass WifiActivityEnergyInfo, making WifiPowerCalculator rely exclusively // on the packet counts. - batteryStats.updateWifiState(/* energyInfo */ null, 1_000_000, 1000, 1000); + batteryStats.updateWifiState(/* energyInfo */ null, 1_000_000, 1000, 1000, + mNetworkStatsManager); WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile()); mStatsRule.apply(calculator); diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index f17fa3b17fe5..ee0fb4456336 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -275,6 +275,7 @@ applications that come with the platform <privapp-permissions package="com.android.server.telecom"> <permission name="android.permission.BIND_CONNECTION_SERVICE"/> <permission name="android.permission.BIND_INCALL_SERVICE"/> + <permission name="android.permission.BLUETOOTH_PRIVILEGED"/> <permission name="android.permission.CALL_PRIVILEGED"/> <permission name="android.permission.HANDLE_CAR_MODE_CHANGES"/> <permission name="android.permission.INTERACT_ACROSS_USERS"/> @@ -293,6 +294,7 @@ applications that come with the platform <privapp-permissions package="com.android.shell"> <!-- Needed for test only --> + <permission name="android.permission.LAUNCH_DEVICE_MANAGER_SETUP"/> <permission name="android.permission.MODIFY_DAY_NIGHT_MODE"/> <permission name="android.permission.ACCESS_LOWPAN_STATE"/> <permission name="android.permission.BACKUP"/> @@ -518,13 +520,9 @@ applications that come with the platform <permission name="android.permission.MANAGE_VOICE_KEYPHRASES" /> <!-- Permission required for ATS test - CarDevicePolicyManagerTest --> <permission name="android.permission.LOCK_DEVICE" /> - <!-- Permission required for CTS test - CtsSafetyCenterTestCases --> + <!-- Permissions required for CTS test - CtsSafetyCenterTestCases --> <permission name="android.permission.SEND_SAFETY_CENTER_UPDATE" /> - <!-- Permission required for CTS test - CtsSafetyCenterTestCases --> <permission name="android.permission.READ_SAFETY_CENTER_STATUS" /> - <!-- Permission required for CTS test - CommunalManagerTest --> - <permission name="android.permission.WRITE_COMMUNAL_STATE" /> - <permission name="android.permission.READ_COMMUNAL_STATE" /> </privapp-permissions> <privapp-permissions package="com.android.statementservice"> diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java index a612265793a3..425a37891afb 100644 --- a/graphics/java/android/graphics/BaseCanvas.java +++ b/graphics/java/android/graphics/BaseCanvas.java @@ -67,7 +67,7 @@ public abstract class BaseCanvas { * @hide */ protected int mDensity = Bitmap.DENSITY_NONE; - private boolean mAllowHwBitmapsInSwMode = false; + private boolean mAllowHwFeaturesInSwMode = false; protected void throwIfCannotDraw(Bitmap bitmap) { if (bitmap.isRecycled()) { @@ -101,14 +101,14 @@ public abstract class BaseCanvas { public void drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, @NonNull Paint paint) { - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); nDrawArc(mNativeCanvasWrapper, left, top, right, bottom, startAngle, sweepAngle, useCenter, paint.getNativeInstance()); } public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter, @NonNull Paint paint) { - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); drawArc(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle, useCenter, paint); } @@ -119,14 +119,14 @@ public abstract class BaseCanvas { public void drawBitmap(@NonNull Bitmap bitmap, float left, float top, @Nullable Paint paint) { throwIfCannotDraw(bitmap); - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); nDrawBitmap(mNativeCanvasWrapper, bitmap.getNativeInstance(), left, top, paint != null ? paint.getNativeInstance() : 0, mDensity, mScreenDensity, bitmap.mDensity); } public void drawBitmap(@NonNull Bitmap bitmap, @NonNull Matrix matrix, @Nullable Paint paint) { - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); nDrawBitmapMatrix(mNativeCanvasWrapper, bitmap.getNativeInstance(), matrix.ni(), paint != null ? paint.getNativeInstance() : 0); } @@ -137,7 +137,7 @@ public abstract class BaseCanvas { throw new NullPointerException(); } throwIfCannotDraw(bitmap); - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); final long nativePaint = paint == null ? 0 : paint.getNativeInstance(); int left, top, right, bottom; @@ -163,7 +163,7 @@ public abstract class BaseCanvas { throw new NullPointerException(); } throwIfCannotDraw(bitmap); - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); final long nativePaint = paint == null ? 0 : paint.getNativeInstance(); float left, top, right, bottom; @@ -202,7 +202,7 @@ public abstract class BaseCanvas { || (lastScanline + width > length)) { throw new ArrayIndexOutOfBoundsException(); } - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); // quick escape if there's nothing to draw if (width == 0 || height == 0) { return; @@ -226,7 +226,7 @@ public abstract class BaseCanvas { if ((meshWidth | meshHeight | vertOffset | colorOffset) < 0) { throw new ArrayIndexOutOfBoundsException(); } - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); if (meshWidth == 0 || meshHeight == 0) { return; } @@ -243,7 +243,7 @@ public abstract class BaseCanvas { } public void drawCircle(float cx, float cy, float radius, @NonNull Paint paint) { - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); nDrawCircle(mNativeCanvasWrapper, cx, cy, radius, paint.getNativeInstance()); } @@ -275,23 +275,23 @@ public abstract class BaseCanvas { public void drawLine(float startX, float startY, float stopX, float stopY, @NonNull Paint paint) { - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); nDrawLine(mNativeCanvasWrapper, startX, startY, stopX, stopY, paint.getNativeInstance()); } public void drawLines(@Size(multiple = 4) @NonNull float[] pts, int offset, int count, @NonNull Paint paint) { - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); nDrawLines(mNativeCanvasWrapper, pts, offset, count, paint.getNativeInstance()); } public void drawLines(@Size(multiple = 4) @NonNull float[] pts, @NonNull Paint paint) { - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); drawLines(pts, 0, pts.length, paint); } public void drawOval(float left, float top, float right, float bottom, @NonNull Paint paint) { - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); nDrawOval(mNativeCanvasWrapper, left, top, right, bottom, paint.getNativeInstance()); } @@ -299,18 +299,19 @@ public abstract class BaseCanvas { if (oval == null) { throw new NullPointerException(); } - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); drawOval(oval.left, oval.top, oval.right, oval.bottom, paint); } public void drawPaint(@NonNull Paint paint) { + throwIfHasHwFeaturesInSwMode(paint); nDrawPaint(mNativeCanvasWrapper, paint.getNativeInstance()); } public void drawPatch(@NonNull NinePatch patch, @NonNull Rect dst, @Nullable Paint paint) { Bitmap bitmap = patch.getBitmap(); throwIfCannotDraw(bitmap); - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); final long nativePaint = paint == null ? 0 : paint.getNativeInstance(); nDrawNinePatch(mNativeCanvasWrapper, bitmap.getNativeInstance(), patch.mNativeChunk, dst.left, dst.top, dst.right, dst.bottom, nativePaint, @@ -320,7 +321,7 @@ public abstract class BaseCanvas { public void drawPatch(@NonNull NinePatch patch, @NonNull RectF dst, @Nullable Paint paint) { Bitmap bitmap = patch.getBitmap(); throwIfCannotDraw(bitmap); - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); final long nativePaint = paint == null ? 0 : paint.getNativeInstance(); nDrawNinePatch(mNativeCanvasWrapper, bitmap.getNativeInstance(), patch.mNativeChunk, dst.left, dst.top, dst.right, dst.bottom, nativePaint, @@ -328,7 +329,7 @@ public abstract class BaseCanvas { } public void drawPath(@NonNull Path path, @NonNull Paint paint) { - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); if (path.isSimplePath && path.rects != null) { nDrawRegion(mNativeCanvasWrapper, path.rects.mNativeRegion, paint.getNativeInstance()); } else { @@ -337,18 +338,18 @@ public abstract class BaseCanvas { } public void drawPoint(float x, float y, @NonNull Paint paint) { - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); nDrawPoint(mNativeCanvasWrapper, x, y, paint.getNativeInstance()); } public void drawPoints(@Size(multiple = 2) float[] pts, int offset, int count, @NonNull Paint paint) { - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); nDrawPoints(mNativeCanvasWrapper, pts, offset, count, paint.getNativeInstance()); } public void drawPoints(@Size(multiple = 2) @NonNull float[] pts, @NonNull Paint paint) { - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); drawPoints(pts, 0, pts.length, paint); } @@ -359,7 +360,7 @@ public abstract class BaseCanvas { if (index < 0 || index + count > text.length || count * 2 > pos.length) { throw new IndexOutOfBoundsException(); } - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); for (int i = 0; i < count; i++) { drawText(text, index + i, 1, pos[i * 2], pos[i * 2 + 1], paint); } @@ -368,22 +369,22 @@ public abstract class BaseCanvas { @Deprecated public void drawPosText(@NonNull String text, @NonNull @Size(multiple = 2) float[] pos, @NonNull Paint paint) { - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); drawPosText(text.toCharArray(), 0, text.length(), pos, paint); } public void drawRect(float left, float top, float right, float bottom, @NonNull Paint paint) { - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); nDrawRect(mNativeCanvasWrapper, left, top, right, bottom, paint.getNativeInstance()); } public void drawRect(@NonNull Rect r, @NonNull Paint paint) { - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); drawRect(r.left, r.top, r.right, r.bottom, paint); } public void drawRect(@NonNull RectF rect, @NonNull Paint paint) { - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); nDrawRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom, paint.getNativeInstance()); } @@ -394,13 +395,13 @@ public abstract class BaseCanvas { public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, @NonNull Paint paint) { - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); nDrawRoundRect(mNativeCanvasWrapper, left, top, right, bottom, rx, ry, paint.getNativeInstance()); } public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint) { - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); drawRoundRect(rect.left, rect.top, rect.right, rect.bottom, rx, ry, paint); } @@ -410,7 +411,7 @@ public abstract class BaseCanvas { */ public void drawDoubleRoundRect(@NonNull RectF outer, float outerRx, float outerRy, @NonNull RectF inner, float innerRx, float innerRy, @NonNull Paint paint) { - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); float outerLeft = outer.left; float outerTop = outer.top; float outerRight = outer.right; @@ -431,7 +432,7 @@ public abstract class BaseCanvas { */ public void drawDoubleRoundRect(@NonNull RectF outer, @NonNull float[] outerRadii, @NonNull RectF inner, @NonNull float[] innerRadii, @NonNull Paint paint) { - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); if (innerRadii == null || outerRadii == null || innerRadii.length != 8 || outerRadii.length != 8) { throw new IllegalArgumentException("Both inner and outer radii arrays must contain " @@ -509,7 +510,7 @@ public abstract class BaseCanvas { (text.length - index - count)) < 0) { throw new IndexOutOfBoundsException(); } - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); nDrawText(mNativeCanvasWrapper, text, index, count, x, y, paint.mBidiFlags, paint.getNativeInstance()); } @@ -519,7 +520,7 @@ public abstract class BaseCanvas { if ((start | end | (end - start) | (text.length() - end)) < 0) { throw new IndexOutOfBoundsException(); } - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); if (text instanceof String || text instanceof SpannedString || text instanceof SpannableString) { nDrawText(mNativeCanvasWrapper, text.toString(), start, end, x, y, @@ -537,7 +538,7 @@ public abstract class BaseCanvas { } public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) { - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); nDrawText(mNativeCanvasWrapper, text, 0, text.length(), x, y, paint.mBidiFlags, paint.getNativeInstance()); } @@ -547,7 +548,7 @@ public abstract class BaseCanvas { if ((start | end | (end - start) | (text.length() - end)) < 0) { throw new IndexOutOfBoundsException(); } - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); nDrawText(mNativeCanvasWrapper, text, start, end, x, y, paint.mBidiFlags, paint.getNativeInstance()); } @@ -557,7 +558,7 @@ public abstract class BaseCanvas { if (index < 0 || index + count > text.length) { throw new ArrayIndexOutOfBoundsException(); } - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); nDrawTextOnPath(mNativeCanvasWrapper, text, index, count, path.readOnlyNI(), hOffset, vOffset, paint.mBidiFlags, paint.getNativeInstance()); @@ -566,7 +567,7 @@ public abstract class BaseCanvas { public void drawTextOnPath(@NonNull String text, @NonNull Path path, float hOffset, float vOffset, @NonNull Paint paint) { if (text.length() > 0) { - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); nDrawTextOnPath(mNativeCanvasWrapper, text, path.readOnlyNI(), hOffset, vOffset, paint.mBidiFlags, paint.getNativeInstance()); } @@ -587,7 +588,7 @@ public abstract class BaseCanvas { throw new IndexOutOfBoundsException(); } - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); nDrawTextRun(mNativeCanvasWrapper, text, index, count, contextIndex, contextCount, x, y, isRtl, paint.getNativeInstance(), 0 /* measured text */); } @@ -606,7 +607,7 @@ public abstract class BaseCanvas { throw new IndexOutOfBoundsException(); } - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); if (text instanceof String || text instanceof SpannedString || text instanceof SpannableString) { nDrawTextRun(mNativeCanvasWrapper, text.toString(), start, end, contextStart, @@ -664,7 +665,7 @@ public abstract class BaseCanvas { if (indices != null) { checkRange(indices.length, indexOffset, indexCount); } - throwIfHasHwBitmapInSwMode(paint); + throwIfHasHwFeaturesInSwMode(paint); nDrawVertices(mNativeCanvasWrapper, mode.nativeInt, vertexCount, verts, vertOffset, texs, texOffset, colors, colorOffset, indices, indexOffset, indexCount, paint.getNativeInstance()); @@ -680,50 +681,52 @@ public abstract class BaseCanvas { /** * @hide */ - public void setHwBitmapsInSwModeEnabled(boolean enabled) { - mAllowHwBitmapsInSwMode = enabled; + public void setHwFeaturesInSwModeEnabled(boolean enabled) { + mAllowHwFeaturesInSwMode = enabled; } /** * @hide */ - public boolean isHwBitmapsInSwModeEnabled() { - return mAllowHwBitmapsInSwMode; + public boolean isHwFeaturesInSwModeEnabled() { + return mAllowHwFeaturesInSwMode; } /** + * If true throw an exception * @hide */ - protected void onHwBitmapInSwMode() { - if (!mAllowHwBitmapsInSwMode) { - throw new IllegalArgumentException( - "Software rendering doesn't support hardware bitmaps"); - } + protected boolean onHwFeatureInSwMode() { + return !mAllowHwFeaturesInSwMode; } private void throwIfHwBitmapInSwMode(Bitmap bitmap) { - if (!isHardwareAccelerated() && bitmap.getConfig() == Bitmap.Config.HARDWARE) { - onHwBitmapInSwMode(); + if (!isHardwareAccelerated() && bitmap.getConfig() == Bitmap.Config.HARDWARE + && onHwFeatureInSwMode()) { + throw new IllegalArgumentException( + "Software rendering doesn't support hardware bitmaps"); } } - private void throwIfHasHwBitmapInSwMode(Paint p) { + private void throwIfHasHwFeaturesInSwMode(Paint p) { if (isHardwareAccelerated() || p == null) { return; } - throwIfHasHwBitmapInSwMode(p.getShader()); + throwIfHasHwFeaturesInSwMode(p.getShader()); } - private void throwIfHasHwBitmapInSwMode(Shader shader) { + private void throwIfHasHwFeaturesInSwMode(Shader shader) { if (shader == null) { return; } if (shader instanceof BitmapShader) { throwIfHwBitmapInSwMode(((BitmapShader) shader).mBitmap); - } - if (shader instanceof ComposeShader) { - throwIfHasHwBitmapInSwMode(((ComposeShader) shader).mShaderA); - throwIfHasHwBitmapInSwMode(((ComposeShader) shader).mShaderB); + } else if (shader instanceof RuntimeShader && onHwFeatureInSwMode()) { + throw new IllegalArgumentException( + "Software rendering doesn't support RuntimeShader"); + } else if (shader instanceof ComposeShader) { + throwIfHasHwFeaturesInSwMode(((ComposeShader) shader).mShaderA); + throwIfHasHwFeaturesInSwMode(((ComposeShader) shader).mShaderB); } } diff --git a/graphics/java/android/graphics/Outline.java b/graphics/java/android/graphics/Outline.java index fc7f84c3c83e..618e6dcc4433 100644 --- a/graphics/java/android/graphics/Outline.java +++ b/graphics/java/android/graphics/Outline.java @@ -169,8 +169,7 @@ public final class Outline { } /** - * Sets the Outline to the rounded rect defined by the input rect, and - * corner radius. + * Sets the Outline to the rect defined by the input coordinates. */ public void setRect(int left, int top, int right, int bottom) { setRoundRect(left, top, right, bottom, 0.0f); @@ -184,7 +183,7 @@ public final class Outline { } /** - * Sets the Outline to the rounded rect defined by the input rect, and corner radius. + * Sets the Outline to the rounded rect defined by the input coordinates and corner radius. * <p> * Passing a zero radius is equivalent to calling {@link #setRect(int, int, int, int)} */ diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java index 390d3d414346..ee4165b8da05 100644 --- a/graphics/java/android/graphics/Picture.java +++ b/graphics/java/android/graphics/Picture.java @@ -124,7 +124,7 @@ public class Picture { public void endRecording() { verifyValid(); if (mRecordingCanvas != null) { - mRequiresHwAcceleration = mRecordingCanvas.mHoldsHwBitmap; + mRequiresHwAcceleration = mRecordingCanvas.mUsesHwFeature; mRecordingCanvas = null; nativeEndRecording(mNativePicture); } @@ -182,8 +182,10 @@ public class Picture { if (mRecordingCanvas != null) { endRecording(); } - if (mRequiresHwAcceleration && !canvas.isHardwareAccelerated()) { - canvas.onHwBitmapInSwMode(); + if (mRequiresHwAcceleration && !canvas.isHardwareAccelerated() + && canvas.onHwFeatureInSwMode()) { + throw new IllegalArgumentException("Software rendering not supported for Pictures that" + + " require hardware acceleration"); } nativeDraw(canvas.getNativeCanvasWrapper(), mNativePicture); } @@ -242,7 +244,7 @@ public class Picture { private static class PictureCanvas extends Canvas { private final Picture mPicture; - boolean mHoldsHwBitmap; + boolean mUsesHwFeature; public PictureCanvas(Picture pict, long nativeCanvas) { super(nativeCanvas); @@ -265,8 +267,9 @@ public class Picture { } @Override - protected void onHwBitmapInSwMode() { - mHoldsHwBitmap = true; + protected boolean onHwFeatureInSwMode() { + mUsesHwFeature = true; + return false; } } } diff --git a/graphics/java/android/graphics/RuntimeShader.java b/graphics/java/android/graphics/RuntimeShader.java index 9ca8e3b46317..ef57f4a76007 100644 --- a/graphics/java/android/graphics/RuntimeShader.java +++ b/graphics/java/android/graphics/RuntimeShader.java @@ -178,36 +178,6 @@ public class RuntimeShader extends Shader { setUniform(uniformName, values, false); } - /** - * Old method signature used by some callers within the platform code - * @hide - * @deprecated use setFloatUniform instead - */ - @Deprecated - public void setUniform(@NonNull String uniformName, float[] values) { - setFloatUniform(uniformName, values); - } - - /** - * Old method signature used by some callers within the platform code - * @hide - * @deprecated use setFloatUniform instead - */ - @Deprecated - public void setUniform(@NonNull String uniformName, float value) { - setFloatUniform(uniformName, value); - } - - /** - * Old method signature used by some callers within the platform code - * @hide - * @deprecated use setFloatUniform instead - */ - @Deprecated - public void setUniform(@NonNull String uniformName, float value1, float value2) { - setFloatUniform(uniformName, value1, value2); - } - private void setFloatUniform(@NonNull String uniformName, float value1, float value2, float value3, float value4, int count) { if (uniformName == null) { diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java index b843589376c4..ffaa4ea51452 100644 --- a/graphics/java/android/graphics/drawable/RippleDrawable.java +++ b/graphics/java/android/graphics/drawable/RippleDrawable.java @@ -868,7 +868,7 @@ public class RippleDrawable extends LayerDrawable { private void drawPatterned(@NonNull Canvas canvas) { final Rect bounds = mHotspotBounds; final int saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG); - boolean useCanvasProps = shouldUseCanvasProps(canvas); + boolean useCanvasProps = !mForceSoftware; if (isBounded()) { canvas.clipRect(getDirtyBounds()); } @@ -914,7 +914,11 @@ public class RippleDrawable extends LayerDrawable { } for (int i = 0; i < mRunningAnimations.size(); i++) { RippleAnimationSession s = mRunningAnimations.get(i); - if (useCanvasProps) { + if (!canvas.isHardwareAccelerated()) { + Log.e(TAG, "The RippleDrawable.STYLE_PATTERNED animation is not supported for a " + + "non-hardware accelerated Canvas. Skipping animation."); + break; + } else if (useCanvasProps) { RippleAnimationSession.AnimationProperties<CanvasProperty<Float>, CanvasProperty<Paint>> p = s.getCanvasProperties(); @@ -1002,10 +1006,6 @@ public class RippleDrawable extends LayerDrawable { return color; } - private boolean shouldUseCanvasProps(Canvas c) { - return !mForceSoftware && c.isHardwareAccelerated(); - } - @Override public void invalidateSelf() { invalidateSelf(true); diff --git a/graphics/java/android/graphics/drawable/RippleShader.java b/graphics/java/android/graphics/drawable/RippleShader.java index 272b840219c2..53a673122040 100644 --- a/graphics/java/android/graphics/drawable/RippleShader.java +++ b/graphics/java/android/graphics/drawable/RippleShader.java @@ -17,7 +17,6 @@ package android.graphics.drawable; import android.annotation.ColorInt; -import android.graphics.Color; import android.graphics.RuntimeShader; import android.graphics.Shader; @@ -37,8 +36,8 @@ final class RippleShader extends RuntimeShader { + "uniform vec2 in_tRotation1;\n" + "uniform vec2 in_tRotation2;\n" + "uniform vec2 in_tRotation3;\n" - + "uniform vec4 in_color;\n" - + "uniform vec4 in_sparkleColor;\n" + + "layout(color) uniform vec4 in_color;\n" + + "layout(color) uniform vec4 in_sparkleColor;\n" + "uniform shader in_shader;\n"; private static final String SHADER_LIB = "float triangleNoise(vec2 n) {\n" @@ -134,78 +133,68 @@ final class RippleShader extends RuntimeShader { if (shader != null) { setInputShader("in_shader", shader); } - setUniform("in_hasMask", shader == null ? 0 : 1); + setFloatUniform("in_hasMask", shader == null ? 0 : 1); } public void setRadius(float radius) { - setUniform("in_maxRadius", radius * 2.3f); + setFloatUniform("in_maxRadius", radius * 2.3f); } public void setOrigin(float x, float y) { - setUniform("in_origin", new float[] {x, y}); + setFloatUniform("in_origin", x, y); } public void setTouch(float x, float y) { - setUniform("in_touch", new float[] {x, y}); + setFloatUniform("in_touch", x, y); } public void setProgress(float progress) { - setUniform("in_progress", progress); + setFloatUniform("in_progress", progress); } /** * Continuous offset used as noise phase. */ public void setNoisePhase(float phase) { - setUniform("in_noisePhase", phase * 0.001f); + setFloatUniform("in_noisePhase", phase * 0.001f); // // Keep in sync with: frameworks/base/libs/hwui/pipeline/skia/AnimatedDrawables.h // final float turbulencePhase = phase; - setUniform("in_turbulencePhase", turbulencePhase); + setFloatUniform("in_turbulencePhase", turbulencePhase); final float scale = 1.5f; - setUniform("in_tCircle1", new float[]{ + setFloatUniform("in_tCircle1", (float) (scale * 0.5 + (turbulencePhase * 0.01 * Math.cos(scale * 0.55))), - (float) (scale * 0.5 + (turbulencePhase * 0.01 * Math.sin(scale * 0.55))) - }); - setUniform("in_tCircle2", new float[]{ + (float) (scale * 0.5 + (turbulencePhase * 0.01 * Math.sin(scale * 0.55)))); + setFloatUniform("in_tCircle2", (float) (scale * 0.2 + (turbulencePhase * -0.0066 * Math.cos(scale * 0.45))), - (float) (scale * 0.2 + (turbulencePhase * -0.0066 * Math.sin(scale * 0.45))) - }); - setUniform("in_tCircle3", new float[]{ + (float) (scale * 0.2 + (turbulencePhase * -0.0066 * Math.sin(scale * 0.45)))); + setFloatUniform("in_tCircle3", (float) (scale + (turbulencePhase * -0.0066 * Math.cos(scale * 0.35))), - (float) (scale + (turbulencePhase * -0.0066 * Math.sin(scale * 0.35))) - }); + (float) (scale + (turbulencePhase * -0.0066 * Math.sin(scale * 0.35)))); final double rotation1 = turbulencePhase * PI_ROTATE_RIGHT + 1.7 * Math.PI; - setUniform("in_tRotation1", new float[]{ - (float) Math.cos(rotation1), (float) Math.sin(rotation1) - }); + setFloatUniform("in_tRotation1", + (float) Math.cos(rotation1), (float) Math.sin(rotation1)); final double rotation2 = turbulencePhase * PI_ROTATE_LEFT + 2 * Math.PI; - setUniform("in_tRotation2", new float[]{ - (float) Math.cos(rotation2), (float) Math.sin(rotation2) - }); + setFloatUniform("in_tRotation2", + (float) Math.cos(rotation2), (float) Math.sin(rotation2)); final double rotation3 = turbulencePhase * PI_ROTATE_RIGHT + 2.75 * Math.PI; - setUniform("in_tRotation3", new float[]{ - (float) Math.cos(rotation3), (float) Math.sin(rotation3) - }); + setFloatUniform("in_tRotation3", + (float) Math.cos(rotation3), (float) Math.sin(rotation3)); } /** * Color of the circle that's under the sparkles. Sparkles will always be white. */ public void setColor(@ColorInt int colorInt, @ColorInt int sparkleColorInt) { - Color color = Color.valueOf(colorInt); - Color sparkleColor = Color.valueOf(sparkleColorInt); - setUniform("in_color", new float[] {color.red(), - color.green(), color.blue(), color.alpha()}); - setUniform("in_sparkleColor", new float[] {sparkleColor.red(), - sparkleColor.green(), sparkleColor.blue(), sparkleColor.alpha()}); + setColorUniform("in_color", colorInt); + setColorUniform("in_sparkleColor", sparkleColorInt); } public void setResolution(float w, float h) { final float densityScale = 2.1f; - setUniform("in_resolutionScale", new float[] {1f / w, 1f / h}); - setUniform("in_noiseScale", new float[] {densityScale / w, densityScale / h}); + setFloatUniform("in_resolutionScale", 1f / w, 1f / h); + setFloatUniform("in_noiseScale", densityScale / w, densityScale / h); } } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java index df751fc9fa48..180c77250fd1 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java @@ -79,22 +79,23 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { } @Override - public void registerOrganizer() { - if (mAnimationController != null) { - throw new IllegalStateException("Must unregister the organizer before re-register."); + public void unregisterOrganizer() { + stopOverrideSplitAnimation(); + mAnimationController = null; + super.unregisterOrganizer(); + } + + void startOverrideSplitAnimation() { + if (mAnimationController == null) { + mAnimationController = new TaskFragmentAnimationController(this); } - super.registerOrganizer(); - mAnimationController = new TaskFragmentAnimationController(this); mAnimationController.registerRemoteAnimations(); } - @Override - public void unregisterOrganizer() { + void stopOverrideSplitAnimation() { if (mAnimationController != null) { mAnimationController.unregisterRemoteAnimations(); - mAnimationController = null; } - super.unregisterOrganizer(); } /** diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java index b8e8b0114b47..8f368c2bee22 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -33,6 +33,7 @@ import android.app.Instrumentation; import android.content.Context; import android.content.Intent; import android.content.res.Configuration; +import android.graphics.Rect; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -63,6 +64,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen private @NonNull Consumer<List<SplitInfo>> mEmbeddingCallback; private final List<SplitInfo> mLastReportedSplitStates = new ArrayList<>(); + // We currently only support split activity embedding within the one root Task. + private final Rect mParentBounds = new Rect(); + public SplitController() { mPresenter = new SplitPresenter(new MainThreadExecutor(), this); ActivityThread activityThread = ActivityThread.currentActivityThread(); @@ -79,6 +83,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen public void setEmbeddingRules(@NonNull Set<EmbeddingRule> rules) { mSplitRules.clear(); mSplitRules.addAll(rules); + updateAnimationOverride(); } @NonNull @@ -158,6 +163,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen @Override public void onTaskFragmentParentInfoChanged(@NonNull IBinder fragmentToken, @NonNull Configuration parentConfig) { + onParentBoundsMayChange(parentConfig.windowConfiguration.getBounds()); TaskFragmentContainer container = getContainer(fragmentToken); if (container != null) { mPresenter.updateContainer(container); @@ -165,6 +171,51 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } } + private void onParentBoundsMayChange(Activity activity) { + if (activity.isFinishing()) { + return; + } + + onParentBoundsMayChange(mPresenter.getParentContainerBounds(activity)); + } + + private void onParentBoundsMayChange(Rect parentBounds) { + if (!parentBounds.isEmpty() && !mParentBounds.equals(parentBounds)) { + mParentBounds.set(parentBounds); + updateAnimationOverride(); + } + } + + /** + * Updates if we should override transition animation. We only want to override if the Task + * bounds is large enough for at least one split rule. + */ + private void updateAnimationOverride() { + if (mParentBounds.isEmpty()) { + // We don't know about the parent bounds yet. + return; + } + + // Check if the parent container bounds can support any split rule. + boolean supportSplit = false; + for (EmbeddingRule rule : mSplitRules) { + if (!(rule instanceof SplitRule)) { + continue; + } + if (mPresenter.shouldShowSideBySide(mParentBounds, (SplitRule) rule)) { + supportSplit = true; + break; + } + } + + // We only want to override if it supports split. + if (supportSplit) { + mPresenter.startOverrideSplitAnimation(); + } else { + mPresenter.stopOverrideSplitAnimation(); + } + } + void onActivityCreated(@NonNull Activity launchedActivity) { handleActivityCreated(launchedActivity); updateCallbackIfNecessary(); @@ -180,6 +231,11 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen final TaskFragmentContainer currentContainer = getContainerWithActivity( launchedActivity.getActivityToken()); + if (currentContainer == null) { + // Initial check before any TaskFragment is created. + onParentBoundsMayChange(launchedActivity); + } + // Check if the activity is configured to always be expanded. if (shouldExpand(launchedActivity, null, splitRules)) { if (shouldContainerBeExpanded(currentContainer)) { @@ -257,6 +313,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // onTaskFragmentParentInfoChanged return; } + // The bounds of the container may have been changed. + onParentBoundsMayChange(activity); // Check if activity requires a placeholder launchPlaceholderIfNecessary(activity); @@ -346,7 +404,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen TaskFragmentContainer getTopActiveContainer() { for (int i = mContainers.size() - 1; i >= 0; i--) { TaskFragmentContainer container = mContainers.get(i); - if (!container.isFinished()) { + if (!container.isFinished() && container.getTopNonFinishingActivity() != null) { return container; } } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java index 3c7d2de6165f..a801dc8193fd 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java @@ -37,32 +37,42 @@ class TaskFragmentAnimationController { private final TaskFragmentOrganizer mOrganizer; private final TaskFragmentAnimationRunner mRemoteRunner = new TaskFragmentAnimationRunner(); + private final RemoteAnimationDefinition mDefinition; + private boolean mIsRegister; TaskFragmentAnimationController(TaskFragmentOrganizer organizer) { mOrganizer = organizer; + mDefinition = new RemoteAnimationDefinition(); + final RemoteAnimationAdapter animationAdapter = + new RemoteAnimationAdapter(mRemoteRunner, 0, 0, true /* changeNeedsSnapshot */); + mDefinition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_OPEN, animationAdapter); + mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_OPEN, animationAdapter); + mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_OPEN, animationAdapter); + mDefinition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_CLOSE, animationAdapter); + mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CLOSE, animationAdapter); + mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_CLOSE, animationAdapter); + mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CHANGE, animationAdapter); } void registerRemoteAnimations() { if (DEBUG) { Log.v(TAG, "registerRemoteAnimations"); } - final RemoteAnimationDefinition definition = new RemoteAnimationDefinition(); - final RemoteAnimationAdapter animationAdapter = - new RemoteAnimationAdapter(mRemoteRunner, 0, 0, true /* changeNeedsSnapshot */); - definition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_OPEN, animationAdapter); - definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_OPEN, animationAdapter); - definition.addRemoteAnimation(TRANSIT_OLD_TASK_OPEN, animationAdapter); - definition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_CLOSE, animationAdapter); - definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CLOSE, animationAdapter); - definition.addRemoteAnimation(TRANSIT_OLD_TASK_CLOSE, animationAdapter); - definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CHANGE, animationAdapter); - mOrganizer.registerRemoteAnimations(definition); + if (mIsRegister) { + return; + } + mOrganizer.registerRemoteAnimations(mDefinition); + mIsRegister = true; } void unregisterRemoteAnimations() { if (DEBUG) { Log.v(TAG, "unregisterRemoteAnimations"); } + if (!mIsRegister) { + return; + } mOrganizer.unregisterRemoteAnimations(); + mIsRegister = false; } } diff --git a/packages/SystemUI/res-keyguard/values-sw720dp/bools.xml b/libs/WindowManager/Shell/res/layout/background_panel.xml index 54bb1fcab54b..c3569d80fa1e 100644 --- a/packages/SystemUI/res-keyguard/values-sw720dp/bools.xml +++ b/libs/WindowManager/Shell/res/layout/background_panel.xml @@ -1,6 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> + <!-- - ~ Copyright (C) 2021 The Android Open Source Project + ~ Copyright (C) 2022 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. @@ -12,15 +13,14 @@ ~ 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. + ~ limitations under the License --> - -<resources> - <!-- Allows PIN/Pattern to be drawn on one side of a display, and for the user to - switch sides --> - <bool name="can_use_one_handed_bouncer">false</bool> - - <!-- Will display the bouncer on one side of the display, and the current user icon and - user switcher on the other side --> - <bool name="bouncer_display_user_switcher">true</bool> -</resources> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/background_panel_layout" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:gravity="center_horizontal | center_vertical" + android:background="@android:color/transparent"> +</LinearLayout>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java index eaeade82ebb6..cf4647a09a58 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java @@ -364,13 +364,15 @@ public class Bubble implements BubbleViewProvider { * @param context the context for the bubble. * @param controller the bubble controller. * @param stackView the stackView the bubble is eventually added to. - * @param iconFactory the iconfactory use to create badged images for the bubble. + * @param iconFactory the icon factory use to create images for the bubble. + * @param badgeIconFactory the icon factory to create app badges for the bubble. */ void inflate(BubbleViewInfoTask.Callback callback, Context context, BubbleController controller, BubbleStackView stackView, BubbleIconFactory iconFactory, + BubbleBadgeIconFactory badgeIconFactory, boolean skipInflation) { if (isBubbleLoading()) { mInflationTask.cancel(true /* mayInterruptIfRunning */); @@ -380,6 +382,7 @@ public class Bubble implements BubbleViewProvider { controller, stackView, iconFactory, + badgeIconFactory, skipInflation, callback, mMainExecutor); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleBadgeIconFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleBadgeIconFactory.java new file mode 100644 index 000000000000..4eeb20769e09 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleBadgeIconFactory.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2022 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.wm.shell.bubbles; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.drawable.AdaptiveIconDrawable; +import android.graphics.drawable.Drawable; + +import com.android.launcher3.icons.BaseIconFactory; +import com.android.launcher3.icons.BitmapInfo; +import com.android.launcher3.icons.ShadowGenerator; +import com.android.wm.shell.R; + +/** + * Factory for creating app badge icons that are shown on bubbles. + */ +public class BubbleBadgeIconFactory extends BaseIconFactory { + + public BubbleBadgeIconFactory(Context context) { + super(context, context.getResources().getConfiguration().densityDpi, + context.getResources().getDimensionPixelSize(R.dimen.bubble_badge_size)); + } + + /** + * Returns a {@link BitmapInfo} for the app-badge that is shown on top of each bubble. This + * will include the workprofile indicator on the badge if appropriate. + */ + BitmapInfo getBadgeBitmap(Drawable userBadgedAppIcon, boolean isImportantConversation) { + ShadowGenerator shadowGenerator = new ShadowGenerator(mIconBitmapSize); + Bitmap userBadgedBitmap = createIconBitmap(userBadgedAppIcon, 1f, mIconBitmapSize); + + if (userBadgedAppIcon instanceof AdaptiveIconDrawable) { + userBadgedBitmap = Bitmap.createScaledBitmap( + getCircleBitmap((AdaptiveIconDrawable) userBadgedAppIcon, /* size */ + userBadgedAppIcon.getIntrinsicWidth()), + mIconBitmapSize, mIconBitmapSize, /* filter */ true); + } + + if (isImportantConversation) { + final float ringStrokeWidth = mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.importance_ring_stroke_width); + final int importantConversationColor = mContext.getResources().getColor( + R.color.important_conversation, null); + Bitmap badgeAndRing = Bitmap.createBitmap(userBadgedBitmap.getWidth(), + userBadgedBitmap.getHeight(), userBadgedBitmap.getConfig()); + Canvas c = new Canvas(badgeAndRing); + + Paint ringPaint = new Paint(); + ringPaint.setStyle(Paint.Style.FILL); + ringPaint.setColor(importantConversationColor); + ringPaint.setAntiAlias(true); + c.drawCircle(c.getWidth() / 2, c.getHeight() / 2, c.getWidth() / 2, ringPaint); + + final int bitmapTop = (int) ringStrokeWidth; + final int bitmapLeft = (int) ringStrokeWidth; + final int bitmapWidth = c.getWidth() - 2 * (int) ringStrokeWidth; + final int bitmapHeight = c.getHeight() - 2 * (int) ringStrokeWidth; + + Bitmap scaledBitmap = Bitmap.createScaledBitmap(userBadgedBitmap, bitmapWidth, + bitmapHeight, /* filter */ true); + c.drawBitmap(scaledBitmap, bitmapTop, bitmapLeft, /* paint */null); + + shadowGenerator.recreateIcon(Bitmap.createBitmap(badgeAndRing), c); + return createIconBitmap(badgeAndRing); + } else { + Canvas c = new Canvas(); + c.setBitmap(userBadgedBitmap); + shadowGenerator.recreateIcon(Bitmap.createBitmap(userBadgedBitmap), c); + return createIconBitmap(userBadgedBitmap); + } + } + + private Bitmap getCircleBitmap(AdaptiveIconDrawable icon, int size) { + Drawable foreground = icon.getForeground(); + Drawable background = icon.getBackground(); + Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(); + canvas.setBitmap(bitmap); + + // Clip canvas to circle. + Path circlePath = new Path(); + circlePath.addCircle(/* x */ size / 2f, + /* y */ size / 2f, + /* radius */ size / 2f, + Path.Direction.CW); + canvas.clipPath(circlePath); + + // Draw background. + background.setBounds(0, 0, size, size); + background.draw(canvas); + + // Draw foreground. The foreground and background drawables are derived from adaptive icons + // Some icon shapes fill more space than others, so adaptive icons are normalized to about + // the same size. This size is smaller than the original bounds, so we estimate + // the difference in this offset. + int offset = size / 5; + foreground.setBounds(-offset, -offset, size + offset, size + offset); + foreground.draw(canvas); + + canvas.setBitmap(null); + return bitmap; + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index 7903a5102dde..22bec3d63bcf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -151,6 +151,7 @@ public class BubbleController { private BubbleData mBubbleData; @Nullable private BubbleStackView mStackView; private BubbleIconFactory mBubbleIconFactory; + private BubbleBadgeIconFactory mBubbleBadgeIconFactory; private BubblePositioner mBubblePositioner; private Bubbles.SysuiProxy mSysuiProxy; @@ -278,6 +279,7 @@ public class BubbleController { mBubbleData = data; mSavedBubbleKeysPerUser = new SparseSetArray<>(); mBubbleIconFactory = new BubbleIconFactory(context); + mBubbleBadgeIconFactory = new BubbleBadgeIconFactory(context); mDisplayController = displayController; mTaskViewTransitions = taskViewTransitions; mOneHandedOptional = oneHandedOptional; @@ -500,6 +502,7 @@ public class BubbleController { } mStackView.updateStackPosition(); mBubbleIconFactory = new BubbleIconFactory(mContext); + mBubbleBadgeIconFactory = new BubbleBadgeIconFactory(mContext); mStackView.onDisplaySizeChanged(); } if (b.getBoolean(EXTRA_BUBBLE_OVERFLOW_OPENED, false)) { @@ -778,13 +781,17 @@ public class BubbleController { mStackView.onThemeChanged(); } mBubbleIconFactory = new BubbleIconFactory(mContext); + mBubbleBadgeIconFactory = new BubbleBadgeIconFactory(mContext); + // Reload each bubble - for (Bubble b: mBubbleData.getBubbles()) { + for (Bubble b : mBubbleData.getBubbles()) { b.inflate(null /* callback */, mContext, this, mStackView, mBubbleIconFactory, + mBubbleBadgeIconFactory, false /* skipInflation */); } - for (Bubble b: mBubbleData.getOverflowBubbles()) { + for (Bubble b : mBubbleData.getOverflowBubbles()) { b.inflate(null /* callback */, mContext, this, mStackView, mBubbleIconFactory, + mBubbleBadgeIconFactory, false /* skipInflation */); } } @@ -800,6 +807,7 @@ public class BubbleController { mScreenBounds.set(newConfig.windowConfiguration.getBounds()); mBubbleData.onMaxBubblesChanged(); mBubbleIconFactory = new BubbleIconFactory(mContext); + mBubbleBadgeIconFactory = new BubbleBadgeIconFactory(mContext); mStackView.onDisplaySizeChanged(); } if (newConfig.fontScale != mFontScale) { @@ -961,7 +969,8 @@ public class BubbleController { } bubble.inflate( (b) -> mBubbleData.overflowBubble(Bubbles.DISMISS_RELOAD_FROM_DISK, bubble), - mContext, this, mStackView, mBubbleIconFactory, true /* skipInflation */); + mContext, this, mStackView, mBubbleIconFactory, mBubbleBadgeIconFactory, + true /* skipInflation */); }); return null; }); @@ -996,7 +1005,8 @@ public class BubbleController { ensureStackViewCreated(); bubble.setInflateSynchronously(mInflateSynchronously); bubble.inflate(b -> mBubbleData.notificationEntryUpdated(b, suppressFlyout, showInShade), - mContext, this, mStackView, mBubbleIconFactory, false /* skipInflation */); + mContext, this, mStackView, mBubbleIconFactory, mBubbleBadgeIconFactory, + false /* skipInflation */); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java index 9374da4c4fab..f878a46d26f2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java @@ -231,8 +231,9 @@ public class BubbleFlyoutView extends FrameLayout { * Fade animation for consecutive flyouts. */ void animateUpdate(Bubble.FlyoutMessage flyoutMessage, PointF stackPos, - boolean hideDot, Runnable onHide) { + boolean hideDot, float[] dotCenter, Runnable onHide) { mOnHide = onHide; + mDotCenter = dotCenter; final Runnable afterFadeOut = () -> { updateFlyoutMessage(flyoutMessage); // Wait for TextViews to layout with updated height. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java index b0e029fdc681..9d3bf34895d3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java @@ -21,19 +21,12 @@ import android.content.Context; import android.content.Intent; import android.content.pm.LauncherApps; import android.content.pm.ShortcutInfo; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.drawable.AdaptiveIconDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import androidx.annotation.VisibleForTesting; import com.android.launcher3.icons.BaseIconFactory; -import com.android.launcher3.icons.BitmapInfo; -import com.android.launcher3.icons.ShadowGenerator; import com.android.wm.shell.R; /** @@ -44,12 +37,9 @@ import com.android.wm.shell.R; @VisibleForTesting public class BubbleIconFactory extends BaseIconFactory { - private int mBadgeSize; - public BubbleIconFactory(Context context) { super(context, context.getResources().getConfiguration().densityDpi, context.getResources().getDimensionPixelSize(R.dimen.bubble_size)); - mBadgeSize = mContext.getResources().getDimensionPixelSize(R.dimen.bubble_badge_size); } /** @@ -75,84 +65,4 @@ public class BubbleIconFactory extends BaseIconFactory { return null; } } - - /** - * Returns a {@link BitmapInfo} for the app-badge that is shown on top of each bubble. This - * will include the workprofile indicator on the badge if appropriate. - */ - BitmapInfo getBadgeBitmap(Drawable userBadgedAppIcon, boolean isImportantConversation) { - ShadowGenerator shadowGenerator = new ShadowGenerator(mBadgeSize); - Bitmap userBadgedBitmap = createIconBitmap(userBadgedAppIcon, 1f, mBadgeSize); - - if (userBadgedAppIcon instanceof AdaptiveIconDrawable) { - userBadgedBitmap = Bitmap.createScaledBitmap( - getCircleBitmap((AdaptiveIconDrawable) userBadgedAppIcon, /* size */ - userBadgedAppIcon.getIntrinsicWidth()), - mBadgeSize, mBadgeSize, /* filter */ true); - } - - if (isImportantConversation) { - final float ringStrokeWidth = mContext.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.importance_ring_stroke_width); - final int importantConversationColor = mContext.getResources().getColor( - R.color.important_conversation, null); - Bitmap badgeAndRing = Bitmap.createBitmap(userBadgedBitmap.getWidth(), - userBadgedBitmap.getHeight(), userBadgedBitmap.getConfig()); - Canvas c = new Canvas(badgeAndRing); - - Paint ringPaint = new Paint(); - ringPaint.setStyle(Paint.Style.FILL); - ringPaint.setColor(importantConversationColor); - ringPaint.setAntiAlias(true); - c.drawCircle(c.getWidth() / 2, c.getHeight() / 2, c.getWidth() / 2, ringPaint); - - final int bitmapTop = (int) ringStrokeWidth; - final int bitmapLeft = (int) ringStrokeWidth; - final int bitmapWidth = c.getWidth() - 2 * (int) ringStrokeWidth; - final int bitmapHeight = c.getHeight() - 2 * (int) ringStrokeWidth; - - Bitmap scaledBitmap = Bitmap.createScaledBitmap(userBadgedBitmap, bitmapWidth, - bitmapHeight, /* filter */ true); - c.drawBitmap(scaledBitmap, bitmapTop, bitmapLeft, /* paint */null); - - shadowGenerator.recreateIcon(Bitmap.createBitmap(badgeAndRing), c); - return createIconBitmap(badgeAndRing); - } else { - Canvas c = new Canvas(); - c.setBitmap(userBadgedBitmap); - shadowGenerator.recreateIcon(Bitmap.createBitmap(userBadgedBitmap), c); - return createIconBitmap(userBadgedBitmap); - } - } - - public Bitmap getCircleBitmap(AdaptiveIconDrawable icon, int size) { - Drawable foreground = icon.getForeground(); - Drawable background = icon.getBackground(); - Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(); - canvas.setBitmap(bitmap); - - // Clip canvas to circle. - Path circlePath = new Path(); - circlePath.addCircle(/* x */ size / 2f, - /* y */ size / 2f, - /* radius */ size / 2f, - Path.Direction.CW); - canvas.clipPath(circlePath); - - // Draw background. - background.setBounds(0, 0, size, size); - background.draw(canvas); - - // Draw foreground. The foreground and background drawables are derived from adaptive icons - // Some icon shapes fill more space than others, so adaptive icons are normalized to about - // the same size. This size is smaller than the original bounds, so we estimate - // the difference in this offset. - int offset = size / 5; - foreground.setBounds(-offset, -offset, size + offset, size + offset); - foreground.draw(canvas); - - canvas.setBitmap(null); - return bitmap; - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java index 7bf4439410f9..d5d8b71056d6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java @@ -2516,6 +2516,7 @@ public class BubbleStackView extends FrameLayout if (mFlyout.getVisibility() == View.VISIBLE) { mFlyout.animateUpdate(bubble.getFlyoutMessage(), mStackAnimationController.getStackPosition(), !bubble.showDot(), + bubble.getIconView().getDotCenter(), mAfterFlyoutHidden /* onHide */); } else { mFlyout.setVisibility(INVISIBLE); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java index b01c756c5a7e..69762c9bc06a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java @@ -71,6 +71,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask private WeakReference<BubbleController> mController; private WeakReference<BubbleStackView> mStackView; private BubbleIconFactory mIconFactory; + private BubbleBadgeIconFactory mBadgeIconFactory; private boolean mSkipInflation; private Callback mCallback; private Executor mMainExecutor; @@ -84,6 +85,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask BubbleController controller, BubbleStackView stackView, BubbleIconFactory factory, + BubbleBadgeIconFactory badgeFactory, boolean skipInflation, Callback c, Executor mainExecutor) { @@ -92,6 +94,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask mController = new WeakReference<>(controller); mStackView = new WeakReference<>(stackView); mIconFactory = factory; + mBadgeIconFactory = badgeFactory; mSkipInflation = skipInflation; mCallback = c; mMainExecutor = mainExecutor; @@ -100,7 +103,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask @Override protected BubbleViewInfo doInBackground(Void... voids) { return BubbleViewInfo.populate(mContext.get(), mController.get(), mStackView.get(), - mIconFactory, mBubble, mSkipInflation); + mIconFactory, mBadgeIconFactory, mBubble, mSkipInflation); } @Override @@ -135,7 +138,8 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask @VisibleForTesting @Nullable public static BubbleViewInfo populate(Context c, BubbleController controller, - BubbleStackView stackView, BubbleIconFactory iconFactory, Bubble b, + BubbleStackView stackView, BubbleIconFactory iconFactory, + BubbleBadgeIconFactory badgeIconFactory, Bubble b, boolean skipInflation) { BubbleViewInfo info = new BubbleViewInfo(); @@ -187,11 +191,11 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask bubbleDrawable = appIcon; } - BitmapInfo badgeBitmapInfo = iconFactory.getBadgeBitmap(badgedIcon, + BitmapInfo badgeBitmapInfo = badgeIconFactory.getBadgeBitmap(badgedIcon, b.isImportantConversation()); info.badgeBitmap = badgeBitmapInfo.icon; // Raw badge bitmap never includes the important conversation ring - info.mRawBadgeBitmap = iconFactory.getBadgeBitmap(badgedIcon, false).icon; + info.mRawBadgeBitmap = badgeIconFactory.getBadgeBitmap(badgedIcon, false).icon; info.bubbleBitmap = iconFactory.createBadgedIconBitmap(bubbleDrawable).icon; // Dot color & placement diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java index ad9ebb2ef6ae..36e55bae18c3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java @@ -137,14 +137,16 @@ public class SplitDecorManager extends WindowlessWindowManager { return; } - if (mIcon == null) { - // TODO: add fade-in animation. + if (mBackgroundLeash == null) { mBackgroundLeash = SurfaceUtils.makeColorLayer(mHostLeash, RESIZING_BACKGROUND_SURFACE_NAME, mSurfaceSession); t.setColor(mBackgroundLeash, getResizingBackgroundColor(resizingTask)) .setLayer(mBackgroundLeash, SPLIT_DIVIDER_LAYER - 1) .show(mBackgroundLeash); + } + if (mIcon == null && resizingTask.topActivityInfo != null) { + // TODO: add fade-in animation. mIcon = mIconProvider.getIcon(resizingTask.topActivityInfo); mResizingIconView.setImageDrawable(mIcon); mResizingIconView.setVisibility(View.VISIBLE); @@ -168,12 +170,16 @@ public class SplitDecorManager extends WindowlessWindowManager { return; } + if (mBackgroundLeash != null) { + t.remove(mBackgroundLeash); + mBackgroundLeash = null; + } + if (mIcon != null) { mResizingIconView.setVisibility(View.GONE); mResizingIconView.setImageDrawable(null); - t.remove(mBackgroundLeash).hide(mIconLeash); + t.hide(mIconLeash); mIcon = null; - mBackgroundLeash = null; } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/BackgroundWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/BackgroundWindowManager.java new file mode 100644 index 000000000000..c20b7d9b2747 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/BackgroundWindowManager.java @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2022 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.wm.shell.onehanded; + +import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; +import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; +import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY; +import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; + +import static com.android.wm.shell.onehanded.OneHandedState.STATE_ACTIVE; +import static com.android.wm.shell.onehanded.OneHandedState.STATE_ENTERING; + +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Color; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.os.Binder; +import android.util.Slog; +import android.view.ContextThemeWrapper; +import android.view.IWindow; +import android.view.LayoutInflater; +import android.view.SurfaceControl; +import android.view.SurfaceControlViewHost; +import android.view.SurfaceSession; +import android.view.View; +import android.view.WindowManager; +import android.view.WindowlessWindowManager; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.wm.shell.R; +import com.android.wm.shell.common.DisplayLayout; + +import java.io.PrintWriter; + +/** + * Holds view hierarchy of a root surface and helps inflate a themeable view for background. + */ +public final class BackgroundWindowManager extends WindowlessWindowManager { + private static final String TAG = BackgroundWindowManager.class.getSimpleName(); + private static final int THEME_COLOR_OFFSET = 10; + + private final OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory + mTransactionFactory; + + private Context mContext; + private Rect mDisplayBounds; + private SurfaceControlViewHost mViewHost; + private SurfaceControl mLeash; + private View mBackgroundView; + private @OneHandedState.State int mCurrentState; + + public BackgroundWindowManager(Context context) { + super(context.getResources().getConfiguration(), null /* rootSurface */, + null /* hostInputToken */); + mContext = context; + mTransactionFactory = SurfaceControl.Transaction::new; + } + + @Override + public SurfaceControl getSurfaceControl(IWindow window) { + return super.getSurfaceControl(window); + } + + @Override + public void setConfiguration(Configuration configuration) { + super.setConfiguration(configuration); + mContext = mContext.createConfigurationContext(configuration); + } + + /** + * onConfigurationChanged events for updating background theme color. + */ + public void onConfigurationChanged() { + if (mCurrentState == STATE_ENTERING || mCurrentState == STATE_ACTIVE) { + updateThemeOnly(); + } + } + + /** + * One-handed mode state changed callback + * @param newState of One-handed mode representing by {@link OneHandedState} + */ + public void onStateChanged(int newState) { + mCurrentState = newState; + } + + @Override + protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) { + final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession()) + .setColorLayer() + .setBufferSize(mDisplayBounds.width(), mDisplayBounds.height()) + .setFormat(PixelFormat.RGB_888) + .setOpaque(true) + .setName(TAG) + .setCallsite("BackgroundWindowManager#attachToParentSurface"); + mLeash = builder.build(); + b.setParent(mLeash); + } + + /** Inflates background view on to the root surface. */ + boolean initView() { + if (mBackgroundView != null || mViewHost != null) { + return false; + } + + mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this); + mBackgroundView = (View) LayoutInflater.from(mContext) + .inflate(R.layout.background_panel, null /* root */); + WindowManager.LayoutParams lp = new WindowManager.LayoutParams( + mDisplayBounds.width(), mDisplayBounds.height(), 0 /* TYPE NONE */, + FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL | FLAG_WATCH_OUTSIDE_TOUCH + | FLAG_SLIPPERY, PixelFormat.TRANSLUCENT); + lp.token = new Binder(); + lp.setTitle("background-panel"); + lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY; + mBackgroundView.setBackgroundColor(getThemeColorForBackground()); + mViewHost.setView(mBackgroundView, lp); + return true; + } + + /** + * Called when onDisplayAdded() or onDisplayRemoved() callback. + * @param displayLayout The latest {@link DisplayLayout} for display bounds. + */ + public void onDisplayChanged(DisplayLayout displayLayout) { + // One-handed mode is only available on portrait. + if (displayLayout.height() > displayLayout.width()) { + mDisplayBounds = new Rect(0, 0, displayLayout.width(), displayLayout.height()); + } else { + mDisplayBounds = new Rect(0, 0, displayLayout.height(), displayLayout.width()); + } + } + + private void updateThemeOnly() { + if (mBackgroundView == null || mViewHost == null || mLeash == null) { + Slog.w(TAG, "Background view or SurfaceControl does not exist when trying to " + + "update theme only!"); + return; + } + + WindowManager.LayoutParams lp = (WindowManager.LayoutParams) + mBackgroundView.getLayoutParams(); + mBackgroundView.setBackgroundColor(getThemeColorForBackground()); + mViewHost.setView(mBackgroundView, lp); + } + + /** + * Shows the background layer when One-handed mode triggered. + */ + public void showBackgroundLayer() { + if (!initView()) { + updateThemeOnly(); + return; + } + if (mLeash == null) { + Slog.w(TAG, "SurfaceControl mLeash is null, can't show One-handed mode " + + "background panel!"); + return; + } + + mTransactionFactory.getTransaction() + .setAlpha(mLeash, 1.0f) + .setLayer(mLeash, -1 /* at bottom-most layer */) + .show(mLeash) + .apply(); + } + + /** + * Remove the leash of background layer after stop One-handed mode. + */ + public void removeBackgroundLayer() { + if (mBackgroundView != null) { + mBackgroundView = null; + } + + if (mViewHost != null) { + mViewHost.release(); + mViewHost = null; + } + + if (mLeash != null) { + mTransactionFactory.getTransaction().remove(mLeash).apply(); + mLeash = null; + } + } + + /** + * Gets {@link SurfaceControl} of the background layer. + * @return {@code null} if not exist. + */ + @Nullable + SurfaceControl getSurfaceControl() { + return mLeash; + } + + private int getThemeColor() { + final Context themedContext = new ContextThemeWrapper(mContext, + com.android.internal.R.style.Theme_DeviceDefault_DayNight); + return themedContext.getColor(R.color.one_handed_tutorial_background_color); + } + + int getThemeColorForBackground() { + final int origThemeColor = getThemeColor(); + return android.graphics.Color.argb(Color.alpha(origThemeColor), + Color.red(origThemeColor) - THEME_COLOR_OFFSET, + Color.green(origThemeColor) - THEME_COLOR_OFFSET, + Color.blue(origThemeColor) - THEME_COLOR_OFFSET); + } + + private float adjustColor(int origColor) { + return Math.max(origColor - THEME_COLOR_OFFSET, 0) / 255.0f; + } + + void dump(@NonNull PrintWriter pw) { + final String innerPrefix = " "; + pw.println(TAG); + pw.print(innerPrefix + "mDisplayBounds="); + pw.println(mDisplayBounds); + pw.print(innerPrefix + "mViewHost="); + pw.println(mViewHost); + pw.print(innerPrefix + "mLeash="); + pw.println(mLeash); + pw.print(innerPrefix + "mBackgroundView="); + pw.println(mBackgroundView); + } + +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java deleted file mode 100644 index 9e1c61aac868..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java +++ /dev/null @@ -1,272 +0,0 @@ -/* - * 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.wm.shell.onehanded; - -import static com.android.wm.shell.onehanded.OneHandedState.STATE_ACTIVE; - -import android.animation.ValueAnimator; -import android.content.Context; -import android.graphics.Color; -import android.graphics.PixelFormat; -import android.graphics.Rect; -import android.view.ContextThemeWrapper; -import android.view.SurfaceControl; -import android.view.SurfaceSession; -import android.view.animation.LinearInterpolator; -import android.window.DisplayAreaAppearedInfo; -import android.window.DisplayAreaInfo; -import android.window.DisplayAreaOrganizer; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; - -import com.android.wm.shell.R; -import com.android.wm.shell.common.DisplayLayout; - -import java.io.PrintWriter; -import java.util.List; -import java.util.concurrent.Executor; - -/** - * Manages OneHanded color background layer areas. - * To avoid when turning the Dark theme on, users can not clearly identify - * the screen has entered one handed mode. - */ -public class OneHandedBackgroundPanelOrganizer extends DisplayAreaOrganizer - implements OneHandedAnimationCallback, OneHandedState.OnStateChangedListener { - private static final String TAG = "OneHandedBackgroundPanelOrganizer"; - private static final int THEME_COLOR_OFFSET = 10; - private static final int ALPHA_ANIMATION_DURATION = 200; - - private final Context mContext; - private final SurfaceSession mSurfaceSession = new SurfaceSession(); - private final OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory - mTransactionFactory; - - private @OneHandedState.State int mCurrentState; - private ValueAnimator mAlphaAnimator; - - private float mTranslationFraction; - private float[] mThemeColor; - - /** - * The background to distinguish the boundary of translated windows and empty region when - * one handed mode triggered. - */ - private Rect mBkgBounds; - private Rect mStableInsets; - - @Nullable - @VisibleForTesting - SurfaceControl mBackgroundSurface; - @Nullable - private SurfaceControl mParentLeash; - - public OneHandedBackgroundPanelOrganizer(Context context, DisplayLayout displayLayout, - OneHandedSettingsUtil settingsUtil, Executor executor) { - super(executor); - mContext = context; - mTranslationFraction = settingsUtil.getTranslationFraction(context); - mTransactionFactory = SurfaceControl.Transaction::new; - updateThemeColors(); - } - - @Override - public void onDisplayAreaAppeared(@NonNull DisplayAreaInfo displayAreaInfo, - @NonNull SurfaceControl leash) { - mParentLeash = leash; - } - - @Override - public List<DisplayAreaAppearedInfo> registerOrganizer(int displayAreaFeature) { - final List<DisplayAreaAppearedInfo> displayAreaInfos; - displayAreaInfos = super.registerOrganizer(displayAreaFeature); - for (int i = 0; i < displayAreaInfos.size(); i++) { - final DisplayAreaAppearedInfo info = displayAreaInfos.get(i); - onDisplayAreaAppeared(info.getDisplayAreaInfo(), info.getLeash()); - } - return displayAreaInfos; - } - - @Override - public void unregisterOrganizer() { - super.unregisterOrganizer(); - removeBackgroundPanelLayer(); - mParentLeash = null; - } - - @Override - public void onAnimationUpdate(SurfaceControl.Transaction tx, float xPos, float yPos) { - final int yTopPos = (mStableInsets.top - mBkgBounds.height()) + Math.round(yPos); - tx.setPosition(mBackgroundSurface, 0, yTopPos); - } - - @Nullable - @VisibleForTesting - boolean isRegistered() { - return mParentLeash != null; - } - - void createBackgroundSurface() { - mBackgroundSurface = new SurfaceControl.Builder(mSurfaceSession) - .setBufferSize(mBkgBounds.width(), mBkgBounds.height()) - .setColorLayer() - .setFormat(PixelFormat.RGB_888) - .setOpaque(true) - .setName("one-handed-background-panel") - .setCallsite("OneHandedBackgroundPanelOrganizer") - .build(); - - // TODO(185890335) Avoid Dimming for mid-range luminance wallpapers flash. - mAlphaAnimator = ValueAnimator.ofFloat(1.0f, 0.0f); - mAlphaAnimator.setInterpolator(new LinearInterpolator()); - mAlphaAnimator.setDuration(ALPHA_ANIMATION_DURATION); - mAlphaAnimator.addUpdateListener( - animator -> detachBackgroundFromParent(animator)); - } - - void detachBackgroundFromParent(ValueAnimator animator) { - if (mBackgroundSurface == null || mParentLeash == null) { - return; - } - // TODO(185890335) Avoid Dimming for mid-range luminance wallpapers flash. - final float currentValue = (float) animator.getAnimatedValue(); - final SurfaceControl.Transaction tx = mTransactionFactory.getTransaction(); - if (currentValue == 0.0f) { - tx.reparent(mBackgroundSurface, null).apply(); - } else { - tx.setAlpha(mBackgroundSurface, (float) animator.getAnimatedValue()).apply(); - } - } - - /** - * Called when onDisplayAdded() or onDisplayRemoved() callback. - * - * @param displayLayout The latest {@link DisplayLayout} representing current displayId - */ - public void onDisplayChanged(DisplayLayout displayLayout) { - mStableInsets = displayLayout.stableInsets(); - // Ensure the mBkgBounds is portrait, due to OHM only support on portrait - if (displayLayout.height() > displayLayout.width()) { - mBkgBounds = new Rect(0, 0, displayLayout.width(), - Math.round(displayLayout.height() * mTranslationFraction) + mStableInsets.top); - } else { - mBkgBounds = new Rect(0, 0, displayLayout.height(), - Math.round(displayLayout.width() * mTranslationFraction) + mStableInsets.top); - } - } - - @VisibleForTesting - void onStart() { - if (mBackgroundSurface == null) { - createBackgroundSurface(); - } - showBackgroundPanelLayer(); - } - - /** - * Called when transition finished. - */ - public void onStopFinished() { - if (mAlphaAnimator == null) { - return; - } - mAlphaAnimator.start(); - } - - @VisibleForTesting - void showBackgroundPanelLayer() { - if (mParentLeash == null) { - return; - } - - if (mBackgroundSurface == null) { - createBackgroundSurface(); - } - - // TODO(185890335) Avoid Dimming for mid-range luminance wallpapers flash. - if (mAlphaAnimator.isRunning()) { - mAlphaAnimator.end(); - } - - mTransactionFactory.getTransaction() - .reparent(mBackgroundSurface, mParentLeash) - .setAlpha(mBackgroundSurface, 1.0f) - .setLayer(mBackgroundSurface, -1 /* at bottom-most layer */) - .setColor(mBackgroundSurface, mThemeColor) - .show(mBackgroundSurface) - .apply(); - } - - @VisibleForTesting - void removeBackgroundPanelLayer() { - if (mBackgroundSurface == null) { - return; - } - - mTransactionFactory.getTransaction() - .remove(mBackgroundSurface) - .apply(); - mBackgroundSurface = null; - } - - /** - * onConfigurationChanged events for updating tutorial text. - */ - public void onConfigurationChanged() { - updateThemeColors(); - - if (mCurrentState != STATE_ACTIVE) { - return; - } - showBackgroundPanelLayer(); - } - - private void updateThemeColors() { - final Context themedContext = new ContextThemeWrapper(mContext, - com.android.internal.R.style.Theme_DeviceDefault_DayNight); - final int themeColor = themedContext.getColor( - R.color.one_handed_tutorial_background_color); - mThemeColor = new float[]{ - adjustColor(Color.red(themeColor)), - adjustColor(Color.green(themeColor)), - adjustColor(Color.blue(themeColor))}; - } - - private float adjustColor(int origColor) { - return Math.max(origColor - THEME_COLOR_OFFSET, 0) / 255.0f; - } - - @Override - public void onStateChanged(int newState) { - mCurrentState = newState; - } - - void dump(@NonNull PrintWriter pw) { - final String innerPrefix = " "; - pw.println(TAG); - pw.print(innerPrefix + "mBackgroundSurface="); - pw.println(mBackgroundSurface); - pw.print(innerPrefix + "mBkgBounds="); - pw.println(mBkgBounds); - pw.print(innerPrefix + "mThemeColor="); - pw.println(mThemeColor); - pw.print(innerPrefix + "mTranslationFraction="); - pw.println(mTranslationFraction); - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java index 96f82fa5ce36..48acfc1c76e7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java @@ -99,7 +99,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, private OneHandedEventCallback mEventCallback; private OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer; - private OneHandedBackgroundPanelOrganizer mBackgroundPanelOrganizer; private OneHandedUiEventLogger mOneHandedUiEventLogger; private final DisplayController.OnDisplaysChangedListener mDisplaysChangedListener = @@ -163,7 +162,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, public void onStopFinished(Rect bounds) { mState.setState(STATE_NONE); notifyShortcutStateChanged(STATE_NONE); - mBackgroundPanelOrganizer.onStopFinished(); } }; @@ -201,32 +199,28 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, OneHandedAccessibilityUtil accessibilityUtil = new OneHandedAccessibilityUtil(context); OneHandedTimeoutHandler timeoutHandler = new OneHandedTimeoutHandler(mainExecutor); OneHandedState oneHandedState = new OneHandedState(); + BackgroundWindowManager backgroundWindowManager = new BackgroundWindowManager(context); OneHandedTutorialHandler tutorialHandler = new OneHandedTutorialHandler(context, - settingsUtil, windowManager); + settingsUtil, windowManager, backgroundWindowManager); OneHandedAnimationController animationController = new OneHandedAnimationController(context); OneHandedTouchHandler touchHandler = new OneHandedTouchHandler(timeoutHandler, mainExecutor); - OneHandedBackgroundPanelOrganizer oneHandedBackgroundPanelOrganizer = - new OneHandedBackgroundPanelOrganizer(context, displayLayout, settingsUtil, - mainExecutor); OneHandedDisplayAreaOrganizer organizer = new OneHandedDisplayAreaOrganizer( context, displayLayout, settingsUtil, animationController, tutorialHandler, - oneHandedBackgroundPanelOrganizer, jankMonitor, mainExecutor); + jankMonitor, mainExecutor); OneHandedUiEventLogger oneHandedUiEventsLogger = new OneHandedUiEventLogger(uiEventLogger); IOverlayManager overlayManager = IOverlayManager.Stub.asInterface( ServiceManager.getService(Context.OVERLAY_SERVICE)); - return new OneHandedController(context, displayController, - oneHandedBackgroundPanelOrganizer, organizer, touchHandler, tutorialHandler, - settingsUtil, accessibilityUtil, timeoutHandler, oneHandedState, jankMonitor, - oneHandedUiEventsLogger, overlayManager, taskStackListener, mainExecutor, - mainHandler); + return new OneHandedController(context, displayController, organizer, touchHandler, + tutorialHandler, settingsUtil, accessibilityUtil, timeoutHandler, oneHandedState, + jankMonitor, oneHandedUiEventsLogger, overlayManager, taskStackListener, + mainExecutor, mainHandler); } @VisibleForTesting OneHandedController(Context context, DisplayController displayController, - OneHandedBackgroundPanelOrganizer backgroundPanelOrganizer, OneHandedDisplayAreaOrganizer displayAreaOrganizer, OneHandedTouchHandler touchHandler, OneHandedTutorialHandler tutorialHandler, @@ -243,7 +237,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, mContext = context; mOneHandedSettingsUtil = settingsUtil; mOneHandedAccessibilityUtil = oneHandedAccessibilityUtil; - mBackgroundPanelOrganizer = backgroundPanelOrganizer; mDisplayAreaOrganizer = displayAreaOrganizer; mDisplayController = displayController; mTouchHandler = touchHandler; @@ -286,7 +279,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, mAccessibilityManager.addAccessibilityStateChangeListener( mAccessibilityStateChangeListener); - mState.addSListeners(mBackgroundPanelOrganizer); mState.addSListeners(mTutorialHandler); } @@ -368,7 +360,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, mDisplayAreaOrganizer.getDisplayLayout().height() * mOffSetFraction); mOneHandedAccessibilityUtil.announcementForScreenReader( mOneHandedAccessibilityUtil.getOneHandedStartDescription()); - mBackgroundPanelOrganizer.onStart(); mDisplayAreaOrganizer.scheduleOffset(0, yOffSet); mTimeoutHandler.resetTimer(); mOneHandedUiEventLogger.writeEvent( @@ -461,7 +452,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, } mDisplayAreaOrganizer.setDisplayLayout(newDisplayLayout); mTutorialHandler.onDisplayChanged(newDisplayLayout); - mBackgroundPanelOrganizer.onDisplayChanged(newDisplayLayout); } private ContentObserver getObserver(Runnable onChangeRunnable) { @@ -585,7 +575,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, if (!mIsOneHandedEnabled) { mDisplayAreaOrganizer.unregisterOrganizer(); - mBackgroundPanelOrganizer.unregisterOrganizer(); // Do NOT register + unRegister DA in the same call return; } @@ -594,11 +583,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, mDisplayAreaOrganizer.registerOrganizer( OneHandedDisplayAreaOrganizer.FEATURE_ONE_HANDED); } - - if (!mBackgroundPanelOrganizer.isRegistered()) { - mBackgroundPanelOrganizer.registerOrganizer( - OneHandedBackgroundPanelOrganizer.FEATURE_ONE_HANDED_BACKGROUND_PANEL); - } } @VisibleForTesting @@ -613,13 +597,12 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, } private void onConfigChanged(Configuration newConfig) { - if (mTutorialHandler == null || mBackgroundPanelOrganizer == null) { + if (mTutorialHandler == null) { return; } if (!mIsOneHandedEnabled || newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { return; } - mBackgroundPanelOrganizer.onConfigurationChanged(); mTutorialHandler.onConfigurationChanged(); } @@ -650,10 +633,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, pw.print(innerPrefix + "mIsSwipeToNotificationEnabled="); pw.println(mIsSwipeToNotificationEnabled); - if (mBackgroundPanelOrganizer != null) { - mBackgroundPanelOrganizer.dump(pw); - } - if (mDisplayAreaOrganizer != null) { mDisplayAreaOrganizer.dump(pw); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java index 87eb40cbde62..f61d1b95bd85 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java @@ -80,7 +80,6 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { mSurfaceControlTransactionFactory; private OneHandedTutorialHandler mTutorialHandler; private List<OneHandedTransitionCallback> mTransitionCallbacks = new ArrayList<>(); - private OneHandedBackgroundPanelOrganizer mBackgroundPanelOrganizer; @VisibleForTesting OneHandedAnimationCallback mOneHandedAnimationCallback = @@ -135,7 +134,6 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { OneHandedSettingsUtil oneHandedSettingsUtil, OneHandedAnimationController animationController, OneHandedTutorialHandler tutorialHandler, - OneHandedBackgroundPanelOrganizer oneHandedBackgroundGradientOrganizer, InteractionJankMonitor jankMonitor, ShellExecutor mainExecutor) { super(mainExecutor); @@ -150,7 +148,6 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { SystemProperties.getInt(ONE_HANDED_MODE_TRANSLATE_ANIMATION_DURATION, animationDurationConfig); mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new; - mBackgroundPanelOrganizer = oneHandedBackgroundGradientOrganizer; mTutorialHandler = tutorialHandler; } @@ -258,7 +255,6 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { animator.setTransitionDirection(direction) .addOneHandedAnimationCallback(mOneHandedAnimationCallback) .addOneHandedAnimationCallback(mTutorialHandler) - .addOneHandedAnimationCallback(mBackgroundPanelOrganizer) .setDuration(durationMs) .start(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java index 88f33755fa2d..04e8cf9d2c44 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java @@ -32,7 +32,6 @@ import android.content.res.ColorStateList; import android.content.res.TypedArray; import android.graphics.PixelFormat; import android.graphics.Rect; -import android.os.SystemProperties; import android.view.ContextThemeWrapper; import android.view.Gravity; import android.view.LayoutInflater; @@ -65,6 +64,7 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback, private final float mTutorialHeightRatio; private final WindowManager mWindowManager; + private final BackgroundWindowManager mBackgroundWindowManager; private @OneHandedState.State int mCurrentState; private int mTutorialAreaHeight; @@ -79,9 +79,10 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback, private int mAlphaAnimationDurationMs; public OneHandedTutorialHandler(Context context, OneHandedSettingsUtil settingsUtil, - WindowManager windowManager) { + WindowManager windowManager, BackgroundWindowManager backgroundWindowManager) { mContext = context; mWindowManager = windowManager; + mBackgroundWindowManager = backgroundWindowManager; mTutorialHeightRatio = settingsUtil.getTranslationFraction(context); mAlphaAnimationDurationMs = settingsUtil.getTransitionDuration(context); } @@ -110,8 +111,19 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback, } @Override + public void onStartFinished(Rect bounds) { + fillBackgroundColor(); + } + + @Override + public void onStopFinished(Rect bounds) { + removeBackgroundSurface(); + } + + @Override public void onStateChanged(int newState) { mCurrentState = newState; + mBackgroundWindowManager.onStateChanged(newState); switch (newState) { case STATE_ENTERING: createViewAndAttachToWindow(mContext); @@ -126,7 +138,6 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback, case STATE_NONE: checkTransitionEnd(); removeTutorialFromWindowManager(); - break; default: break; } @@ -146,6 +157,7 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback, } mTutorialAreaHeight = Math.round(mDisplayBounds.height() * mTutorialHeightRatio); mAlphaTransitionStart = mTutorialAreaHeight * START_TRANSITION_FRACTION; + mBackgroundWindowManager.onDisplayChanged(displayLayout); } @VisibleForTesting @@ -169,6 +181,7 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback, private void attachTargetToWindow() { try { mWindowManager.addView(mTargetViewContainer, getTutorialTargetLayoutParams()); + mBackgroundWindowManager.showBackgroundLayer(); } catch (IllegalStateException e) { // This shouldn't happen, but if the target is already added, just update its // layout params. @@ -186,6 +199,11 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback, mTargetViewContainer = null; } + @VisibleForTesting + void removeBackgroundSurface() { + mBackgroundWindowManager.removeBackgroundLayer(); + } + /** * Returns layout params for the dismiss target, using the latest display metrics. */ @@ -213,9 +231,12 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback, * onConfigurationChanged events for updating tutorial text. */ public void onConfigurationChanged() { + mBackgroundWindowManager.onConfigurationChanged(); + removeTutorialFromWindowManager(); if (mCurrentState == STATE_ENTERING || mCurrentState == STATE_ACTIVE) { createViewAndAttachToWindow(mContext); + fillBackgroundColor(); updateThemeColor(); checkTransitionEnd(); } @@ -247,6 +268,14 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback, tutorialDesc.setTextColor(themedTextColorSecondary); } + private void fillBackgroundColor() { + if (mTargetViewContainer == null || mBackgroundWindowManager == null) { + return; + } + mTargetViewContainer.setBackgroundColor( + mBackgroundWindowManager.getThemeColorForBackground()); + } + private void setupAlphaTransition(boolean isEntering) { final float start = isEntering ? 0.0f : 1.0f; final float end = isEntering ? 1.0f : 0.0f; @@ -282,5 +311,9 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback, pw.println(mAlphaTransitionStart); pw.print(innerPrefix + "mAlphaAnimationDurationMs="); pw.println(mAlphaAnimationDurationMs); + + if (mBackgroundWindowManager != null) { + mBackgroundWindowManager.dump(pw); + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index 1716380c1f7c..69e78364c5e1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -155,7 +155,8 @@ public class PipTransition extends PipTransitionController { switch (type) { case TRANSIT_EXIT_PIP: - startExitAnimation(info, startTransaction, finishCallback, exitPipChange); + startExitAnimation(info, startTransaction, finishTransaction, finishCallback, + exitPipChange); break; case TRANSIT_EXIT_PIP_TO_SPLIT: startExitToSplitAnimation(info, startTransaction, finishTransaction, @@ -283,6 +284,7 @@ public class PipTransition extends PipTransitionController { private void startExitAnimation(@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback, @NonNull TransitionInfo.Change pipChange) { TransitionInfo.Change displayRotationChange = null; @@ -298,8 +300,8 @@ public class PipTransition extends PipTransitionController { if (displayRotationChange != null) { // Exiting PIP to fullscreen with orientation change. - startExpandAndRotationAnimation(info, startTransaction, finishCallback, - displayRotationChange, pipChange); + startExpandAndRotationAnimation(info, startTransaction, finishTransaction, + finishCallback, displayRotationChange, pipChange); return; } @@ -322,6 +324,7 @@ public class PipTransition extends PipTransitionController { private void startExpandAndRotationAnimation(@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback, @NonNull TransitionInfo.Change displayRotationChange, @NonNull TransitionInfo.Change pipChange) { @@ -335,7 +338,6 @@ public class PipTransition extends PipTransitionController { rotator.handleClosingChanges(info, startTransaction, rotateDelta, displayW, displayH); mFinishCallback = (wct, wctCB) -> { - rotator.cleanUp(); mPipOrganizer.onExitPipFinished(pipChange.getTaskInfo()); finishCallback.onTransitionFinished(wct, wctCB); }; @@ -366,6 +368,7 @@ public class PipTransition extends PipTransitionController { endBounds, startBounds, new Rect(), degree, x, y, true /* isExpanding */, pipRotateDelta == ROTATION_270 /* clockwise */); startTransaction.apply(); + rotator.cleanUp(finishTransaction); // Expand and rotate the pip window to fullscreen. final PipAnimationController.PipTransitionAnimator animator = diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java index 92a359891003..915c5939c34b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java @@ -140,6 +140,7 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen }); mMagnetizedPip = mMotionHelper.getMagnetizedPip(); + mMagnetizedPip.clearAllTargets(); mMagneticTarget = mMagnetizedPip.addTarget(mTargetView, 0); updateMagneticTargetSize(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java index 7decb54d16bb..338c944f7eec 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java @@ -292,7 +292,6 @@ public class RecentTasksController implements TaskStackListenerCallback, * Invalidates this instance, preventing future calls from updating the controller. */ void invalidate() { - Slog.d("b/206648922", "invalidating controller: " + mController); mController = null; } @@ -313,13 +312,16 @@ public class RecentTasksController implements TaskStackListenerCallback, @Override public GroupedRecentTaskInfo[] getRecentTasks(int maxNum, int flags, int userId) throws RemoteException { + if (mController == null) { + // The controller is already invalidated -- just return an empty task list for now + return new GroupedRecentTaskInfo[0]; + } + final GroupedRecentTaskInfo[][] out = new GroupedRecentTaskInfo[][]{null}; executeRemoteCallWithTaskPermission(mController, "getRecentTasks", (controller) -> out[0] = controller.getRecentTasks(maxNum, flags, userId) .toArray(new GroupedRecentTaskInfo[0]), true /* blocking */); - Slog.d("b/206648922", "getRecentTasks(" + maxNum + "): " + out[0] - + " mController=" + mController); return out[0]; } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/CounterRotatorHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/CounterRotatorHelper.java index 08c99b2c4a83..8c71f749b2c3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/CounterRotatorHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/CounterRotatorHelper.java @@ -35,13 +35,11 @@ import java.util.List; */ public class CounterRotatorHelper { private final ArrayMap<WindowContainerToken, CounterRotator> mRotatorMap = new ArrayMap<>(); - private SurfaceControl mRootLeash; /** Puts the surface controls of closing changes to counter-rotated surfaces. */ public void handleClosingChanges(@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, int rotateDelta, int displayW, int displayH) { - mRootLeash = info.getRootLeash(); final List<TransitionInfo.Change> changes = info.getChanges(); final int numChanges = changes.size(); for (int i = numChanges - 1; i >= 0; --i) { @@ -71,10 +69,15 @@ public class CounterRotatorHelper { } } - /** Restores to the original state, i.e. reparent back to transition root. */ - public void cleanUp() { + /** + * Removes the counter rotation surface in the finish transaction. No need to reparent the + * children as the finish transaction should have already taken care of that. + * + * This can only be called after startTransaction for {@link #handleClosingChanges} is applied. + */ + public void cleanUp(@NonNull SurfaceControl.Transaction finishTransaction) { for (int i = mRotatorMap.size() - 1; i >= 0; --i) { - mRotatorMap.valueAt(i).cleanUp(mRootLeash); + mRotatorMap.valueAt(i).cleanUp(finishTransaction); } mRotatorMap.clear(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index 5833ca80d384..11b23875437f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -282,7 +282,6 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { final Runnable onAnimFinish = () -> { if (!animations.isEmpty()) return; - rotator.cleanUp(); if (mRotationAnimation != null) { mRotationAnimation.kill(); mRotationAnimation = null; @@ -382,6 +381,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { } startTransaction.apply(); + rotator.cleanUp(finishTransaction); TransitionMetrics.getInstance().reportAnimationStart(transition); // run finish now in-case there are no animations onAnimFinish.run(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/CounterRotator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/CounterRotator.java index b9b671635010..7f8eaf1a9b55 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/CounterRotator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/CounterRotator.java @@ -18,14 +18,11 @@ package com.android.wm.shell.util; import android.view.SurfaceControl; -import java.util.ArrayList; - /** * Utility class that takes care of counter-rotating surfaces during a transition animation. */ public class CounterRotator { - SurfaceControl mSurface = null; - ArrayList<SurfaceControl> mRotateChildren = null; + private SurfaceControl mSurface = null; /** Gets the surface with the counter-rotation. */ public SurfaceControl getSurface() { @@ -41,7 +38,6 @@ public class CounterRotator { public void setup(SurfaceControl.Transaction t, SurfaceControl parent, int rotateDelta, float displayW, float displayH) { if (rotateDelta == 0) return; - mRotateChildren = new ArrayList<>(); // We want to counter-rotate, so subtract from 4 rotateDelta = 4 - (rotateDelta + 4) % 4; mSurface = new SurfaceControl.Builder() @@ -64,24 +60,19 @@ public class CounterRotator { } /** - * Add a surface that needs to be counter-rotate. + * Adds a surface that needs to be counter-rotate. */ public void addChild(SurfaceControl.Transaction t, SurfaceControl child) { if (mSurface == null) return; t.reparent(child, mSurface); - mRotateChildren.add(child); } /** - * Clean-up. This undoes any reparenting and effectively stops the counter-rotation. + * Clean-up. Since finishTransaction should reset all change leashes, we only need to remove the + * counter rotation surface. */ - public void cleanUp(SurfaceControl rootLeash) { + public void cleanUp(SurfaceControl.Transaction finishTransaction) { if (mSurface == null) return; - SurfaceControl.Transaction t = new SurfaceControl.Transaction(); - for (int i = mRotateChildren.size() - 1; i >= 0; --i) { - t.reparent(mRotateChildren.get(i), rootLeash); - } - t.remove(mSurface); - t.apply(); + finishTransaction.remove(mSurface); } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt index 6c9fed9dc19b..9a220070db01 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt @@ -22,6 +22,7 @@ import androidx.test.filters.FlakyTest import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.LAUNCHER_COMPONENT import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled import com.android.server.wm.flicker.helpers.setRotation import org.junit.Test @@ -51,11 +52,28 @@ abstract class ExitPipTransition(testSpec: FlickerTestParameter) : PipTransition @Presubmit @Test open fun pipWindowBecomesInvisible() { - testSpec.assertWm { - this.invoke("hasPipWindow") { - it.isPinned(pipApp.component).isAppWindowVisible(pipApp.component) - }.then().invoke("!hasPipWindow") { - it.isNotPinned(pipApp.component).isAppWindowInvisible(pipApp.component) + if (isShellTransitionsEnabled) { + // When Shell transition is enabled, we change the windowing mode at start, but + // update the visibility after the transition is finished, so we can't check isNotPinned + // and isAppWindowInvisible in the same assertion block. + testSpec.assertWm { + this.invoke("hasPipWindow") { + it.isPinned(pipApp.component) + .isAppWindowVisible(pipApp.component) + .isAppWindowOnTop(pipApp.component) + }.then().invoke("!hasPipWindow") { + it.isNotPinned(pipApp.component) + .isAppWindowNotOnTop(pipApp.component) + } + } + testSpec.assertWmEnd { isAppWindowInvisible(pipApp.component) } + } else { + testSpec.assertWm { + this.invoke("hasPipWindow") { + it.isPinned(pipApp.component).isAppWindowVisible(pipApp.component) + }.then().invoke("!hasPipWindow") { + it.isNotPinned(pipApp.component).isAppWindowInvisible(pipApp.component) + } } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt index c75076d00574..8adebb8f28c9 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt @@ -24,10 +24,7 @@ import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.annotation.Group3 import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled import com.android.server.wm.flicker.rules.WMFlickerServiceRuleForTestSpec -import org.junit.Assume.assumeFalse -import org.junit.Before import org.junit.FixMethodOrder import org.junit.Rule import org.junit.Test @@ -88,20 +85,10 @@ class ExitPipWithDismissButtonTest(testSpec: FlickerTestParameter) : ExitPipTran flickerRule.checkFlakyAssertions() } - @Before - fun onBefore() { - // This CUJ don't work in shell transitions because of b/204570898 b/204562589 - assumeFalse(isShellTransitionsEnabled) - } - /** {@inheritDoc} */ @FlakyTest(bugId = 206753786) @Test - override fun statusBarLayerRotatesScales() { - // This test doesn't work in shell transitions because of b/206753786 - assumeFalse(isShellTransitionsEnabled) - super.statusBarLayerRotatesScales() - } + override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales() companion object { /** diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt index 52177c2c7684..ef9ff4fc63c2 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt @@ -26,9 +26,6 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.LAUNCHER_COMPONENT import com.android.server.wm.flicker.annotation.Group3 import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled -import org.junit.Assume.assumeFalse -import org.junit.Before import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -65,12 +62,6 @@ class ExpandPipOnDoubleClickTest(testSpec: FlickerTestParameter) : PipTransition } } - @Before - fun onBefore() { - // This CUJ don't work in shell transitions because of b/204570898 b/204562589 - assumeFalse(isShellTransitionsEnabled) - } - /** * Checks that the pip app window remains inside the display bounds throughout the whole * animation diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/BackgroundWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/BackgroundWindowManagerTest.java new file mode 100644 index 000000000000..f3f70673b332 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/BackgroundWindowManagerTest.java @@ -0,0 +1,61 @@ +/** + * Copyright (C) 2022 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.wm.shell.onehanded; + +import static com.google.common.truth.Truth.assertThat; + +import android.testing.TestableLooper; + +import androidx.test.annotation.UiThreadTest; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import com.android.wm.shell.ShellTestCase; +import com.android.wm.shell.common.DisplayLayout; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** Tests for {@link BackgroundWindowManager} */ +@SmallTest +@TestableLooper.RunWithLooper(setAsMainLooper = true) +@RunWith(AndroidJUnit4.class) +public class BackgroundWindowManagerTest extends ShellTestCase { + private BackgroundWindowManager mBackgroundWindowManager; + @Mock + private DisplayLayout mMockDisplayLayout; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mBackgroundWindowManager = new BackgroundWindowManager(mContext); + mBackgroundWindowManager.onDisplayChanged(mMockDisplayLayout); + } + + @Test + @UiThreadTest + public void testInitRelease() { + mBackgroundWindowManager.initView(); + assertThat(mBackgroundWindowManager.getSurfaceControl()).isNotNull(); + + mBackgroundWindowManager.removeBackgroundLayer(); + assertThat(mBackgroundWindowManager.getSurfaceControl()).isNull(); + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java deleted file mode 100644 index 7b9553c5ef9b..000000000000 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * 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.wm.shell.onehanded; - -import static android.view.Display.DEFAULT_DISPLAY; -import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED_BACKGROUND_PANEL; - -import static com.android.wm.shell.onehanded.OneHandedState.STATE_ACTIVE; -import static com.android.wm.shell.onehanded.OneHandedState.STATE_NONE; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; -import android.view.Display; -import android.view.SurfaceControl; -import android.window.DisplayAreaInfo; -import android.window.IWindowContainerToken; -import android.window.WindowContainerToken; - -import androidx.test.filters.SmallTest; - -import com.android.wm.shell.common.DisplayController; -import com.android.wm.shell.common.DisplayLayout; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper -public class OneHandedBackgroundPanelOrganizerTest extends OneHandedTestCase { - private DisplayAreaInfo mDisplayAreaInfo; - private Display mDisplay; - private DisplayLayout mDisplayLayout; - private OneHandedBackgroundPanelOrganizer mSpiedBackgroundPanelOrganizer; - private WindowContainerToken mToken; - private SurfaceControl mLeash; - - @Mock - IWindowContainerToken mMockRealToken; - @Mock - DisplayController mMockDisplayController; - @Mock - OneHandedSettingsUtil mMockSettingsUtil; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mToken = new WindowContainerToken(mMockRealToken); - mLeash = new SurfaceControl(); - mDisplay = mContext.getDisplay(); - mDisplayLayout = new DisplayLayout(mContext, mDisplay); - when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay); - mDisplayAreaInfo = new DisplayAreaInfo(mToken, DEFAULT_DISPLAY, - FEATURE_ONE_HANDED_BACKGROUND_PANEL); - - mSpiedBackgroundPanelOrganizer = spy( - new OneHandedBackgroundPanelOrganizer(mContext, mDisplayLayout, mMockSettingsUtil, - Runnable::run)); - mSpiedBackgroundPanelOrganizer.onDisplayChanged(mDisplayLayout); - } - - @Test - public void testOnDisplayAreaAppeared() { - mSpiedBackgroundPanelOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash); - - assertThat(mSpiedBackgroundPanelOrganizer.isRegistered()).isTrue(); - verify(mSpiedBackgroundPanelOrganizer, never()).showBackgroundPanelLayer(); - } - - @Test - public void testShowBackgroundLayer() { - mSpiedBackgroundPanelOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, null); - mSpiedBackgroundPanelOrganizer.onStart(); - - verify(mSpiedBackgroundPanelOrganizer).showBackgroundPanelLayer(); - } - - @Test - public void testRemoveBackgroundLayer() { - mSpiedBackgroundPanelOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash); - - assertThat(mSpiedBackgroundPanelOrganizer.isRegistered()).isNotNull(); - - reset(mSpiedBackgroundPanelOrganizer); - mSpiedBackgroundPanelOrganizer.removeBackgroundPanelLayer(); - - assertThat(mSpiedBackgroundPanelOrganizer.mBackgroundSurface).isNull(); - } - - @Test - public void testStateNone_onConfigurationChanged() { - mSpiedBackgroundPanelOrganizer.onStateChanged(STATE_NONE); - mSpiedBackgroundPanelOrganizer.onConfigurationChanged(); - - verify(mSpiedBackgroundPanelOrganizer, never()).showBackgroundPanelLayer(); - } - - @Test - public void testStateActivate_onConfigurationChanged() { - mSpiedBackgroundPanelOrganizer.onStateChanged(STATE_ACTIVE); - mSpiedBackgroundPanelOrganizer.onConfigurationChanged(); - - verify(mSpiedBackgroundPanelOrganizer).showBackgroundPanelLayer(); - } -} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java index 636e875bed7e..2886b97a3020 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java @@ -73,8 +73,6 @@ public class OneHandedControllerTest extends OneHandedTestCase { @Mock DisplayController mMockDisplayController; @Mock - OneHandedBackgroundPanelOrganizer mMockBackgroundOrganizer; - @Mock OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer; @Mock OneHandedEventCallback mMockEventCallback; @@ -115,7 +113,6 @@ public class OneHandedControllerTest extends OneHandedTestCase { when(mMockDisplayController.getDisplayLayout(anyInt())).thenReturn(null); when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>()); when(mMockDisplayAreaOrganizer.isReady()).thenReturn(true); - when(mMockBackgroundOrganizer.isRegistered()).thenReturn(true); when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any(), anyInt())).thenReturn( mDefaultEnabled); when(mMockSettingsUitl.getSettingsOneHandedModeTimeout(any(), anyInt())).thenReturn( @@ -134,7 +131,6 @@ public class OneHandedControllerTest extends OneHandedTestCase { mSpiedOneHandedController = spy(new OneHandedController( mContext, mMockDisplayController, - mMockBackgroundOrganizer, mMockDisplayAreaOrganizer, mMockTouchHandler, mMockTutorialHandler, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java index df21163c68cd..9c7f7237871a 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java @@ -95,8 +95,6 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase { @Mock WindowContainerTransaction mMockWindowContainerTransaction; @Mock - OneHandedBackgroundPanelOrganizer mMockBackgroundOrganizer; - @Mock ShellExecutor mMockShellMainExecutor; @Mock OneHandedSettingsUtil mMockSettingsUitl; @@ -143,7 +141,6 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase { mMockSettingsUitl, mMockAnimationController, mTutorialHandler, - mMockBackgroundOrganizer, mJankMonitor, mMockShellMainExecutor)); @@ -431,7 +428,6 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase { mMockSettingsUitl, mMockAnimationController, mTutorialHandler, - mMockBackgroundOrganizer, mJankMonitor, mMockShellMainExecutor)); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java index 58399b6444fa..dba1b8b86261 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java @@ -67,8 +67,6 @@ public class OneHandedStateTest extends OneHandedTestCase { @Mock DisplayController mMockDisplayController; @Mock - OneHandedBackgroundPanelOrganizer mMockBackgroundOrganizer; - @Mock OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer; @Mock OneHandedTouchHandler mMockTouchHandler; @@ -105,7 +103,6 @@ public class OneHandedStateTest extends OneHandedTestCase { when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay); when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>()); - when(mMockBackgroundOrganizer.isRegistered()).thenReturn(true); when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any(), anyInt())).thenReturn( mDefaultEnabled); when(mMockSettingsUitl.getSettingsOneHandedModeTimeout(any(), anyInt())).thenReturn( @@ -123,7 +120,6 @@ public class OneHandedStateTest extends OneHandedTestCase { mSpiedOneHandedController = spy(new OneHandedController( mContext, mMockDisplayController, - mMockBackgroundOrganizer, mMockDisplayAreaOrganizer, mMockTouchHandler, mMockTutorialHandler, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java index b1434ca325b7..63d8bfd1e7ef 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java @@ -56,6 +56,8 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase { OneHandedSettingsUtil mMockSettingsUtil; @Mock WindowManager mMockWindowManager; + @Mock + BackgroundWindowManager mMockBackgroundWindowManager; @Before public void setUp() { @@ -63,10 +65,11 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase { when(mMockSettingsUtil.getTutorialShownCounts(any(), anyInt())).thenReturn(0); mDisplay = mContext.getDisplay(); - mDisplayLayout = new DisplayLayout(mContext, mDisplay); + mDisplayLayout = new DisplayLayout(getTestContext().getApplicationContext(), mDisplay); mSpiedTransitionState = spy(new OneHandedState()); mSpiedTutorialHandler = spy( - new OneHandedTutorialHandler(mContext, mMockSettingsUtil, mMockWindowManager)); + new OneHandedTutorialHandler(mContext, mMockSettingsUtil, mMockWindowManager, + mMockBackgroundWindowManager)); mTimeoutHandler = new OneHandedTimeoutHandler(mMockShellMainExecutor); } diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index a2d0103450f5..dc31bdd6f8ff 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -35,7 +35,6 @@ cc_defaults { "skia_deps", //"hwui_bugreport_font_cache_usage", //"hwui_compile_for_perf", - "hwui_pgo", "hwui_lto", ], @@ -159,22 +158,6 @@ cc_defaults { ], } -// Build libhwui with PGO by default. -// Location of PGO profile data is defined in build/soong/cc/pgo.go -// and is separate from hwui. -// To turn it off, set ANDROID_PGO_NO_PROFILE_USE environment variable -// or set enable_profile_use property to false. -cc_defaults { - name: "hwui_pgo", - - pgo: { - instrumentation: true, - profile_file: "hwui/hwui.profdata", - benchmarks: ["hwui"], - enable_profile_use: true, - }, -} - // Build hwui library with ThinLTO by default. cc_defaults { name: "hwui_lto", @@ -631,6 +614,7 @@ cc_library { version_script: "libhwui.map.txt", }, }, + afdo: true, } cc_library_static { @@ -764,15 +748,3 @@ cc_benchmark { "tests/microbench/RenderNodeBench.cpp", ], } - -// ---------------------------------------- -// Phony target to build benchmarks for PGO -// ---------------------------------------- - -phony { - name: "pgo-targets-hwui", - required: [ - "hwuimicro", - "hwuimacro", - ], -} diff --git a/libs/hwui/jni/NinePatch.cpp b/libs/hwui/jni/NinePatch.cpp index b7ddd211b0de..08fc80fbdafd 100644 --- a/libs/hwui/jni/NinePatch.cpp +++ b/libs/hwui/jni/NinePatch.cpp @@ -67,7 +67,7 @@ public: size_t chunkSize = obj != NULL ? env->GetArrayLength(obj) : 0; if (chunkSize < (int) (sizeof(Res_png_9patch))) { jniThrowRuntimeException(env, "Array too small for chunk."); - return NULL; + return 0; } int8_t* storage = new int8_t[chunkSize]; diff --git a/libs/hwui/jni/android_util_PathParser.cpp b/libs/hwui/jni/android_util_PathParser.cpp index 72995efb1c21..8cbb70ed2c86 100644 --- a/libs/hwui/jni/android_util_PathParser.cpp +++ b/libs/hwui/jni/android_util_PathParser.cpp @@ -61,7 +61,7 @@ static jlong createPathDataFromStringPath(JNIEnv* env, jobject, jstring inputStr } else { delete pathData; doThrowIAE(env, result.failureMessage.c_str()); - return NULL; + return 0; } } diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java index 2c94820d50c1..ecdd4b616e0f 100644 --- a/location/java/android/location/GnssMeasurement.java +++ b/location/java/android/location/GnssMeasurement.java @@ -1860,7 +1860,7 @@ public final class GnssMeasurement implements Parcelable { gnssMeasurement.mSatelliteInterSignalBiasUncertaintyNanos = parcel.readDouble(); if (gnssMeasurement.hasSatellitePvt()) { ClassLoader classLoader = getClass().getClassLoader(); - gnssMeasurement.mSatellitePvt = parcel.readParcelable(classLoader, android.location.SatellitePvt.class); + gnssMeasurement.mSatellitePvt = parcel.readParcelable(classLoader); } if (gnssMeasurement.hasCorrelationVectors()) { CorrelationVector[] correlationVectorsArray = diff --git a/location/java/android/location/GnssMeasurementsEvent.java b/location/java/android/location/GnssMeasurementsEvent.java index b744017027b7..a07a64acb6e6 100644 --- a/location/java/android/location/GnssMeasurementsEvent.java +++ b/location/java/android/location/GnssMeasurementsEvent.java @@ -156,7 +156,7 @@ public final class GnssMeasurementsEvent implements Parcelable { public GnssMeasurementsEvent createFromParcel(Parcel in) { ClassLoader classLoader = getClass().getClassLoader(); - GnssClock clock = in.readParcelable(classLoader, android.location.GnssClock.class); + GnssClock clock = in.readParcelable(classLoader); int measurementsLength = in.readInt(); GnssMeasurement[] measurementsArray = new GnssMeasurement[measurementsLength]; diff --git a/location/java/android/location/GpsMeasurementsEvent.java b/location/java/android/location/GpsMeasurementsEvent.java index 6b834f324839..f3feb7a4c7b6 100644 --- a/location/java/android/location/GpsMeasurementsEvent.java +++ b/location/java/android/location/GpsMeasurementsEvent.java @@ -112,7 +112,7 @@ public class GpsMeasurementsEvent implements Parcelable { public GpsMeasurementsEvent createFromParcel(Parcel in) { ClassLoader classLoader = getClass().getClassLoader(); - GpsClock clock = in.readParcelable(classLoader, android.location.GpsClock.class); + GpsClock clock = in.readParcelable(classLoader); int measurementsLength = in.readInt(); GpsMeasurement[] measurementsArray = new GpsMeasurement[measurementsLength]; diff --git a/location/java/android/location/GpsNavigationMessageEvent.java b/location/java/android/location/GpsNavigationMessageEvent.java index b37fe3dfb792..2d5d6ebd5990 100644 --- a/location/java/android/location/GpsNavigationMessageEvent.java +++ b/location/java/android/location/GpsNavigationMessageEvent.java @@ -92,7 +92,7 @@ public class GpsNavigationMessageEvent implements Parcelable { @Override public GpsNavigationMessageEvent createFromParcel(Parcel in) { ClassLoader classLoader = getClass().getClassLoader(); - GpsNavigationMessage navigationMessage = in.readParcelable(classLoader, android.location.GpsNavigationMessage.class); + GpsNavigationMessage navigationMessage = in.readParcelable(classLoader); return new GpsNavigationMessageEvent(navigationMessage); } diff --git a/location/java/android/location/SatellitePvt.java b/location/java/android/location/SatellitePvt.java index aa43cfd8711c..794a8d0731f9 100644 --- a/location/java/android/location/SatellitePvt.java +++ b/location/java/android/location/SatellitePvt.java @@ -465,9 +465,9 @@ public final class SatellitePvt implements Parcelable { public SatellitePvt createFromParcel(Parcel in) { int flags = in.readInt(); ClassLoader classLoader = getClass().getClassLoader(); - PositionEcef positionEcef = in.readParcelable(classLoader, android.location.SatellitePvt.PositionEcef.class); - VelocityEcef velocityEcef = in.readParcelable(classLoader, android.location.SatellitePvt.VelocityEcef.class); - ClockInfo clockInfo = in.readParcelable(classLoader, android.location.SatellitePvt.ClockInfo.class); + PositionEcef positionEcef = in.readParcelable(classLoader); + VelocityEcef velocityEcef = in.readParcelable(classLoader); + ClockInfo clockInfo = in.readParcelable(classLoader); double ionoDelayMeters = in.readDouble(); double tropoDelayMeters = in.readDouble(); diff --git a/location/java/android/location/provider/LocationProviderBase.java b/location/java/android/location/provider/LocationProviderBase.java index 88a24794411c..529edddf4cf0 100644 --- a/location/java/android/location/provider/LocationProviderBase.java +++ b/location/java/android/location/provider/LocationProviderBase.java @@ -26,7 +26,9 @@ import android.content.Context; import android.content.Intent; import android.location.Location; import android.os.Bundle; +import android.os.Handler; import android.os.IBinder; +import android.os.Looper; import android.os.RemoteException; import android.util.Log; @@ -308,9 +310,7 @@ public abstract class LocationProviderBase { synchronized (mBinder) { try { manager.onInitialize(mAllowed, mProperties, mAttributionTag); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } catch (RuntimeException e) { + } catch (RemoteException | RuntimeException e) { Log.w(mTag, e); } @@ -320,12 +320,28 @@ public abstract class LocationProviderBase { @Override public void setRequest(ProviderRequest request) { - onSetRequest(request); + try { + onSetRequest(request); + } catch (RuntimeException e) { + // exceptions on one-way binder threads are dropped - move to a different thread + Log.w(mTag, e); + new Handler(Looper.getMainLooper()).post(() -> { + throw new AssertionError(e); + }); + } } @Override public void flush() { - onFlush(this::onFlushComplete); + try { + onFlush(this::onFlushComplete); + } catch (RuntimeException e) { + // exceptions on one-way binder threads are dropped - move to a different thread + Log.w(mTag, e); + new Handler(Looper.getMainLooper()).post(() -> { + throw new AssertionError(e); + }); + } } private void onFlushComplete() { @@ -333,9 +349,7 @@ public abstract class LocationProviderBase { if (manager != null) { try { manager.onFlushComplete(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } catch (RuntimeException e) { + } catch (RemoteException | RuntimeException e) { Log.w(mTag, e); } } @@ -343,7 +357,15 @@ public abstract class LocationProviderBase { @Override public void sendExtraCommand(String command, Bundle extras) { - onSendExtraCommand(command, extras); + try { + onSendExtraCommand(command, extras); + } catch (RuntimeException e) { + // exceptions on one-way binder threads are dropped - move to a different thread + Log.w(mTag, e); + new Handler(Looper.getMainLooper()).post(() -> { + throw new AssertionError(e); + }); + } } } } diff --git a/media/OWNERS b/media/OWNERS index 0aff43e00671..5f501372666b 100644 --- a/media/OWNERS +++ b/media/OWNERS @@ -3,7 +3,6 @@ elaurent@google.com essick@google.com etalvala@google.com hdmoon@google.com -hkuang@google.com hunga@google.com insun@google.com jaewan@google.com diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index d2c49e0a0a32..af5a3da5f3e2 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -1357,7 +1357,7 @@ public class AudioSystem return DEVICE_OUT_BLE_SPEAKER_NAME; case DEVICE_OUT_DEFAULT: default: - return Integer.toString(device); + return "0x" + Integer.toHexString(device); } } diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java index bd0f32e6ffee..69ce8d2a3d93 100644 --- a/media/java/android/media/ImageReader.java +++ b/media/java/android/media/ImageReader.java @@ -18,10 +18,13 @@ package android.media; import android.annotation.IntRange; import android.annotation.NonNull; +import android.annotation.SuppressLint; import android.graphics.GraphicBuffer; import android.graphics.ImageFormat; import android.graphics.ImageFormat.Format; import android.graphics.Rect; +import android.hardware.DataSpace; +import android.hardware.DataSpace.NamedDataSpace; import android.hardware.HardwareBuffer; import android.hardware.HardwareBuffer.Usage; import android.hardware.camera2.MultiResolutionImageReader; @@ -136,8 +139,7 @@ public class ImageReader implements AutoCloseable { // If the format is private don't default to USAGE_CPU_READ_OFTEN since it may not // work, and is inscrutable anyway return new ImageReader(width, height, format, maxImages, - format == ImageFormat.PRIVATE ? 0 : HardwareBuffer.USAGE_CPU_READ_OFTEN, - /*parent*/ null); + format == ImageFormat.PRIVATE ? 0 : HardwareBuffer.USAGE_CPU_READ_OFTEN, null); } /** @@ -268,44 +270,32 @@ public class ImageReader implements AutoCloseable { // If the format is private don't default to USAGE_CPU_READ_OFTEN since it may not // work, and is inscrutable anyway return new ImageReader(width, height, format, maxImages, - format == ImageFormat.PRIVATE ? 0 : HardwareBuffer.USAGE_CPU_READ_OFTEN, - parent); + format == ImageFormat.PRIVATE ? 0 : HardwareBuffer.USAGE_CPU_READ_OFTEN, parent); } - - /** - * @hide - */ - protected ImageReader(int width, int height, int format, int maxImages, long usage, - MultiResolutionImageReader parent) { - mWidth = width; - mHeight = height; - mFormat = format; - mUsage = usage; - mMaxImages = maxImages; - mParent = parent; - + private void initializeImageReader(int width, int height, int imageFormat, int maxImages, + long usage, int hardwareBufferFormat, long dataSpace, boolean useLegacyImageFormat) { if (width < 1 || height < 1) { throw new IllegalArgumentException( "The image dimensions must be positive"); } - if (mMaxImages < 1) { + + if (maxImages < 1) { throw new IllegalArgumentException( "Maximum outstanding image count must be at least 1"); } - if (format == ImageFormat.NV21) { + if (imageFormat == ImageFormat.NV21) { throw new IllegalArgumentException( - "NV21 format is not supported"); + "NV21 format is not supported"); } - mNumPlanes = ImageUtils.getNumPlanesForFormat(mFormat); + nativeInit(new WeakReference<>(this), width, height, maxImages, usage, + hardwareBufferFormat, dataSpace); - nativeInit(new WeakReference<>(this), width, height, format, maxImages, usage); + mIsReaderValid = true; mSurface = nativeGetSurface(); - - mIsReaderValid = true; // Estimate the native buffer allocation size and register it so it gets accounted for // during GC. Note that this doesn't include the buffers required by the buffer queue // itself and the buffers requested by the producer. @@ -313,10 +303,46 @@ public class ImageReader implements AutoCloseable { // complex, and 1 buffer is enough for the VM to treat the ImageReader as being of some // size. mEstimatedNativeAllocBytes = ImageUtils.getEstimatedNativeAllocBytes( - width, height, format, /*buffer count*/ 1); + width, height, useLegacyImageFormat ? imageFormat : hardwareBufferFormat, + /*buffer count*/ 1); VMRuntime.getRuntime().registerNativeAllocation(mEstimatedNativeAllocBytes); } + private ImageReader(int width, int height, int imageFormat, int maxImages, long usage, + MultiResolutionImageReader parent) { + mWidth = width; + mHeight = height; + mFormat = imageFormat; + mUsage = usage; + mMaxImages = maxImages; + mParent = parent; + // retrieve hal Format and hal dataspace from imageFormat + mHardwareBufferFormat = PublicFormatUtils.getHalFormat(mFormat); + mDataSpace = PublicFormatUtils.getHalDataspace(mFormat); + mUseLegacyImageFormat = true; + mNumPlanes = ImageUtils.getNumPlanesForFormat(mFormat); + + initializeImageReader(width, height, imageFormat, maxImages, usage, mHardwareBufferFormat, + mDataSpace, mUseLegacyImageFormat); + } + + private ImageReader(int width, int height, int maxImages, long usage, + MultiResolutionImageReader parent, int hardwareBufferFormat, long dataSpace) { + mWidth = width; + mHeight = height; + mFormat = ImageFormat.UNKNOWN; // set default image format value as UNKNOWN + mUsage = usage; + mMaxImages = maxImages; + mParent = parent; + mHardwareBufferFormat = hardwareBufferFormat; + mDataSpace = dataSpace; + mUseLegacyImageFormat = false; + mNumPlanes = ImageUtils.getNumPlanesForHardwareBufferFormat(mHardwareBufferFormat); + + initializeImageReader(width, height, mFormat, maxImages, usage, hardwareBufferFormat, + dataSpace, mUseLegacyImageFormat); + } + /** * The default width of {@link Image Images}, in pixels. * @@ -354,6 +380,10 @@ public class ImageReader implements AutoCloseable { * As of now, each format is only compatible to itself. * The actual format of the images can be found using {@link Image#getFormat}.</p> * + * <p>Use this function if the ImageReader instance is created by factory method + * {@code newInstance} function or by builder pattern {@code ImageReader.Builder} and using + * {@link Builder#setImageFormat}.</p> + * * @return the expected format of an Image * * @see ImageFormat @@ -363,6 +393,32 @@ public class ImageReader implements AutoCloseable { } /** + * The default {@link HardwareBuffer} format of {@link Image Images}. + * + * <p>Use this function if the ImageReader instance is created by builder pattern + * {@code ImageReader.Builder} and using {@link Builder#setDefaultHardwareBufferFormat} and + * {@link Builder#setDefaultDataSpace}.</p> + * + * @return the expected {@link HardwareBuffer} format of an Image. + */ + public @HardwareBuffer.Format int getHardwareBufferFormat() { + return mHardwareBufferFormat; + } + + /** + * The default dataspace of {@link Image Images}. + * + * <p>Use this function if the ImageReader instance is created by builder pattern + * {@code ImageReader.Builder} and {@link Builder#setDefaultDataSpace}.</p> + * + * @return the expected dataspace of an Image. + */ + @SuppressLint("MethodNameUnits") + public @NamedDataSpace long getDataSpace() { + return mDataSpace; + } + + /** * Maximum number of images that can be acquired from the ImageReader by any time (for example, * with {@link #acquireNextImage}). * @@ -384,6 +440,15 @@ public class ImageReader implements AutoCloseable { } /** + * The usage flag of images that can be produced by the ImageReader. + * + * @return The usage flag of the images for this ImageReader. + */ + public @Usage long getUsage() { + return mUsage; + } + + /** * <p>Get a {@link Surface} that can be used to produce {@link Image Images} for this * {@code ImageReader}.</p> * @@ -469,7 +534,12 @@ public class ImageReader implements AutoCloseable { * @hide */ public Image acquireNextImageNoThrowISE() { - SurfaceImage si = new SurfaceImage(mFormat); + SurfaceImage si; + if (mUseLegacyImageFormat) { + si = new SurfaceImage(mFormat); + } else { + si = new SurfaceImage(mHardwareBufferFormat, mDataSpace); + } return acquireNextSurfaceImage(si) == ACQUIRE_SUCCESS ? si : null; } @@ -492,7 +562,7 @@ public class ImageReader implements AutoCloseable { // A null image will eventually be returned if ImageReader is already closed. int status = ACQUIRE_NO_BUFS; if (mIsReaderValid) { - status = nativeImageSetup(si); + status = nativeImageSetup(si, mUseLegacyImageFormat); } switch (status) { @@ -545,7 +615,12 @@ public class ImageReader implements AutoCloseable { public Image acquireNextImage() { // Initialize with reader format, but can be overwritten by native if the image // format is different from the reader format. - SurfaceImage si = new SurfaceImage(mFormat); + SurfaceImage si; + if (mUseLegacyImageFormat) { + si = new SurfaceImage(mFormat); + } else { + si = new SurfaceImage(mHardwareBufferFormat, mDataSpace); + } int status = acquireNextSurfaceImage(si); switch (status) { @@ -838,13 +913,161 @@ public class ImageReader implements AutoCloseable { } } + /** + * Builder class for {@link ImageReader} objects. + */ + public static final class Builder { + private int mWidth; + private int mHeight; + private int mMaxImages = 1; + private int mImageFormat = ImageFormat.UNKNOWN; + private int mHardwareBufferFormat = HardwareBuffer.RGBA_8888; + private long mDataSpace = DataSpace.DATASPACE_UNKNOWN; + private long mUsage = HardwareBuffer.USAGE_CPU_READ_OFTEN; + private boolean mUseLegacyImageFormat = false; + + /** + * Constructs a new builder for {@link ImageReader}. + * + * @param width The default width in pixels that will be passed to the producer. + * May be overridden by the producer. + * @param height The default height in pixels that will be passed to the producer. + * May be overridden by the producer. + * @see Image + */ + public Builder(@IntRange(from = 1) int width, @IntRange(from = 1) int height) { + mWidth = width; + mHeight = height; + } + + /** + * Set the maximal number of images. + * + * @param maxImages The maximum number of images the user will want to + * access simultaneously. This should be as small as possible to + * limit memory use. Default value is 1. + * @return the Builder instance with customized usage value. + */ + public @NonNull Builder setMaxImages(int maxImages) { + mMaxImages = maxImages; + return this; + } + + /** + * Set the consumer usage flag. + * + * @param usage The intended usage of the images consumed by this ImageReader. + * See the usages on {@link HardwareBuffer} for a list of valid usage bits. + * Default value is {@link HardwareBuffer#USAGE_CPU_READ_OFTEN}. + * @return the Builder instance with customized usage value. + * + * @see HardwareBuffer + */ + public @NonNull Builder setUsage(long usage) { + mUsage = usage; + return this; + } + + /** + * Set the default image format passed by the producer. May be overridden by the producer. + * + * <p>{@link #setImageFormat} function replaces the combination of + * {@link #setDefaultHardwareBufferFormat} and {@link #setDefaultDataSpace} functions. + * Either this or these two functions must be called to initialize an {@code ImageReader} + * instance.</p> + * + * @param imageFormat The format of the image that this reader will produce. This + * must be one of the {@link android.graphics.ImageFormat} or + * {@link android.graphics.PixelFormat} constants. Note that not + * all formats are supported, like ImageFormat.NV21. The default value is + * {@link ImageFormat#UNKNOWN}. + * @return the builder instance with customized image format value. + * + * @see #setDefaultHardwareBufferFormat + * @see #setDefaultDataSpace + */ + public @NonNull Builder setImageFormat(@Format int imageFormat) { + mImageFormat = imageFormat; + mUseLegacyImageFormat = true; + mHardwareBufferFormat = HardwareBuffer.RGBA_8888; + mDataSpace = DataSpace.DATASPACE_UNKNOWN; + return this; + } + + /** + * Set the default hardwareBuffer format passed by the producer. + * May be overridden by the producer. + * + * <p>This function works together with {@link #setDefaultDataSpace} for an + * {@link ImageReader} instance. Setting at least one of these two replaces + * {@link #setImageFormat} function.</p> + * + * <p>The format of the Image can be overridden after {@link #setImageFormat} by calling + * this function and then {@link #setDefaultDataSpace} functions. + * <i>Warning:</i> Missing one of callings for initializing or overriding the format may + * involve undefined behaviors.</p> + * + * @param hardwareBufferFormat The HardwareBuffer format of the image that this reader + * will produce. The default value is + * {@link HardwareBuffer#RGBA_8888 HardwareBuffer.RGBA_8888}. + * @return the builder instance with customized hardwareBuffer value. + * + * @see #setDefaultDataSpace + * @see #setImageFormat + */ + @SuppressLint("MissingGetterMatchingBuilder") + public @NonNull Builder setDefaultHardwareBufferFormat( + @HardwareBuffer.Format int hardwareBufferFormat) { + mHardwareBufferFormat = hardwareBufferFormat; + mUseLegacyImageFormat = false; + mImageFormat = ImageFormat.UNKNOWN; + return this; + } + + /** + * Set the default dataspace passed by the producer. + * May be overridden by the producer. + * + * <p>This function works together with {@link #setDefaultHardwareBufferFormat} for an + * {@link ImageReader} instance. Setting at least one of these two replaces + * {@link #setImageFormat} function.</p> + * + * @param dataSpace The dataspace of the image that this reader will produce. + * The default value is {@link DataSpace#DATASPACE_UNKNOWN}. + * @return the builder instance with customized dataspace value. + * + * @see #setDefaultHardwareBufferFormat + */ + @SuppressLint("MissingGetterMatchingBuilder") + public @NonNull Builder setDefaultDataSpace(@NamedDataSpace long dataSpace) { + mDataSpace = dataSpace; + mUseLegacyImageFormat = false; + mImageFormat = ImageFormat.UNKNOWN; + return this; + } + + /** + * Builds a new ImageReader object. + * + * @return The new ImageReader object. + */ + public @NonNull ImageReader build() { + if (mUseLegacyImageFormat) { + return new ImageReader(mWidth, mHeight, mImageFormat, mMaxImages, mUsage, null); + } else { + return new ImageReader(mWidth, mHeight, mMaxImages, mUsage, null, + mHardwareBufferFormat, mDataSpace); + } + } + } + private final int mWidth; private final int mHeight; private final int mFormat; private final long mUsage; private final int mMaxImages; private final int mNumPlanes; - private final Surface mSurface; + private Surface mSurface; private int mEstimatedNativeAllocBytes; private final Object mListenerLock = new Object(); @@ -861,6 +1084,12 @@ public class ImageReader implements AutoCloseable { // MultiResolutionImageReader. private final MultiResolutionImageReader mParent; + private final int mHardwareBufferFormat; + + private final long mDataSpace; + + private final boolean mUseLegacyImageFormat; + /** * This field is used by native code, do not access or modify. */ @@ -897,6 +1126,12 @@ public class ImageReader implements AutoCloseable { mFormat = format; } + SurfaceImage(int hardwareBufferFormat, long dataSpace) { + mHardwareBufferFormat = hardwareBufferFormat; + mDataSpace = dataSpace; + mFormat = PublicFormatUtils.getPublicFormat(mHardwareBufferFormat, mDataSpace); + } + @Override public void close() { ImageReader.this.releaseImage(this); @@ -909,10 +1144,15 @@ public class ImageReader implements AutoCloseable { @Override public int getFormat() { throwISEIfImageIsInvalid(); - int readerFormat = ImageReader.this.getImageFormat(); - // Assume opaque reader always produce opaque images. - mFormat = (readerFormat == ImageFormat.PRIVATE) ? readerFormat : - nativeGetFormat(readerFormat); + // update mFormat only if ImageReader is initialized by factory pattern. + // if using builder pattern, mFormat has been updated upon initialization. + // no need update here. + if (ImageReader.this.mUseLegacyImageFormat) { + int readerFormat = ImageReader.this.getImageFormat(); + // Assume opaque reader always produce opaque images. + mFormat = (readerFormat == ImageFormat.PRIVATE) ? readerFormat : + nativeGetFormat(readerFormat); + } return mFormat; } @@ -1125,6 +1365,8 @@ public class ImageReader implements AutoCloseable { private SurfacePlane[] mPlanes; private int mFormat = ImageFormat.UNKNOWN; + private int mHardwareBufferFormat = HardwareBuffer.RGBA_8888; + private long mDataSpace = DataSpace.DATASPACE_UNKNOWN; // If this image is detached from the ImageReader. private AtomicBoolean mIsDetached = new AtomicBoolean(false); @@ -1137,8 +1379,8 @@ public class ImageReader implements AutoCloseable { private synchronized native HardwareBuffer nativeGetHardwareBuffer(); } - private synchronized native void nativeInit(Object weakSelf, int w, int h, - int fmt, int maxImgs, long consumerUsage); + private synchronized native void nativeInit(Object weakSelf, int w, int h, int maxImgs, + long consumerUsage, int hardwareBufferFormat, long dataSpace); private synchronized native void nativeClose(); private synchronized native void nativeReleaseImage(Image i); private synchronized native Surface nativeGetSurface(); @@ -1152,7 +1394,7 @@ public class ImageReader implements AutoCloseable { * @see #ACQUIRE_NO_BUFS * @see #ACQUIRE_MAX_IMAGES */ - private synchronized native int nativeImageSetup(Image i); + private synchronized native int nativeImageSetup(Image i, boolean legacyValidateImageFormat); /** * @hide diff --git a/media/java/android/media/ImageUtils.java b/media/java/android/media/ImageUtils.java index 7837d7e39599..2f1a36cba9d0 100644 --- a/media/java/android/media/ImageUtils.java +++ b/media/java/android/media/ImageUtils.java @@ -18,6 +18,7 @@ package android.media; import android.graphics.ImageFormat; import android.graphics.PixelFormat; +import android.hardware.HardwareBuffer; import android.media.Image.Plane; import android.util.Size; @@ -77,6 +78,34 @@ class ImageUtils { } /** + * Only a subset of the formats defined in + * {@link android.graphics.HardwareBuffer.Format} constants are supported by ImageReader. + */ + public static int getNumPlanesForHardwareBufferFormat(int hardwareBufferFormat) { + switch(hardwareBufferFormat) { + case HardwareBuffer.YCBCR_420_888: + return 3; + case HardwareBuffer.RGBA_8888: + case HardwareBuffer.RGBX_8888: + case HardwareBuffer.RGB_888: + case HardwareBuffer.RGB_565: + case HardwareBuffer.RGBA_FP16: + case HardwareBuffer.RGBA_1010102: + case HardwareBuffer.BLOB: + case HardwareBuffer.D_16: + case HardwareBuffer.D_24: + case HardwareBuffer.DS_24UI8: + case HardwareBuffer.D_FP32: + case HardwareBuffer.DS_FP32UI8: + case HardwareBuffer.S_UI8: + return 1; + default: + throw new UnsupportedOperationException( + String.format("Invalid hardwareBuffer format specified %d", + hardwareBufferFormat)); + } + } + /** * <p> * Copy source image data to destination Image. * </p> diff --git a/media/java/android/media/MediaDescription.java b/media/java/android/media/MediaDescription.java index dece6bdbb35f..458562afb6ef 100644 --- a/media/java/android/media/MediaDescription.java +++ b/media/java/android/media/MediaDescription.java @@ -142,10 +142,10 @@ public class MediaDescription implements Parcelable { mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); mSubtitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); mDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); - mIcon = in.readParcelable(null, android.graphics.Bitmap.class); - mIconUri = in.readParcelable(null, android.net.Uri.class); + mIcon = in.readParcelable(null); + mIconUri = in.readParcelable(null); mExtras = in.readBundle(); - mMediaUri = in.readParcelable(null, android.net.Uri.class); + mMediaUri = in.readParcelable(null); } /** diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java index 2427fa64562d..9c9e83b0987d 100644 --- a/media/java/android/media/MediaRoute2Info.java +++ b/media/java/android/media/MediaRoute2Info.java @@ -371,7 +371,7 @@ public final class MediaRoute2Info implements Parcelable { mFeatures = in.createStringArrayList(); mType = in.readInt(); mIsSystem = in.readBoolean(); - mIconUri = in.readParcelable(null, android.net.Uri.class); + mIconUri = in.readParcelable(null); mDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); mConnectionState = in.readInt(); mClientPackageName = in.readString(); diff --git a/media/java/android/media/PublicFormatUtils.java b/media/java/android/media/PublicFormatUtils.java new file mode 100644 index 000000000000..6268804128c6 --- /dev/null +++ b/media/java/android/media/PublicFormatUtils.java @@ -0,0 +1,34 @@ +/* + * Copyright 2022 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.media; + +/** + * Package private utility class for PublicFormat related methods. + */ +class PublicFormatUtils { + public static int getHalFormat(int imageFormat) { + return nativeGetHalFormat(imageFormat); + } + public static long getHalDataspace(int imageFormat) { + return nativeGetHalDataspace(imageFormat); + } + public static int getPublicFormat(int imageFormat, long dataspace) { + return nativeGetPublicFormat(imageFormat, dataspace); + } + private static native int nativeGetHalFormat(int imageFormat); + private static native long nativeGetHalDataspace(int imageFormat); + private static native int nativeGetPublicFormat(int imageFormat, long dataspace); +} diff --git a/media/java/android/media/midi/MidiDeviceStatus.java b/media/java/android/media/midi/MidiDeviceStatus.java index aa0626742ac2..b11827966b1a 100644 --- a/media/java/android/media/midi/MidiDeviceStatus.java +++ b/media/java/android/media/midi/MidiDeviceStatus.java @@ -115,7 +115,7 @@ public final class MidiDeviceStatus implements Parcelable { new Parcelable.Creator<MidiDeviceStatus>() { public MidiDeviceStatus createFromParcel(Parcel in) { ClassLoader classLoader = MidiDeviceInfo.class.getClassLoader(); - MidiDeviceInfo deviceInfo = in.readParcelable(classLoader, android.media.midi.MidiDeviceInfo.class); + MidiDeviceInfo deviceInfo = in.readParcelable(classLoader); boolean[] inputPortOpen = in.createBooleanArray(); int[] outputPortOpenCount = in.createIntArray(); return new MidiDeviceStatus(deviceInfo, inputPortOpen, outputPortOpenCount); diff --git a/media/java/android/media/musicrecognition/RecognitionRequest.java b/media/java/android/media/musicrecognition/RecognitionRequest.java index b8757a351e24..3298d634d342 100644 --- a/media/java/android/media/musicrecognition/RecognitionRequest.java +++ b/media/java/android/media/musicrecognition/RecognitionRequest.java @@ -152,8 +152,8 @@ public final class RecognitionRequest implements Parcelable { } private RecognitionRequest(Parcel in) { - mAudioFormat = in.readParcelable(AudioFormat.class.getClassLoader(), android.media.AudioFormat.class); - mAudioAttributes = in.readParcelable(AudioAttributes.class.getClassLoader(), android.media.AudioAttributes.class); + mAudioFormat = in.readParcelable(AudioFormat.class.getClassLoader()); + mAudioAttributes = in.readParcelable(AudioAttributes.class.getClassLoader()); mCaptureSession = in.readInt(); mMaxAudioLengthSeconds = in.readInt(); mIgnoreBeginningFrames = in.readInt(); diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java index 955ae3ca28fb..1da41fb87b40 100644 --- a/media/java/android/media/session/MediaController.java +++ b/media/java/android/media/session/MediaController.java @@ -1022,7 +1022,7 @@ public final class MediaController { mVolumeControl = in.readInt(); mMaxVolume = in.readInt(); mCurrentVolume = in.readInt(); - mAudioAttrs = in.readParcelable(null, android.media.AudioAttributes.class); + mAudioAttrs = in.readParcelable(null); mVolumeControlId = in.readString(); } diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl index d34e6360d95f..770b8aa705af 100644 --- a/media/java/android/media/tv/ITvInputManager.aidl +++ b/media/java/android/media/tv/ITvInputManager.aidl @@ -66,6 +66,7 @@ interface ITvInputManager { int seq, int userId); void releaseSession(in IBinder sessionToken, int userId); int getClientPid(in String sessionId); + int getClientPriority(int useCase, in String sessionId); void setMainSession(in IBinder sessionToken, int userId); void setSurface(in IBinder sessionToken, in Surface surface, int userId); diff --git a/media/java/android/media/tv/TvContentRatingSystemInfo.java b/media/java/android/media/tv/TvContentRatingSystemInfo.java index 947b2d67bfce..f44ded3dbd37 100644 --- a/media/java/android/media/tv/TvContentRatingSystemInfo.java +++ b/media/java/android/media/tv/TvContentRatingSystemInfo.java @@ -94,8 +94,8 @@ public final class TvContentRatingSystemInfo implements Parcelable { }; private TvContentRatingSystemInfo(Parcel in) { - mXmlUri = in.readParcelable(null, android.net.Uri.class); - mApplicationInfo = in.readParcelable(null, android.content.pm.ApplicationInfo.class); + mXmlUri = in.readParcelable(null); + mApplicationInfo = in.readParcelable(null); } @Override diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java index e60d5378f88c..54cb2bff5566 100644 --- a/media/java/android/media/tv/TvInputInfo.java +++ b/media/java/android/media/tv/TvInputInfo.java @@ -653,16 +653,16 @@ public final class TvInputInfo implements Parcelable { mType = in.readInt(); mIsHardwareInput = in.readByte() == 1; mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); - mIconUri = in.readParcelable(null, android.net.Uri.class); + mIconUri = in.readParcelable(null); mLabelResId = in.readInt(); - mIcon = in.readParcelable(null, android.graphics.drawable.Icon.class); - mIconStandby = in.readParcelable(null, android.graphics.drawable.Icon.class); - mIconDisconnected = in.readParcelable(null, android.graphics.drawable.Icon.class); + mIcon = in.readParcelable(null); + mIconStandby = in.readParcelable(null); + mIconDisconnected = in.readParcelable(null); mSetupActivity = in.readString(); mCanRecord = in.readByte() == 1; mCanPauseRecording = in.readByte() == 1; mTunerCount = in.readInt(); - mHdmiDeviceInfo = in.readParcelable(null, android.hardware.hdmi.HdmiDeviceInfo.class); + mHdmiDeviceInfo = in.readParcelable(null); mIsConnectedToHdmiSwitch = in.readByte() == 1; mHdmiConnectionRelativePosition = in.readInt(); mParentId = in.readString(); diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index ad86002f862d..52036b0c511e 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -764,6 +764,9 @@ public final class TvInputManager { @Override public void run() { mSessionCallback.onVideoAvailable(mSession); + if (mSession.mIAppNotificationEnabled && mSession.getIAppSession() != null) { + mSession.getIAppSession().notifyVideoAvailable(); + } } }); } @@ -773,6 +776,9 @@ public final class TvInputManager { @Override public void run() { mSessionCallback.onVideoUnavailable(mSession, reason); + if (mSession.mIAppNotificationEnabled && mSession.getIAppSession() != null) { + mSession.getIAppSession().notifyVideoUnavailable(reason); + } } }); } @@ -782,6 +788,9 @@ public final class TvInputManager { @Override public void run() { mSessionCallback.onContentAllowed(mSession); + if (mSession.mIAppNotificationEnabled && mSession.getIAppSession() != null) { + mSession.getIAppSession().notifyContentAllowed(); + } } }); } @@ -791,6 +800,9 @@ public final class TvInputManager { @Override public void run() { mSessionCallback.onContentBlocked(mSession, rating); + if (mSession.mIAppNotificationEnabled && mSession.getIAppSession() != null) { + mSession.getIAppSession().notifyContentBlocked(rating); + } } }); } @@ -1786,6 +1798,29 @@ public final class TvInputManager { }; /** + * Returns a priority for the given use case type and the client's foreground or background + * status. + * + * @param useCase the use case type of the client. When the given use case type is invalid, + * the default use case type will be used. {@see TvInputService#PriorityHintUseCaseType}. + * @param sessionId the unique id of the session owned by the client. When {@code null}, + * the caller will be used as a client. When the session is invalid, background status + * will be used as a client's status. Otherwise, TV app corresponding to the given + * session id will be used as a client. + * {@see TvInputService#onCreateSession(String, String)}. + * + * @return the use case priority value for the given use case type and the client's foreground + * or background status. + * + * @hide + */ + @SystemApi + public int getClientPriority(@TvInputService.PriorityHintUseCaseType int useCase, + @Nullable String sessionId) { + return getClientPriorityInternal(useCase, sessionId); + }; + + /** * Creates a recording {@link Session} for a given TV input. * * <p>The number of sessions that can be created at the same time is limited by the capability @@ -1829,6 +1864,14 @@ public final class TvInputManager { return clientPid; } + private int getClientPriorityInternal(int useCase, String sessionId) { + try { + return mService.getClientPriority(useCase, sessionId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Returns the TvStreamConfig list of the given TV input. * diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java index bd5a343963df..3a40d6ff1fb4 100755 --- a/media/java/android/media/tv/TvInputService.java +++ b/media/java/android/media/tv/TvInputService.java @@ -592,6 +592,24 @@ public abstract class TvInputService extends Service { }); } + /** @hide */ + public void notifyTuned(@NonNull Uri channelUri) { + executeOrPostRunnableOnMainThread(new Runnable() { + @MainThread + @Override + public void run() { + try { + if (DEBUG) Log.d(TAG, "notifyTuned"); + if (mSessionCallback != null) { + mSessionCallback.onTuned(channelUri); + } + } catch (RemoteException e) { + Log.w(TAG, "error in notifyTuned", e); + } + } + }); + } + /** * Sends the list of all audio/video/subtitle tracks. The is used by the framework to * maintain the track information for a given session, which in turn is used by diff --git a/media/java/android/media/tv/interactive/ITvIAppManager.aidl b/media/java/android/media/tv/interactive/ITvIAppManager.aidl index 23201faa1c5a..2e0435927ae8 100644 --- a/media/java/android/media/tv/interactive/ITvIAppManager.aidl +++ b/media/java/android/media/tv/interactive/ITvIAppManager.aidl @@ -51,6 +51,10 @@ interface ITvIAppManager { void notifyTuned(in IBinder sessionToken, in Uri channelUri, int userId); void notifyTrackSelected(in IBinder sessionToken, int type, in String trackId, int userId); void notifyTracksChanged(in IBinder sessionToken, in List<TvTrackInfo> tracks, int userId); + void notifyVideoAvailable(in IBinder sessionToken, int userId); + void notifyVideoUnavailable(in IBinder sessionToken, int reason, int userId); + void notifyContentAllowed(in IBinder sessionToken, int userId); + void notifyContentBlocked(in IBinder sessionToken, in String rating, int userId); void setSurface(in IBinder sessionToken, in Surface surface, int userId); void dispatchSurfaceChanged(in IBinder sessionToken, int format, int width, int height, int userId); diff --git a/media/java/android/media/tv/interactive/ITvIAppSession.aidl b/media/java/android/media/tv/interactive/ITvIAppSession.aidl index 52f9a874aca2..2788ff65e301 100644 --- a/media/java/android/media/tv/interactive/ITvIAppSession.aidl +++ b/media/java/android/media/tv/interactive/ITvIAppSession.aidl @@ -42,6 +42,10 @@ oneway interface ITvIAppSession { void notifyTuned(in Uri channelUri); void notifyTrackSelected(int type, in String trackId); void notifyTracksChanged(in List<TvTrackInfo> tracks); + void notifyVideoAvailable(); + void notifyVideoUnavailable(int reason); + void notifyContentAllowed(); + void notifyContentBlocked(in String rating); void setSurface(in Surface surface); void dispatchSurfaceChanged(int format, int width, int height); void notifyBroadcastInfoResponse(in BroadcastInfoResponse response); diff --git a/media/java/android/media/tv/interactive/TvIAppInfo.java b/media/java/android/media/tv/interactive/TvIAppInfo.java index 79d94cce77fe..b5245fc0856e 100644 --- a/media/java/android/media/tv/interactive/TvIAppInfo.java +++ b/media/java/android/media/tv/interactive/TvIAppInfo.java @@ -127,6 +127,7 @@ public final class TvIAppInfo implements Parcelable { /** * Gets supported interactive app types */ + @NonNull public List<String> getSupportedTypes() { return new ArrayList<>(mTypes); } diff --git a/media/java/android/media/tv/interactive/TvIAppManager.java b/media/java/android/media/tv/interactive/TvIAppManager.java index d1fd1df74e1b..9685e3af8c53 100644 --- a/media/java/android/media/tv/interactive/TvIAppManager.java +++ b/media/java/android/media/tv/interactive/TvIAppManager.java @@ -26,6 +26,7 @@ import android.media.tv.AdRequest; import android.media.tv.AdResponse; import android.media.tv.BroadcastInfoRequest; import android.media.tv.BroadcastInfoResponse; +import android.media.tv.TvContentRating; import android.media.tv.TvInputManager; import android.media.tv.TvTrackInfo; import android.net.Uri; @@ -1037,6 +1038,66 @@ public final class TvIAppManager { } } + /** + * Notifies IAPP session when video is available. + */ + public void notifyVideoAvailable() { + if (mToken == null) { + Log.w(TAG, "The session has been already released"); + return; + } + try { + mService.notifyVideoAvailable(mToken, mUserId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Notifies IAPP session when video is unavailable. + */ + public void notifyVideoUnavailable(int reason) { + if (mToken == null) { + Log.w(TAG, "The session has been already released"); + return; + } + try { + mService.notifyVideoUnavailable(mToken, reason, mUserId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Notifies IAPP session when content is allowed. + */ + public void notifyContentAllowed() { + if (mToken == null) { + Log.w(TAG, "The session has been already released"); + return; + } + try { + mService.notifyContentAllowed(mToken, mUserId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Notifies IAPP session when content is blocked. + */ + public void notifyContentBlocked(TvContentRating rating) { + if (mToken == null) { + Log.w(TAG, "The session has been already released"); + return; + } + try { + mService.notifyContentBlocked(mToken, rating.flattenToString(), mUserId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + private void flushPendingEventsLocked() { mHandler.removeMessages(InputEventHandler.MSG_FLUSH_INPUT_EVENT); diff --git a/media/java/android/media/tv/interactive/TvIAppService.java b/media/java/android/media/tv/interactive/TvIAppService.java index 1480ff643d99..4993bc31768c 100644 --- a/media/java/android/media/tv/interactive/TvIAppService.java +++ b/media/java/android/media/tv/interactive/TvIAppService.java @@ -31,6 +31,7 @@ import android.media.tv.AdRequest; import android.media.tv.AdResponse; import android.media.tv.BroadcastInfoRequest; import android.media.tv.BroadcastInfoResponse; +import android.media.tv.TvContentRating; import android.media.tv.TvTrackInfo; import android.net.Uri; import android.os.AsyncTask; @@ -113,6 +114,19 @@ public abstract class TvIAppService extends Service { public static final String IAPP_SERVICE_COMMAND_TYPE_SET_STREAM_VOLUME = "set_stream_volume"; /** @hide */ public static final String IAPP_SERVICE_COMMAND_TYPE_SELECT_TRACK = "select_track"; + /** @hide */ + public static final String COMMAND_PARAMETER_KEY_CHANNEL_URI = "command_channel_uri"; + /** @hide */ + public static final String COMMAND_PARAMETER_KEY_INPUT_ID = "command_input_id"; + /** @hide */ + public static final String COMMAND_PARAMETER_KEY_VOLUME = "command_volume"; + /** @hide */ + public static final String COMMAND_PARAMETER_KEY_TRACK_TYPE = "command_track_type"; + /** @hide */ + public static final String COMMAND_PARAMETER_KEY_TRACK_ID = "command_track_id"; + /** @hide */ + public static final String COMMAND_PARAMETER_KEY_TRACK_SELECT_MODE = + "command_track_select_mode"; private final Handler mServiceHandler = new ServiceHandler(); private final RemoteCallbackList<ITvIAppServiceCallback> mCallbacks = @@ -429,6 +443,34 @@ public abstract class TvIAppService extends Service { } /** + * Called when video is available. + * @hide + */ + public void onVideoAvailable() { + } + + /** + * Called when video is unavailable. + * @hide + */ + public void onVideoUnavailable(int reason) { + } + + /** + * Called when content is allowed. + * @hide + */ + public void onContentAllowed() { + } + + /** + * Called when content is blocked. + * @hide + */ + public void onContentBlocked(TvContentRating rating) { + } + + /** * Called when a broadcast info response is received. * @hide */ @@ -803,6 +845,33 @@ public abstract class TvIAppService extends Service { onTracksChanged(tracks); } + void notifyVideoAvailable() { + if (DEBUG) { + Log.d(TAG, "notifyVideoAvailable"); + } + onVideoAvailable(); + } + + void notifyVideoUnavailable(int reason) { + if (DEBUG) { + Log.d(TAG, "notifyVideoAvailable (reason=" + reason + ")"); + } + onVideoUnavailable(reason); + } + + void notifyContentAllowed() { + if (DEBUG) { + Log.d(TAG, "notifyContentAllowed"); + } + notifyContentAllowed(); + } + + void notifyContentBlocked(TvContentRating rating) { + if (DEBUG) { + Log.d(TAG, "notifyContentBlocked (rating=" + rating.flattenToString() + ")"); + } + onContentBlocked(rating); + } /** * Calls {@link #onBroadcastInfoResponse}. @@ -1169,6 +1238,26 @@ public abstract class TvIAppService extends Service { } @Override + public void notifyVideoAvailable() { + mSessionImpl.notifyVideoAvailable(); + } + + @Override + public void notifyVideoUnavailable(int reason) { + mSessionImpl.notifyVideoUnavailable(reason); + } + + @Override + public void notifyContentAllowed() { + mSessionImpl.notifyContentAllowed(); + } + + @Override + public void notifyContentBlocked(String rating) { + mSessionImpl.notifyContentBlocked(TvContentRating.unflattenFromString(rating)); + } + + @Override public void setSurface(Surface surface) { mSessionImpl.setSurface(surface); } diff --git a/media/java/android/media/tv/interactive/TvIAppView.java b/media/java/android/media/tv/interactive/TvIAppView.java index 1ce14ae98351..b29505578184 100644 --- a/media/java/android/media/tv/interactive/TvIAppView.java +++ b/media/java/android/media/tv/interactive/TvIAppView.java @@ -27,6 +27,7 @@ import android.media.tv.TvInputManager; import android.media.tv.TvTrackInfo; import android.media.tv.TvView; import android.media.tv.interactive.TvIAppManager.Session; +import android.media.tv.interactive.TvIAppManager.Session.FinishedInputEventCallback; import android.media.tv.interactive.TvIAppManager.SessionCallback; import android.net.Uri; import android.os.Bundle; @@ -34,11 +35,14 @@ import android.os.Handler; import android.util.AttributeSet; import android.util.Log; import android.util.Xml; +import android.view.InputEvent; +import android.view.KeyEvent; import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.ViewGroup; +import android.view.ViewRootImpl; import java.util.List; @@ -80,6 +84,7 @@ public class TvIAppView extends ViewGroup { private final AttributeSet mAttrs; private final int mDefStyleAttr; private final XmlResourceParser mParser; + private OnUnhandledInputEventListener mOnUnhandledInputEventListener; private final SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() { @Override @@ -272,6 +277,77 @@ public class TvIAppView extends ViewGroup { mSession.dispatchSurfaceChanged(format, width, height); } + private final FinishedInputEventCallback mFinishedInputEventCallback = + new FinishedInputEventCallback() { + @Override + public void onFinishedInputEvent(Object token, boolean handled) { + if (DEBUG) { + Log.d(TAG, "onFinishedInputEvent(token=" + token + ", handled=" + + handled + ")"); + } + if (handled) { + return; + } + // TODO: Re-order unhandled events. + InputEvent event = (InputEvent) token; + if (dispatchUnhandledInputEvent(event)) { + return; + } + ViewRootImpl viewRootImpl = getViewRootImpl(); + if (viewRootImpl != null) { + viewRootImpl.dispatchUnhandledInputEvent(event); + } + } + }; + + /** + * Dispatches an unhandled input event to the next receiver. + */ + public boolean dispatchUnhandledInputEvent(@NonNull InputEvent event) { + if (mOnUnhandledInputEventListener != null) { + if (mOnUnhandledInputEventListener.onUnhandledInputEvent(event)) { + return true; + } + } + return onUnhandledInputEvent(event); + } + + /** + * Called when an unhandled input event also has not been handled by the user provided + * callback. This is the last chance to handle the unhandled input event in the TvIAppView. + * + * @param event The input event. + * @return If you handled the event, return {@code true}. If you want to allow the event to be + * handled by the next receiver, return {@code false}. + */ + public boolean onUnhandledInputEvent(@NonNull InputEvent event) { + return false; + } + + /** + * Registers a callback to be invoked when an input event is not handled + * by the TV Interactive App. + * + * @param listener The callback to be invoked when the unhandled input event is received. + */ + public void setOnUnhandledInputEventListener(@NonNull OnUnhandledInputEventListener listener) { + mOnUnhandledInputEventListener = listener; + } + + @Override + public boolean dispatchKeyEvent(@NonNull KeyEvent event) { + if (super.dispatchKeyEvent(event)) { + return true; + } + if (mSession == null) { + return false; + } + InputEvent copiedEvent = event.copy(); + int ret = mSession.dispatchInputEvent(copiedEvent, copiedEvent, mFinishedInputEventCallback, + mHandler); + return ret != Session.DISPATCH_NOT_HANDLED; + } + /** * Prepares the interactive application. */ @@ -514,6 +590,24 @@ public class TvIAppView extends ViewGroup { */ public void onRequestTrackInfoList(@NonNull String iAppServiceId) { } + + } + + /** + * Interface definition for a callback to be invoked when the unhandled input event is received. + */ + public interface OnUnhandledInputEventListener { + /** + * Called when an input event was not handled by the TV Interactive App. + * + * <p>This is called asynchronously from where the event is dispatched. It gives the host + * application a chance to handle the unhandled input events. + * + * @param event The input event. + * @return If you handled the event, return {@code true}. If you want to allow the event to + * be handled by the next receiver, return {@code false}. + */ + boolean onUnhandledInputEvent(@NonNull InputEvent event); } private class MySessionCallback extends SessionCallback { diff --git a/media/java/android/media/tv/tuner/Lnb.java b/media/java/android/media/tv/tuner/Lnb.java index 6a6a22c1fc0e..50a208344c3a 100644 --- a/media/java/android/media/tv/tuner/Lnb.java +++ b/media/java/android/media/tv/tuner/Lnb.java @@ -28,6 +28,9 @@ import android.media.tv.tuner.Tuner.Result; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; import java.util.concurrent.Executor; /** @@ -145,9 +148,9 @@ public class Lnb implements AutoCloseable { private static final String TAG = "Lnb"; - LnbCallback mCallback; - Executor mExecutor; - Tuner mTuner; + Map<LnbCallback, Executor> mCallbackMap = + new HashMap<LnbCallback, Executor>(); + Tuner mOwner; private final Object mCallbackLock = new Object(); @@ -164,38 +167,82 @@ public class Lnb implements AutoCloseable { private Lnb() {} - void setCallback(Executor executor, @Nullable LnbCallback callback, Tuner tuner) { + void setCallbackAndOwner(Executor executor, @Nullable LnbCallback callback, Tuner tuner) { synchronized (mCallbackLock) { - mCallback = callback; - mExecutor = executor; - mTuner = tuner; + if (callback != null && executor != null) { + addCallback(callback, executor); + } + } + setOwner(tuner); + } + + /** + * Adds LnbCallback + * + * @param callback the callback to receive notifications from LNB. + * @param executor the executor on which callback will be invoked. Cannot be null. + */ + public void addCallback(@NonNull LnbCallback callback, @NonNull Executor executor) { + Objects.requireNonNull(callback, "callback must not be null"); + Objects.requireNonNull(executor, "executor must not be null"); + synchronized (mCallbackLock) { + mCallbackMap.put(callback, executor); + } + } + + /** + * Removes LnbCallback + * + * @param callback the callback be removed for callback + * + * @return {@code true} when successful. {@code false} otherwise. + */ + public boolean removeCallback(@NonNull LnbCallback callback) { + Objects.requireNonNull(callback, "callback must not be null"); + synchronized (mCallbackLock) { + boolean result = (mCallbackMap.remove(callback) != null); + return result; + } + } + + // allow owner transfer independent of whether callback is registered or not + /* package */ void setOwner(@NonNull Tuner newOwner) { + Objects.requireNonNull(newOwner, "newOwner must not be null"); + synchronized (mLock) { + mOwner = newOwner; } } private void onEvent(int eventType) { synchronized (mCallbackLock) { - if (mExecutor != null && mCallback != null) { - mExecutor.execute(() -> { - synchronized (mCallbackLock) { - if (mCallback != null) { - mCallback.onEvent(eventType); + for (LnbCallback callback : mCallbackMap.keySet()) { + Executor executor = mCallbackMap.get(callback); + if (callback != null && executor != null) { + executor.execute(() -> { + synchronized (mCallbackLock) { + if (callback != null) { + callback.onEvent(eventType); + } } - } - }); + }); + } } } } private void onDiseqcMessage(byte[] diseqcMessage) { synchronized (mCallbackLock) { - if (mExecutor != null && mCallback != null) { - mExecutor.execute(() -> { - synchronized (mCallbackLock) { - if (mCallback != null) { - mCallback.onDiseqcMessage(diseqcMessage); + for (LnbCallback callback : mCallbackMap.keySet()) { + Executor executor = mCallbackMap.get(callback); + if (callback != null && executor != null) { + executor.execute(() -> { + synchronized (mCallbackLock) { + if (callback != null) { + callback.onDiseqcMessage(diseqcMessage); + } } - } - }); + }); + } } } } @@ -279,7 +326,11 @@ public class Lnb implements AutoCloseable { TunerUtils.throwExceptionForResult(res, "Failed to close LNB"); } else { mIsClosed = true; - mTuner.releaseLnb(); + if (mOwner != null) { + mOwner.releaseLnb(); + mOwner = null; + } + mCallbackMap.clear(); } } } diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java index 4128abf03e07..9c4a83a235c0 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -241,7 +241,7 @@ public class Tuner implements AutoCloseable { private static final String TAG = "MediaTvTuner"; - private static final boolean DEBUG = false; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final int MSG_RESOURCE_LOST = 1; private static final int MSG_ON_FILTER_EVENT = 2; @@ -250,7 +250,6 @@ public class Tuner implements AutoCloseable { private static final int FILTER_CLEANUP_THRESHOLD = 256; - /** @hide */ @IntDef(prefix = "DVR_TYPE_", value = {DVR_TYPE_RECORD, DVR_TYPE_PLAYBACK}) @Retention(RetentionPolicy.SOURCE) @@ -455,6 +454,260 @@ public class Tuner implements AutoCloseable { } /** + * Transfers the ownership of shared frontend and its associated resources. + * + * @param newOwner the Tuner instance to be the new owner. + * + * @return result status of tune operation. + */ + public int transferOwner(@NonNull Tuner newOwner) { + acquireTRMSLock("transferOwner()"); + mFrontendLock.lock(); + mFrontendCiCamLock.lock(); + mLnbLock.lock(); + try { + + if (!isFrontendOwner() || !isNewOwnerQualifiedForTransfer(newOwner)) { + return RESULT_INVALID_STATE; + } + + int res = transferFeOwner(newOwner); + if (res != RESULT_SUCCESS) { + return res; + } + + res = transferCiCamOwner(newOwner); + if (res != RESULT_SUCCESS) { + return res; + } + + res = transferLnbOwner(newOwner); + if (res != RESULT_SUCCESS) { + return res; + } + } finally { + mFrontendLock.unlock(); + mFrontendCiCamLock.unlock(); + mLnbLock.unlock(); + releaseTRMSLock(); + } + return RESULT_SUCCESS; + } + + /** + * Resets or copies Frontend related settings. + */ + private void replicateFrontendSettings(@Nullable Tuner src) { + mFrontendLock.lock(); + try { + if (src == null) { + if (DEBUG) { + Log.d(TAG, "resetting Frontend params for " + mClientId); + } + mFrontend = null; + mFrontendHandle = null; + mFrontendInfo = null; + mFrontendType = FrontendSettings.TYPE_UNDEFINED; + } else { + if (DEBUG) { + Log.d(TAG, "copying Frontend params from " + src.mClientId + + " to " + mClientId); + } + mFrontend = src.mFrontend; + mFrontendHandle = src.mFrontendHandle; + mFrontendInfo = src.mFrontendInfo; + mFrontendType = src.mFrontendType; + } + } finally { + mFrontendLock.unlock(); + } + } + + /** + * Sets the frontend owner. mFeOwnerTuner should be null for the owner Tuner instance. + */ + private void setFrontendOwner(Tuner owner) { + mFrontendLock.lock(); + try { + mFeOwnerTuner = owner; + } finally { + mFrontendLock.unlock(); + } + } + + /** + * Resets or copies the CiCam related settings. + */ + private void replicateCiCamSettings(@Nullable Tuner src) { + mFrontendCiCamLock.lock(); + try { + if (src == null) { + if (DEBUG) { + Log.d(TAG, "resetting CiCamParams: " + mClientId); + } + mFrontendCiCamHandle = null; + mFrontendCiCamId = null; + } else { + if (DEBUG) { + Log.d(TAG, "copying CiCamParams from " + src.mClientId + " to " + mClientId); + Log.d(TAG, "mFrontendCiCamHandle:" + src.mFrontendCiCamHandle + ", " + + "mFrontendCiCamId:" + src.mFrontendCiCamId); + } + mFrontendCiCamHandle = src.mFrontendCiCamHandle; + mFrontendCiCamId = src.mFrontendCiCamId; + } + } finally { + mFrontendCiCamLock.unlock(); + } + } + + /** + * Resets or copies Lnb related settings. + */ + private void replicateLnbSettings(@Nullable Tuner src) { + mLnbLock.lock(); + try { + if (src == null) { + if (DEBUG) { + Log.d(TAG, "resetting Lnb params"); + } + mLnb = null; + mLnbHandle = null; + } else { + if (DEBUG) { + Log.d(TAG, "copying Lnb params from " + src.mClientId + " to " + mClientId); + } + mLnb = src.mLnb; + mLnbHandle = src.mLnbHandle; + } + } finally { + mLnbLock.unlock(); + } + } + + /** + * Checks if it is a frontend resource owner. + * Proper mutex must be held prior to calling this. + */ + private boolean isFrontendOwner() { + boolean notAnOwner = (mFeOwnerTuner != null); + if (notAnOwner) { + Log.e(TAG, "transferOwner() - cannot be called on the non-owner"); + return false; + } + return true; + } + + /** + * Checks if the newOwner is qualified. + * Proper mutex must be held prior to calling this. + */ + private boolean isNewOwnerQualifiedForTransfer(@NonNull Tuner newOwner) { + // new owner must be the current sharee + boolean newOwnerIsTheCurrentSharee = (newOwner.mFeOwnerTuner == this) + && (newOwner.mFrontendHandle.equals(mFrontendHandle)); + if (!newOwnerIsTheCurrentSharee) { + Log.e(TAG, "transferOwner() - new owner must be the current sharee"); + return false; + } + + // new owner must not be holding any of the to-be-shared resources + boolean newOwnerAlreadyHoldsToBeSharedResource = + (newOwner.mFrontendCiCamHandle != null || newOwner.mLnb != null); + if (newOwnerAlreadyHoldsToBeSharedResource) { + Log.e(TAG, "transferOwner() - new owner cannot be holding CiCam" + + " nor Lnb resource"); + return false; + } + + return true; + } + + /** + * Transfers the ownership of the already held frontend resource. + * Proper mutex must be held prior to calling this. + */ + private int transferFeOwner(@NonNull Tuner newOwner) { + // handle native resource first + newOwner.nativeUpdateFrontend(getNativeContext()); + nativeUpdateFrontend(0); + + // transfer frontend related settings + newOwner.replicateFrontendSettings(this); + + // transfer the frontend owner info + setFrontendOwner(newOwner); + newOwner.setFrontendOwner(null); + + // handle TRM + if (mTunerResourceManager.transferOwner( + TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND, + mClientId, newOwner.mClientId)) { + return RESULT_SUCCESS; + } else { + return RESULT_UNKNOWN_ERROR; + } + } + + /** + * Transfers the ownership of CiCam resource. + * This is a no-op if the CiCam resource is not held. + * Proper mutex must be held prior to calling this. + */ + private int transferCiCamOwner(Tuner newOwner) { + boolean notAnOwner = (mFrontendCiCamHandle == null); + if (notAnOwner) { + // There is nothing to do here if there is no CiCam + return RESULT_SUCCESS; + } + + // no need to handle at native level + + // transfer the CiCam info at Tuner level + newOwner.replicateCiCamSettings(this); + replicateCiCamSettings(null); + + // handle TRM + if (mTunerResourceManager.transferOwner( + TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, + mClientId, newOwner.mClientId)) { + return RESULT_SUCCESS; + } else { + return RESULT_UNKNOWN_ERROR; + } + } + + /** + * Transfers the ownership of Lnb resource. + * This is a no-op if the Lnb resource is not held. + * Proper mutex must be held prior to calling this. + */ + private int transferLnbOwner(Tuner newOwner) { + boolean notAnOwner = (mLnb == null); + if (notAnOwner) { + // There is nothing to do here if there is no Lnb + return RESULT_SUCCESS; + } + + // no need to handle at native level + + // set the new owner + mLnb.setOwner(newOwner); + + newOwner.replicateLnbSettings(this); + replicateLnbSettings(null); + + // handle TRM + if (mTunerResourceManager.transferOwner( + TunerResourceManager.TUNER_RESOURCE_TYPE_LNB, + mClientId, newOwner.mClientId)) { + return RESULT_SUCCESS; + } else { + return RESULT_UNKNOWN_ERROR; + } + } + + /** * Updates client priority with an arbitrary value along with a nice value. * * <p>Tuner resource manager (TRM) uses the client priority value to decide whether it is able @@ -547,59 +800,114 @@ public class Tuner implements AutoCloseable { } } + /** + * Either unshares the frontend resource (for sharee) or release Frontend (for owner) + */ + public void closeFrontend() { + acquireTRMSLock("closeFrontend()"); + try { + releaseFrontend(); + } finally { + releaseTRMSLock(); + } + } + + /** + * Releases frontend resource for the owner. Unshares frontend resource for the sharee. + */ private void releaseFrontend() { + if (DEBUG) { + Log.d(TAG, "Tuner#releaseFrontend"); + } mFrontendLock.lock(); try { if (mFrontendHandle != null) { + if (DEBUG) { + Log.d(TAG, "mFrontendHandle not null"); + } if (mFeOwnerTuner != null) { + if (DEBUG) { + Log.d(TAG, "mFeOwnerTuner not null - sharee"); + } // unregister self from the Frontend callback mFeOwnerTuner.unregisterFrontendCallbackListener(this); mFeOwnerTuner = null; + nativeUnshareFrontend(); } else { + if (DEBUG) { + Log.d(TAG, "mFeOwnerTuner null - owner"); + } // close resource as owner int res = nativeCloseFrontend(mFrontendHandle); if (res != Tuner.RESULT_SUCCESS) { TunerUtils.throwExceptionForResult(res, "failed to close frontend"); } - mTunerResourceManager.releaseFrontend(mFrontendHandle, mClientId); } + if (DEBUG) { + Log.d(TAG, "call TRM#releaseFrontend :" + mFrontendHandle + ", " + mClientId); + } + mTunerResourceManager.releaseFrontend(mFrontendHandle, mClientId); FrameworkStatsLog .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId, FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__UNKNOWN); - mFrontendHandle = null; - mFrontend = null; + replicateFrontendSettings(null); } } finally { mFrontendLock.unlock(); } } + /** + * Releases CiCam resource if held. No-op otherwise. + */ + private void releaseCiCam() { + mFrontendCiCamLock.lock(); + try { + if (mFrontendCiCamHandle != null) { + if (DEBUG) { + Log.d(TAG, "unlinking CiCam : " + mFrontendCiCamHandle + " for " + mClientId); + } + int result = nativeUnlinkCiCam(mFrontendCiCamId); + if (result == RESULT_SUCCESS) { + mTunerResourceManager.releaseCiCam(mFrontendCiCamHandle, mClientId); + replicateCiCamSettings(null); + } else { + Log.e(TAG, "nativeUnlinkCiCam(" + mFrontendCiCamHandle + ") for mClientId:" + + mClientId + "failed with result:" + result); + } + } else { + if (DEBUG) { + Log.d(TAG, "NOT unlinking CiCam : " + mClientId); + } + } + } finally { + mFrontendCiCamLock.unlock(); + } + } + private void releaseAll() { + // release CiCam before frontend because frontend handle is needed to unlink CiCam + releaseCiCam(); + releaseFrontend(); mLnbLock.lock(); try { // mLnb will be non-null only for owner tuner if (mLnb != null) { + if (DEBUG) { + Log.d(TAG, "calling mLnb.close() : " + mClientId); + } mLnb.close(); + } else { + if (DEBUG) { + Log.d(TAG, "NOT calling mLnb.close() : " + mClientId); + } } } finally { mLnbLock.unlock(); } - mFrontendCiCamLock.lock(); - try { - if (mFrontendCiCamHandle != null) { - int result = nativeUnlinkCiCam(mFrontendCiCamId); - if (result == RESULT_SUCCESS) { - mTunerResourceManager.releaseCiCam(mFrontendCiCamHandle, mClientId); - mFrontendCiCamId = null; - mFrontendCiCamHandle = null; - } - } - } finally { - mFrontendCiCamLock.unlock(); - } synchronized (mDescramblers) { if (!mDescramblers.isEmpty()) { @@ -669,8 +977,11 @@ public class Tuner implements AutoCloseable { */ private native Frontend nativeOpenFrontendByHandle(int handle); private native int nativeShareFrontend(int id); + private native int nativeUnshareFrontend(); private native void nativeRegisterFeCbListener(long nativeContext); private native void nativeUnregisterFeCbListener(long nativeContext); + // nativeUpdateFrontend must be called on the new owner first + private native void nativeUpdateFrontend(long nativeContext); @Result private native int nativeTune(int type, FrontendSettings settings); private native int nativeStopTune(); @@ -997,6 +1308,21 @@ public class Tuner implements AutoCloseable { mFrontendHandle = feHandle[0]; mFrontend = nativeOpenFrontendByHandle(mFrontendHandle); } + + // For satellite type, set Lnb if valid handle exists. + // This is necessary as now that we support closeFrontend(). + if (mFrontendType == FrontendSettings.TYPE_DVBS + || mFrontendType == FrontendSettings.TYPE_ISDBS + || mFrontendType == FrontendSettings.TYPE_ISDBS3) { + mLnbLock.lock(); + try { + if (mLnbHandle != null && mLnb != null) { + nativeSetLnb(mLnb); + } + } finally { + mLnbLock.unlock(); + } + } return granted; } @@ -1756,12 +2082,12 @@ public class Tuner implements AutoCloseable { Objects.requireNonNull(executor, "executor must not be null"); Objects.requireNonNull(cb, "LnbCallback must not be null"); if (mLnb != null) { - mLnb.setCallback(executor, cb, this); + mLnb.setCallbackAndOwner(executor, cb, this); return mLnb; } if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_LNB, mLnbLock) && mLnb != null) { - mLnb.setCallback(executor, cb, this); + mLnb.setCallbackAndOwner(executor, cb, this); setLnb(mLnb); return mLnb; } @@ -1795,7 +2121,7 @@ public class Tuner implements AutoCloseable { mLnbHandle = null; } mLnb = newLnb; - mLnb.setCallback(executor, cb, this); + mLnb.setCallbackAndOwner(executor, cb, this); setLnb(mLnb); } return mLnb; @@ -2081,8 +2407,15 @@ public class Tuner implements AutoCloseable { try { if (mLnbHandle != null) { // LNB handle can be null if it's opened by name. + if (DEBUG) { + Log.d(TAG, "releasing Lnb"); + } mTunerResourceManager.releaseLnb(mLnbHandle, mClientId); mLnbHandle = null; + } else { + if (DEBUG) { + Log.d(TAG, "NOT releasing Lnb because mLnbHandle is null"); + } } mLnb = null; } finally { diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java index 45c2a5a90c70..9f4423644877 100644 --- a/media/java/android/media/tv/tuner/filter/Filter.java +++ b/media/java/android/media/tv/tuner/filter/Filter.java @@ -626,8 +626,12 @@ public class Filter implements AutoCloseable { * be a no-op. Use {@link TunerVersionChecker#getTunerVersion()} to get the version information. * * @param delayInMs specifies the duration of the delay in milliseconds. + * @return one of the following results: {@link Tuner#RESULT_SUCCESS}, + * {@link Tuner#RESULT_UNAVAILABLE}, {@link Tuner#RESULT_NOT_INITIALIZED}, + * {@link Tuner#RESULT_INVALID_STATE}, {@link Tuner#RESULT_INVALID_ARGUMENT}, + * {@link Tuner#RESULT_OUT_OF_MEMORY}, or {@link Tuner#RESULT_UNKNOWN_ERROR}. */ - public int delayCallbackUntilTimeMillis(long delayInMs) { + public int delayCallbackUntilMillisElapsed(long delayInMs) { if (!TunerVersionChecker.checkHigherOrEqualVersionTo( TunerVersionChecker.TUNER_VERSION_2_0, "setTimeDelayHint")) { return Tuner.RESULT_UNAVAILABLE; @@ -652,8 +656,12 @@ public class Filter implements AutoCloseable { * be a no-op. Use {@link TunerVersionChecker#getTunerVersion()} to get the version information. * * @param delayInBytes specifies the duration of the delay in bytes. + * @return one of the following results: {@link Tuner#RESULT_SUCCESS}, + * {@link Tuner#RESULT_UNAVAILABLE}, {@link Tuner#RESULT_NOT_INITIALIZED}, + * {@link Tuner#RESULT_INVALID_STATE}, {@link Tuner#RESULT_INVALID_ARGUMENT}, + * {@link Tuner#RESULT_OUT_OF_MEMORY}, or {@link Tuner#RESULT_UNKNOWN_ERROR}. */ - public int delayCallbackUntilBufferFilled(int delayInBytes) { + public int delayCallbackUntilBytesAccumulated(int delayInBytes) { if (!TunerVersionChecker.checkHigherOrEqualVersionTo( TunerVersionChecker.TUNER_VERSION_2_0, "setTimeDelayHint")) { return Tuner.RESULT_UNAVAILABLE; diff --git a/media/java/android/media/tv/tuner/filter/SectionSettings.java b/media/java/android/media/tv/tuner/filter/SectionSettings.java index 94fda30fecb1..f123675a8940 100644 --- a/media/java/android/media/tv/tuner/filter/SectionSettings.java +++ b/media/java/android/media/tv/tuner/filter/SectionSettings.java @@ -16,12 +16,13 @@ package android.media.tv.tuner.filter; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.SystemApi; import android.media.tv.tuner.TunerUtils; /** - * Filter Settings for Section data according to ISO/IEC 13818-1. + * Filter Settings for Section data according to ISO/IEC 13818-1 and ISO/IEC 23008-1. * * @hide */ @@ -30,12 +31,15 @@ public abstract class SectionSettings extends Settings { final boolean mCrcEnabled; final boolean mIsRepeat; final boolean mIsRaw; + final int mBitWidthOfLengthField; - SectionSettings(int mainType, boolean crcEnabled, boolean isRepeat, boolean isRaw) { + SectionSettings(int mainType, boolean crcEnabled, boolean isRepeat, boolean isRaw, + int bitWidthOfLengthField) { super(TunerUtils.getFilterSubtype(mainType, Filter.SUBTYPE_SECTION)); mCrcEnabled = crcEnabled; mIsRepeat = isRepeat; mIsRaw = isRaw; + mBitWidthOfLengthField = bitWidthOfLengthField; } /** @@ -62,6 +66,7 @@ public abstract class SectionSettings extends Settings { public boolean isRepeat() { return mIsRepeat; } + /** * Returns whether the filter sends {@link FilterCallback#onFilterStatusChanged} instead of * {@link FilterCallback#onFilterEvent}. @@ -71,6 +76,17 @@ public abstract class SectionSettings extends Settings { } /** + * Returns the bit width of the MMTP (MPEG Media Transport Protocol) section message's length + * field according to ISO/IEC 23008-1. + * + * The section filter uses this for CRC (Cyclic redundancy check) checking when + * {@link #isCrcEnabled()} is {@code true}. + */ + public int getBitWidthOfLengthField() { + return mBitWidthOfLengthField; + } + + /** * Builder for {@link SectionSettings}. * * @param <T> The subclass to be built. @@ -80,6 +96,7 @@ public abstract class SectionSettings extends Settings { boolean mCrcEnabled; boolean mIsRepeat; boolean mIsRaw; + int mBitWidthOfLengthField; Builder(int mainType) { mMainType = mainType; @@ -114,6 +131,7 @@ public abstract class SectionSettings extends Settings { mIsRepeat = isRepeat; return self(); } + /** * Sets whether the filter send onFilterStatus instead of * {@link FilterCallback#onFilterEvent}. @@ -124,6 +142,23 @@ public abstract class SectionSettings extends Settings { return self(); } + /** + * Sets the bit width for the MMTP(MPEG Media Transport Protocol) section message's length + * field according to ISO/IEC 23008-1. + * + * The section filter uses this for CRC (Cyclic redundancy check) checking when + * {@link #isCrcEnabled()} is {@code true}. + * + * <p>This field is only supported in Tuner 2.0 or higher version. Unsupported version will + * cause no-op. Use {@link android.media.tv.tuner.TunerVersionChecker#getTunerVersion()} + * to get the version information. + */ + @NonNull + public T setBitWidthOfLengthField(@IntRange(from = 0) int bitWidthOfLengthField) { + mBitWidthOfLengthField = bitWidthOfLengthField; + return self(); + } + /* package */ abstract T self(); } } diff --git a/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java b/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java index edfe85ede943..766a5c9e67cb 100644 --- a/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java +++ b/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java @@ -30,10 +30,9 @@ public class SectionSettingsWithSectionBits extends SectionSettings { private final byte[] mMask; private final byte[] mMode; - private SectionSettingsWithSectionBits(int mainType, boolean isCheckCrc, boolean isRepeat, - boolean isRaw, byte[] filter, byte[] mask, byte[] mode) { - super(mainType, isCheckCrc, isRepeat, isRaw); + boolean isRaw, int bitWidthOfLengthField, byte[] filter, byte[] mask, byte[] mode) { + super(mainType, isCheckCrc, isRepeat, isRaw, bitWidthOfLengthField); mFilter = filter; mMask = mask; mMode = mode; @@ -126,8 +125,8 @@ public class SectionSettingsWithSectionBits extends SectionSettings { */ @NonNull public SectionSettingsWithSectionBits build() { - return new SectionSettingsWithSectionBits( - mMainType, mCrcEnabled, mIsRepeat, mIsRaw, mFilter, mMask, mMode); + return new SectionSettingsWithSectionBits(mMainType, mCrcEnabled, mIsRepeat, mIsRaw, + mBitWidthOfLengthField, mFilter, mMask, mMode); } @Override diff --git a/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java b/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java index ff8f796455c6..eddef40caba4 100644 --- a/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java +++ b/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java @@ -37,8 +37,8 @@ public class SectionSettingsWithTableInfo extends SectionSettings { private final int mVersion; private SectionSettingsWithTableInfo(int mainType, boolean isCheckCrc, boolean isRepeat, - boolean isRaw, int tableId, int version) { - super(mainType, isCheckCrc, isRepeat, isRaw); + boolean isRaw, int bitWidthOfLengthField, int tableId, int version) { + super(mainType, isCheckCrc, isRepeat, isRaw, bitWidthOfLengthField); mTableId = tableId; mVersion = version; } @@ -99,8 +99,8 @@ public class SectionSettingsWithTableInfo extends SectionSettings { */ @NonNull public SectionSettingsWithTableInfo build() { - return new SectionSettingsWithTableInfo( - mMainType, mCrcEnabled, mIsRepeat, mIsRaw, mTableId, mVersion); + return new SectionSettingsWithTableInfo(mMainType, mCrcEnabled, mIsRepeat, mIsRaw, + mBitWidthOfLengthField, mTableId, mVersion); } @Override diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java index fe611c711273..5ada89e9dea7 100644 --- a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java +++ b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java @@ -21,9 +21,12 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresFeature; +import android.annotation.SuppressLint; import android.annotation.SystemService; +import android.annotation.TestApi; import android.content.Context; import android.content.pm.PackageManager; +import android.media.tv.TvInputService; import android.os.Binder; import android.os.RemoteException; import android.util.Log; @@ -415,6 +418,25 @@ public class TunerResourceManager { } /** + * Transfers the ownership of shared resource. + * + * <p><strong>Note:</strong> Only the existing frontend sharee can be the new owner. + * + * @param resourceType the type of the resource to transfer the ownership for. + * @param currentOwnerId the id of the current owner client. + * @param newOwnerId the id of the new owner client. + * + * @return true if successful and false otherwise. + */ + public boolean transferOwner(int resourceType, int currentOwnerId, int newOwnerId) { + try { + return mService.transferOwner(resourceType, currentOwnerId, newOwnerId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Requests a Tuner Demux resource. * * <p>There are three possible scenarios: @@ -702,6 +724,49 @@ public class TunerResourceManager { } /** + * Returns a priority for the given use case type and the client's foreground or background + * status. + * + * @param useCase the use case type of the client. When the given use case type is invalid, + * the default use case type will be used. {@see TvInputService#PriorityHintUseCaseType}. + * @param pid the pid of the client. When the pid is invalid, background status will be used as + * a client's status. Otherwise, client's app corresponding to the given session id will + * be used as a client. {@see TvInputService#onCreateSession(String, String)}. + * + * @return the client priority.. + */ + public int getClientPriority(@TvInputService.PriorityHintUseCaseType int useCase, int pid) { + try { + return mService.getClientPriority(useCase, pid); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Returns a config priority for the given use case type and the foreground or background + * status. + * + * @param useCase the use case type of the client. When the given use case type is invalid, + * the default use case type will be used. {@see TvInputService#PriorityHintUseCaseType}. + * @param isForeground {@code true} if foreground, {@code false} otherwise. + * + * @return the config priority. + * + * @hide + */ + @TestApi + @SuppressLint("ShowingMemberInHiddenClass") + public int getConfigPriority(@TvInputService.PriorityHintUseCaseType int useCase, + boolean isForeground) { + try { + return mService.getConfigPriority(useCase, isForeground); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Interface used to receive events from TunerResourceManager. */ public abstract static class ResourcesReclaimListener { diff --git a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl index 5f3582046d15..d16fc6ca1dc7 100644 --- a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl +++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl @@ -177,6 +177,19 @@ interface ITunerResourceManager { void shareFrontend(in int selfClientId, in int targetClientId); /* + * Transfers the ownership of the shared resource. + * + * <p><strong>Note:</strong> Only the existing frontend sharee can be the new owner. + * + * @param resourceType the type of resource to transfer the ownership for. + * @param currentOwnerId the id of the current owner client. + * @param newOwnerId the id of the new owner client. + * + * @return true if successful. false otherwise. + */ + boolean transferOwner(in int resourceType, in int currentOwnerId, in int newOwnerId); + + /* * This API is used by the Tuner framework to request an available demux from the TunerHAL. * * <p>There are three possible scenarios: @@ -442,4 +455,30 @@ interface ITunerResourceManager { * guaranteed to work and may be unrecoverrable. (This should not happen.) */ boolean releaseLock(in int clientId); + + /** + * Returns a priority for the given use case type and the client's foreground or background + * status. + * + * @param useCase the use case type of the client. When the given use case type is invalid, + * the default use case type will be used. {@see TvInputService#PriorityHintUseCaseType}. + * @param pid the pid of the client. When the pid is invalid, background status will be used as + * a client's status. Otherwise, client's app corresponding to the given session id will + * be used as a client. {@see TvInputService#onCreateSession(String, String)}. + * + * @return the client priority.. + */ + int getClientPriority(int useCase, int pid); + + /** + * Returns a config priority for the given use case type and the foreground or background + * status. + * + * @param useCase the use case type of the client. When the given use case type is invalid, + * the default use case type will be used. {@see TvInputService#PriorityHintUseCaseType}. + * @param isForeground {@code true} if foreground, {@code false} otherwise. + * + * @return the config priority. + */ + int getConfigPriority(int useCase, boolean isForeground); } diff --git a/media/jni/Android.bp b/media/jni/Android.bp index e817f2dc9e1d..feae6065c680 100644 --- a/media/jni/Android.bp +++ b/media/jni/Android.bp @@ -39,6 +39,7 @@ cc_library_shared { "android_media_MediaProfiles.cpp", "android_media_MediaRecorder.cpp", "android_media_MediaSync.cpp", + "android_media_PublicFormatUtils.cpp", "android_media_ResampleInputStream.cpp", "android_media_Streams.cpp", "android_media_SyncParams.cpp", diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp index 021507c8631c..6002e2884db8 100644 --- a/media/jni/android_media_ImageReader.cpp +++ b/media/jni/android_media_ImageReader.cpp @@ -375,18 +375,13 @@ static void ImageReader_classInit(JNIEnv* env, jclass clazz) } static void ImageReader_init(JNIEnv* env, jobject thiz, jobject weakThiz, jint width, jint height, - jint format, jint maxImages, jlong ndkUsage) -{ + jint maxImages, jlong ndkUsage, jint nativeFormat, jlong dataSpace) { status_t res; - int nativeFormat; - android_dataspace nativeDataspace; - ALOGV("%s: width:%d, height: %d, format: 0x%x, maxImages:%d", - __FUNCTION__, width, height, format, maxImages); + ALOGV("%s: width:%d, height: %d, nativeFormat: %d, maxImages:%d", + __FUNCTION__, width, height, nativeFormat, maxImages); - PublicFormat publicFormat = static_cast<PublicFormat>(format); - nativeFormat = mapPublicFormatToHalFormat(publicFormat); - nativeDataspace = mapPublicFormatToHalDataspace(publicFormat); + android_dataspace nativeDataspace = static_cast<android_dataspace>(dataSpace); jclass clazz = env->GetObjectClass(thiz); if (clazz == NULL) { @@ -400,7 +395,7 @@ static void ImageReader_init(JNIEnv* env, jobject thiz, jobject weakThiz, jint w BufferQueue::createBufferQueue(&gbProducer, &gbConsumer); sp<BufferItemConsumer> bufferConsumer; String8 consumerName = String8::format("ImageReader-%dx%df%xm%d-%d-%d", - width, height, format, maxImages, getpid(), + width, height, nativeFormat, maxImages, getpid(), createProcessUniqueId()); uint64_t consumerUsage = android_hardware_HardwareBuffer_convertToGrallocUsageBits(ndkUsage); @@ -527,7 +522,8 @@ static void ImageReader_imageRelease(JNIEnv* env, jobject thiz, jobject image) ALOGV("%s: Image (format: 0x%x) has been released", __FUNCTION__, ctx->getBufferFormat()); } -static jint ImageReader_imageSetup(JNIEnv* env, jobject thiz, jobject image) { +static jint ImageReader_imageSetup(JNIEnv* env, jobject thiz, jobject image, + jboolean legacyValidateImageFormat) { ALOGV("%s:", __FUNCTION__); JNIImageReaderContext* ctx = ImageReader_getContext(env, thiz); if (ctx == NULL) { @@ -590,7 +586,7 @@ static jint ImageReader_imageSetup(JNIEnv* env, jobject thiz, jobject image) { ALOGV("%s: Producer buffer size: %dx%d, doesn't match ImageReader configured size: %dx%d", __FUNCTION__, outputWidth, outputHeight, imageReaderWidth, imageReaderHeight); } - if (imgReaderFmt != bufferFormat) { + if (legacyValidateImageFormat && imgReaderFmt != bufferFormat) { if (imgReaderFmt == HAL_PIXEL_FORMAT_YCbCr_420_888 && isPossiblyYUV(bufferFormat)) { // Treat formats that are compatible with flexible YUV @@ -958,10 +954,10 @@ static jobject Image_getHardwareBuffer(JNIEnv* env, jobject thiz) { static const JNINativeMethod gImageReaderMethods[] = { {"nativeClassInit", "()V", (void*)ImageReader_classInit }, - {"nativeInit", "(Ljava/lang/Object;IIIIJ)V", (void*)ImageReader_init }, + {"nativeInit", "(Ljava/lang/Object;IIIJIJ)V", (void*)ImageReader_init }, {"nativeClose", "()V", (void*)ImageReader_close }, {"nativeReleaseImage", "(Landroid/media/Image;)V", (void*)ImageReader_imageRelease }, - {"nativeImageSetup", "(Landroid/media/Image;)I", (void*)ImageReader_imageSetup }, + {"nativeImageSetup", "(Landroid/media/Image;Z)I", (void*)ImageReader_imageSetup }, {"nativeGetSurface", "()Landroid/view/Surface;", (void*)ImageReader_getSurface }, {"nativeDetachImage", "(Landroid/media/Image;)I", (void*)ImageReader_detachImage }, {"nativeCreateImagePlanes", diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp index 8dcdc989ec8f..a548a472fc3a 100644 --- a/media/jni/android_media_MediaPlayer.cpp +++ b/media/jni/android_media_MediaPlayer.cpp @@ -1454,6 +1454,7 @@ extern int register_android_media_MediaMetadataRetriever(JNIEnv *env); extern int register_android_media_MediaMuxer(JNIEnv *env); extern int register_android_media_MediaRecorder(JNIEnv *env); extern int register_android_media_MediaSync(JNIEnv *env); +extern int register_android_media_PublicFormatUtils(JNIEnv *env); extern int register_android_media_ResampleInputStream(JNIEnv *env); extern int register_android_media_MediaProfiles(JNIEnv *env); extern int register_android_mtp_MtpDatabase(JNIEnv *env); @@ -1501,6 +1502,11 @@ jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) goto bail; } + if (register_android_media_PublicFormatUtils(env) < 0) { + ALOGE("ERROR: PublicFormatUtils native registration failed\n"); + goto bail; + } + if (register_android_media_ResampleInputStream(env) < 0) { ALOGE("ERROR: ResampleInputStream native registration failed\n"); goto bail; diff --git a/media/jni/android_media_PublicFormatUtils.cpp b/media/jni/android_media_PublicFormatUtils.cpp new file mode 100644 index 000000000000..09ebdeeff06f --- /dev/null +++ b/media/jni/android_media_PublicFormatUtils.cpp @@ -0,0 +1,59 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "PublicFormatUtils_JNI" + +#include <utils/misc.h> +#include <ui/PublicFormat.h> +#include <android_runtime/AndroidRuntime.h> +#include <jni.h> + +using namespace android; + +static jint android_media_PublicFormatUtils_getHalFormat(JNIEnv* /*env*/, jobject /*thiz*/, + jint imageFormat) { + PublicFormat publicFormat = static_cast<PublicFormat>(imageFormat); + int nativeFormat = mapPublicFormatToHalFormat(publicFormat); + return static_cast<jint>(nativeFormat); +} + +static jlong android_media_PublicFormatUtils_getHalDataspace(JNIEnv* /*env*/, jobject /*thiz*/, + jint imageFormat) { + PublicFormat publicFormat = static_cast<PublicFormat>(imageFormat); + android_dataspace + nativeDataspace = mapPublicFormatToHalDataspace(publicFormat); + return static_cast<jlong>(nativeDataspace); +} + +static jint android_media_PublicFormatUtils_getPublicFormat(JNIEnv* /*env*/, jobject /*thiz*/, + jint hardwareBufferFormat, + jlong dataspace) { + PublicFormat nativeFormat = mapHalFormatDataspaceToPublicFormat( + hardwareBufferFormat, static_cast<android_dataspace>(dataspace)); + return static_cast<jint>(nativeFormat); +} + +static const JNINativeMethod gMethods[] = { + {"nativeGetHalFormat", "(I)I", (void*)android_media_PublicFormatUtils_getHalFormat}, + {"nativeGetHalDataspace", "(I)J", (void*)android_media_PublicFormatUtils_getHalDataspace}, + {"nativeGetPublicFormat", "(IJ)I",(void*)android_media_PublicFormatUtils_getPublicFormat} +}; + +int register_android_media_PublicFormatUtils(JNIEnv *env) { + return AndroidRuntime::registerNativeMethods(env, + "android/media/PublicFormatUtils", gMethods, NELEM(gMethods)); +} + diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index c601649b74a1..1b41494814b7 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -461,6 +461,7 @@ MediaEvent::MediaEvent(sp<FilterClient> filterClient, native_handle_t *avHandle, } MediaEvent::~MediaEvent() { + android::Mutex::Autolock autoLock(mLock); JNIEnv *env = AndroidRuntime::getJNIEnv(); env->DeleteWeakGlobalRef(mMediaEventObj); mMediaEventObj = nullptr; @@ -977,7 +978,8 @@ FrontendClientCallbackImpl::FrontendClientCallbackImpl(JTuner* jtuner, jweak lis void FrontendClientCallbackImpl::addCallbackListener(JTuner* jtuner, jweak listener) { JNIEnv *env = AndroidRuntime::getJNIEnv(); jweak listenerRef = env->NewWeakGlobalRef(listener); - ALOGV("addCallbackListener() with listener:%p and ref:%p @%p", listener, listenerRef, this); + ALOGV("addCallbackListener() with listener:%p and ref:%p @%p", + listener, listenerRef, this); std::scoped_lock<std::mutex> lock(mMutex); mListenersMap[jtuner] = listenerRef; } @@ -1344,18 +1346,43 @@ int JTuner::shareFrontend(int feId) { return (int)Result::SUCCESS; } +int JTuner::unshareFrontend() { + if (mFeClient != nullptr) { + ALOGE("Cannot unshare frontend because this session is already holding %d" + " as an owner instead of as a sharee", mFeClient->getId()); + return (int)Result::INVALID_STATE; + } + + mSharedFeId = (int)Constant::INVALID_FRONTEND_ID; + return (int)Result::SUCCESS; +} + void JTuner::registerFeCbListener(JTuner* jtuner) { + ALOGV("registerFeCbListener: %p", jtuner); if (mFeClientCb != nullptr && jtuner != nullptr) { mFeClientCb->addCallbackListener(jtuner, jtuner->getObject()); } } void JTuner::unregisterFeCbListener(JTuner* jtuner) { + ALOGV("unregisterFeCbListener: %p", jtuner); if (mFeClientCb != nullptr && jtuner != nullptr) { mFeClientCb->removeCallbackListener(jtuner); } } +void JTuner::updateFrontend(JTuner* jtuner) { + if (jtuner == nullptr) { + ALOGV("JTuner::updateFrontend(null) called for previous owner: %p", this); + mFeClient = nullptr; + mFeClientCb = nullptr; + } else { + ALOGV("JTuner::updateFrontend(%p) called for new owner: %p", jtuner, this); + mFeClient = jtuner->mFeClient; + mFeClientCb = jtuner->mFeClientCb; + } +} + jobject JTuner::getAnalogFrontendCaps(JNIEnv *env, FrontendCapabilities &caps) { jclass clazz = env->FindClass("android/media/tv/tuner/frontend/AnalogFrontendCapabilities"); jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(II)V"); @@ -3318,6 +3345,12 @@ static int android_media_tv_Tuner_share_frontend( return tuner->shareFrontend(id); } +static int android_media_tv_Tuner_unshare_frontend( + JNIEnv *env, jobject thiz) { + sp<JTuner> tuner = getTuner(env, thiz); + return tuner->unshareFrontend(); +} + static void android_media_tv_Tuner_register_fe_cb_listener( JNIEnv *env, jobject thiz, jlong shareeJTuner) { sp<JTuner> tuner = getTuner(env, thiz); @@ -3332,6 +3365,17 @@ static void android_media_tv_Tuner_unregister_fe_cb_listener( tuner->unregisterFeCbListener(jtuner); } +static void android_media_tv_Tuner_update_frontend(JNIEnv *env, jobject thiz, jlong jtunerPtr) { + sp<JTuner> tuner = getTuner(env, thiz); + JTuner *jtuner; + if (jtunerPtr == 0) { + jtuner = nullptr; + } else { + jtuner = (JTuner *) jtunerPtr; + } + tuner->updateFrontend(jtuner); +} + static int android_media_tv_Tuner_tune(JNIEnv *env, jobject thiz, jint type, jobject settings) { sp<JTuner> tuner = getTuner(env, thiz); FrontendSettings setting = getFrontendSettings(env, type, settings); @@ -3515,11 +3559,14 @@ static DemuxFilterSectionSettings getFilterSectionSettings(JNIEnv *env, const jo bool isCheckCrc = env->GetBooleanField(settings, env->GetFieldID(clazz, "mCrcEnabled", "Z")); bool isRepeat = env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsRepeat", "Z")); bool isRaw = env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsRaw", "Z")); + int32_t bitWidthOfLengthField = + env->GetIntField(settings, env->GetFieldID(clazz, "mBitWidthOfLengthField", "I")); DemuxFilterSectionSettings filterSectionSettings { .isCheckCrc = isCheckCrc, .isRepeat = isRepeat, .isRaw = isRaw, + .bitWidthOfLengthField = bitWidthOfLengthField, }; if (env->IsInstanceOf( settings, @@ -4570,10 +4617,14 @@ static const JNINativeMethod gTunerMethods[] = { (void *)android_media_tv_Tuner_open_frontend_by_handle }, { "nativeShareFrontend", "(I)I", (void *)android_media_tv_Tuner_share_frontend }, + { "nativeUnshareFrontend", "()I", + (void *)android_media_tv_Tuner_unshare_frontend }, { "nativeRegisterFeCbListener", "(J)V", (void*)android_media_tv_Tuner_register_fe_cb_listener }, { "nativeUnregisterFeCbListener", "(J)V", (void*)android_media_tv_Tuner_unregister_fe_cb_listener }, + { "nativeUpdateFrontend", "(J)V", + (void*)android_media_tv_Tuner_update_frontend }, { "nativeTune", "(ILandroid/media/tv/tuner/frontend/FrontendSettings;)I", (void *)android_media_tv_Tuner_tune }, { "nativeStopTune", "()I", (void *)android_media_tv_Tuner_stop_tune }, diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h index f1b31e3520b1..502bd6b18413 100644 --- a/media/jni/android_media_tv_Tuner.h +++ b/media/jni/android_media_tv_Tuner.h @@ -178,8 +178,10 @@ struct JTuner : public RefBase { jobject getFrontendIds(); jobject openFrontendByHandle(int feHandle); int shareFrontend(int feId); + int unshareFrontend(); void registerFeCbListener(JTuner* jtuner); void unregisterFeCbListener(JTuner* jtuner); + void updateFrontend(JTuner* jtuner); jint closeFrontendById(int id); jobject getFrontendInfo(int id); int tune(const FrontendSettings& settings); diff --git a/media/jni/soundpool/Stream.cpp b/media/jni/soundpool/Stream.cpp index 773cdc982af7..50bb79ccaa0b 100644 --- a/media/jni/soundpool/Stream.cpp +++ b/media/jni/soundpool/Stream.cpp @@ -275,118 +275,104 @@ void Stream::play_l(const std::shared_ptr<Sound>& sound, int32_t nextStreamID, float leftVolume, float rightVolume, int32_t priority, int32_t loop, float rate, std::vector<std::any>& garbage) { - // oldTrack and newTrack are placeholders to be released by garbage without the lock. - sp<AudioTrack> oldTrack; - sp<AudioTrack> newTrack; - status_t status = NO_ERROR; - - { - ALOGV("%s(%p)(soundID=%d, streamID=%d, leftVolume=%f, rightVolume=%f," - " priority=%d, loop=%d, rate=%f)", - __func__, this, sound->getSoundID(), nextStreamID, leftVolume, rightVolume, - priority, loop, rate); - - // initialize track - const audio_stream_type_t streamType = - AudioSystem::attributesToStreamType(*mStreamManager->getAttributes()); - const int32_t channelCount = sound->getChannelCount(); - const auto sampleRate = (uint32_t)lround(double(sound->getSampleRate()) * rate); - size_t frameCount = 0; - - if (loop) { - const audio_format_t format = sound->getFormat(); - const size_t frameSize = audio_is_linear_pcm(format) - ? channelCount * audio_bytes_per_sample(format) : 1; - frameCount = sound->getSizeInBytes() / frameSize; - } + ALOGV("%s(%p)(soundID=%d, streamID=%d, leftVolume=%f, rightVolume=%f," + " priority=%d, loop=%d, rate=%f)", + __func__, this, sound->getSoundID(), nextStreamID, leftVolume, rightVolume, + priority, loop, rate); + + // initialize track + const audio_stream_type_t streamType = + AudioSystem::attributesToStreamType(*mStreamManager->getAttributes()); + const int32_t channelCount = sound->getChannelCount(); + const auto sampleRate = (uint32_t)lround(double(sound->getSampleRate()) * rate); + size_t frameCount = 0; + + if (loop) { + const audio_format_t format = sound->getFormat(); + const size_t frameSize = audio_is_linear_pcm(format) + ? channelCount * audio_bytes_per_sample(format) : 1; + frameCount = sound->getSizeInBytes() / frameSize; + } - // check if the existing track has the same sound id. - if (mAudioTrack != nullptr && mSoundID == sound->getSoundID()) { + if (mAudioTrack != nullptr) { + if (mSoundID == sound->getSoundID() + && mAudioTrack->setSampleRate(sampleRate) == NO_ERROR) { + // Reuse the old track if the soundID matches. // the sample rate may fail to change if the audio track is a fast track. - if (mAudioTrack->setSampleRate(sampleRate) == NO_ERROR) { - newTrack = mAudioTrack; - ALOGV("%s: reusing track %p for sound %d", - __func__, mAudioTrack.get(), sound->getSoundID()); - } - } - if (newTrack == nullptr) { - // mToggle toggles each time a track is started on a given stream. - // The toggle is concatenated with the Stream address and passed to AudioTrack - // as callback user data. This enables the detection of callbacks received from the old - // audio track while the new one is being started and avoids processing them with - // wrong audio audio buffer size (mAudioBufferSize) - auto toggle = mToggle ^ 1; - // NOLINTNEXTLINE(performance-no-int-to-ptr) - void* userData = reinterpret_cast<void*>((uintptr_t)this | toggle); - audio_channel_mask_t soundChannelMask = sound->getChannelMask(); - // When sound contains a valid channel mask, use it as is. - // Otherwise, use stream count to calculate channel mask. - audio_channel_mask_t channelMask = soundChannelMask != AUDIO_CHANNEL_NONE - ? soundChannelMask : audio_channel_out_mask_from_count(channelCount); - - // do not create a new audio track if current track is compatible with sound parameters - - android::content::AttributionSourceState attributionSource; - attributionSource.packageName = mStreamManager->getOpPackageName(); - attributionSource.token = sp<BBinder>::make(); - // TODO b/182469354 make consistent with AudioRecord, add util for native source - newTrack = new AudioTrack(streamType, sampleRate, sound->getFormat(), - channelMask, sound->getIMemory(), AUDIO_OUTPUT_FLAG_FAST, - staticCallback, userData, - 0 /*default notification frames*/, AUDIO_SESSION_ALLOCATE, - AudioTrack::TRANSFER_DEFAULT, - nullptr /*offloadInfo*/, attributionSource, - mStreamManager->getAttributes(), - false /*doNotReconnect*/, 1.0f /*maxRequiredSpeed*/); - // Set caller name so it can be logged in destructor. - // MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_SOUNDPOOL - newTrack->setCallerName("soundpool"); - oldTrack = mAudioTrack; - status = newTrack->initCheck(); - if (status != NO_ERROR) { - ALOGE("%s: error creating AudioTrack", __func__); - // newTrack goes out of scope, so reference count drops to zero - goto exit; - } - // From now on, AudioTrack callbacks received with previous toggle value will be ignored. - mToggle = toggle; - mAudioTrack = newTrack; - ALOGV("%s: using new track %p for sound %d", - __func__, newTrack.get(), sound->getSoundID()); - } - if (mMuted) { - newTrack->setVolume(0.0f, 0.0f); + ALOGV("%s: reusing track %p for sound %d", + __func__, mAudioTrack.get(), sound->getSoundID()); } else { - newTrack->setVolume(leftVolume, rightVolume); + // If reuse not possible, move mAudioTrack to garbage, set to nullptr. + garbage.emplace_back(std::move(mAudioTrack)); + mAudioTrack.clear(); // move should have cleared the sp<>, but we clear just in case. } - newTrack->setLoop(0, frameCount, loop); - mAudioTrack->start(); - mSound = sound; - mSoundID = sound->getSoundID(); - mPriority = priority; - mLoop = loop; - mLeftVolume = leftVolume; - mRightVolume = rightVolume; - mRate = rate; - mState = PLAYING; - mStopTimeNs = 0; - mStreamID = nextStreamID; // prefer this to be the last, as it is an atomic sync point } - -exit: - ALOGV("%s: delete oldTrack %p", __func__, oldTrack.get()); - if (status != NO_ERROR) { - // TODO: should we consider keeping the soundID if the old track is OK? - // Do not attempt to restart this track (should we remove the stream id?) - mState = IDLE; - mSoundID = 0; - mSound.reset(); - mAudioTrack.clear(); // actual release from garbage + if (mAudioTrack == nullptr) { + // mToggle toggles each time a track is started on a given stream. + // The toggle is concatenated with the Stream address and passed to AudioTrack + // as callback user data. This enables the detection of callbacks received from the old + // audio track while the new one is being started and avoids processing them with + // wrong audio audio buffer size (mAudioBufferSize) + auto toggle = mToggle ^ 1; + // NOLINTNEXTLINE(performance-no-int-to-ptr) + void* userData = reinterpret_cast<void*>((uintptr_t)this | toggle); + audio_channel_mask_t soundChannelMask = sound->getChannelMask(); + // When sound contains a valid channel mask, use it as is. + // Otherwise, use stream count to calculate channel mask. + audio_channel_mask_t channelMask = soundChannelMask != AUDIO_CHANNEL_NONE + ? soundChannelMask : audio_channel_out_mask_from_count(channelCount); + + // do not create a new audio track if current track is compatible with sound parameters + + android::content::AttributionSourceState attributionSource; + attributionSource.packageName = mStreamManager->getOpPackageName(); + attributionSource.token = sp<BBinder>::make(); + // TODO b/182469354 make consistent with AudioRecord, add util for native source + mAudioTrack = new AudioTrack(streamType, sampleRate, sound->getFormat(), + channelMask, sound->getIMemory(), AUDIO_OUTPUT_FLAG_FAST, + staticCallback, userData, + 0 /*default notification frames*/, AUDIO_SESSION_ALLOCATE, + AudioTrack::TRANSFER_DEFAULT, + nullptr /*offloadInfo*/, attributionSource, + mStreamManager->getAttributes(), + false /*doNotReconnect*/, 1.0f /*maxRequiredSpeed*/); + // Set caller name so it can be logged in destructor. + // MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_SOUNDPOOL + mAudioTrack->setCallerName("soundpool"); + + if (status_t status = mAudioTrack->initCheck(); + status != NO_ERROR) { + ALOGE("%s: error %d creating AudioTrack", __func__, status); + // TODO: should we consider keeping the soundID and reusing the old track? + mState = IDLE; + mSoundID = 0; + mSound.reset(); + garbage.emplace_back(std::move(mAudioTrack)); // remove mAudioTrack. + mAudioTrack.clear(); // move should have cleared the sp<>, but we clear just in case. + return; + } + // From now on, AudioTrack callbacks received with previous toggle value will be ignored. + mToggle = toggle; + ALOGV("%s: using new track %p for sound %d", + __func__, mAudioTrack.get(), sound->getSoundID()); } - - // move tracks to garbage to be released later outside of lock. - if (newTrack) garbage.emplace_back(std::move(newTrack)); - if (oldTrack) garbage.emplace_back(std::move(oldTrack)); + if (mMuted) { + mAudioTrack->setVolume(0.f, 0.f); + } else { + mAudioTrack->setVolume(leftVolume, rightVolume); + } + mAudioTrack->setLoop(0, frameCount, loop); + mAudioTrack->start(); + mSound = sound; + mSoundID = sound->getSoundID(); + mPriority = priority; + mLoop = loop; + mLeftVolume = leftVolume; + mRightVolume = rightVolume; + mRate = rate; + mState = PLAYING; + mStopTimeNs = 0; + mStreamID = nextStreamID; // prefer this to be the last, as it is an atomic sync point } /* static */ diff --git a/media/jni/tuner/FilterClient.cpp b/media/jni/tuner/FilterClient.cpp index 8568383385ac..959e756b8a16 100644 --- a/media/jni/tuner/FilterClient.cpp +++ b/media/jni/tuner/FilterClient.cpp @@ -43,6 +43,7 @@ FilterClient::FilterClient(DemuxFilterType type, shared_ptr<ITunerFilter> tunerF } FilterClient::~FilterClient() { + Mutex::Autolock _l(mLock); mTunerFilter = nullptr; mAvSharedHandle = nullptr; mAvSharedMemSize = 0; @@ -74,6 +75,7 @@ Result FilterClient::configure(DemuxFilterSettings configure) { Result res; checkIsPassthroughFilter(configure); + Mutex::Autolock _l(mLock); if (mTunerFilter != nullptr) { Status s = mTunerFilter->configure(configure); res = ClientHelper::getServiceSpecificErrorCode(s); @@ -87,6 +89,7 @@ Result FilterClient::configure(DemuxFilterSettings configure) { } Result FilterClient::configureMonitorEvent(int32_t monitorEventType) { + Mutex::Autolock _l(mLock); if (mTunerFilter != nullptr) { Status s = mTunerFilter->configureMonitorEvent(monitorEventType); return ClientHelper::getServiceSpecificErrorCode(s); @@ -96,6 +99,7 @@ Result FilterClient::configureMonitorEvent(int32_t monitorEventType) { } Result FilterClient::configureIpFilterContextId(int32_t cid) { + Mutex::Autolock _l(mLock); if (mTunerFilter != nullptr) { Status s = mTunerFilter->configureIpFilterContextId(cid); return ClientHelper::getServiceSpecificErrorCode(s); @@ -105,6 +109,7 @@ Result FilterClient::configureIpFilterContextId(int32_t cid) { } Result FilterClient::configureAvStreamType(AvStreamType avStreamType) { + Mutex::Autolock _l(mLock); if (mTunerFilter != nullptr) { Status s = mTunerFilter->configureAvStreamType(avStreamType); return ClientHelper::getServiceSpecificErrorCode(s); @@ -114,6 +119,7 @@ Result FilterClient::configureAvStreamType(AvStreamType avStreamType) { } Result FilterClient::start() { + Mutex::Autolock _l(mLock); if (mTunerFilter != nullptr) { Status s = mTunerFilter->start(); return ClientHelper::getServiceSpecificErrorCode(s); @@ -123,6 +129,7 @@ Result FilterClient::start() { } Result FilterClient::stop() { + Mutex::Autolock _l(mLock); if (mTunerFilter != nullptr) { Status s = mTunerFilter->stop(); return ClientHelper::getServiceSpecificErrorCode(s); @@ -132,6 +139,7 @@ Result FilterClient::stop() { } Result FilterClient::flush() { + Mutex::Autolock _l(mLock); if (mTunerFilter != nullptr) { Status s = mTunerFilter->flush(); return ClientHelper::getServiceSpecificErrorCode(s); @@ -141,6 +149,7 @@ Result FilterClient::flush() { } Result FilterClient::getId(int32_t& id) { + Mutex::Autolock _l(mLock); if (mTunerFilter != nullptr) { Status s = mTunerFilter->getId(&id); return ClientHelper::getServiceSpecificErrorCode(s); @@ -150,6 +159,7 @@ Result FilterClient::getId(int32_t& id) { } Result FilterClient::getId64Bit(int64_t& id) { + Mutex::Autolock _l(mLock); if (mTunerFilter != nullptr) { Status s = mTunerFilter->getId64Bit(&id); return ClientHelper::getServiceSpecificErrorCode(s); @@ -159,6 +169,7 @@ Result FilterClient::getId64Bit(int64_t& id) { } Result FilterClient::releaseAvHandle(native_handle_t* handle, uint64_t avDataId) { + Mutex::Autolock _l(mLock); if (mTunerFilter != nullptr) { Status s = mTunerFilter->releaseAvHandle(dupToAidl(handle), avDataId); return ClientHelper::getServiceSpecificErrorCode(s); @@ -168,6 +179,7 @@ Result FilterClient::releaseAvHandle(native_handle_t* handle, uint64_t avDataId) } Result FilterClient::setDataSource(sp<FilterClient> filterClient){ + Mutex::Autolock _l(mLock); if (mTunerFilter != nullptr) { Status s = mTunerFilter->setDataSource(filterClient->getAidlFilter()); return ClientHelper::getServiceSpecificErrorCode(s); @@ -177,6 +189,7 @@ Result FilterClient::setDataSource(sp<FilterClient> filterClient){ } Result FilterClient::close() { + Mutex::Autolock _l(mLock); if (mFilterMQEventFlag != nullptr) { EventFlag::deleteEventFlag(&mFilterMQEventFlag); mFilterMQEventFlag = nullptr; @@ -197,6 +210,7 @@ Result FilterClient::close() { } string FilterClient::acquireSharedFilterToken() { + Mutex::Autolock _l(mLock); if (mTunerFilter != nullptr) { string filterToken; if (mTunerFilter->acquireSharedFilterToken(&filterToken).isOk()) { @@ -208,6 +222,7 @@ string FilterClient::acquireSharedFilterToken() { } Result FilterClient::freeSharedFilterToken(const string& filterToken) { + Mutex::Autolock _l(mLock); if (mTunerFilter != nullptr) { Status s = mTunerFilter->freeSharedFilterToken(filterToken); return ClientHelper::getServiceSpecificErrorCode(s); @@ -237,6 +252,7 @@ Status TunerFilterCallback::onFilterEvent(const vector<DemuxFilterEvent>& filter } Result FilterClient::getFilterMq() { + Mutex::Autolock _l(mLock); if (mFilterMQ != nullptr) { return Result::SUCCESS; } diff --git a/media/jni/tuner/FilterClient.h b/media/jni/tuner/FilterClient.h index 20e56102a909..9e9b2332ac33 100644 --- a/media/jni/tuner/FilterClient.h +++ b/media/jni/tuner/FilterClient.h @@ -21,6 +21,7 @@ #include <aidl/android/media/tv/tuner/BnTunerFilterCallback.h> #include <aidl/android/media/tv/tuner/ITunerFilter.h> #include <fmq/AidlMessageQueue.h> +#include <utils/Mutex.h> #include "ClientHelper.h" #include "FilterClientCallback.h" @@ -37,6 +38,7 @@ using ::aidl::android::hardware::tv::tuner::FilterDelayHint; using ::aidl::android::media::tv::tuner::BnTunerFilterCallback; using ::aidl::android::media::tv::tuner::ITunerFilter; using ::android::hardware::EventFlag; +using ::android::Mutex; using namespace std; @@ -179,6 +181,7 @@ private: uint64_t mAvSharedMemSize; bool mIsMediaFilter; bool mIsPassthroughFilter; + Mutex mLock; }; } // namespace android diff --git a/native/android/libandroid_net.map.txt b/native/android/libandroid_net.map.txt index a6c1b5098066..32fd734d61a0 100644 --- a/native/android/libandroid_net.map.txt +++ b/native/android/libandroid_net.map.txt @@ -18,6 +18,10 @@ LIBANDROID_NET { android_getprocnetwork; # llndk android_setprocdns; # llndk android_getprocdns; # llndk + # These functions have been part of the NDK since API 33. + android_tag_socket_with_uid; # llndk + android_tag_socket; # llndk + android_untag_socket; # llndk local: *; }; diff --git a/native/android/net.c b/native/android/net.c index e2f36a77b7c6..d7c22e1a5741 100644 --- a/native/android/net.c +++ b/native/android/net.c @@ -161,3 +161,15 @@ int android_res_nsend(net_handle_t network, const uint8_t *msg, size_t msglen, void android_res_cancel(int nsend_fd) { resNetworkCancel(nsend_fd); } + +int android_tag_socket_with_uid(int sockfd, int tag, uid_t uid) { + return tagSocket(sockfd, tag, uid); +} + +int android_tag_socket(int sockfd, int tag) { + return tagSocket(sockfd, tag, -1); +} + +int android_untag_socket(int sockfd) { + return untagSocket(sockfd); +} diff --git a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java index a316b8a617b7..583f7ba8cde5 100644 --- a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java +++ b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java @@ -54,6 +54,7 @@ import com.android.net.module.util.NetworkIdentityUtils; import java.util.List; import java.util.Objects; +import java.util.Set; /** * Provides access to network usage history and statistics. Usage data is collected in @@ -535,6 +536,31 @@ public class NetworkStatsManager { return result; } + /** + * Query realtime network usage statistics details with interfaces constrains. + * Return snapshot of current UID statistics, including any {@link TrafficStats#UID_TETHERING}, + * video calling data usage and count of network operations that set by + * {@link TrafficStats#incrementOperationCount}. The returned data doesn't include any + * statistics that is reported by {@link NetworkStatsProvider}. + * + * @param requiredIfaces A list of interfaces the stats should be restricted to, or + * {@link NetworkStats#INTERFACES_ALL}. + * + * @hide + */ + //@SystemApi + @RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) + @NonNull public android.net.NetworkStats getDetailedUidStats( + @NonNull Set<String> requiredIfaces) { + Objects.requireNonNull(requiredIfaces, "requiredIfaces cannot be null"); + try { + return mService.getDetailedUidStats(requiredIfaces.toArray(new String[0])); + } catch (RemoteException e) { + if (DBG) Log.d(TAG, "Remote exception when get detailed uid stats"); + throw e.rethrowFromSystemServer(); + } + } + /** @hide */ public void registerUsageCallback(NetworkTemplate template, int networkType, long thresholdBytes, UsageCallback callback, @Nullable Handler handler) { diff --git a/packages/ConnectivityT/framework-t/src/android/net/DataUsageRequest.java b/packages/ConnectivityT/framework-t/src/android/net/DataUsageRequest.java index f0ff46522d15..b06d515b3acf 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/DataUsageRequest.java +++ b/packages/ConnectivityT/framework-t/src/android/net/DataUsageRequest.java @@ -75,7 +75,7 @@ public final class DataUsageRequest implements Parcelable { @Override public DataUsageRequest createFromParcel(Parcel in) { int requestId = in.readInt(); - NetworkTemplate template = in.readParcelable(null, android.net.NetworkTemplate.class); + NetworkTemplate template = in.readParcelable(null); long thresholdInBytes = in.readLong(); DataUsageRequest result = new DataUsageRequest(requestId, template, thresholdInBytes); diff --git a/packages/ConnectivityT/framework-t/src/android/net/IpSecConfig.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecConfig.java index 03bb187f119f..575c5ed968f8 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/IpSecConfig.java +++ b/packages/ConnectivityT/framework-t/src/android/net/IpSecConfig.java @@ -267,14 +267,14 @@ public final class IpSecConfig implements Parcelable { mMode = in.readInt(); mSourceAddress = in.readString(); mDestinationAddress = in.readString(); - mNetwork = (Network) in.readParcelable(Network.class.getClassLoader(), android.net.Network.class); + mNetwork = (Network) in.readParcelable(Network.class.getClassLoader()); mSpiResourceId = in.readInt(); mEncryption = - (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader(), android.net.IpSecAlgorithm.class); + (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader()); mAuthentication = - (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader(), android.net.IpSecAlgorithm.class); + (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader()); mAuthenticatedEncryption = - (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader(), android.net.IpSecAlgorithm.class); + (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader()); mEncapType = in.readInt(); mEncapSocketResourceId = in.readInt(); mEncapRemotePort = in.readInt(); diff --git a/packages/ConnectivityT/framework-t/src/android/net/IpSecUdpEncapResponse.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecUdpEncapResponse.java index 390af8236696..732cf198a9cc 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/IpSecUdpEncapResponse.java +++ b/packages/ConnectivityT/framework-t/src/android/net/IpSecUdpEncapResponse.java @@ -81,7 +81,7 @@ public final class IpSecUdpEncapResponse implements Parcelable { status = in.readInt(); resourceId = in.readInt(); port = in.readInt(); - fileDescriptor = in.readParcelable(ParcelFileDescriptor.class.getClassLoader(), android.os.ParcelFileDescriptor.class); + fileDescriptor = in.readParcelable(ParcelFileDescriptor.class.getClassLoader()); } @android.annotation.NonNull diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.java index d577aa8fba54..39156343924d 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.java +++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.java @@ -73,9 +73,9 @@ public final class NetworkStateSnapshot implements Parcelable { /** @hide */ public NetworkStateSnapshot(@NonNull Parcel in) { - mNetwork = in.readParcelable(null, android.net.Network.class); - mNetworkCapabilities = in.readParcelable(null, android.net.NetworkCapabilities.class); - mLinkProperties = in.readParcelable(null, android.net.LinkProperties.class); + mNetwork = in.readParcelable(null); + mNetworkCapabilities = in.readParcelable(null); + mLinkProperties = in.readParcelable(null); mSubscriberId = in.readString(); mLegacyType = in.readInt(); } diff --git a/packages/ConnectivityT/framework-t/src/android/net/UnderlyingNetworkInfo.java b/packages/ConnectivityT/framework-t/src/android/net/UnderlyingNetworkInfo.java index 7ab53b1da856..33f9375c03bf 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/UnderlyingNetworkInfo.java +++ b/packages/ConnectivityT/framework-t/src/android/net/UnderlyingNetworkInfo.java @@ -60,7 +60,7 @@ public final class UnderlyingNetworkInfo implements Parcelable { mOwnerUid = in.readInt(); mIface = in.readString(); List<String> underlyingIfaces = new ArrayList<>(); - in.readList(underlyingIfaces, null /*classLoader*/, java.lang.String.class); + in.readList(underlyingIfaces, null /*classLoader*/); mUnderlyingIfaces = Collections.unmodifiableList(underlyingIfaces); } diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java index 97281ed42452..74f31563055d 100644 --- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java +++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java @@ -44,6 +44,7 @@ import static android.net.NetworkTemplate.buildTemplateMobileWildcard; import static android.net.NetworkTemplate.buildTemplateWifiWildcard; import static android.net.TrafficStats.KB_IN_BYTES; import static android.net.TrafficStats.MB_IN_BYTES; +import static android.net.TrafficStats.UID_TETHERING; import static android.net.TrafficStats.UNSUPPORTED; import static android.os.Trace.TRACE_TAG_NETWORK; import static android.provider.Settings.Global.NETSTATS_AUGMENT_ENABLED; @@ -70,7 +71,7 @@ import static android.text.format.DateUtils.MINUTE_IN_MILLIS; import static android.text.format.DateUtils.SECOND_IN_MILLIS; import static com.android.net.module.util.NetworkCapabilitiesUtils.getDisplayTransport; -import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT; +import static com.android.net.module.util.NetworkStatsUtils.LIMIT_GLOBAL_ALERT; import static com.android.server.NetworkManagementSocketTagger.resetKernelUidStats; import static com.android.server.NetworkManagementSocketTagger.setKernelCounterSet; @@ -89,7 +90,6 @@ import android.content.pm.PackageManager; import android.database.ContentObserver; import android.net.DataUsageRequest; import android.net.INetd; -import android.net.INetworkManagementEventObserver; import android.net.INetworkStatsService; import android.net.INetworkStatsSession; import android.net.Network; @@ -106,6 +106,7 @@ import android.net.NetworkStatsCollection; import android.net.NetworkStatsHistory; import android.net.NetworkTemplate; import android.net.TelephonyNetworkSpecifier; +import android.net.TetherStatsParcel; import android.net.TetheringManager; import android.net.TrafficStats; import android.net.UnderlyingNetworkInfo; @@ -120,12 +121,12 @@ import android.os.Handler; import android.os.HandlerExecutor; import android.os.HandlerThread; import android.os.IBinder; -import android.os.INetworkManagementService; import android.os.Looper; import android.os.Message; import android.os.Messenger; import android.os.PowerManager; import android.os.RemoteException; +import android.os.ServiceSpecificException; import android.os.SystemClock; import android.os.Trace; import android.os.UserHandle; @@ -148,6 +149,7 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FileRotator; +import com.android.net.module.util.BaseNetdUnsolicitedEventListener; import com.android.net.module.util.BestClock; import com.android.net.module.util.BinderUtils; import com.android.net.module.util.CollectionUtils; @@ -208,7 +210,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private static final String TAG_NETSTATS_ERROR = "netstats_error"; private final Context mContext; - private final INetworkManagementService mNetworkManager; private final NetworkStatsFactory mStatsFactory; private final AlarmManager mAlarmManager; private final Clock mClock; @@ -223,6 +224,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private final ContentObserver mContentObserver; private final ContentResolver mContentResolver; + protected INetd mNetd; + private final AlertObserver mAlertObserver = new AlertObserver(); + @VisibleForTesting public static final String ACTION_NETWORK_STATS_POLL = "com.android.server.action.NETWORK_STATS_POLL"; @@ -405,15 +409,16 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } } - public static NetworkStatsService create(Context context, - INetworkManagementService networkManager) { + /** Creates a new NetworkStatsService */ + public static NetworkStatsService create(Context context) { AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); final INetd netd = INetd.Stub.asInterface( (IBinder) context.getSystemService(Context.NETD_SERVICE)); - final NetworkStatsService service = new NetworkStatsService(context, networkManager, + final NetworkStatsService service = new NetworkStatsService(context, + INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE)), alarmManager, wakeLock, getDefaultClock(), new DefaultNetworkStatsSettings(context), new NetworkStatsFactory(netd), new NetworkStatsObservers(), getDefaultSystemDir(), getDefaultBaseDir(), @@ -426,14 +431,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // This must not be called outside of tests, even within the same package, as this constructor // does not register the local service. Use the create() helper above. @VisibleForTesting - NetworkStatsService(Context context, INetworkManagementService networkManager, - AlarmManager alarmManager, PowerManager.WakeLock wakeLock, Clock clock, - NetworkStatsSettings settings, NetworkStatsFactory factory, - NetworkStatsObservers statsObservers, File systemDir, File baseDir, - @NonNull Dependencies deps) { + NetworkStatsService(Context context, INetd netd, AlarmManager alarmManager, + PowerManager.WakeLock wakeLock, Clock clock, NetworkStatsSettings settings, + NetworkStatsFactory factory, NetworkStatsObservers statsObservers, File systemDir, + File baseDir, @NonNull Dependencies deps) { mContext = Objects.requireNonNull(context, "missing Context"); - mNetworkManager = Objects.requireNonNull(networkManager, - "missing INetworkManagementService"); + mNetd = Objects.requireNonNull(netd, "missing Netd"); mAlarmManager = Objects.requireNonNull(alarmManager, "missing AlarmManager"); mClock = Objects.requireNonNull(clock, "missing Clock"); mSettings = Objects.requireNonNull(settings, "missing NetworkStatsSettings"); @@ -506,6 +509,26 @@ public class NetworkStatsService extends INetworkStatsService.Stub { new NetworkStatsManagerInternalImpl()); } + /** + * Observer that watches for {@link INetdUnsolicitedEventListener} alerts. + */ + @VisibleForTesting + public class AlertObserver extends BaseNetdUnsolicitedEventListener { + @Override + public void onQuotaLimitReached(@NonNull String alertName, @NonNull String ifName) { + PermissionUtils.enforceNetworkStackPermission(mContext); + + if (LIMIT_GLOBAL_ALERT.equals(alertName)) { + // kick off background poll to collect network stats unless there is already + // such a call pending; UID stats are handled during normal polling interval. + if (!mHandler.hasMessages(MSG_PERFORM_POLL_REGISTER_ALERT)) { + mHandler.sendEmptyMessageDelayed(MSG_PERFORM_POLL_REGISTER_ALERT, + mSettings.getPollDelay()); + } + } + } + } + public void systemReady() { synchronized (mStatsLock) { mSystemReady = true; @@ -551,9 +574,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mContext.registerReceiver(mShutdownReceiver, shutdownFilter); try { - mNetworkManager.registerObserver(mAlertObserver); - } catch (RemoteException e) { - // ignored; service lives in system_server + mNetd.registerUnsolicitedEventListener(mAlertObserver); + } catch (RemoteException | ServiceSpecificException e) { + Log.wtf(TAG, "Error registering event listener :", e); } // schedule periodic pall alarm based on {@link NetworkStatsSettings#getPollInterval()}. @@ -641,13 +664,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } /** - * Register for a global alert that is delivered through {@link INetworkManagementEventObserver} + * Register for a global alert that is delivered through {@link AlertObserver} * or {@link NetworkStatsProviderCallback#onAlertReached()} once a threshold amount of data has * been transferred. */ private void registerGlobalAlert() { try { - mNetworkManager.setGlobalAlert(mGlobalAlertBytes); + mNetd.bandwidthSetGlobalAlert(mGlobalAlertBytes); } catch (IllegalStateException e) { Log.w(TAG, "problem registering for global alert: " + e); } catch (RemoteException e) { @@ -922,6 +945,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @Override public NetworkStats getDetailedUidStats(String[] requiredIfaces) { + enforceAnyPermissionOf(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK); try { final String[] ifacesToQuery = mStatsFactory.augmentWithStackedInterfaces(requiredIfaces); @@ -1226,26 +1250,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { }; /** - * Observer that watches for {@link INetworkManagementService} alerts. - */ - private final INetworkManagementEventObserver mAlertObserver = new BaseNetworkObserver() { - @Override - public void limitReached(String limitName, String iface) { - // only someone like NMS should be calling us - PermissionUtils.enforceNetworkStackPermission(mContext); - - if (LIMIT_GLOBAL_ALERT.equals(limitName)) { - // kick off background poll to collect network stats unless there is already - // such a call pending; UID stats are handled during normal polling interval. - if (!mHandler.hasMessages(MSG_PERFORM_POLL_REGISTER_ALERT)) { - mHandler.sendEmptyMessageDelayed(MSG_PERFORM_POLL_REGISTER_ALERT, - mSettings.getPollDelay()); - } - } - } - }; - - /** * Handle collapsed RAT type changed event. */ @VisibleForTesting @@ -1956,13 +1960,29 @@ public class NetworkStatsService extends INetworkStatsService.Stub { */ // TODO: Remove this by implementing {@link NetworkStatsProvider} for non-offloaded // tethering stats. - private NetworkStats getNetworkStatsTethering(int how) throws RemoteException { + private @NonNull NetworkStats getNetworkStatsTethering(int how) throws RemoteException { + // We only need to return per-UID stats. Per-device stats are already counted by + // interface counters. + if (how != STATS_PER_UID) { + return new NetworkStats(SystemClock.elapsedRealtime(), 0); + } + + final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1); try { - return mNetworkManager.getNetworkStatsTethering(how); + final TetherStatsParcel[] tetherStatsParcels = mNetd.tetherGetStats(); + for (TetherStatsParcel tetherStats : tetherStatsParcels) { + try { + stats.combineValues(new NetworkStats.Entry(tetherStats.iface, UID_TETHERING, + SET_DEFAULT, TAG_NONE, tetherStats.rxBytes, tetherStats.rxPackets, + tetherStats.txBytes, tetherStats.txPackets, 0L)); + } catch (ArrayIndexOutOfBoundsException e) { + throw new IllegalStateException("invalid tethering stats " + e); + } + } } catch (IllegalStateException e) { Log.wtf(TAG, "problem reading network stats", e); - return new NetworkStats(0L, 10); } + return stats; } // TODO: It is copied from ConnectivityService, consider refactor these check permission @@ -2044,7 +2064,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @NonNull final INetworkStatsProvider mProvider; @NonNull private final Semaphore mSemaphore; - @NonNull final INetworkManagementEventObserver mAlertObserver; + @NonNull final AlertObserver mAlertObserver; @NonNull final CopyOnWriteArrayList<NetworkStatsProviderCallbackImpl> mStatsProviderCbList; @NonNull private final Object mProviderStatsLock = new Object(); @@ -2058,7 +2078,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { NetworkStatsProviderCallbackImpl( @NonNull String tag, @NonNull INetworkStatsProvider provider, @NonNull Semaphore semaphore, - @NonNull INetworkManagementEventObserver alertObserver, + @NonNull AlertObserver alertObserver, @NonNull CopyOnWriteArrayList<NetworkStatsProviderCallbackImpl> cbList) throws RemoteException { mTag = tag; @@ -2106,7 +2126,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // This binder object can only have been obtained by a process that holds // NETWORK_STATS_PROVIDER. Thus, no additional permission check is required. BinderUtils.withCleanCallingIdentity(() -> - mAlertObserver.limitReached(LIMIT_GLOBAL_ALERT, null /* unused */)); + mAlertObserver.onQuotaLimitReached(LIMIT_GLOBAL_ALERT, null /* unused */)); } @Override diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java index 6df6de32da7a..4875f1cf5aaa 100644 --- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java +++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java @@ -18,14 +18,16 @@ package com.android.server.net; import static android.net.NetworkTemplate.NETWORK_TYPE_5G_NSA; import static android.net.NetworkTemplate.getCollapsedRatType; +import static android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED; +import static android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA; +import static android.telephony.TelephonyManager.NETWORK_TYPE_LTE; import android.annotation.NonNull; import android.content.Context; import android.telephony.Annotation; -import android.telephony.NetworkRegistrationInfo; -import android.telephony.PhoneStateListener; -import android.telephony.ServiceState; import android.telephony.SubscriptionManager; +import android.telephony.TelephonyCallback; +import android.telephony.TelephonyDisplayInfo; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; @@ -63,7 +65,7 @@ public class NetworkStatsSubscriptionsMonitor extends private final Delegate mDelegate; /** - * Receivers that watches for {@link ServiceState} changes for each subscription, to + * Receivers that watches for {@link TelephonyDisplayInfo} changes for each subscription, to * monitor the transitioning between Radio Access Technology(RAT) types for each sub. */ @NonNull @@ -115,13 +117,12 @@ public class NetworkStatsSubscriptionsMonitor extends continue; } - final RatTypeListener listener = - new RatTypeListener(mExecutor, this, sub.first, sub.second); + final RatTypeListener listener = new RatTypeListener(this, sub.first, sub.second); mRatListeners.add(listener); // Register listener to the telephony manager that associated with specific sub. mTeleManager.createForSubscriptionId(sub.first) - .listen(listener, PhoneStateListener.LISTEN_SERVICE_STATE); + .registerTelephonyCallback(mExecutor, listener); Log.d(NetworkStatsService.TAG, "RAT type listener registered for sub " + sub.first); } @@ -175,7 +176,7 @@ public class NetworkStatsSubscriptionsMonitor extends private void handleRemoveRatTypeListener(@NonNull RatTypeListener listener) { mTeleManager.createForSubscriptionId(listener.mSubId) - .listen(listener, PhoneStateListener.LISTEN_NONE); + .unregisterTelephonyCallback(listener); Log.d(NetworkStatsService.TAG, "RAT type listener unregistered for sub " + listener.mSubId); mRatListeners.remove(listener); @@ -185,7 +186,8 @@ public class NetworkStatsSubscriptionsMonitor extends listener.mSubscriberId, TelephonyManager.NETWORK_TYPE_UNKNOWN); } - static class RatTypeListener extends PhoneStateListener { + static class RatTypeListener extends TelephonyCallback + implements TelephonyCallback.DisplayInfoListener { // Unique id for the subscription. See {@link SubscriptionInfo#getSubscriptionId}. @NonNull private final int mSubId; @@ -199,29 +201,27 @@ public class NetworkStatsSubscriptionsMonitor extends @NonNull private final NetworkStatsSubscriptionsMonitor mMonitor; - RatTypeListener(@NonNull Executor executor, - @NonNull NetworkStatsSubscriptionsMonitor monitor, int subId, + RatTypeListener(@NonNull NetworkStatsSubscriptionsMonitor monitor, int subId, @NonNull String subscriberId) { - super(executor); mSubId = subId; mSubscriberId = subscriberId; mMonitor = monitor; } @Override - public void onServiceStateChanged(@NonNull ServiceState ss) { + public void onDisplayInfoChanged(TelephonyDisplayInfo displayInfo) { // In 5G SA (Stand Alone) mode, the primary cell itself will be 5G hence telephony // would report RAT = 5G_NR. // However, in 5G NSA (Non Stand Alone) mode, the primary cell is still LTE and // network allocates a secondary 5G cell so telephony reports RAT = LTE along with // NR state as connected. In such case, attributes the data usage to NR. // See b/160727498. - final boolean is5GNsa = (ss.getDataNetworkType() == TelephonyManager.NETWORK_TYPE_LTE - || ss.getDataNetworkType() == TelephonyManager.NETWORK_TYPE_LTE_CA) - && ss.getNrState() == NetworkRegistrationInfo.NR_STATE_CONNECTED; + final boolean is5GNsa = displayInfo.getNetworkType() == NETWORK_TYPE_LTE + && (displayInfo.getOverrideNetworkType() == OVERRIDE_NETWORK_TYPE_NR_NSA + || displayInfo.getOverrideNetworkType() == OVERRIDE_NETWORK_TYPE_NR_ADVANCED); final int networkType = - (is5GNsa ? NETWORK_TYPE_5G_NSA : ss.getDataNetworkType()); + (is5GNsa ? NETWORK_TYPE_5G_NSA : displayInfo.getNetworkType()); final int collapsedRatType = getCollapsedRatType(networkType); if (collapsedRatType == mLastCollapsedRatType) return; diff --git a/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml index c20690342c19..3f2b8ac7609d 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml @@ -43,4 +43,8 @@ <color name="settingslib_accent_primary_variant">@android:color/system_accent1_300</color> <color name="settingslib_text_color_primary_device_default">@android:color/system_neutral1_50</color> + + <color name="settingslib_text_color_secondary_device_default">@android:color/system_neutral2_200</color> + + <color name="settingslib_text_color_preference_category_title">@android:color/system_accent1_100</color> </resources>
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml index 04010985fe74..ec3c336eba46 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml @@ -65,4 +65,8 @@ <color name="settingslib_background_device_default_light">@android:color/system_neutral1_50</color> <color name="settingslib_text_color_primary_device_default">@android:color/system_neutral1_900</color> + + <color name="settingslib_text_color_secondary_device_default">@android:color/system_neutral2_700</color> + + <color name="settingslib_text_color_preference_category_title">@android:color/system_accent1_600</color> </resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml index 1c33f1a57ea5..11546c8ed3d9 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml @@ -20,4 +20,9 @@ <dimen name="app_icon_min_width">52dp</dimen> <dimen name="settingslib_preferred_minimum_touch_target">48dp</dimen> <dimen name="settingslib_dialogCornerRadius">28dp</dimen> + + <!-- Left padding of the preference --> + <dimen name="settingslib_listPreferredItemPaddingStart">24dp</dimen> + <!-- Right padding of the preference --> + <dimen name="settingslib_listPreferredItemPaddingEnd">24dp</dimen> </resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml index 58006369988e..8e7226b0eb53 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml @@ -17,13 +17,19 @@ <resources> <style name="TextAppearance.PreferenceTitle.SettingsLib" parent="@android:style/TextAppearance.Material.Subhead"> + <item name="android:textColor">@color/settingslib_text_color_primary_device_default</item> <item name="android:fontFamily">@string/settingslib_config_headlineFontFamily</item> <item name="android:textSize">20sp</item> </style> + <style name="TextAppearance.PreferenceSummary.SettingsLib" + parent="@android:style/TextAppearance.DeviceDefault.Small"> + <item name="android:textColor">@color/settingslib_text_color_secondary_device_default</item> + </style> + <style name="TextAppearance.CategoryTitle.SettingsLib" parent="@android:style/TextAppearance.DeviceDefault.Medium"> - <item name="android:textColor">?android:attr/textColorPrimary</item> + <item name="android:textColor">@color/settingslib_text_color_preference_category_title</item> <item name="android:textSize">14sp</item> </style> diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml index 6bf288b74d5a..7bf75bcc8a53 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml @@ -19,8 +19,8 @@ <!-- Only using in Settings application --> <style name="Theme.SettingsBase" parent="@android:style/Theme.DeviceDefault.Settings" > <item name="android:textAppearanceListItem">@style/TextAppearance.PreferenceTitle.SettingsLib</item> - <item name="android:listPreferredItemPaddingStart">24dp</item> - <item name="android:listPreferredItemPaddingLeft">24dp</item> + <item name="android:listPreferredItemPaddingStart">@dimen/settingslib_listPreferredItemPaddingStart</item> + <item name="android:listPreferredItemPaddingLeft">@dimen/settingslib_listPreferredItemPaddingStart</item> <item name="android:listPreferredItemPaddingEnd">16dp</item> <item name="android:listPreferredItemPaddingRight">16dp</item> <item name="preferenceTheme">@style/PreferenceTheme.SettingsLib</item> diff --git a/packages/SettingsLib/SettingsTheme/res/values/config.xml b/packages/SettingsLib/SettingsTheme/res/values/config.xml new file mode 100644 index 000000000000..e73dcc0cc559 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values/config.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 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. + --> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <bool name="settingslib_config_icon_space_reserved">true</bool> +</resources>
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTheme/res/values/dimens.xml b/packages/SettingsLib/SettingsTheme/res/values/dimens.xml index 18af1f9c15d0..f7e01444f4d4 100644 --- a/packages/SettingsLib/SettingsTheme/res/values/dimens.xml +++ b/packages/SettingsLib/SettingsTheme/res/values/dimens.xml @@ -21,4 +21,11 @@ <dimen name="app_icon_min_width">56dp</dimen> <dimen name="two_target_min_width">72dp</dimen> <dimen name="settingslib_dialogCornerRadius">8dp</dimen> + + <!-- Left padding of the preference --> + <dimen name="settingslib_listPreferredItemPaddingStart">16dp</dimen> + <!-- Right padding of the preference --> + <dimen name="settingslib_listPreferredItemPaddingEnd">16dp</dimen> + <!-- Icon size of the preference --> + <dimen name="settingslib_preferenceIconSize">24dp</dimen> </resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values/styles.xml b/packages/SettingsLib/SettingsTheme/res/values/styles.xml new file mode 100644 index 000000000000..aaab0f041fe3 --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values/styles.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 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. + --> +<resources> + <style name="TextAppearance.PreferenceTitle.SettingsLib" + parent="@android:style/TextAppearance.Material.Subhead"> + </style> + + <style name="TextAppearance.PreferenceSummary.SettingsLib" + parent="@style/PreferenceSummaryTextStyle"> + </style> + + <style name="TextAppearance.CategoryTitle.SettingsLib" + parent="@android:style/TextAppearance.DeviceDefault.Medium"> + </style> +</resources> diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index f3fb48fdfe54..13a5cafc53e4 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Vra elke keer"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Totdat jy dit afskakel"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Sopas"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Foonluidspreker"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Hierdie foon"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Hierdie foon"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Kan nie koppel nie. Skakel toestel af en weer aan"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Bedrade oudiotoestel"</string> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index 647d0528b052..7ad455ae3e67 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ሁልጊዜ ጠይቅ"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"እስኪያጠፉት ድረስ"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"ልክ አሁን"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"የስልክ ድምጽ ማጉያ"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ይህ ስልክ"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ይህ ስልክ"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"መገናኘት ላይ ችግር። መሳሪያውን ያጥፉት እና እንደገና ያብሩት"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ባለገመድ የኦዲዮ መሣሪያ"</string> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index 6357f3c0ac2e..a4646f16bd6e 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -556,7 +556,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"السؤال في كل مرة"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"إلى أن يتم إيقاف الوضع"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"للتو"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"مكبر صوت الهاتف"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"هذا الهاتف"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"هذا الهاتف"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"حدثت مشكلة أثناء الاتصال. يُرجى إيقاف الجهاز ثم إعادة تشغيله."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"جهاز سماعي سلكي"</string> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index cb74e1f36ff4..c304921f48d7 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"প্ৰতিবাৰতে সোধক"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"আপুনি অফ নকৰা পর্যন্ত"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"এই মাত্ৰ"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ফ’নৰ স্পীকাৰ"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"এই ফ’নটো"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"এই ফ’নটো"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"সংযোগ হোৱাত সমস্যা হৈছে। ডিভাইচটো অফ কৰি পুনৰ অন কৰক"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"তাঁৰযুক্ত অডিঅ’ ডিভাইচ"</string> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index 979c957fb9d4..6e3947e56b4a 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Həmişə soruşulsun"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Deaktiv edilənə qədər"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"İndicə"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefon dinamiki"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Bu telefon"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Bu telefon"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Qoşulmaqla bağlı problem. Cihazı deaktiv edin, sonra yenidən aktiv edin"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Simli audio cihaz"</string> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index 5af3006f12d6..a05dae9cfc4c 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -553,7 +553,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Pitaj svaki put"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Dok ne isključite"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Upravo"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Zvučnik telefona"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ovaj telefon"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ovaj telefon"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem pri povezivanju. Isključite uređaj, pa ga ponovo uključite"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Žičani audio uređaj"</string> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index d1b6226f40a0..e3b05677bea4 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -554,7 +554,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Заўсёды пытацца"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Пакуль не выключыце"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Толькі што"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Дынамік тэлефона"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Гэты тэлефон"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Гэты тэлефон"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Праблема з падключэннем. Выключыце і зноў уключыце прыладу"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Правадная аўдыяпрылада"</string> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index 375879a881b7..a8eaaf0d4c0b 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Да се пита винаги"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"До изключване"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Току-що"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Високоговорител"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Този телефон"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Този телефон"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"При свързването възникна проблем. Изключете устройството и го включете отново"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Аудиоустройство с кабел"</string> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index 89fcbd3ec640..c28e92708542 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"প্রতিবার জিজ্ঞেস করা হবে"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"যতক্ষণ না আপনি বন্ধ করছেন"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"এখনই"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ফোনের স্পিকার"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"এই ফোন"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"এই ফোনটি"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"কানেক্ট করতে সমস্যা হচ্ছে। ডিভাইস বন্ধ করে আবার চালু করুন"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ওয়্যার অডিও ডিভাইস"</string> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index e33978ff27f1..232c22fd939d 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -553,7 +553,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Pitaj svaki put"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Dok ne isključite"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Upravo"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Zvučnik telefona"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ovaj telefon"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ovaj telefon"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Došlo je do problema prilikom povezivanja. Isključite, pa ponovo uključite uređaj"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Žičani audio uređaj"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index 2b10a30dd786..bc393c361392 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Pregunta sempre"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Fins que no el desactivis"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Ara mateix"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altaveu del telèfon"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Aquest telèfon"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Aquest telèfon"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Hi ha hagut un problema amb la connexió. Apaga el dispositiu i torna\'l a encendre."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositiu d\'àudio amb cable"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index 7a94bb433c96..f7583659c87f 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -554,7 +554,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Pokaždé se zeptat"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Dokud funkci nevypnete"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Právě teď"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Reproduktor telefonu"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Tento telefon"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Tento telefon"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problém s připojením. Vypněte zařízení a znovu jej zapněte"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Kabelové audiozařízení"</string> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index ff5c6aed660c..0fd2569881a6 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Spørg hver gang"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Indtil du deaktiverer"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Lige nu"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefonens højttaler"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Denne telefon"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Denne telefon"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Der kunne ikke oprettes forbindelse. Sluk og tænd enheden"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Lydenhed med ledning"</string> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index defc22312ab4..f75685b711df 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Jedes Mal fragen"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Bis zur Deaktivierung"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Gerade eben"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Smartphone-Lautsprecher"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Dieses Smartphone"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Dieses Smartphone"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Verbindung kann nicht hergestellt werden. Schalte das Gerät aus & und wieder ein."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Netzbetriebenes Audiogerät"</string> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index 402e4897fab9..51157289b82a 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Να ερωτώμαι κάθε φορά"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Μέχρι την απενεργοποίηση"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Μόλις τώρα"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Ηχείο τηλεφώνου"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Αυτό το τηλέφωνο"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Αυτό το τηλέφωνο"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Πρόβλημα κατά τη σύνδεση. Απενεργοποιήστε τη συσκευή και ενεργοποιήστε την ξανά"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Ενσύρματη συσκευή ήχου"</string> diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index fb3bc7a335ae..1481ceec7f2b 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Ask every time"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Until you turn off"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Just now"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Phone speaker"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"This phone"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"This phone"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem connecting. Turn device off and back on"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Wired audio device"</string> diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml index e33af37626c3..b3dd58a1636b 100644 --- a/packages/SettingsLib/res/values-en-rCA/strings.xml +++ b/packages/SettingsLib/res/values-en-rCA/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Ask every time"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Until you turn off"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Just now"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Phone speaker"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"This phone"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"This phone"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem connecting. Turn device off and back on"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Wired audio device"</string> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index fb3bc7a335ae..1481ceec7f2b 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Ask every time"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Until you turn off"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Just now"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Phone speaker"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"This phone"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"This phone"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem connecting. Turn device off and back on"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Wired audio device"</string> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index fb3bc7a335ae..1481ceec7f2b 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Ask every time"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Until you turn off"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Just now"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Phone speaker"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"This phone"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"This phone"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem connecting. Turn device off and back on"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Wired audio device"</string> diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml index 2c58236be394..2a6c82cbc28d 100644 --- a/packages/SettingsLib/res/values-en-rXC/strings.xml +++ b/packages/SettingsLib/res/values-en-rXC/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Ask every time"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Until you turn off"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Just now"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Phone speaker"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"This phone"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"This phone"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem connecting. Turn device off & back on"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Wired audio device"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index 471e71509847..ee0e0bcb8dd8 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -219,7 +219,7 @@ <item msgid="3075292553049300105">"Normal"</item> <item msgid="1158955023692670059">"Ligera"</item> <item msgid="5664310435707146591">"Muy ligera"</item> - <item msgid="5491266922147715962">"A velocidad muy alta"</item> + <item msgid="5491266922147715962">"Muy rápida"</item> <item msgid="7659240015901486196">"Rápida"</item> <item msgid="7147051179282410945">"Muy rápida"</item> <item msgid="581904787661470707">"A velocidad máxima"</item> @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Preguntar siempre"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Hasta que lo desactives"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Recién"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altavoz del teléfono"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Este teléfono"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este teléfono"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Error al establecer la conexión. Apaga el dispositivo y vuelve a encenderlo."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de audio con cable"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index bd687e467dba..1f64bad6c45f 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Preguntar siempre"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Hasta que lo desactives"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"justo ahora"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altavoz del teléfono"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Este teléfono"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este teléfono"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"No se ha podido conectar; reinicia el dispositivo"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de audio con cable"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index aaf320122419..ed7b9dc161c0 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Küsi iga kord"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Kuni välja lülitate"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Äsja"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefoni kõlar"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"See telefon"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"See telefon"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Probleem ühendamisel. Lülitage seade välja ja uuesti sisse"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Juhtmega heliseade"</string> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index 0f929de91fce..4cdcca7dd389 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -552,9 +552,9 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Galdetu beti"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Zuk desaktibatu arte"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Oraintxe"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefonoaren bozgorailua"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Telefono hau"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Telefono hau"</string> - <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Arazoren bat izan da konektatzean. Itzali gailua eta pitz ezazu berriro."</string> + <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Arazo bat izan da konektatzean. Itzali gailua eta pitz ezazu berriro."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Audio-gailu kableduna"</string> <string name="help_label" msgid="3528360748637781274">"Laguntza eta iritziak"</string> <string name="storage_category" msgid="2287342585424631813">"Biltegiratzea"</string> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index 3dce25d3ff7e..1b5f9790633f 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"هربار پرسیده شود"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"تا زمانیکه آن را خاموش کنید"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"هماکنون"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"بلندگوی تلفن"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"این تلفن"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"این تلفن"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"مشکل در اتصال. دستگاه را خاموش و دوباره روشن کنید"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"دستگاه صوتی سیمی"</string> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index 373f6a4f2742..9490ede09ff8 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Kysy aina"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Kunnes laitat pois päältä"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Äsken"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Puhelimen kaiutin"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Tämä puhelin"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Tämä puhelin"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Yhteysvirhe. Sammuta laite ja käynnistä se uudelleen."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Langallinen äänilaite"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index ebaf4b1747a7..ab36e11e03cb 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Toujours demander"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Jusqu\'à la désactivation"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"À l\'instant"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Haut-parleur du téléphone"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ce téléphone"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ce téléphone"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problème de connexion. Éteingez et rallumez l\'appareil"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Appareil audio à câble"</string> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index 5f635b0a98a9..35f2fcee3de4 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Toujours demander"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Jusqu\'à la désactivation"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"À l\'instant"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Haut-parleur du téléphone"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ce téléphone"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ce téléphone"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problème de connexion. Éteignez l\'appareil, puis rallumez-le"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Appareil audio filaire"</string> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index cb33a5152961..8d23864d354a 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Preguntar sempre"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Ata a desactivación"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Agora mesmo"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altofalante do teléfono"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Este teléfono"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este teléfono"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Produciuse un problema coa conexión. Apaga e acende o dispositivo."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de audio con cable"</string> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index 79ca4ab52ca8..ac38a64ffcf9 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"દર વખતે પૂછો"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"તમે બંધ ન કરો ત્યાં સુધી"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"હમણાં જ"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ફોન સ્પીકર"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"આ ફોન"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"આ ફોન"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"કનેક્ટ કરવામાં સમસ્યા આવી રહી છે. ડિવાઇસને બંધ કરીને ફરી ચાલુ કરો"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"વાયરવાળો ઑડિયો ડિવાઇસ"</string> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index fcf04bc48133..5c008f0bb50f 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"हर बार पूछें"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"जब तक आप इसे बंद नहीं करते"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"अभी-अभी"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"फ़ोन का स्पीकर"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"यह फ़ोन"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"यह फ़ोन"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"कनेक्ट करने में समस्या हो रही है. डिवाइस को बंद करके चालू करें"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"वायर वाला ऑडियो डिवाइस"</string> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index 1ef94fb0981e..d681a1edfbdc 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -553,7 +553,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Pitaj svaki put"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Dok ne isključite"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Upravo sad"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Zvučnik telefona"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ovaj telefon"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ovaj telefon"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem s povezivanjem. Isključite i ponovo uključite uređaj"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Žičani audiouređaj"</string> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index deb0f81bc5da..57894a0c6377 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Mindig kérdezzen rá"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Kikapcsolásig"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Az imént"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefon hangszórója"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ez a telefon"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ez a telefon"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Sikertelen csatlakozás. Kapcsolja ki az eszközt, majd kapcsolja be újra."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Vezetékes audioeszköz"</string> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index 485af5490567..e6ed2b00ee6a 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Ամեն անգամ հարցնել"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Մինչև անջատեք"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Հենց նոր"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Հեռախոսի բարձրախոս"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Այս հեռախոսը"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Այս հեռախոսը"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Կապի խնդիր կա: Սարքն անջատեք և նորից միացրեք:"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Լարով աուդիո սարք"</string> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index f82798a4f0e1..6fbab2b8a3df 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Selalu tanya"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Sampai Anda menonaktifkannya"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Baru saja"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Speaker ponsel"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ponsel ini"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ponsel ini"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ada masalah saat menghubungkan. Nonaktifkan perangkat & aktifkan kembali"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Perangkat audio berkabel"</string> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index a43eb14cf063..fa11dfb25649 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Spyrja í hvert skipti"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Þar til þú slekkur"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Rétt í þessu"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Símahátalari"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Þessi sími"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Þessi sími"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Vandamál í tengingu. Slökktu og kveiktu á tækinu"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Snúrutengt hljómtæki"</string> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index 0a96f59c8a8a..0db0448d3140 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Chiedi ogni volta"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Fino alla disattivazione"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Adesso"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altoparlante telefono"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Questo telefono"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Questo telefono"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problema di connessione. Spegni e riaccendi il dispositivo"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo audio cablato"</string> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index 6f3cfb99dc54..7ac591916858 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -554,7 +554,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"יש לשאול בכל פעם"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"עד הכיבוי"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"הרגע"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"הרמקול של הטלפון"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"הטלפון הזה"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"הטלפון הזה"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"יש בעיה בחיבור. עליך לכבות את המכשיר ולהפעיל אותו מחדש"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"התקן אודיו חוטי"</string> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index 620aca6edb4d..d82ca5d976bd 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"毎回確認"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"OFF にするまで"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"たった今"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"スマートフォンのスピーカー"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"このスマートフォン"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"このスマートフォン"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"接続エラーです。デバイスを OFF にしてから ON に戻してください"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"有線オーディオ デバイス"</string> diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml index daa0faef6046..02c1084a2bd1 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ყოველთვის მკითხეთ"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"გამორთვამდე"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"ახლახან"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ტელეფონის დინამიკი"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ეს ტელეფონი"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ეს ტელეფონი"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"დაკავშირებისას წარმოიქმნა პრობლემა. გამორთეთ და კვლავ ჩართეთ მოწყობილობა"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"სადენიანი აუდიო მოწყობილობა"</string> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index 6305891ea839..6c55b62b19a7 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Әрдайым сұрау"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Өшірілгенге дейін"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Дәл қазір"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Телефон динамигі"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Осы телефон"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Осы телефон"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Байланыс орнату қатесі шығуып жатыр. Құрылғыны өшіріп, қайта қосыңыз."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Сымды аудио құрылғысы"</string> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index fb00aa0bb425..58a2c7cf9e46 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"សួរគ្រប់ពេល"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"រហូតទាល់តែអ្នកបិទ"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"អម្បាញ់មិញ"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ឧបករណ៍បំពងសំឡេងទូរសព្ទ"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ទូរសព្ទនេះ"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ទូរសព្ទនេះ"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"មានបញ្ហាក្នុងការភ្ជាប់។ បិទ រួចបើកឧបករណ៍វិញ"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ឧបករណ៍សំឡេងប្រើខ្សែ"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index a1f0825d1e16..91e76e2de3d2 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ಪ್ರತಿ ಬಾರಿ ಕೇಳಿ"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"ನೀವು ಆಫ್ ಮಾಡುವವರೆಗೆ"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"ಇದೀಗ"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ಫೋನ್ ಸ್ಪೀಕರ್"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ಈ ಫೋನ್"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ಈ ಫೋನ್"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ಕನೆಕ್ಟ್ ಮಾಡುವಾಗ ಸಮಸ್ಯೆ ಎದುರಾಗಿದೆ ಸಾಧನವನ್ನು ಆಫ್ ಮಾಡಿ ಹಾಗೂ ನಂತರ ಪುನಃ ಆನ್ ಮಾಡಿ"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ವೈರ್ ಹೊಂದಿರುವ ಆಡಿಯೋ ಸಾಧನ"</string> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index 1ff18c636876..d47056062085 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"항상 확인"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"사용 중지할 때까지"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"조금 전"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"휴대전화 스피커"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"이 휴대전화"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"이 휴대전화"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"연결 중에 문제가 발생했습니다. 기기를 껐다가 다시 켜 보세요."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"유선 오디오 기기"</string> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index 58e31071c5d6..e38bb51a3c44 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Ар дайым суралсын"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Бул функция өчүрүлгөнгө чейин"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Жаңы эле"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Телефондун динамиги"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ушул телефон"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ушул телефон"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Туташууда маселе келип чыкты. Түзмөктү өчүрүп, кайра күйгүзүп көрүңүз"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Зымдуу аудио түзмөк"</string> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index a2b8c6ba5b22..cc0229241527 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ຖາມທຸກເທື່ອ"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"ຈົນກວ່າທ່ານຈະປິດ"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"ຕອນນີ້"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ລຳໂພງໂທລະສັບ"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ໂທລະສັບນີ້"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ໂທລະສັບນີ້"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ເກີດບັນຫາໃນການເຊື່ອມຕໍ່. ປິດອຸປະກອນແລ້ວເປີດກັບຄືນມາໃໝ່"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ອຸປະກອນສຽງແບບມີສາຍ"</string> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index d8c1a45192ce..fb645a09aa37 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -554,7 +554,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Klausti kaskart"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Kol išjungsite"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Ką tik"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefono garsiakalbis"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Šis telefonas"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Šis telefonas"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Prisijungiant kilo problema. Išjunkite įrenginį ir vėl jį įjunkite"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Laidinis garso įrenginys"</string> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index 9ac8c656c5d0..694ff0a52eb8 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -553,7 +553,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Vaicāt katru reizi"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Līdz brīdim, kad izslēgsiet"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Tikko"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Tālruņa skaļrunis"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Šis tālrunis"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Šis tālrunis"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Radās problēma ar savienojuma izveidi. Izslēdziet un atkal ieslēdziet ierīci."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Vadu audioierīce"</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index c08fd246e959..9d412fcec67d 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Прашувај секогаш"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Додека не го исклучите"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Пред малку"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Телефонски звучник"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Овој телефон"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Овој телефон"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Проблем со поврзување. Исклучете го уредот и повторно вклучете го"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Жичен аудиоуред"</string> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index 27693e68f988..82df16c06c6f 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"എപ്പോഴും ചോദിക്കുക"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"നിങ്ങൾ ഓഫാക്കുന്നത് വരെ"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"ഇപ്പോൾ"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ഫോൺ സ്പീക്കർ"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ഈ ഫോൺ"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ഈ ഫോൺ"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"കണക്റ്റ് ചെയ്യുന്നതിൽ പ്രശ്നമുണ്ടായി. ഉപകരണം ഓഫാക്കി വീണ്ടും ഓണാക്കുക"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"വയർ മുഖേന ബന്ധിപ്പിച്ച ഓഡിയോ ഉപകരണം"</string> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index b62d103c7835..027b7227ef93 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Тухай бүрд асуух"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Таныг унтраах хүртэл"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Дөнгөж сая"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Утасны чанга яригч"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Энэ утас"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Энэ утас"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Холбогдоход асуудал гарлаа. Төхөөрөмжийг унтраагаад дахин асаана уу"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Утастай аудио төхөөрөмж"</string> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index d90c08af1823..176d764dfe23 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"प्रत्येक वेळी विचारा"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"तुम्ही बंद करेपर्यंत"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"आत्ताच"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"फोनचा स्पीकर"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"हा फोन"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"हा फोन"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"कनेक्ट करण्यात समस्या आली. डिव्हाइस बंद करा आणि नंतर सुरू करा"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"वायर असलेले ऑडिओ डिव्हाइस"</string> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index 7011aec19665..6bf15fe25f77 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Tanya setiap kali"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Sehingga anda matikan"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Sebentar tadi"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Pembesar suara telefon"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Telefon ini"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Telefon ini"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Masalah penyambungan. Matikan & hidupkan kembali peranti"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Peranti audio berwayar"</string> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index 3de8cf928383..13fc6adf55ba 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"အမြဲမေးရန်"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"သင်ပိတ်လိုက်သည် အထိ"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"ယခုလေးတင်"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ဖုန်းစပီကာ"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ဤဖုန်း"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ဤဖုန်း"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ချိတ်ဆက်ရာတွင် ပြဿနာရှိပါသည်။ စက်ကိုပိတ်ပြီး ပြန်ဖွင့်ပါ"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ကြိုးတပ် အသံစက်ပစ္စည်း"</string> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index e7f70d12beb3..c2e449b4850c 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Spør hver gang"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Til du slår av"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Nå nettopp"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefonhøyttaler"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Denne telefonen"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Denne telefonen"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Tilkoblingsproblemer. Slå enheten av og på igjen"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Lydenhet med kabel"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index 8fc329f885d1..85860f0d80e1 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"प्रत्येक पटक सोधियोस्"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"तपाईंले अफ नगरेसम्म"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"अहिले भर्खरै"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"फोनको स्पिकर"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"यो फोन"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"यो फोन"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"जोड्ने क्रममा समस्या भयो। यन्त्रलाई निष्क्रिय पारेर फेरि सक्रिय गर्नुहोस्"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"तारयुक्त अडियो यन्त्र"</string> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index 35e651bfb626..2ffa559f4401 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -222,7 +222,7 @@ <item msgid="5491266922147715962">"Nog sneller"</item> <item msgid="7659240015901486196">"Heel erg snel"</item> <item msgid="7147051179282410945">"Snelst"</item> - <item msgid="581904787661470707">"Allerallersnelst"</item> + <item msgid="581904787661470707">"Allersnelst"</item> </string-array> <string name="choose_profile" msgid="343803890897657450">"Profiel kiezen"</string> <string name="category_personal" msgid="6236798763159385225">"Persoonlijk"</string> @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Vraag altijd"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Totdat je uitzet"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Zojuist"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefoonspeaker"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Deze telefoon"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Deze telefoon"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Probleem bij verbinding maken. Zet het apparaat uit en weer aan."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Bedraad audioapparaat"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index 49018be6c8d2..e2fee1399b25 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ପ୍ରତ୍ୟେକ ଥର ପଚାରନ୍ତୁ"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"ଆପଣ ବନ୍ଦ ନକରିବା ପର୍ଯ୍ୟନ୍ତ"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"ଏହିକ୍ଷଣି"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ଫୋନ୍ ସ୍ପିକର୍"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ଏହି ଫୋନ"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ଏହି ଫୋନ୍"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ସଂଯୋଗ କରିବାରେ ସମସ୍ୟା ହେଉଛି। ଡିଭାଇସ୍ ବନ୍ଦ କରି ପୁଣି ଚାଲୁ କରନ୍ତୁ"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ତାରଯୁକ୍ତ ଅଡିଓ ଡିଭାଇସ୍"</string> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index 9efc4c4a4b8f..11cd481703f7 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ਹਰ ਵਾਰ ਪੁੱਛੋ"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਬੰਦ ਨਹੀਂ ਕਰਦੇ"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"ਹੁਣੇ ਹੀ"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ਫ਼ੋਨ ਦਾ ਸਪੀਕਰ"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ਇਹ ਫ਼ੋਨ"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ਇਹ ਫ਼ੋਨ"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ਕਨੈਕਟ ਕਰਨ ਵਿੱਚ ਸਮੱਸਿਆ ਆਈ। ਡੀਵਾਈਸ ਨੂੰ ਬੰਦ ਕਰਕੇ ਵਾਪਸ ਚਾਲੂ ਕਰੋ"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ਤਾਰ ਵਾਲਾ ਆਡੀਓ ਡੀਵਾਈਸ"</string> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index 1eb2dd9bffba..0c670bc7c8ae 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -554,7 +554,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Zawsze pytaj"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Dopóki nie wyłączysz"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Przed chwilą"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Głośnik telefonu"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ten telefon"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ten telefon"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem z połączeniem. Wyłącz i ponownie włącz urządzenie"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Przewodowe urządzenie audio"</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index 38d637f9e893..10568a116689 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Perguntar sempre"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Até você desativar"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Agora"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Alto-falante do smartphone"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Este smartphone"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este smartphone"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ocorreu um problema na conexão. Desligue o dispositivo e ligue-o novamente"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de áudio com fio"</string> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index 5a0ec2e36543..54582790ed10 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Perguntar sempre"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Até desativar"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Agora mesmo"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altifalante do telemóvel"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Este telemóvel"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este telemóvel"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problema ao ligar. Desligue e volte a ligar o dispositivo."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de áudio com fios"</string> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index 38d637f9e893..10568a116689 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Perguntar sempre"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Até você desativar"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Agora"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Alto-falante do smartphone"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Este smartphone"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este smartphone"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ocorreu um problema na conexão. Desligue o dispositivo e ligue-o novamente"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de áudio com fio"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index ce58f1c75cc2..00fc8ffdb34f 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -553,7 +553,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Întreabă de fiecare dată"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Până când dezactivați"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Chiar acum"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Difuzorul telefonului"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Acest telefon"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Acest telefon"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problemă la conectare. Opriți și reporniți dispozitivul."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispozitiv audio cu fir"</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index 14db2252f4dc..15e345619c63 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -554,7 +554,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Всегда спрашивать"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Пока вы не отключите"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Только что"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Встроен. динамик"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Этот смартфон"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Этот смартфон"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ошибка подключения. Выключите и снова включите устройство."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Проводное аудиоустройство"</string> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index 41f10c94b92d..d56dd34a99cc 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"සෑම විටම ඉල්ලන්න"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"ඔබ ක්රියාවිරහිත කරන තුරු"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"මේ දැන්"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"දුරකථන ස්පීකරය"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"මෙම දුරකථනය"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"මෙම දුරකථනය"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"සම්බන්ධ කිරීමේ ගැටලුවකි උපාංගය ක්රියාවිරහිත කර & ආපසු ක්රියාත්මක කරන්න"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"රැහැන්ගත කළ ඕඩියෝ උපාංගය"</string> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index 801c213f1e20..e7ea915aa49b 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -554,7 +554,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Vždy sa opýtať"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Dokým funkciu nevypnete"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Teraz"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Reproduktor telefónu"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Tento telefón"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Tento telefón"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Pri pripájaní sa vyskytol problém. Zariadenie vypnite a znova zapnite."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Audio zariadenie s káblom"</string> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index a33101899406..95a6fb929d59 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -554,7 +554,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Vedno vprašaj"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Dokler ne izklopite"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Pravkar"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Zvočnik telefona"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ta telefon"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ta telefon"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Težava pri povezovanju. Napravo izklopite in znova vklopite."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Žična zvočna naprava"</string> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index cb9bea8cf963..8524164aa78b 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Pyet çdo herë"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Derisa ta çaktivizosh"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Pikërisht tani"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altoparlanti i telefonit"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ky telefon"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ky telefon"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem me lidhjen. Fike dhe ndize përsëri pajisjen"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Pajisja audio me tel"</string> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index 9e9033341982..b19198aa24ee 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -553,7 +553,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Питај сваки пут"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Док не искључите"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Управо"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Звучник телефона"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Овај телефон"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Овај телефон"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Проблем при повезивању. Искључите уређај, па га поново укључите"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Жичани аудио уређај"</string> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index 215d9ade4aab..59cde86f081e 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Fråga varje gång"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Tills du inaktiverar funktionen"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Nyss"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefonhögtalare"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Den här telefonen"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Den här telefonen"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Det gick inte att ansluta. Stäng av enheten och slå på den igen"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Ljudenhet med kabelanslutning"</string> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index f51825b50320..64c2e071e5fc 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Uliza kila wakati"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Hadi utakapoizima"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Sasa hivi"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Spika ya simu"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Simu hii"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Simu hii"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Kuna tatizo la kuunganisha kwenye Intaneti. Zima kisha uwashe kifaa"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Kifaa cha sauti kinachotumia waya"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index 8128683c824a..ccef6b9682ad 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ஒவ்வொரு முறையும் கேள்"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"ஆஃப் செய்யும் வரை"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"சற்றுமுன்"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"மொபைல் ஸ்பீக்கர்"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"இந்த மொபைல்"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"இந்த மொபைல்"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"இணைப்பதில் சிக்கல். சாதனத்தை ஆஃப் செய்து மீண்டும் ஆன் செய்யவும்"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"வயருடன்கூடிய ஆடியோ சாதனம்"</string> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index 298f850b7ecb..4013620565df 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ప్రతిసారి అడుగు"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"మీరు ఆఫ్ చేసే వరకు"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"ఇప్పుడే"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ఫోన్ స్పీకర్"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"ఈ ఫోన్"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ఈ ఫోన్"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"కనెక్ట్ చేయడంలో సమస్య ఉంది. పరికరాన్ని ఆఫ్ చేసి, ఆపై తిరిగి ఆన్ చేయండి"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"వైర్ గల ఆడియో పరికరం"</string> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index e18c6492d845..c42295fbcdfd 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ถามทุกครั้ง"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"จนกว่าคุณจะปิด"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"เมื่อสักครู่"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ลำโพงโทรศัพท์"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"โทรศัพท์เครื่องนี้"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"โทรศัพท์เครื่องนี้"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"เกิดปัญหาในการเชื่อมต่อ ปิดอุปกรณ์แล้วเปิดใหม่อีกครั้ง"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"อุปกรณ์เสียงแบบมีสาย"</string> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index 628bacc51189..ab36a556156f 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Magtanong palagi"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Hanggang sa i-off mo"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Ngayon lang"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Speaker ng telepono"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Ang teleponong ito"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ang teleponong ito"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Nagkaproblema sa pagkonekta. I-off at pagkatapos ay i-on ang device"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Wired na audio device"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index 3e30d4320bd2..ce71496d82e4 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Her zaman sor"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Siz kapatana kadar"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Az önce"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefon hoparlörü"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Bu telefon"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Bu telefon"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Bağlanırken sorun oluştu. Cihazı kapatıp tekrar açın"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Kablolu ses cihazı"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index 86839539abb7..b7d370d239f4 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -554,7 +554,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Запитувати щоразу"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Доки не вимкнути"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Щойно"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Динамік"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Цей телефон"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Цей телефон"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Не вдається підключитися. Перезавантажте пристрій."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Дротовий аудіопристрій"</string> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index 09e50f62abc9..c3f4d56d84ce 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"ہر بار پوچھیں"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"یہاں تک کہ آپ آف کر دیں"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"ابھی ابھی"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"فون اسپیکر"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"یہ فون"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"یہ فون"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"منسلک کرنے میں مسئلہ پیش آ گیا۔ آلہ کو آف اور بیک آن کریں"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"وائرڈ آڈیو آلہ"</string> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index d0b9506e79f7..aa3d8826dd22 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Har safar so‘ralsin"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Rejimdan chiqilgunicha"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Hozir"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Telefon karnayi"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Shu telefon"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Shu telefon"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ulanishda muammo yuz berdi. Qurilmani oʻchiring va yoqing"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Simli audio qurilma"</string> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index bd97cc5804ee..7291d061ddde 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Luôn hỏi"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Cho đến khi bạn tắt"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Vừa xong"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Loa điện thoại"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Điện thoại này"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Điện thoại này"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Sự cố kết nối. Hãy tắt thiết bị rồi bật lại"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Thiết bị âm thanh có dây"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index 3ed18600682f..3f1b9ae581eb 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"每次都询问"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"直到您将其关闭"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"刚刚"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"手机扬声器"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"这部手机"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"这部手机"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"连接时遇到问题。请关闭并重新开启设备"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"有线音频设备"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index 965e78f5bbe4..c91a13f7f56b 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"每次都詢問"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"直至您關閉為止"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"剛剛"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"手機喇叭"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"此手機"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"這部手機"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"無法連接,請關閉裝置然後重新開機"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"有線音響裝置"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index e3ca3444ad32..8b469ea2154d 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"每次都詢問"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"直到你關閉為止"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"剛剛"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"手機喇叭"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"這支手機"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"這支手機"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"無法連線,請關閉裝置後再重新開啟"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"有線音訊裝置"</string> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index 1af1fa112ae1..9eac12cedc2b 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -552,7 +552,7 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Buza njalo"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"Uze uvale isikrini"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"Khona manje"</string> - <string name="media_transfer_this_device_name" msgid="2716555073132169240">"Isipikha sefoni"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"Le foni"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Le foni"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Inkinga yokuxhumeka. Vala idivayisi futhi uphinde uyivule"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Idivayisi yomsindo enentambo"</string> diff --git a/packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java b/packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java index de45ea536e27..d3fe4a7fcb9f 100644 --- a/packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java +++ b/packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java @@ -47,7 +47,7 @@ import javax.tools.Diagnostic.Kind; * Annotation processor for {@link SearchIndexable} that generates {@link SearchIndexableResources} * subclasses. */ -@SupportedSourceVersion(SourceVersion.RELEASE_8) +@SupportedSourceVersion(SourceVersion.RELEASE_9) @SupportedAnnotationTypes({"com.android.settingslib.search.SearchIndexable"}) public class IndexableProcessor extends AbstractProcessor { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java index 9dd329ed7cd7..b11bbdec191f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java @@ -138,13 +138,6 @@ public class HeadsetProfile implements LocalBluetoothProfile { return mService.getActiveDevice(); } - public boolean isAudioOn() { - if (mService == null) { - return false; - } - return mService.isAudioOn(); - } - public int getAudioState(BluetoothDevice device) { if (mService == null) { return BluetoothHeadset.STATE_AUDIO_DISCONNECTED; diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractConnectivityPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractConnectivityPreferenceController.java index c6552f77a2b2..d3934bf131ba 100644 --- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractConnectivityPreferenceController.java +++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractConnectivityPreferenceController.java @@ -73,7 +73,8 @@ public abstract class AbstractConnectivityPreferenceController } mContext.registerReceiver(mConnectivityReceiver, connectivityIntentFilter, - android.Manifest.permission.CHANGE_NETWORK_STATE, null); + android.Manifest.permission.CHANGE_NETWORK_STATE, null, + Context.RECEIVER_EXPORTED_UNAUDITED); } protected abstract String[] getConnectivityIntents(); diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java index 011ca0b38e5d..cff45c6be0e0 100644 --- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java +++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java @@ -16,8 +16,6 @@ package com.android.settingslib.net; -import static android.net.NetworkStatsHistory.FIELD_RX_BYTES; -import static android.net.NetworkStatsHistory.FIELD_TX_BYTES; import static android.net.TrafficStats.MB_IN_BYTES; import static android.telephony.TelephonyManager.SIM_STATE_READY; import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH; @@ -27,12 +25,9 @@ import android.app.usage.NetworkStats.Bucket; import android.app.usage.NetworkStatsManager; import android.content.Context; import android.net.ConnectivityManager; -import android.net.INetworkStatsService; -import android.net.INetworkStatsSession; import android.net.NetworkPolicy; import android.net.NetworkPolicyManager; import android.net.NetworkTemplate; -import android.os.ServiceManager; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.text.format.DateUtils; @@ -51,25 +46,20 @@ public class DataUsageController { private static final String TAG = "DataUsageController"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - private static final int FIELDS = FIELD_RX_BYTES | FIELD_TX_BYTES; private static final StringBuilder PERIOD_BUILDER = new StringBuilder(50); private static final java.util.Formatter PERIOD_FORMATTER = new java.util.Formatter( PERIOD_BUILDER, Locale.getDefault()); private final Context mContext; - private final INetworkStatsService mStatsService; private final NetworkPolicyManager mPolicyManager; private final NetworkStatsManager mNetworkStatsManager; - private INetworkStatsSession mSession; private Callback mCallback; private NetworkNameProvider mNetworkController; private int mSubscriptionId; public DataUsageController(Context context) { mContext = context; - mStatsService = INetworkStatsService.Stub.asInterface( - ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); mPolicyManager = NetworkPolicyManager.from(mContext); mNetworkStatsManager = context.getSystemService(NetworkStatsManager.class); mSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; @@ -112,8 +102,7 @@ public class DataUsageController { } public DataUsageInfo getWifiDataUsageInfo() { - NetworkTemplate template = NetworkTemplate.buildTemplateWifi( - NetworkTemplate.WIFI_NETWORKID_ALL, null); + NetworkTemplate template = new NetworkTemplate.Builder(NetworkTemplate.MATCH_WIFI).build(); return getDataUsageInfo(template); } diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java index 3f95a07cc750..afd44d5bbc90 100644 --- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java @@ -17,6 +17,7 @@ package com.android.settingslib.net; import android.content.Context; +import android.net.NetworkStats; import android.net.NetworkTemplate; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; @@ -26,6 +27,7 @@ import android.util.Log; import com.android.internal.util.ArrayUtils; import java.util.List; +import java.util.Set; /** * Utils class for data usage @@ -73,10 +75,15 @@ public class DataUsageUtils { private static NetworkTemplate getMobileTemplateForSubId( TelephonyManager telephonyManager, int subId) { - // The null subscriberId means that no any mobile/carrier network will be matched. - // Using old API: buildTemplateMobileAll for the null subscriberId to avoid NPE. + // Create template that matches any mobile network when the subscriberId is null. String subscriberId = telephonyManager.getSubscriberId(subId); - return subscriberId != null ? NetworkTemplate.buildTemplateCarrierMetered(subscriberId) - : NetworkTemplate.buildTemplateMobileAll(subscriberId); + return subscriberId != null + ? new NetworkTemplate.Builder(NetworkTemplate.MATCH_CARRIER) + .setSubscriberIds(Set.of(subscriberId)) + .setMeteredness(NetworkStats.METERED_YES) + .build() + : new NetworkTemplate.Builder(NetworkTemplate.MATCH_MOBILE) + .setMeteredness(NetworkStats.METERED_YES) + .build(); } } diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtils.java index 21a4ac6220fb..fa4ae6712aaa 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtils.java @@ -60,6 +60,22 @@ public class WifiEnterpriseRestrictionUtils { return true; } + /** + * Confirm Wi-Fi Config is allowed to add according to whether user restriction is set + * + * @param context A context + * @return whether the device is permitted to add new Wi-Fi config + */ + public static boolean isAddWifiConfigAllowed(Context context) { + final UserManager userManager = context.getSystemService(UserManager.class); + final Bundle restrictions = userManager.getUserRestrictions(); + if (isAtLeastT() && restrictions.getBoolean(UserManager.DISALLOW_ADD_WIFI_CONFIG)) { + Log.i(TAG, "Wi-Fi Add network isn't available due to user restriction."); + return false; + } + return true; + } + @ChecksSdkIntAtLeast(api=Build.VERSION_CODES.TIRAMISU) private static boolean isAtLeastT() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU; diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/NetworkPolicyEditorTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/NetworkPolicyEditorTest.java index 7e389a19e403..919f602862b4 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/NetworkPolicyEditorTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/NetworkPolicyEditorTest.java @@ -20,6 +20,7 @@ import static junit.framework.Assert.assertEquals; import android.net.NetworkPolicy; import android.net.NetworkPolicyManager; +import android.net.NetworkStats; import android.net.NetworkTemplate; import androidx.test.InstrumentationRegistry; @@ -32,6 +33,8 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.Set; + @RunWith(AndroidJUnit4.class) @SmallTest public class NetworkPolicyEditorTest { @@ -44,7 +47,9 @@ public class NetworkPolicyEditorTest { @Before public void setUp() { - mNetworkTemplate = NetworkTemplate.buildTemplateCarrierMetered("123456789123456"); + mNetworkTemplate = new NetworkTemplate.Builder(NetworkTemplate.MATCH_CARRIER) + .setMeteredness(NetworkStats.METERED_YES) + .setSubscriberIds(Set.of("123456789123456")).build(); NetworkPolicyManager policyManager = NetworkPolicyManager.from(InstrumentationRegistry .getContext()); mNetworkPolicyEditor = new NetworkPolicyEditor(policyManager); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java index 30182c476855..f5ce6647e531 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java @@ -55,15 +55,6 @@ public class HeadsetProfileTest { } @Test - public void bluetoothProfile_shouldReturnTheAudioStatusFromBlueToothHeadsetService() { - when(mService.isAudioOn()).thenReturn(true); - assertThat(mProfile.isAudioOn()).isTrue(); - - when(mService.isAudioOn()).thenReturn(false); - assertThat(mProfile.isAudioOn()).isFalse(); - } - - @Test public void testHeadsetProfile_shouldReturnAudioState() { when(mService.getAudioState(mBluetoothDevice)). thenReturn(BluetoothHeadset.STATE_AUDIO_DISCONNECTED); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java index 9be783d61ea8..f0456b3d51ba 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java @@ -18,26 +18,21 @@ package com.android.settingslib.net; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.anyLong; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.usage.NetworkStats; import android.app.usage.NetworkStatsManager; import android.content.Context; -import android.net.INetworkStatsSession; -import android.net.NetworkStatsHistory; import android.net.NetworkTemplate; import android.os.RemoteException; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; -import android.text.format.DateUtils; import org.junit.Before; import org.junit.Test; @@ -47,6 +42,8 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.shadows.ShadowSubscriptionManager; +import java.util.Set; + @RunWith(RobolectricTestRunner.class) public class DataUsageControllerTest { @@ -54,8 +51,6 @@ public class DataUsageControllerTest { private static final String SUB_ID_2 = "Test Subscriber 2"; @Mock - private INetworkStatsSession mSession; - @Mock private TelephonyManager mTelephonyManager; @Mock private SubscriptionManager mSubscriptionManager; @@ -68,7 +63,6 @@ public class DataUsageControllerTest { private NetworkTemplate mWifiNetworkTemplate; private DataUsageController mController; - private NetworkStatsHistory mNetworkStatsHistory; private final int mDefaultSubscriptionId = 1234; @Before @@ -80,17 +74,16 @@ public class DataUsageControllerTest { .thenReturn(mSubscriptionManager); when(mContext.getSystemService(NetworkStatsManager.class)).thenReturn(mNetworkStatsManager); mController = new DataUsageController(mContext); - mNetworkStatsHistory = spy( - new NetworkStatsHistory(DateUtils.DAY_IN_MILLIS /* bucketDuration */)); - doReturn(mNetworkStatsHistory) - .when(mSession).getHistoryForNetwork(any(NetworkTemplate.class), anyInt()); ShadowSubscriptionManager.setDefaultDataSubscriptionId(mDefaultSubscriptionId); doReturn(SUB_ID).when(mTelephonyManager).getSubscriberId(); - mNetworkTemplate = NetworkTemplate.buildTemplateCarrierMetered(SUB_ID); - mNetworkTemplate2 = NetworkTemplate.buildTemplateCarrierMetered(SUB_ID_2); - mWifiNetworkTemplate = NetworkTemplate.buildTemplateWifi( - NetworkTemplate.WIFI_NETWORKID_ALL, null); + mNetworkTemplate = new NetworkTemplate.Builder(NetworkTemplate.MATCH_CARRIER) + .setMeteredness(android.net.NetworkStats.METERED_YES) + .setSubscriberIds(Set.of(SUB_ID)).build(); + mNetworkTemplate2 = new NetworkTemplate.Builder(NetworkTemplate.MATCH_CARRIER) + .setMeteredness(android.net.NetworkStats.METERED_YES) + .setSubscriberIds(Set.of(SUB_ID_2)).build(); + mWifiNetworkTemplate = new NetworkTemplate.Builder(NetworkTemplate.MATCH_WIFI).build(); } @Test diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java index e8d584486746..5b0f659e9c7a 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java @@ -30,6 +30,7 @@ import android.app.usage.NetworkStatsManager; import android.content.Context; import android.net.NetworkPolicy; import android.net.NetworkPolicyManager; +import android.net.NetworkStats; import android.net.NetworkTemplate; import android.text.format.DateUtils; @@ -40,6 +41,8 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; +import java.util.Set; + @RunWith(RobolectricTestRunner.class) public class NetworkCycleDataForUidLoaderTest { private static final String SUB_ID = "Test Subscriber"; @@ -62,7 +65,9 @@ public class NetworkCycleDataForUidLoaderTest { when(mContext.getSystemService(Context.NETWORK_POLICY_SERVICE)) .thenReturn(mNetworkPolicyManager); when(mNetworkPolicyManager.getNetworkPolicies()).thenReturn(new NetworkPolicy[0]); - mNetworkTemplate = NetworkTemplate.buildTemplateCarrierMetered(SUB_ID); + mNetworkTemplate = new NetworkTemplate.Builder(NetworkTemplate.MATCH_CARRIER) + .setMeteredness(NetworkStats.METERED_YES) + .setSubscriberIds(Set.of(SUB_ID)).build(); } @Test diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtilsTest.java index 3c339defbaf4..f6af09a34388 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtilsTest.java @@ -103,4 +103,30 @@ public class WifiEnterpriseRestrictionUtilsTest { assertThat(WifiEnterpriseRestrictionUtils.isWifiDirectAllowed(mContext)).isTrue(); } + + @Test + public void isAddWifiConfigAllowed_setSDKForS_shouldReturnTrue() { + ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.S); + when(mBundle.getBoolean(UserManager.DISALLOW_ADD_WIFI_CONFIG)).thenReturn(true); + + assertThat(WifiEnterpriseRestrictionUtils.isAddWifiConfigAllowed(mContext)).isTrue(); + } + + @Test + public void isAddWifiConfigAllowed_setSDKForTAndDisallowForRestriction_shouldReturnFalse() { + ReflectionHelpers.setStaticField( + Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.TIRAMISU); + when(mBundle.getBoolean(UserManager.DISALLOW_ADD_WIFI_CONFIG)).thenReturn(true); + + assertThat(WifiEnterpriseRestrictionUtils.isAddWifiConfigAllowed(mContext)).isFalse(); + } + + @Test + public void isAddWifiConfigAllowed_setSDKForTAndAllowForRestriction_shouldReturnTrue() { + ReflectionHelpers.setStaticField( + Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.TIRAMISU); + when(mBundle.getBoolean(UserManager.DISALLOW_ADD_WIFI_CONFIG)).thenReturn(false); + + assertThat(WifiEnterpriseRestrictionUtils.isAddWifiConfigAllowed(mContext)).isTrue(); + } } diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java index 71accc416416..00b5f5019485 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java @@ -78,6 +78,8 @@ public class SystemSettings { Settings.System.NOTIFICATION_SOUND, Settings.System.ACCELEROMETER_ROTATION, Settings.System.SHOW_BATTERY_PERCENT, + Settings.System.ALARM_VIBRATION_INTENSITY, + Settings.System.MEDIA_VIBRATION_INTENSITY, Settings.System.NOTIFICATION_VIBRATION_INTENSITY, Settings.System.RING_VIBRATION_INTENSITY, Settings.System.HAPTIC_FEEDBACK_INTENSITY, diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java index 84e9d2809205..6bcb7695cd22 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java @@ -118,6 +118,8 @@ public class SystemSettingsValidators { VALIDATORS.put(System.MUTE_STREAMS_AFFECTED, NON_NEGATIVE_INTEGER_VALIDATOR); VALIDATORS.put(System.VIBRATE_ON, BOOLEAN_VALIDATOR); VALIDATORS.put(System.APPLY_RAMPING_RINGER, BOOLEAN_VALIDATOR); + VALIDATORS.put(System.ALARM_VIBRATION_INTENSITY, VIBRATION_INTENSITY_VALIDATOR); + VALIDATORS.put(System.MEDIA_VIBRATION_INTENSITY, VIBRATION_INTENSITY_VALIDATOR); VALIDATORS.put(System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_VALIDATOR); VALIDATORS.put(System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_VALIDATOR); VALIDATORS.put(System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_VALIDATOR); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 00cdc9b0a058..c5f027b829d9 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -2916,6 +2916,18 @@ class SettingsProtoDumpUtil { dumpSetting(s, p, Settings.System.VIBRATE_WHEN_RINGING, SystemSettingsProto.Vibrate.WHEN_RINGING); + + // NOTIFICATION_VIBRATION_INTENSITY is already logged at Notification.vibration_intensity + // HAPTIC_FEEDBACK_INTENSITY is already logged at HapticFeedback.intensity + dumpSetting(s, p, + Settings.System.ALARM_VIBRATION_INTENSITY, + SystemSettingsProto.Vibrate.ALARM_INTENSITY); + dumpSetting(s, p, + Settings.System.MEDIA_VIBRATION_INTENSITY, + SystemSettingsProto.Vibrate.MEDIA_INTENSITY); + dumpSetting(s, p, + Settings.System.RING_VIBRATION_INTENSITY, + SystemSettingsProto.Vibrate.RING_INTENSITY); p.end(vibrateToken); final long volumeToken = p.start(SystemSettingsProto.VOLUME); diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 10252ee3bc60..0c70821527dd 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -23,6 +23,7 @@ > <!-- Standard permissions granted to the shell. --> + <uses-permission android:name="android.permission.LAUNCH_DEVICE_MANAGER_SETUP" /> <uses-permission android:name="android.permission.GET_RUNTIME_PERMISSIONS" /> <uses-permission android:name="android.permission.SEND_SMS" /> <uses-permission android:name="android.permission.READ_SMS" /> @@ -607,15 +608,14 @@ <!-- Permission required for ATS test - CarDevicePolicyManagerTest --> <uses-permission android:name="android.permission.LOCK_DEVICE" /> - <!-- Permission required for CTS test - CtsSafetyCenterTestCases --> + <!-- Permissions required for CTS test - CtsSafetyCenterTestCases --> <uses-permission android:name="android.permission.SEND_SAFETY_CENTER_UPDATE" /> - - <!-- Permission required for CTS test - CtsSafetyCenterTestCases --> <uses-permission android:name="android.permission.READ_SAFETY_CENTER_STATUS" /> + <uses-permission android:name="android.permission.MANAGE_SAFETY_CENTER" /> + - <!-- Permission required for CTS test - CommunalManagerTest --> - <uses-permission android:name="android.permission.WRITE_COMMUNAL_STATE" /> - <uses-permission android:name="android.permission.READ_COMMUNAL_STATE" /> + <!-- Permission required for CTS test - Notification test suite --> + <uses-permission android:name="android.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL" /> <application android:label="@string/app_label" android:theme="@android:style/Theme.DeviceDefault.DayNight" diff --git a/packages/Shell/res/values-watch/strings.xml b/packages/Shell/res/values-watch/strings.xml new file mode 100644 index 000000000000..5f7bfcb25d85 --- /dev/null +++ b/packages/Shell/res/values-watch/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Title for Bug report notification indicating the number of the bug report and the + percentage complete. Example: "Bug report #3 is 20% complete" [CHAR LIMIT=50] --> + <string name="bugreport_in_progress_title">Bug report <xliff:g id="id" example="#3">#%1$d</xliff:g> is <xliff:g id="percentage" example="20%">%2$s</xliff:g> complete</string> +</resources> diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java index ee9d4301770a..c5a01a1c2b93 100644 --- a/packages/Shell/src/com/android/shell/BugreportProgressService.java +++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java @@ -199,6 +199,15 @@ public class BugreportProgressService extends Service { */ private static final String BUGREPORT_DIR = "bugreports"; + /** + * The directory in which System Trace files from the native System Tracing app are stored for + * Wear devices. + */ + private static final String WEAR_SYSTEM_TRACES_DIRECTORY_ON_DEVICE = "data/local/traces/"; + + /** The directory that contains System Traces in bugreports that include System Traces. */ + private static final String WEAR_SYSTEM_TRACES_DIRECTORY_IN_BUGREPORT = "systraces/"; + private static final String NOTIFICATION_CHANNEL_ID = "bugreports"; /** @@ -724,14 +733,16 @@ public class BugreportProgressService extends Service { nf.setMaximumFractionDigits(2); final String percentageText = nf.format((double) info.progress.intValue() / 100); - String title = mContext.getString(R.string.bugreport_in_progress_title, info.id); - - // TODO: Remove this workaround when notification progress is implemented on Wear. + final String title; if (mIsWatch) { + // TODO: Remove this workaround when notification progress is implemented on Wear. nf.setMinimumFractionDigits(0); nf.setMaximumFractionDigits(0); final String watchPercentageText = nf.format((double) info.progress.intValue() / 100); - title = title + "\n" + watchPercentageText; + title = mContext.getString( + R.string.bugreport_in_progress_title, info.id, watchPercentageText); + } else { + title = mContext.getString(R.string.bugreport_in_progress_title, info.id); } final String name = @@ -1456,6 +1467,16 @@ public class BugreportProgressService extends Service { } } + /** Returns an array of the system trace files collected by the System Tracing native app. */ + private static File[] getSystemTraceFiles() { + try { + return new File(WEAR_SYSTEM_TRACES_DIRECTORY_ON_DEVICE).listFiles(); + } catch (SecurityException e) { + Log.e(TAG, "Error getting system trace files.", e); + return new File[]{}; + } + } + /** * Adds the user-provided info into the bugreport zip file. * <p> @@ -1475,8 +1496,17 @@ public class BugreportProgressService extends Service { Log.wtf(TAG, "addDetailsToZipFile(): no bugreportFile on " + info); return; } - if (TextUtils.isEmpty(info.getTitle()) && TextUtils.isEmpty(info.getDescription())) { - Log.d(TAG, "Not touching zip file since neither title nor description are set"); + + File[] systemTracesToIncludeInBugreport = new File[] {}; + if (mIsWatch) { + systemTracesToIncludeInBugreport = getSystemTraceFiles(); + Log.d(TAG, "Found " + systemTracesToIncludeInBugreport.length + " system traces."); + } + + if (TextUtils.isEmpty(info.getTitle()) + && TextUtils.isEmpty(info.getDescription()) + && systemTracesToIncludeInBugreport.length == 0) { + Log.d(TAG, "Not touching zip file: no detail to add."); return; } if (info.addedDetailsToZip || info.addingDetailsToZip) { @@ -1487,7 +1517,10 @@ public class BugreportProgressService extends Service { // It's not possible to add a new entry into an existing file, so we need to create a new // zip, copy all entries, then rename it. - sendBugreportBeingUpdatedNotification(mContext, info.id); // ...and that takes time + if (!mIsWatch) { + // TODO(b/184854609): re-introduce this notification for Wear. + sendBugreportBeingUpdatedNotification(mContext, info.id); // ...and that takes time + } final File dir = info.bugreportFile.getParentFile(); final File tmpZip = new File(dir, "tmp-" + info.bugreportFile.getName()); @@ -1508,6 +1541,13 @@ public class BugreportProgressService extends Service { } // Then add the user-provided info. + if (systemTracesToIncludeInBugreport.length != 0) { + for (File trace : systemTracesToIncludeInBugreport) { + addEntry(zos, + WEAR_SYSTEM_TRACES_DIRECTORY_IN_BUGREPORT + trace.getName(), + new FileInputStream(trace)); + } + } addEntry(zos, "title.txt", info.getTitle()); addEntry(zos, "description.txt", info.getDescription()); } catch (IOException e) { diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index c805e2d0330a..137a1fd4f299 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -129,8 +129,15 @@ android_library { } filegroup { + name: "AAA-src", + srcs: ["tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java"], + path: "tests/src", +} + +filegroup { name: "SystemUI-tests-utils", srcs: [ + "tests/src/com/android/systemui/SysuiBaseFragmentTest.java", "tests/src/com/android/systemui/SysuiTestCase.java", "tests/src/com/android/systemui/TestableDependency.java", "tests/src/com/android/systemui/classifier/FalsingManagerFake.java", diff --git a/packages/SystemUI/animation/Android.bp b/packages/SystemUI/animation/Android.bp index 1b15d20d2c52..46adfeba0fb0 100644 --- a/packages/SystemUI/animation/Android.bp +++ b/packages/SystemUI/animation/Android.bp @@ -39,5 +39,5 @@ android_library { ], manifest: "AndroidManifest.xml", - + kotlincflags: ["-Xjvm-default=enable"], } diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt index a0d335db92d6..08d217d15a5a 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt @@ -95,6 +95,9 @@ class ActivityLaunchAnimator( */ var callback: Callback? = null + /** The set of [Listener] that should be notified of any animation started by this animator. */ + private val listeners = LinkedHashSet<Listener>() + /** * Start an intent and animate the opening window. The intent will be started by running * [intentStarter], which should use the provided [RemoteAnimationAdapter] and return the launch @@ -214,6 +217,16 @@ class ActivityLaunchAnimator( } } + /** Add a [Listener] that can listen to launch animations. */ + fun addListener(listener: Listener) { + listeners.add(listener) + } + + /** Remove a [Listener]. */ + fun removeListener(listener: Listener) { + listeners.remove(listener) + } + /** Create a new animation [Runner] controlled by [controller]. */ @VisibleForTesting fun createRunner(controller: Controller): Runner = Runner(controller) @@ -234,13 +247,27 @@ class ActivityLaunchAnimator( /** Hide the keyguard and animate using [runner]. */ fun hideKeyguardWithAnimation(runner: IRemoteAnimationRunner) - /** Enable/disable window blur so they don't overlap with the window launch animation **/ - fun setBlursDisabledForAppLaunch(disabled: Boolean) - /* Get the background color of [task]. */ fun getBackgroundColor(task: TaskInfo): Int } + interface Listener { + /** Called when an activity launch animation started. */ + @JvmDefault + fun onLaunchAnimationStart() {} + + /** + * Called when an activity launch animation is finished. This will be called if and only if + * [onLaunchAnimationStart] was called earlier. + */ + @JvmDefault + fun onLaunchAnimationEnd() {} + + /** Called when an activity launch animation made progress. */ + @JvmDefault + fun onLaunchAnimationProgress(linearProgress: Float) {} + } + /** * A controller that takes care of applying the animation to an expanding view. * @@ -396,12 +423,12 @@ class ActivityLaunchAnimator( val delegate = this.controller val controller = object : LaunchAnimator.Controller by delegate { override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) { - callback.setBlursDisabledForAppLaunch(true) + listeners.forEach { it.onLaunchAnimationStart() } delegate.onLaunchAnimationStart(isExpandingFullyAbove) } override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) { - callback.setBlursDisabledForAppLaunch(false) + listeners.forEach { it.onLaunchAnimationEnd() } iCallback?.invoke() delegate.onLaunchAnimationEnd(isExpandingFullyAbove) } @@ -413,6 +440,7 @@ class ActivityLaunchAnimator( ) { applyStateToWindow(window, state) navigationBar?.let { applyStateToNavigationBar(it, state, linearProgress) } + listeners.forEach { it.onLaunchAnimationProgress(linearProgress) } delegate.onLaunchAnimationProgress(state, progress, linearProgress) } } diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml index 1e142eaeef86..e64b586a3e6f 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml @@ -46,8 +46,6 @@ android:clipChildren="false" android:clipToPadding="false" android:paddingTop="@dimen/keyguard_security_view_top_margin" - android:paddingStart="@dimen/keyguard_security_view_lateral_margin" - android:paddingEnd="@dimen/keyguard_security_view_lateral_margin" android:layout_gravity="center" android:gravity="center"> </com.android.keyguard.KeyguardSecurityViewFlipper> diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml index e1550aa0c87c..e77e084c48e8 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml @@ -25,6 +25,7 @@ android:layout_height="match_parent" androidprv:layout_maxWidth="@dimen/keyguard_security_width" androidprv:layout_maxHeight="@dimen/keyguard_security_height" + android:layout_gravity="center_horizontal|bottom" android:gravity="bottom" > diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml index f613a195ea67..231ead8dc273 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml @@ -28,6 +28,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" androidprv:layout_maxWidth="@dimen/keyguard_security_width" + android:layout_gravity="center_horizontal|bottom" android:clipChildren="false" android:clipToPadding="false"> @@ -65,6 +66,7 @@ android:orientation="vertical" android:layout_gravity="bottom|center_horizontal" android:layout_marginTop="@dimen/keyguard_eca_top_margin" + android:layout_marginBottom="@dimen/keyguard_eca_bottom_margin" android:gravity="center_horizontal" /> </com.android.keyguard.KeyguardPatternView> diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml index 94566c78cc93..7ce6f0e56b8f 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml @@ -25,6 +25,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" androidprv:layout_maxWidth="@dimen/keyguard_security_width" + android:layout_gravity="center_horizontal|bottom" android:orientation="vertical" > diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml index 3e34e4b255eb..b765f497f9c4 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml @@ -25,7 +25,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" androidprv:layout_maxWidth="@dimen/keyguard_security_width" - android:gravity="center_horizontal"> + android:layout_gravity="center_horizontal|bottom"> <Space android:layout_width="match_parent" diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml index d5510e90af29..917ea6b6c518 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml @@ -26,7 +26,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" androidprv:layout_maxWidth="@dimen/keyguard_security_width" - android:gravity="center_horizontal"> + android:layout_gravity="center_horizontal|bottom"> <Space android:layout_width="match_parent" diff --git a/packages/SystemUI/res-keyguard/values/bools.xml b/packages/SystemUI/res-keyguard/values/bools.xml index 2b83787172d3..c5bf4ce48188 100644 --- a/packages/SystemUI/res-keyguard/values/bools.xml +++ b/packages/SystemUI/res-keyguard/values/bools.xml @@ -17,4 +17,5 @@ <resources> <bool name="kg_show_ime_at_screen_on">true</bool> <bool name="kg_use_all_caps">true</bool> + <bool name="flag_active_unlock">false</bool> </resources> diff --git a/packages/SystemUI/res-keyguard/values/config.xml b/packages/SystemUI/res-keyguard/values/config.xml index 6194aa095610..e8244433260d 100644 --- a/packages/SystemUI/res-keyguard/values/config.xml +++ b/packages/SystemUI/res-keyguard/values/config.xml @@ -27,6 +27,6 @@ <bool name="can_use_one_handed_bouncer">false</bool> <!-- Will display the bouncer on one side of the display, and the current user icon and user switcher on the other side --> - <bool name="bouncer_display_user_switcher">false</bool> + <bool name="config_enableBouncerUserSwitcher">false</bool> </resources> diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml index c8bb8e99be17..cbf4f83daeb5 100644 --- a/packages/SystemUI/res-keyguard/values/dimens.xml +++ b/packages/SystemUI/res-keyguard/values/dimens.xml @@ -37,7 +37,6 @@ <!-- Margin around the various security views --> <dimen name="keyguard_security_view_top_margin">8dp</dimen> - <dimen name="keyguard_security_view_lateral_margin">20dp</dimen> <dimen name="keyguard_eca_top_margin">18dp</dimen> <dimen name="keyguard_eca_bottom_margin">12dp</dimen> diff --git a/packages/SystemUI/res-product/values-zu/strings.xml b/packages/SystemUI/res-product/values-zu/strings.xml index e6c140a76bdf..8b79a22a72a9 100644 --- a/packages/SystemUI/res-product/values-zu/strings.xml +++ b/packages/SystemUI/res-product/values-zu/strings.xml @@ -40,7 +40,7 @@ <string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4682221342671290678">"Uzame ngokungalungile ukuvula ifoni izikhathi ezingu-<xliff:g id="NUMBER">%d</xliff:g>. Iphrofayela yomsebenzi izosuswa, okuzosusa yonke idatha yephrofayela."</string> <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="1860049973474855672">"Udwebe ngokungalungile iphethini yakho yokuvula ngezikhathi ezingu-<xliff:g id="NUMBER_0">%1$d</xliff:g>. Ngemuva kwemizamo engaphumelelanga kaningi engu-<xliff:g id="NUMBER_1">%2$d</xliff:g>, uzocelwa ukuthi uvule ithebulethi yakho usebenzisa i-akhawunti ye-imeyili.\n\nZama futhi kumasekhondi angu-<xliff:g id="NUMBER_2">%3$d</xliff:g>."</string> <string name="kg_failed_attempts_almost_at_login" product="default" msgid="44112553371516141">"Ukulayisha ungenisa iphathini yakho yokuvula ngendlela engalungile izikhathi ezi-<xliff:g id="NUMBER_0">%1$d</xliff:g> Emva kweminye imizamo engu-<xliff:g id="NUMBER_1">%2$d</xliff:g>, uzocelwa ukuvula ifoni yakho usebenzisa ukungena ngemvume ku-Google\n\n Zame futhi emumva kwengu- <xliff:g id="NUMBER_2">%3$d</xliff:g> imizuzwana."</string> - <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Vula ifoni yakho ukuthola izinketho ezengeziwe"</string> - <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Vula ithebulethi yakho ukuthola izinketho ezengeziwe"</string> - <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Vula idivayisi yakho ukuthola izinketho ezengeziwe"</string> + <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Vula ifoni yakho ukuthola okunye okungakhethwa"</string> + <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Vula ithebulethi yakho ukuthola okunye okungakhethwa"</string> + <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Vula idivayisi yakho ukuthola okunye okungakhethwa"</string> </resources> diff --git a/packages/SystemUI/res/values-af/tiles_states_strings.xml b/packages/SystemUI/res/values-af/tiles_states_strings.xml index 2059490e0456..4b1a5b85d1d2 100644 --- a/packages/SystemUI/res/values-af/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-af/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Af"</item> <item msgid="2075645297847971154">"Aan"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Onbeskikbaar"</item> <item msgid="9103697205127645916">"Af"</item> diff --git a/packages/SystemUI/res/values-am/tiles_states_strings.xml b/packages/SystemUI/res/values-am/tiles_states_strings.xml index 7fdfd11a9310..0f7ee3ecd8d2 100644 --- a/packages/SystemUI/res/values-am/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-am/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"ጠፍቷል"</item> <item msgid="2075645297847971154">"በርቷል"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"አይገኝም"</item> <item msgid="9103697205127645916">"ጠፍቷል"</item> diff --git a/packages/SystemUI/res/values-ar/tiles_states_strings.xml b/packages/SystemUI/res/values-ar/tiles_states_strings.xml index d7026c7395bf..2da87c467177 100644 --- a/packages/SystemUI/res/values-ar/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ar/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"الميزة غير مفعّلة"</item> <item msgid="2075645297847971154">"الميزة مفعّلة"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"الميزة غير متاحة"</item> <item msgid="9103697205127645916">"الميزة غير مفعّلة"</item> diff --git a/packages/SystemUI/res/values-as/tiles_states_strings.xml b/packages/SystemUI/res/values-as/tiles_states_strings.xml index efb235170486..2ede37473ea5 100644 --- a/packages/SystemUI/res/values-as/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-as/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"অফ আছে"</item> <item msgid="2075645297847971154">"অন কৰা আছে"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"উপলব্ধ নহয়"</item> <item msgid="9103697205127645916">"অফ আছে"</item> diff --git a/packages/SystemUI/res/values-az/tiles_states_strings.xml b/packages/SystemUI/res/values-az/tiles_states_strings.xml index 7c47203ad0c2..f52a9e1bf6fa 100644 --- a/packages/SystemUI/res/values-az/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-az/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Deaktiv"</item> <item msgid="2075645297847971154">"Aktiv"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Əlçatan deyil"</item> <item msgid="9103697205127645916">"Deaktiv"</item> diff --git a/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml b/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml index 88caf9789d63..d9e0bfca36ff 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Isključeno"</item> <item msgid="2075645297847971154">"Uključeno"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Nedostupno"</item> <item msgid="9103697205127645916">"Isključeno"</item> diff --git a/packages/SystemUI/res/values-be/tiles_states_strings.xml b/packages/SystemUI/res/values-be/tiles_states_strings.xml index ecfde0b5a08e..5c7c40fed7a7 100644 --- a/packages/SystemUI/res/values-be/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-be/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Выключана"</item> <item msgid="2075645297847971154">"Уключана"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Недаступна"</item> <item msgid="9103697205127645916">"Выключана"</item> diff --git a/packages/SystemUI/res/values-bg/tiles_states_strings.xml b/packages/SystemUI/res/values-bg/tiles_states_strings.xml index 7424f8155de1..6839b830283c 100644 --- a/packages/SystemUI/res/values-bg/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-bg/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Изкл."</item> <item msgid="2075645297847971154">"Вкл."</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Не е налице"</item> <item msgid="9103697205127645916">"Изкл."</item> diff --git a/packages/SystemUI/res/values-bn/tiles_states_strings.xml b/packages/SystemUI/res/values-bn/tiles_states_strings.xml index eddf8515b207..7a6b3ac1c371 100644 --- a/packages/SystemUI/res/values-bn/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-bn/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"বন্ধ আছে"</item> <item msgid="2075645297847971154">"চালু আছে"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"উপলভ্য নেই"</item> <item msgid="9103697205127645916">"বন্ধ আছে"</item> diff --git a/packages/SystemUI/res/values-bs/tiles_states_strings.xml b/packages/SystemUI/res/values-bs/tiles_states_strings.xml index 88caf9789d63..d9e0bfca36ff 100644 --- a/packages/SystemUI/res/values-bs/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-bs/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Isključeno"</item> <item msgid="2075645297847971154">"Uključeno"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Nedostupno"</item> <item msgid="9103697205127645916">"Isključeno"</item> diff --git a/packages/SystemUI/res/values-ca/tiles_states_strings.xml b/packages/SystemUI/res/values-ca/tiles_states_strings.xml index e3538fad1a6e..28f3da4650eb 100644 --- a/packages/SystemUI/res/values-ca/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ca/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Desactivat"</item> <item msgid="2075645297847971154">"Activat"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"No disponible"</item> <item msgid="9103697205127645916">"Desactivat"</item> diff --git a/packages/SystemUI/res/values-cs/tiles_states_strings.xml b/packages/SystemUI/res/values-cs/tiles_states_strings.xml index 3f40ab6ea7f4..ce64e273dc02 100644 --- a/packages/SystemUI/res/values-cs/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-cs/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Vyp"</item> <item msgid="2075645297847971154">"Zap"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Nedostupné"</item> <item msgid="9103697205127645916">"Vyp"</item> diff --git a/packages/SystemUI/res/values-da/tiles_states_strings.xml b/packages/SystemUI/res/values-da/tiles_states_strings.xml index b479a44b33f4..9e4c6f4b8459 100644 --- a/packages/SystemUI/res/values-da/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-da/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Fra"</item> <item msgid="2075645297847971154">"Til"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Ikke tilgængelig"</item> <item msgid="9103697205127645916">"Fra"</item> diff --git a/packages/SystemUI/res/values-de/tiles_states_strings.xml b/packages/SystemUI/res/values-de/tiles_states_strings.xml index 81f030cef2f8..e958670b502e 100644 --- a/packages/SystemUI/res/values-de/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-de/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Aus"</item> <item msgid="2075645297847971154">"An"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Nicht verfügbar"</item> <item msgid="9103697205127645916">"Aus"</item> diff --git a/packages/SystemUI/res/values-el/tiles_states_strings.xml b/packages/SystemUI/res/values-el/tiles_states_strings.xml index 422f2aa11630..1c9583518595 100644 --- a/packages/SystemUI/res/values-el/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-el/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Ανενεργό"</item> <item msgid="2075645297847971154">"Ενεργό"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Μη διαθέσιμο"</item> <item msgid="9103697205127645916">"Ανενεργό"</item> diff --git a/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml index 9b00c8b32bf1..13f236b639f0 100644 --- a/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml @@ -86,6 +86,11 @@ <item msgid="5715725170633593906">"Off"</item> <item msgid="2075645297847971154">"On"</item> </string-array> + <string-array name="tile_states_color_correction"> + <item msgid="2840507878437297682">"Unavailable"</item> + <item msgid="1909756493418256167">"Off"</item> + <item msgid="4531508423703413340">"On"</item> + </string-array> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Unavailable"</item> <item msgid="9103697205127645916">"Off"</item> diff --git a/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml index 9b00c8b32bf1..13f236b639f0 100644 --- a/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml @@ -86,6 +86,11 @@ <item msgid="5715725170633593906">"Off"</item> <item msgid="2075645297847971154">"On"</item> </string-array> + <string-array name="tile_states_color_correction"> + <item msgid="2840507878437297682">"Unavailable"</item> + <item msgid="1909756493418256167">"Off"</item> + <item msgid="4531508423703413340">"On"</item> + </string-array> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Unavailable"</item> <item msgid="9103697205127645916">"Off"</item> diff --git a/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml index 9b00c8b32bf1..13f236b639f0 100644 --- a/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml @@ -86,6 +86,11 @@ <item msgid="5715725170633593906">"Off"</item> <item msgid="2075645297847971154">"On"</item> </string-array> + <string-array name="tile_states_color_correction"> + <item msgid="2840507878437297682">"Unavailable"</item> + <item msgid="1909756493418256167">"Off"</item> + <item msgid="4531508423703413340">"On"</item> + </string-array> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Unavailable"</item> <item msgid="9103697205127645916">"Off"</item> diff --git a/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml index 9b00c8b32bf1..13f236b639f0 100644 --- a/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml @@ -86,6 +86,11 @@ <item msgid="5715725170633593906">"Off"</item> <item msgid="2075645297847971154">"On"</item> </string-array> + <string-array name="tile_states_color_correction"> + <item msgid="2840507878437297682">"Unavailable"</item> + <item msgid="1909756493418256167">"Off"</item> + <item msgid="4531508423703413340">"On"</item> + </string-array> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Unavailable"</item> <item msgid="9103697205127645916">"Off"</item> diff --git a/packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml index 0012b57522aa..e6baa3127c92 100644 --- a/packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml @@ -86,6 +86,11 @@ <item msgid="5715725170633593906">"Off"</item> <item msgid="2075645297847971154">"On"</item> </string-array> + <string-array name="tile_states_color_correction"> + <item msgid="2840507878437297682">"Unavailable"</item> + <item msgid="1909756493418256167">"Off"</item> + <item msgid="4531508423703413340">"On"</item> + </string-array> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Unavailable"</item> <item msgid="9103697205127645916">"Off"</item> diff --git a/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml b/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml index afb0e727046f..0d77977fd174 100644 --- a/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Desactivado"</item> <item msgid="2075645297847971154">"Activado"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"No disponible"</item> <item msgid="9103697205127645916">"Desactivado"</item> diff --git a/packages/SystemUI/res/values-es/tiles_states_strings.xml b/packages/SystemUI/res/values-es/tiles_states_strings.xml index dd1ed433a68a..080731a2feb1 100644 --- a/packages/SystemUI/res/values-es/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-es/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Desactivado"</item> <item msgid="2075645297847971154">"Activado"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"No disponible"</item> <item msgid="9103697205127645916">"Desactivado"</item> diff --git a/packages/SystemUI/res/values-et/tiles_states_strings.xml b/packages/SystemUI/res/values-et/tiles_states_strings.xml index 26fbe80c170b..26d71fe07a57 100644 --- a/packages/SystemUI/res/values-et/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-et/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Väljas"</item> <item msgid="2075645297847971154">"Sees"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Pole saadaval"</item> <item msgid="9103697205127645916">"Väljas"</item> diff --git a/packages/SystemUI/res/values-eu/tiles_states_strings.xml b/packages/SystemUI/res/values-eu/tiles_states_strings.xml index a2a08dbb01be..11090458f1f5 100644 --- a/packages/SystemUI/res/values-eu/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-eu/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Desaktibatuta"</item> <item msgid="2075645297847971154">"Aktibatuta"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Ez dago erabilgarri"</item> <item msgid="9103697205127645916">"Desaktibatuta"</item> diff --git a/packages/SystemUI/res/values-fa/tiles_states_strings.xml b/packages/SystemUI/res/values-fa/tiles_states_strings.xml index 8c40e716f643..ab755197d0a5 100644 --- a/packages/SystemUI/res/values-fa/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-fa/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"خاموش"</item> <item msgid="2075645297847971154">"روشن"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"دردسترس نیست"</item> <item msgid="9103697205127645916">"خاموش"</item> diff --git a/packages/SystemUI/res/values-fi/tiles_states_strings.xml b/packages/SystemUI/res/values-fi/tiles_states_strings.xml index cb9e07c322fc..6fa9466e1313 100644 --- a/packages/SystemUI/res/values-fi/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-fi/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Poissa päältä"</item> <item msgid="2075645297847971154">"Päällä"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Ei saatavilla"</item> <item msgid="9103697205127645916">"Poissa päältä"</item> diff --git a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml index eb38d94425fa..aec8ab87dcfb 100644 --- a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Désactivé"</item> <item msgid="2075645297847971154">"Activé"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Non disponible"</item> <item msgid="9103697205127645916">"Désactivée"</item> diff --git a/packages/SystemUI/res/values-fr/tiles_states_strings.xml b/packages/SystemUI/res/values-fr/tiles_states_strings.xml index 3aefb1d86c88..01d9c1d74dd0 100644 --- a/packages/SystemUI/res/values-fr/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-fr/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Désactivé"</item> <item msgid="2075645297847971154">"Activé"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Indisponible"</item> <item msgid="9103697205127645916">"Désactivée"</item> diff --git a/packages/SystemUI/res/values-gl/tiles_states_strings.xml b/packages/SystemUI/res/values-gl/tiles_states_strings.xml index 90dcf7d0074b..9045983425df 100644 --- a/packages/SystemUI/res/values-gl/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-gl/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Non"</item> <item msgid="2075645297847971154">"Si"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Non dispoñible"</item> <item msgid="9103697205127645916">"Non"</item> diff --git a/packages/SystemUI/res/values-gu/tiles_states_strings.xml b/packages/SystemUI/res/values-gu/tiles_states_strings.xml index e50f3cff1328..15b0f9fe7fbe 100644 --- a/packages/SystemUI/res/values-gu/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-gu/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"બંધ છે"</item> <item msgid="2075645297847971154">"ચાલુ છે"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"ઉપલબ્ધ નથી"</item> <item msgid="9103697205127645916">"બંધ છે"</item> diff --git a/packages/SystemUI/res/values-hi/tiles_states_strings.xml b/packages/SystemUI/res/values-hi/tiles_states_strings.xml index 769891268e5b..00fa69936dc6 100644 --- a/packages/SystemUI/res/values-hi/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-hi/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"बंद है"</item> <item msgid="2075645297847971154">"चालू है"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"उपलब्ध नहीं है"</item> <item msgid="9103697205127645916">"बंद है"</item> diff --git a/packages/SystemUI/res/values-hr/tiles_states_strings.xml b/packages/SystemUI/res/values-hr/tiles_states_strings.xml index ae9ffb353293..730081667113 100644 --- a/packages/SystemUI/res/values-hr/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-hr/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Isključeno"</item> <item msgid="2075645297847971154">"Uključeno"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Nedostupno"</item> <item msgid="9103697205127645916">"Isključeno"</item> diff --git a/packages/SystemUI/res/values-hu/tiles_states_strings.xml b/packages/SystemUI/res/values-hu/tiles_states_strings.xml index 140f2db0c85c..52450e4b3937 100644 --- a/packages/SystemUI/res/values-hu/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-hu/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Ki"</item> <item msgid="2075645297847971154">"Be"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Nem áll rendelkezésre"</item> <item msgid="9103697205127645916">"Ki"</item> diff --git a/packages/SystemUI/res/values-hy/tiles_states_strings.xml b/packages/SystemUI/res/values-hy/tiles_states_strings.xml index df69ce7544f4..23a096b194af 100644 --- a/packages/SystemUI/res/values-hy/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-hy/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Անջատված է"</item> <item msgid="2075645297847971154">"Միացված է"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Հասանելի չէ"</item> <item msgid="9103697205127645916">"Անջատված է"</item> diff --git a/packages/SystemUI/res/values-in/tiles_states_strings.xml b/packages/SystemUI/res/values-in/tiles_states_strings.xml index 811294f03902..c296e5443190 100644 --- a/packages/SystemUI/res/values-in/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-in/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Nonaktif"</item> <item msgid="2075645297847971154">"Aktif"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Tidak tersedia"</item> <item msgid="9103697205127645916">"Nonaktif"</item> diff --git a/packages/SystemUI/res/values-is/tiles_states_strings.xml b/packages/SystemUI/res/values-is/tiles_states_strings.xml index dd704a74b736..25b3dcc53661 100644 --- a/packages/SystemUI/res/values-is/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-is/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Slökkt"</item> <item msgid="2075645297847971154">"Kveikt"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Ekki í boði"</item> <item msgid="9103697205127645916">"Slökkt"</item> diff --git a/packages/SystemUI/res/values-it/tiles_states_strings.xml b/packages/SystemUI/res/values-it/tiles_states_strings.xml index 159699893513..7e6827a4d8c2 100644 --- a/packages/SystemUI/res/values-it/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-it/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Off"</item> <item msgid="2075645297847971154">"On"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Non disponibile"</item> <item msgid="9103697205127645916">"Off"</item> diff --git a/packages/SystemUI/res/values-iw/tiles_states_strings.xml b/packages/SystemUI/res/values-iw/tiles_states_strings.xml index 28548bcdd656..bf3c2b6ad767 100644 --- a/packages/SystemUI/res/values-iw/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-iw/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"כבוי"</item> <item msgid="2075645297847971154">"פועל"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"לא זמין"</item> <item msgid="9103697205127645916">"כבוי"</item> diff --git a/packages/SystemUI/res/values-ja/tiles_states_strings.xml b/packages/SystemUI/res/values-ja/tiles_states_strings.xml index 7fc9c08c2e83..9197aab790b0 100644 --- a/packages/SystemUI/res/values-ja/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ja/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"OFF"</item> <item msgid="2075645297847971154">"ON"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"使用不可"</item> <item msgid="9103697205127645916">"OFF"</item> diff --git a/packages/SystemUI/res/values-ka/tiles_states_strings.xml b/packages/SystemUI/res/values-ka/tiles_states_strings.xml index 40dfd3993d44..485c3de7bdcf 100644 --- a/packages/SystemUI/res/values-ka/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ka/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"გამორთულია"</item> <item msgid="2075645297847971154">"ჩართულია"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"მიუწვდომელია"</item> <item msgid="9103697205127645916">"გამორთულია"</item> diff --git a/packages/SystemUI/res/values-kk/tiles_states_strings.xml b/packages/SystemUI/res/values-kk/tiles_states_strings.xml index 3e9aefa08a22..b143632803cb 100644 --- a/packages/SystemUI/res/values-kk/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-kk/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Өшірулі"</item> <item msgid="2075645297847971154">"Қосулы"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Қолжетімсіз"</item> <item msgid="9103697205127645916">"Өшірулі"</item> diff --git a/packages/SystemUI/res/values-km/tiles_states_strings.xml b/packages/SystemUI/res/values-km/tiles_states_strings.xml index 32a6e228d9c7..38d3894d07e1 100644 --- a/packages/SystemUI/res/values-km/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-km/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"បិទ"</item> <item msgid="2075645297847971154">"បើក"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"មិនមានទេ"</item> <item msgid="9103697205127645916">"បិទ"</item> diff --git a/packages/SystemUI/res/values-kn/tiles_states_strings.xml b/packages/SystemUI/res/values-kn/tiles_states_strings.xml index c13e1981837e..022c5cf35f5c 100644 --- a/packages/SystemUI/res/values-kn/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-kn/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"ಆಫ್ ಮಾಡಿ"</item> <item msgid="2075645297847971154">"ಆನ್ ಮಾಡಿ"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"ಲಭ್ಯವಿಲ್ಲ"</item> <item msgid="9103697205127645916">"ಆಫ್ ಮಾಡಿ"</item> diff --git a/packages/SystemUI/res/values-ko/tiles_states_strings.xml b/packages/SystemUI/res/values-ko/tiles_states_strings.xml index 28d45cf685de..ae6f148270c5 100644 --- a/packages/SystemUI/res/values-ko/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ko/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"꺼짐"</item> <item msgid="2075645297847971154">"켜짐"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"이용 불가"</item> <item msgid="9103697205127645916">"꺼짐"</item> diff --git a/packages/SystemUI/res/values-ky/tiles_states_strings.xml b/packages/SystemUI/res/values-ky/tiles_states_strings.xml index 139d784e0668..0eadc34e37ba 100644 --- a/packages/SystemUI/res/values-ky/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ky/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Өчүк"</item> <item msgid="2075645297847971154">"Күйүк"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Жеткиликсиз"</item> <item msgid="9103697205127645916">"Өчүк"</item> diff --git a/packages/SystemUI/res/values-lo/tiles_states_strings.xml b/packages/SystemUI/res/values-lo/tiles_states_strings.xml index 74c0f245b653..5fe5cfff03bf 100644 --- a/packages/SystemUI/res/values-lo/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-lo/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"ປິດ"</item> <item msgid="2075645297847971154">"ເປີດ"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"ບໍ່ສາມາດໃຊ້ໄດ້"</item> <item msgid="9103697205127645916">"ປິດ"</item> diff --git a/packages/SystemUI/res/values-lt/tiles_states_strings.xml b/packages/SystemUI/res/values-lt/tiles_states_strings.xml index 08e0e6dac711..7a0caa9c9afa 100644 --- a/packages/SystemUI/res/values-lt/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-lt/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Išjungta"</item> <item msgid="2075645297847971154">"Įjungta"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Nepasiekiama"</item> <item msgid="9103697205127645916">"Išjungta"</item> diff --git a/packages/SystemUI/res/values-lv/tiles_states_strings.xml b/packages/SystemUI/res/values-lv/tiles_states_strings.xml index c5718f8490ac..872dba60ca1d 100644 --- a/packages/SystemUI/res/values-lv/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-lv/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Izslēgts"</item> <item msgid="2075645297847971154">"Ieslēgts"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Nav pieejama"</item> <item msgid="9103697205127645916">"Izslēgta"</item> diff --git a/packages/SystemUI/res/values-mk/tiles_states_strings.xml b/packages/SystemUI/res/values-mk/tiles_states_strings.xml index fa484ae1c76b..65e94f371e7f 100644 --- a/packages/SystemUI/res/values-mk/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-mk/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Исклучено"</item> <item msgid="2075645297847971154">"Вклучено"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Недостапно"</item> <item msgid="9103697205127645916">"Исклучено"</item> diff --git a/packages/SystemUI/res/values-ml/tiles_states_strings.xml b/packages/SystemUI/res/values-ml/tiles_states_strings.xml index 0cca763b8e85..8746c74bd00a 100644 --- a/packages/SystemUI/res/values-ml/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ml/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"ഓഫാണ്"</item> <item msgid="2075645297847971154">"ഓണാണ്"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"ലഭ്യമല്ല"</item> <item msgid="9103697205127645916">"ഓഫാണ്"</item> diff --git a/packages/SystemUI/res/values-mn/tiles_states_strings.xml b/packages/SystemUI/res/values-mn/tiles_states_strings.xml index 41049d851b05..07dde9f76a98 100644 --- a/packages/SystemUI/res/values-mn/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-mn/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Унтраалттай"</item> <item msgid="2075645297847971154">"Асаалттай"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Боломжгүй"</item> <item msgid="9103697205127645916">"Унтраалттай"</item> diff --git a/packages/SystemUI/res/values-mr/tiles_states_strings.xml b/packages/SystemUI/res/values-mr/tiles_states_strings.xml index 588c5ad74ba8..f0ca33356bb6 100644 --- a/packages/SystemUI/res/values-mr/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-mr/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"बंद आहे"</item> <item msgid="2075645297847971154">"सुरू आहे"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"उपलब्ध नाही"</item> <item msgid="9103697205127645916">"बंद आहे"</item> diff --git a/packages/SystemUI/res/values-ms/tiles_states_strings.xml b/packages/SystemUI/res/values-ms/tiles_states_strings.xml index 60ce1f0a432b..b682df1ca324 100644 --- a/packages/SystemUI/res/values-ms/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ms/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Mati"</item> <item msgid="2075645297847971154">"Hidup"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Tidak tersedia"</item> <item msgid="9103697205127645916">"Mati"</item> diff --git a/packages/SystemUI/res/values-my/tiles_states_strings.xml b/packages/SystemUI/res/values-my/tiles_states_strings.xml index 7d2f20b9526f..af8d55c8cd7f 100644 --- a/packages/SystemUI/res/values-my/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-my/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"ပိတ်"</item> <item msgid="2075645297847971154">"ဖွင့်"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"မရနိုင်ပါ"</item> <item msgid="9103697205127645916">"ပိတ်"</item> diff --git a/packages/SystemUI/res/values-nb/tiles_states_strings.xml b/packages/SystemUI/res/values-nb/tiles_states_strings.xml index e512a8476ee3..619f6135d56f 100644 --- a/packages/SystemUI/res/values-nb/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-nb/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Av"</item> <item msgid="2075645297847971154">"På"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Utilgjengelig"</item> <item msgid="9103697205127645916">"Av"</item> diff --git a/packages/SystemUI/res/values-ne/tiles_states_strings.xml b/packages/SystemUI/res/values-ne/tiles_states_strings.xml index 355df40807fd..808b58d31ea1 100644 --- a/packages/SystemUI/res/values-ne/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ne/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"अफ छ"</item> <item msgid="2075645297847971154">"अन छ"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"उपलब्ध छैन"</item> <item msgid="9103697205127645916">"अफ छ"</item> diff --git a/packages/SystemUI/res/values-nl/tiles_states_strings.xml b/packages/SystemUI/res/values-nl/tiles_states_strings.xml index fa85b886c847..92332ca81f4a 100644 --- a/packages/SystemUI/res/values-nl/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-nl/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Uit"</item> <item msgid="2075645297847971154">"Aan"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Niet beschikbaar"</item> <item msgid="9103697205127645916">"Uit"</item> diff --git a/packages/SystemUI/res/values-or/tiles_states_strings.xml b/packages/SystemUI/res/values-or/tiles_states_strings.xml index bb94e0daced3..94b012297f49 100644 --- a/packages/SystemUI/res/values-or/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-or/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"ବନ୍ଦ ଅଛି"</item> <item msgid="2075645297847971154">"ଚାଲୁ ଅଛି"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"ଉପଲବ୍ଧ ନାହିଁ"</item> <item msgid="9103697205127645916">"ବନ୍ଦ ଅଛି"</item> diff --git a/packages/SystemUI/res/values-pa/tiles_states_strings.xml b/packages/SystemUI/res/values-pa/tiles_states_strings.xml index 2403141cf226..a7fc06626636 100644 --- a/packages/SystemUI/res/values-pa/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-pa/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"ਬੰਦ ਹੈ"</item> <item msgid="2075645297847971154">"ਚਾਲੂ ਹੈ"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"ਅਣਉਪਲਬਧ ਹੈ"</item> <item msgid="9103697205127645916">"ਬੰਦ ਹੈ"</item> diff --git a/packages/SystemUI/res/values-pl/tiles_states_strings.xml b/packages/SystemUI/res/values-pl/tiles_states_strings.xml index 30026e89d932..94fa858a0abb 100644 --- a/packages/SystemUI/res/values-pl/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-pl/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Wyłączony"</item> <item msgid="2075645297847971154">"Włączony"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Niedostępny"</item> <item msgid="9103697205127645916">"Wyłączony"</item> diff --git a/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml b/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml index f874dd40fe63..932ddc034f60 100644 --- a/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml @@ -86,6 +86,11 @@ <item msgid="5715725170633593906">"Desativado"</item> <item msgid="2075645297847971154">"Ativado"</item> </string-array> + <string-array name="tile_states_color_correction"> + <item msgid="2840507878437297682">"Indisponível"</item> + <item msgid="1909756493418256167">"Desativada"</item> + <item msgid="4531508423703413340">"Ativada"</item> + </string-array> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Indisponível"</item> <item msgid="9103697205127645916">"Desativada"</item> diff --git a/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml b/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml index 1e426e18e5e6..e6ebea8c1472 100644 --- a/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml @@ -86,6 +86,11 @@ <item msgid="5715725170633593906">"Desligado"</item> <item msgid="2075645297847971154">"Ligado"</item> </string-array> + <string-array name="tile_states_color_correction"> + <item msgid="2840507878437297682">"Indisponível"</item> + <item msgid="1909756493418256167">"Desativada"</item> + <item msgid="4531508423703413340">"Ativada"</item> + </string-array> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Indisponível"</item> <item msgid="9103697205127645916">"Desligado"</item> diff --git a/packages/SystemUI/res/values-pt/tiles_states_strings.xml b/packages/SystemUI/res/values-pt/tiles_states_strings.xml index f874dd40fe63..932ddc034f60 100644 --- a/packages/SystemUI/res/values-pt/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-pt/tiles_states_strings.xml @@ -86,6 +86,11 @@ <item msgid="5715725170633593906">"Desativado"</item> <item msgid="2075645297847971154">"Ativado"</item> </string-array> + <string-array name="tile_states_color_correction"> + <item msgid="2840507878437297682">"Indisponível"</item> + <item msgid="1909756493418256167">"Desativada"</item> + <item msgid="4531508423703413340">"Ativada"</item> + </string-array> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Indisponível"</item> <item msgid="9103697205127645916">"Desativada"</item> diff --git a/packages/SystemUI/res/values-ro/tiles_states_strings.xml b/packages/SystemUI/res/values-ro/tiles_states_strings.xml index c63e7fec1492..708c6f03a1d7 100644 --- a/packages/SystemUI/res/values-ro/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ro/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Dezactivat"</item> <item msgid="2075645297847971154">"Activat"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Indisponibilă"</item> <item msgid="9103697205127645916">"Dezactivată"</item> diff --git a/packages/SystemUI/res/values-ru/tiles_states_strings.xml b/packages/SystemUI/res/values-ru/tiles_states_strings.xml index ae31f3227497..3a51c2e1b2b0 100644 --- a/packages/SystemUI/res/values-ru/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ru/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Откл."</item> <item msgid="2075645297847971154">"Вкл."</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Функция недоступна"</item> <item msgid="9103697205127645916">"Откл."</item> diff --git a/packages/SystemUI/res/values-si/tiles_states_strings.xml b/packages/SystemUI/res/values-si/tiles_states_strings.xml index fa78ccc4246b..909d119f2c09 100644 --- a/packages/SystemUI/res/values-si/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-si/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"අක්රියයි"</item> <item msgid="2075645297847971154">"සක්රියයි"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"නොමැත"</item> <item msgid="9103697205127645916">"අක්රියයි"</item> diff --git a/packages/SystemUI/res/values-sk/tiles_states_strings.xml b/packages/SystemUI/res/values-sk/tiles_states_strings.xml index 1920f04610d7..a8c354597a36 100644 --- a/packages/SystemUI/res/values-sk/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-sk/tiles_states_strings.xml @@ -86,6 +86,11 @@ <item msgid="5715725170633593906">"Vypnuté"</item> <item msgid="2075645297847971154">"Zapnuté"</item> </string-array> + <string-array name="tile_states_color_correction"> + <item msgid="2840507878437297682">"Nedostupné"</item> + <item msgid="1909756493418256167">"Vypnuté"</item> + <item msgid="4531508423703413340">"Zapnuté"</item> + </string-array> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Nie je k dispozícii"</item> <item msgid="9103697205127645916">"Vypnuté"</item> diff --git a/packages/SystemUI/res/values-sl/tiles_states_strings.xml b/packages/SystemUI/res/values-sl/tiles_states_strings.xml index 924ec58cbeaa..c09d911bbad7 100644 --- a/packages/SystemUI/res/values-sl/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-sl/tiles_states_strings.xml @@ -86,6 +86,11 @@ <item msgid="5715725170633593906">"Izklopljeno"</item> <item msgid="2075645297847971154">"Vklopljeno"</item> </string-array> + <string-array name="tile_states_color_correction"> + <item msgid="2840507878437297682">"Ni na voljo"</item> + <item msgid="1909756493418256167">"Izklopljeno"</item> + <item msgid="4531508423703413340">"Vklopljeno"</item> + </string-array> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Ni na voljo"</item> <item msgid="9103697205127645916">"Izklopljeno"</item> diff --git a/packages/SystemUI/res/values-sq/tiles_states_strings.xml b/packages/SystemUI/res/values-sq/tiles_states_strings.xml index 34879ce320f4..461cd93b3ba9 100644 --- a/packages/SystemUI/res/values-sq/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-sq/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Joaktive"</item> <item msgid="2075645297847971154">"Aktive"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Nuk ofrohet"</item> <item msgid="9103697205127645916">"Joaktiv"</item> diff --git a/packages/SystemUI/res/values-sr/tiles_states_strings.xml b/packages/SystemUI/res/values-sr/tiles_states_strings.xml index 855b84b0b8d8..ab10ca1f6881 100644 --- a/packages/SystemUI/res/values-sr/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-sr/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Искључено"</item> <item msgid="2075645297847971154">"Укључено"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Недоступно"</item> <item msgid="9103697205127645916">"Искључено"</item> diff --git a/packages/SystemUI/res/values-sv/tiles_states_strings.xml b/packages/SystemUI/res/values-sv/tiles_states_strings.xml index b071b917d6b9..cdcf6e6c5f41 100644 --- a/packages/SystemUI/res/values-sv/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-sv/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Av"</item> <item msgid="2075645297847971154">"På"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Inte tillgängligt"</item> <item msgid="9103697205127645916">"Av"</item> diff --git a/packages/SystemUI/res/values-sw/tiles_states_strings.xml b/packages/SystemUI/res/values-sw/tiles_states_strings.xml index a92c7f920616..563c07803d12 100644 --- a/packages/SystemUI/res/values-sw/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-sw/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Kimezimwa"</item> <item msgid="2075645297847971154">"Kimewashwa"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Hakipatikani"</item> <item msgid="9103697205127645916">"Kimezimwa"</item> diff --git a/packages/SystemUI/res/values-sw720dp-land/config.xml b/packages/SystemUI/res/values-sw720dp-land/config.xml index e0b161456aa2..ae89ef4ccc86 100644 --- a/packages/SystemUI/res/values-sw720dp-land/config.xml +++ b/packages/SystemUI/res/values-sw720dp-land/config.xml @@ -31,7 +31,7 @@ <bool name="config_use_split_notification_shade">true</bool> <!-- The number of columns in the QuickSettings --> - <integer name="quick_settings_num_columns">2</integer> + <integer name="quick_settings_num_columns">3</integer> <!-- Notifications are sized to match the width of two (of 4) qs tiles in landscape. --> <bool name="config_skinnyNotifsInLandscape">false</bool> diff --git a/packages/SystemUI/res/values-ta/tiles_states_strings.xml b/packages/SystemUI/res/values-ta/tiles_states_strings.xml index f3ba1a1515ed..a9e0eabc3d8c 100644 --- a/packages/SystemUI/res/values-ta/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ta/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"முடக்கப்பட்டுள்ளது"</item> <item msgid="2075645297847971154">"இயக்கப்பட்டுள்ளது"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"கிடைக்கவில்லை"</item> <item msgid="9103697205127645916">"முடக்கப்பட்டுள்ளது"</item> diff --git a/packages/SystemUI/res/values-te/tiles_states_strings.xml b/packages/SystemUI/res/values-te/tiles_states_strings.xml index bed919fa7306..4cb7291cafd3 100644 --- a/packages/SystemUI/res/values-te/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-te/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"ఆఫ్లో ఉంది"</item> <item msgid="2075645297847971154">"ఆన్లో ఉంది"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"అందుబాటులో లేదు"</item> <item msgid="9103697205127645916">"ఆఫ్లో ఉంది"</item> diff --git a/packages/SystemUI/res/values-th/tiles_states_strings.xml b/packages/SystemUI/res/values-th/tiles_states_strings.xml index d5591b29a597..67768736afd9 100644 --- a/packages/SystemUI/res/values-th/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-th/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"ปิด"</item> <item msgid="2075645297847971154">"เปิด"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"ไม่พร้อมใช้งาน"</item> <item msgid="9103697205127645916">"ปิด"</item> diff --git a/packages/SystemUI/res/values-tl/tiles_states_strings.xml b/packages/SystemUI/res/values-tl/tiles_states_strings.xml index c601d2ff35c8..62172a4dc907 100644 --- a/packages/SystemUI/res/values-tl/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-tl/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Naka-off"</item> <item msgid="2075645297847971154">"Naka-on"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Hindi available"</item> <item msgid="9103697205127645916">"Naka-off"</item> diff --git a/packages/SystemUI/res/values-tr/tiles_states_strings.xml b/packages/SystemUI/res/values-tr/tiles_states_strings.xml index 17ce6b32e81b..9f836fc6bc2d 100644 --- a/packages/SystemUI/res/values-tr/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-tr/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Kapalı"</item> <item msgid="2075645297847971154">"Açık"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Kullanılamıyor"</item> <item msgid="9103697205127645916">"Kapalı"</item> diff --git a/packages/SystemUI/res/values-uk/tiles_states_strings.xml b/packages/SystemUI/res/values-uk/tiles_states_strings.xml index 6f9ef21a8426..34c40d3d2951 100644 --- a/packages/SystemUI/res/values-uk/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-uk/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Вимкнено"</item> <item msgid="2075645297847971154">"Увімкнено"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Недоступно"</item> <item msgid="9103697205127645916">"Вимкнено"</item> diff --git a/packages/SystemUI/res/values-ur/tiles_states_strings.xml b/packages/SystemUI/res/values-ur/tiles_states_strings.xml index 3284fc5d8021..02943fa8e272 100644 --- a/packages/SystemUI/res/values-ur/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ur/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"آف ہے"</item> <item msgid="2075645297847971154">"آن ہے"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"دستیاب نہیں ہے"</item> <item msgid="9103697205127645916">"آف ہے"</item> diff --git a/packages/SystemUI/res/values-uz/tiles_states_strings.xml b/packages/SystemUI/res/values-uz/tiles_states_strings.xml index 47bcf4db8392..52a8b0a52e52 100644 --- a/packages/SystemUI/res/values-uz/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-uz/tiles_states_strings.xml @@ -86,6 +86,11 @@ <item msgid="5715725170633593906">"Oʻchiq"</item> <item msgid="2075645297847971154">"Yoniq"</item> </string-array> + <string-array name="tile_states_color_correction"> + <item msgid="2840507878437297682">"Ishlamaydi"</item> + <item msgid="1909756493418256167">"Yoqilmagan"</item> + <item msgid="4531508423703413340">"Yoniq"</item> + </string-array> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Ishlamaydi"</item> <item msgid="9103697205127645916">"Oʻchiq"</item> diff --git a/packages/SystemUI/res/values-vi/tiles_states_strings.xml b/packages/SystemUI/res/values-vi/tiles_states_strings.xml index ecc32236f928..bff64ab6b6d3 100644 --- a/packages/SystemUI/res/values-vi/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-vi/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Đang tắt"</item> <item msgid="2075645297847971154">"Đang bật"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Không hoạt động"</item> <item msgid="9103697205127645916">"Đang tắt"</item> diff --git a/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml index 43de98e7c665..0d72f61092d0 100644 --- a/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"已关闭"</item> <item msgid="2075645297847971154">"已开启"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"不可用"</item> <item msgid="9103697205127645916">"已关闭"</item> diff --git a/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml index 637fb4ccbd61..571cfba728a4 100644 --- a/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"已關閉"</item> <item msgid="2075645297847971154">"已開啟"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"無法使用"</item> <item msgid="9103697205127645916">"已關閉"</item> diff --git a/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml index 266cf5df9321..48896579101a 100644 --- a/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"已關閉"</item> <item msgid="2075645297847971154">"已開啟"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"無法使用"</item> <item msgid="9103697205127645916">"已關閉"</item> diff --git a/packages/SystemUI/res/values-zu/tiles_states_strings.xml b/packages/SystemUI/res/values-zu/tiles_states_strings.xml index 9cac4ae184b4..ea40faf4eb56 100644 --- a/packages/SystemUI/res/values-zu/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-zu/tiles_states_strings.xml @@ -86,6 +86,9 @@ <item msgid="5715725170633593906">"Valiwe"</item> <item msgid="2075645297847971154">"Vuliwe"</item> </string-array> + <!-- no translation found for tile_states_color_correction:0 (2840507878437297682) --> + <!-- no translation found for tile_states_color_correction:1 (1909756493418256167) --> + <!-- no translation found for tile_states_color_correction:2 (4531508423703413340) --> <string-array name="tile_states_inversion"> <item msgid="3638187931191394628">"Akutholakali"</item> <item msgid="9103697205127645916">"Valiwe"</item> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 7b8f349777a6..56517ccb873f 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -294,6 +294,7 @@ <string-array name="config_systemUIServiceComponents" translatable="false"> <item>com.android.systemui.util.NotificationChannels</item> <item>com.android.systemui.keyguard.KeyguardViewMediator</item> + <item>com.android.keyguard.KeyguardBiometricLockoutLogger</item> <item>com.android.systemui.recents.Recents</item> <item>com.android.systemui.volume.VolumeUI</item> <item>com.android.systemui.statusbar.phone.StatusBar</item> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 2916c1c9b357..7600eb1ad1d0 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1165,7 +1165,7 @@ <string name="wallet_lockscreen_settings_label">Lock screen settings</string> <!-- QR Code Scanner label, title [CHAR LIMIT=32] --> - <string name="qr_code_scanner_title">QR Code</string> + <string name="qr_code_scanner_title">QR code</string> <!-- QR Code Scanner description [CHAR LIMIT=NONE] --> <string name="qr_code_scanner_description">Tap to scan</string> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java index 98083742d707..1d2caf9ab545 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java @@ -28,6 +28,8 @@ import android.view.View; import android.view.ViewRootImpl; import android.view.ViewTreeObserver; +import androidx.annotation.VisibleForTesting; + import java.io.PrintWriter; import java.util.concurrent.Executor; @@ -60,6 +62,7 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener, private final Rect mRegisteredSamplingBounds = new Rect(); private final SamplingCallback mCallback; private final Executor mBackgroundExecutor; + private final SysuiCompositionSamplingListener mCompositionSamplingListener; private boolean mSamplingEnabled = false; private boolean mSamplingListenerRegistered = false; @@ -91,9 +94,17 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener, public RegionSamplingHelper(View sampledView, SamplingCallback samplingCallback, Executor backgroundExecutor) { + this(sampledView, samplingCallback, sampledView.getContext().getMainExecutor(), + backgroundExecutor, new SysuiCompositionSamplingListener()); + } + + @VisibleForTesting + RegionSamplingHelper(View sampledView, SamplingCallback samplingCallback, + Executor mainExecutor, Executor backgroundExecutor, + SysuiCompositionSamplingListener compositionSamplingListener) { mBackgroundExecutor = backgroundExecutor; - mSamplingListener = new CompositionSamplingListener( - sampledView.getContext().getMainExecutor()) { + mCompositionSamplingListener = compositionSamplingListener; + mSamplingListener = new CompositionSamplingListener(mainExecutor) { @Override public void onSampleCollected(float medianLuma) { if (mSamplingEnabled) { @@ -136,7 +147,7 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener, public void stopAndDestroy() { stop(); - mSamplingListener.destroy(); + mBackgroundExecutor.execute(mSamplingListener::destroy); mIsDestroyed = true; } @@ -189,13 +200,12 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener, // We only want to re-register if something actually changed unregisterSamplingListener(); mSamplingListenerRegistered = true; - SurfaceControl wrappedStopLayer = stopLayerControl == null - ? null : new SurfaceControl(stopLayerControl, "regionSampling"); + SurfaceControl wrappedStopLayer = wrap(stopLayerControl); mBackgroundExecutor.execute(() -> { if (wrappedStopLayer != null && !wrappedStopLayer.isValid()) { return; } - CompositionSamplingListener.register(mSamplingListener, DEFAULT_DISPLAY, + mCompositionSamplingListener.register(mSamplingListener, DEFAULT_DISPLAY, wrappedStopLayer, mSamplingRequestBounds); }); mRegisteredSamplingBounds.set(mSamplingRequestBounds); @@ -208,14 +218,21 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener, } } + @VisibleForTesting + protected SurfaceControl wrap(SurfaceControl stopLayerControl) { + return stopLayerControl == null ? null : new SurfaceControl(stopLayerControl, + "regionSampling"); + } + private void unregisterSamplingListener() { if (mSamplingListenerRegistered) { mSamplingListenerRegistered = false; SurfaceControl wrappedStopLayer = mWrappedStopLayer; mRegisteredStopLayer = null; + mWrappedStopLayer = null; mRegisteredSamplingBounds.setEmpty(); mBackgroundExecutor.execute(() -> { - CompositionSamplingListener.unregister(mSamplingListener); + mCompositionSamplingListener.unregister(mSamplingListener); if (wrappedStopLayer != null && wrappedStopLayer.isValid()) { wrappedStopLayer.release(); } @@ -299,4 +316,19 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener, return true; } } + + @VisibleForTesting + public static class SysuiCompositionSamplingListener { + public void register(CompositionSamplingListener listener, + int displayId, SurfaceControl stopLayer, Rect samplingArea) { + CompositionSamplingListener.register(listener, displayId, stopLayer, samplingArea); + } + + /** + * Unregisters a sampling listener. + */ + public void unregister(CompositionSamplingListener listener) { + CompositionSamplingListener.unregister(listener); + } + } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java index e84b552d65eb..8d26ddd5aa23 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java @@ -205,8 +205,10 @@ public class RemoteAnimationAdapterCompat { @Override @SuppressLint("NewApi") public void run() { - counterLauncher.cleanUp(info.getRootLeash()); - counterWallpaper.cleanUp(info.getRootLeash()); + final SurfaceControl.Transaction finishTransaction = + new SurfaceControl.Transaction(); + counterLauncher.cleanUp(finishTransaction); + counterWallpaper.cleanUp(finishTransaction); // Release surface references now. This is apparently to free GPU memory // while doing quick operations (eg. during CTS). for (int i = info.getChanges().size() - 1; i >= 0; --i) { @@ -216,7 +218,7 @@ public class RemoteAnimationAdapterCompat { leashMap.valueAt(i).release(); } try { - finishCallback.onTransitionFinished(null /* wct */, null /* sct */); + finishCallback.onTransitionFinished(null /* wct */, finishTransaction); } catch (RemoteException e) { Log.e("ActivityOptionsCompat", "Failed to call app controlled animation" + " finished callback", e); diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt index 22698a853726..a274b74f336b 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt @@ -89,6 +89,7 @@ class ScopedUnfoldTransitionProgressProvider @JvmOverloads constructor( override fun destroy() { source?.removeCallback(this) + source?.destroy() } override fun onTransitionStarted() { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt new file mode 100644 index 000000000000..214b284ac4b9 --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2022 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.keyguard + +import android.content.Context +import android.hardware.biometrics.BiometricSourceType +import com.android.internal.annotations.VisibleForTesting +import com.android.internal.logging.UiEvent +import com.android.internal.logging.UiEventLogger +import com.android.internal.widget.LockPatternUtils +import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT +import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT +import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE +import com.android.keyguard.KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent +import com.android.systemui.CoreStartable +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dump.DumpManager +import java.io.FileDescriptor +import java.io.PrintWriter +import javax.inject.Inject + +/** + * Logs events when primary authentication requirements change. Primary authentication is considered + * authentication using pin/pattern/password input. + * + * See [PrimaryAuthRequiredEvent] for all the events and their descriptions. + */ +@SysUISingleton +class KeyguardBiometricLockoutLogger @Inject constructor( + context: Context?, + private val uiEventLogger: UiEventLogger, + private val keyguardUpdateMonitor: KeyguardUpdateMonitor, + private val dumpManager: DumpManager +) : CoreStartable(context) { + private var fingerprintLockedOut = false + private var faceLockedOut = false + private var encryptedOrLockdown = false + private var unattendedUpdate = false + private var timeout = false + + override fun start() { + dumpManager.registerDumpable(this) + mKeyguardUpdateMonitorCallback.onStrongAuthStateChanged( + KeyguardUpdateMonitor.getCurrentUser()) + keyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback) + } + + private val mKeyguardUpdateMonitorCallback: KeyguardUpdateMonitorCallback = + object : KeyguardUpdateMonitorCallback() { + override fun onLockedOutStateChanged(biometricSourceType: BiometricSourceType) { + if (biometricSourceType == BiometricSourceType.FINGERPRINT) { + val lockedOut = keyguardUpdateMonitor.isFingerprintLockedOut + if (lockedOut && !fingerprintLockedOut) { + uiEventLogger.log( + PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT) + } else if (!lockedOut && fingerprintLockedOut) { + uiEventLogger.log( + PrimaryAuthRequiredEvent + .PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT_RESET) + } + fingerprintLockedOut = lockedOut + } else if (biometricSourceType == BiometricSourceType.FACE) { + val lockedOut = keyguardUpdateMonitor.isFaceLockedOut + if (lockedOut && !faceLockedOut) { + uiEventLogger.log( + PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT) + } else if (!lockedOut && faceLockedOut) { + uiEventLogger.log( + PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT_RESET) + } + faceLockedOut = lockedOut + } + } + + override fun onStrongAuthStateChanged(userId: Int) { + if (userId != KeyguardUpdateMonitor.getCurrentUser()) { + return + } + val strongAuthFlags = keyguardUpdateMonitor.strongAuthTracker + .getStrongAuthForUser(userId) + + val newEncryptedOrLockdown = keyguardUpdateMonitor.isEncryptedOrLockdown(userId) + if (newEncryptedOrLockdown && !encryptedOrLockdown) { + uiEventLogger.log( + PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_ENCRYPTED_OR_LOCKDOWN) + } + encryptedOrLockdown = newEncryptedOrLockdown + + val newUnattendedUpdate = isUnattendedUpdate(strongAuthFlags) + if (newUnattendedUpdate && !unattendedUpdate) { + uiEventLogger.log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_UNATTENDED_UPDATE) + } + unattendedUpdate = newUnattendedUpdate + + val newTimeout = isStrongAuthTimeout(strongAuthFlags) + if (newTimeout && !timeout) { + uiEventLogger.log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_TIMEOUT) + } + timeout = newTimeout + } + } + + private fun isUnattendedUpdate( + @LockPatternUtils.StrongAuthTracker.StrongAuthFlags flags: Int + ) = containsFlag(flags, STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE) + + private fun isStrongAuthTimeout( + @LockPatternUtils.StrongAuthTracker.StrongAuthFlags flags: Int + ) = containsFlag(flags, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT) || + containsFlag(flags, STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT) + + override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) { + pw.println(" mFingerprintLockedOut=$fingerprintLockedOut") + pw.println(" mFaceLockedOut=$faceLockedOut") + pw.println(" mIsEncryptedOrLockdown=$encryptedOrLockdown") + pw.println(" mIsUnattendedUpdate=$unattendedUpdate") + pw.println(" mIsTimeout=$timeout") + } + + /** + * Events pertaining to whether primary authentication (pin/pattern/password input) is required + * for device entry. + */ + @VisibleForTesting + enum class PrimaryAuthRequiredEvent(private val mId: Int) : UiEventLogger.UiEventEnum { + @UiEvent(doc = "Fingerprint cannot be used to authenticate for device entry. This" + + "can persist until the next primary auth or may timeout.") + PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT(924), + + @UiEvent(doc = "Fingerprint can be used to authenticate for device entry.") + PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT_RESET(925), + + @UiEvent(doc = "Face cannot be used to authenticate for device entry.") + PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT(926), + + @UiEvent(doc = "Face can be used to authenticate for device entry.") + PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT_RESET(927), + + @UiEvent(doc = "Device is encrypted (ie: after reboot) or device is locked down by DPM " + + "or a manual user lockdown.") + PRIMARY_AUTH_REQUIRED_ENCRYPTED_OR_LOCKDOWN(928), + + @UiEvent(doc = "Primary authentication is required because it hasn't been used for a " + + "time required by a device admin or because primary auth hasn't been used for a " + + "time after a non-strong biometric (weak or convenience) is used to unlock the " + + "device.") + PRIMARY_AUTH_REQUIRED_TIMEOUT(929), + + @UiEvent(doc = "Strong authentication is required to prepare for unattended upgrade.") + PRIMARY_AUTH_REQUIRED_UNATTENDED_UPDATE(931); + + override fun getId(): Int { + return mId + } + } + + companion object { + private fun containsFlag(strongAuthFlags: Int, flagCheck: Int): Boolean { + return strongAuthFlags and flagCheck != 0 + } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt index 03f04d3a2cde..36fe5ba1a851 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt @@ -64,3 +64,19 @@ data class KeyguardFaceListenModel( val secureCameraLaunched: Boolean, val switchingUser: Boolean ) : KeyguardListenModel() +/** + * Verbose debug information associated with [KeyguardUpdateMonitor.shouldTriggerActiveUnlock]. + */ +data class KeyguardActiveUnlockModel( + @CurrentTimeMillisLong override val timeMillis: Long, + override val userId: Int, + override val listening: Boolean, + // keep sorted + val authInterruptActive: Boolean, + val encryptedOrTimedOut: Boolean, + val fpLockout: Boolean, + val lockDown: Boolean, + val switchingUser: Boolean, + val triggerActiveUnlockForAssistant: Boolean, + val userCanDismissLockScreen: Boolean +) : KeyguardListenModel() diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardListenQueue.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardListenQueue.kt index f13a59a84811..210f5e763911 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardListenQueue.kt +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardListenQueue.kt @@ -32,15 +32,17 @@ class KeyguardListenQueue( ) { private val faceQueue = ArrayDeque<KeyguardFaceListenModel>() private val fingerprintQueue = ArrayDeque<KeyguardFingerprintListenModel>() + private val activeUnlockQueue = ArrayDeque<KeyguardActiveUnlockModel>() @get:VisibleForTesting val models: List<KeyguardListenModel> - get() = faceQueue + fingerprintQueue + get() = faceQueue + fingerprintQueue + activeUnlockQueue /** Push a [model] to the queue (will be logged until the queue exceeds [sizePerModality]). */ fun add(model: KeyguardListenModel) { val queue = when (model) { is KeyguardFaceListenModel -> faceQueue.apply { add(model) } is KeyguardFingerprintListenModel -> fingerprintQueue.apply { add(model) } + is KeyguardActiveUnlockModel -> activeUnlockQueue.apply { add(model) } } if (queue.size > sizePerModality) { @@ -63,5 +65,9 @@ class KeyguardListenQueue( for (model in fingerprintQueue) { writer.println(stringify(model)) } + writer.println(" Active unlock triggers (last ${activeUnlockQueue.size} calls):") + for (model in activeUnlockQueue) { + writer.println(stringify(model)) + } } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index b5ea498f185d..3fab72441c89 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -616,9 +616,13 @@ public class KeyguardSecurityContainer extends FrameLayout { final View view = getChildAt(i); if (view.getVisibility() != GONE) { int updatedWidthMeasureSpec = mViewMode.getChildWidthMeasureSpec(widthMeasureSpec); + final LayoutParams lp = (LayoutParams) view.getLayoutParams(); + + // When using EXACTLY spec, measure will use the layout width if > 0. Set before + // measuring the child + lp.width = MeasureSpec.getSize(updatedWidthMeasureSpec); measureChildWithMargins(view, updatedWidthMeasureSpec, 0, heightMeasureSpec, 0); - final LayoutParams lp = (LayoutParams) view.getLayoutParams(); maxWidth = Math.max(maxWidth, view.getMeasuredWidth() + lp.leftMargin + lp.rightMargin); maxHeight = Math.max(maxHeight, @@ -969,7 +973,7 @@ public class KeyguardSecurityContainer extends FrameLayout { public int getChildWidthMeasureSpec(int parentWidthMeasureSpec) { return MeasureSpec.makeMeasureSpec( MeasureSpec.getSize(parentWidthMeasureSpec) / 2, - MeasureSpec.getMode(parentWidthMeasureSpec)); + MeasureSpec.EXACTLY); } @Override @@ -1026,7 +1030,7 @@ public class KeyguardSecurityContainer extends FrameLayout { public int getChildWidthMeasureSpec(int parentWidthMeasureSpec) { return MeasureSpec.makeMeasureSpec( MeasureSpec.getSize(parentWidthMeasureSpec) / 2, - MeasureSpec.getMode(parentWidthMeasureSpec)); + MeasureSpec.EXACTLY); } private void updateSecurityViewGravity() { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index 2fb2211150ec..49a802235619 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -51,6 +51,8 @@ import com.android.settingslib.utils.ThreadUtils; import com.android.systemui.Gefingerpoken; import com.android.systemui.R; import com.android.systemui.classifier.FalsingCollector; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.shared.system.SysUiStatsLog; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -83,6 +85,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard private final FalsingManager mFalsingManager; private final UserSwitcherController mUserSwitcherController; private final GlobalSettings mGlobalSettings; + private final FeatureFlags mFeatureFlags; private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED; @@ -238,6 +241,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard FalsingCollector falsingCollector, FalsingManager falsingManager, UserSwitcherController userSwitcherController, + FeatureFlags featureFlags, GlobalSettings globalSettings) { super(view); mLockPatternUtils = lockPatternUtils; @@ -255,6 +259,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mFalsingCollector = falsingCollector; mFalsingManager = falsingManager; mUserSwitcherController = userSwitcherController; + mFeatureFlags = featureFlags; mGlobalSettings = globalSettings; } @@ -510,7 +515,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard } private boolean canDisplayUserSwitcher() { - return getResources().getBoolean(R.bool.bouncer_display_user_switcher); + return mFeatureFlags.isEnabled(Flags.BOUNCER_USER_SWITCHER); } private void configureMode() { @@ -615,6 +620,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard private final FalsingCollector mFalsingCollector; private final FalsingManager mFalsingManager; private final GlobalSettings mGlobalSettings; + private final FeatureFlags mFeatureFlags; private final UserSwitcherController mUserSwitcherController; @Inject @@ -632,6 +638,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard FalsingCollector falsingCollector, FalsingManager falsingManager, UserSwitcherController userSwitcherController, + FeatureFlags featureFlags, GlobalSettings globalSettings) { mView = view; mAdminSecondaryLockScreenControllerFactory = adminSecondaryLockScreenControllerFactory; @@ -645,6 +652,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mConfigurationController = configurationController; mFalsingCollector = falsingCollector; mFalsingManager = falsingManager; + mFeatureFlags = featureFlags; mGlobalSettings = globalSettings; mUserSwitcherController = userSwitcherController; } @@ -656,8 +664,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger, mKeyguardStateController, securityCallback, mSecurityViewFlipperController, mConfigurationController, mFalsingCollector, mFalsingManager, - mUserSwitcherController, mGlobalSettings); + mUserSwitcherController, mFeatureFlags, mGlobalSettings); } - } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 98721fd78a93..5276679ea104 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -37,6 +37,8 @@ import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.app.ActivityTaskManager.RootTaskInfo; import android.app.AlarmManager; +import android.app.Notification; +import android.app.NotificationManager; import android.app.PendingIntent; import android.app.UserSwitchObserver; import android.app.admin.DevicePolicyManager; @@ -102,6 +104,8 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.shared.system.TaskStackChangeListeners; @@ -109,6 +113,7 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.telephony.TelephonyListenerManager; import com.android.systemui.util.Assert; +import com.android.systemui.util.NotificationChannels; import com.android.systemui.util.RingerModeTracker; import com.google.android.collect.Lists; @@ -143,8 +148,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private static final boolean DEBUG_SIM_STATES = KeyguardConstants.DEBUG_SIM_STATES; private static final boolean DEBUG_FACE = Build.IS_DEBUGGABLE; private static final boolean DEBUG_FINGERPRINT = Build.IS_DEBUGGABLE; + private static final boolean DEBUG_ACTIVE_UNLOCK = Build.IS_DEBUGGABLE; private static final boolean DEBUG_SPEW = false; private static final int BIOMETRIC_LOCKOUT_RESET_DELAY_MS = 600; + private int mNumActiveUnlockTriggers = 0; private static final String ACTION_FACE_UNLOCK_STARTED = "com.android.facelock.FACE_UNLOCK_STARTED"; @@ -183,7 +190,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private static final int MSG_USER_STOPPED = 340; private static final int MSG_USER_REMOVED = 341; private static final int MSG_KEYGUARD_GOING_AWAY = 342; - private static final int MSG_LOCK_SCREEN_MODE = 343; private static final int MSG_TIME_FORMAT_UPDATE = 344; private static final int MSG_REQUIRE_NFC_UNLOCK = 345; @@ -221,7 +227,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private static final int BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED = -1; public static final int BIOMETRIC_HELP_FACE_NOT_RECOGNIZED = -2; - private static final int DEFAULT_CHARGING_VOLTAGE_MICRO_VOLT = 5000000; /** * If no cancel signal has been received after this amount of time, set the biometric running * state to stopped to allow Keyguard to retry authentication. @@ -231,7 +236,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private static final ComponentName FALLBACK_HOME_COMPONENT = new ComponentName( "com.android.settings", "com.android.settings.FallbackHome"); - /** * If true, the system is in the half-boot-to-decryption-screen state. * Prudently disable lockscreen. @@ -334,6 +338,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID; private final Executor mBackgroundExecutor; private SensorPrivacyManager mSensorPrivacyManager; + private FeatureFlags mFeatureFlags; private int mFaceAuthUserId; /** @@ -1250,7 +1255,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN); } - private boolean isEncryptedOrLockdown(int userId) { + /** + * Returns true if primary authentication is required for the given user due to lockdown + * or encryption after reboot. + */ + public boolean isEncryptedOrLockdown(int userId) { final int strongAuth = mStrongAuthTracker.getStrongAuthForUser(userId); final boolean isLockDown = containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW) @@ -1311,6 +1320,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab void setAssistantVisible(boolean assistantVisible) { mAssistantVisible = assistantVisible; updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE); + if (mAssistantVisible) { + requestActiveUnlock(); + } } static class DisplayClientState { @@ -1650,6 +1662,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab Trace.beginSection("KeyguardUpdateMonitor#handleStartedWakingUp"); Assert.isMainThread(); updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE); + requestActiveUnlock(); for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { @@ -1777,7 +1790,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab AuthController authController, TelephonyListenerManager telephonyListenerManager, InteractionJankMonitor interactionJankMonitor, - LatencyTracker latencyTracker) { + LatencyTracker latencyTracker, + FeatureFlags featureFlags) { mContext = context; mSubscriptionManager = SubscriptionManager.from(context); mTelephonyListenerManager = telephonyListenerManager; @@ -1795,6 +1809,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mAuthController = authController; dumpManager.registerDumpable(getClass().getName(), this); mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class); + mFeatureFlags = featureFlags; mHandler = new Handler(mainLooper) { @Override @@ -2180,6 +2195,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } mAuthInterruptActive = active; updateFaceListeningState(BIOMETRIC_ACTION_UPDATE); + requestActiveUnlock(); } /** @@ -2228,6 +2244,97 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } + /** + * Attempts to trigger active unlock. + */ + public void requestActiveUnlock() { + // If this message exists, FP has already authenticated, so wait until that is handled + if (mHandler.hasMessages(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE)) { + return; + } + + if (shouldTriggerActiveUnlock() && mFeatureFlags.isEnabled(Flags.ACTIVE_UNLOCK)) { + // TODO (b/192405661): call new TrustManager API + mNumActiveUnlockTriggers++; + Log.d("ActiveUnlock", "would have triggered times=" + mNumActiveUnlockTriggers); + showActiveUnlockNotification(mNumActiveUnlockTriggers); + } + } + + /** + * TODO (b/192405661): Only for testing. Remove before release. + */ + private void showActiveUnlockNotification(int times) { + final String message = "Active unlock triggered " + times + " times."; + final Notification.Builder nb = + new Notification.Builder(mContext, NotificationChannels.GENERAL) + .setSmallIcon(R.drawable.ic_volume_ringer) + .setContentTitle(message) + .setStyle(new Notification.BigTextStyle().bigText(message)); + mContext.getSystemService(NotificationManager.class).notifyAsUser( + "active_unlock", + 0, + nb.build(), + UserHandle.ALL); + } + + private boolean shouldTriggerActiveUnlock() { + // TODO: check if active unlock is ENABLED / AVAILABLE + + // Triggers: + final boolean triggerActiveUnlockForAssistant = shouldTriggerActiveUnlockForAssistant(); + final boolean awakeKeyguard = mKeyguardIsVisible && mDeviceInteractive && !mGoingToSleep + && mStatusBarState != StatusBarState.SHADE_LOCKED; + + // Gates: + final int user = getCurrentUser(); + + // No need to trigger active unlock if we're already unlocked or don't have + // pin/pattern/password setup + final boolean userCanDismissLockScreen = getUserCanSkipBouncer(user) + || !mLockPatternUtils.isSecure(user); + + // Don't trigger active unlock if fp is locked out TODO: confirm this one + final boolean fpLockedout = mFingerprintLockedOut || mFingerprintLockedOutPermanent; + + // Don't trigger active unlock if primary auth is required + final int strongAuth = mStrongAuthTracker.getStrongAuthForUser(user); + final boolean isLockDown = + containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW) + || containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN); + final boolean isEncryptedOrTimedOut = + containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_BOOT) + || containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT); + + final boolean shouldTriggerActiveUnlock = + (mAuthInterruptActive || triggerActiveUnlockForAssistant || awakeKeyguard) + && !mSwitchingUser + && !userCanDismissLockScreen + && !fpLockedout + && !isLockDown + && !isEncryptedOrTimedOut + && !mKeyguardGoingAway + && !mSecureCameraLaunched; + + // Aggregate relevant fields for debug logging. + if (DEBUG_ACTIVE_UNLOCK || DEBUG_SPEW) { + maybeLogListenerModelData( + new KeyguardActiveUnlockModel( + System.currentTimeMillis(), + user, + shouldTriggerActiveUnlock, + mAuthInterruptActive, + isEncryptedOrTimedOut, + fpLockedout, + isLockDown, + mSwitchingUser, + triggerActiveUnlockForAssistant, + userCanDismissLockScreen)); + } + + return shouldTriggerActiveUnlock; + } + private boolean shouldListenForFingerprintAssistant() { BiometricAuthenticated fingerprint = mUserFingerprintAuthenticated.get(getCurrentUser()); return mAssistantVisible && mKeyguardOccluded @@ -2242,6 +2349,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab && !mUserHasTrust.get(getCurrentUser(), false); } + private boolean shouldTriggerActiveUnlockForAssistant() { + return mAssistantVisible && mKeyguardOccluded + && !mUserHasTrust.get(getCurrentUser(), false); + } + @VisibleForTesting protected boolean shouldListenForFingerprint(boolean isUdfps) { final int user = getCurrentUser(); @@ -2406,6 +2518,13 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab Log.v(TAG, model.toString()); } + if (DEBUG_ACTIVE_UNLOCK + && model instanceof KeyguardActiveUnlockModel + && model.getListening()) { + mListenModels.add(model); + return; + } + // Add model data to the historical buffer. final boolean notYetRunning = (DEBUG_FACE @@ -2514,6 +2633,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab return mFingerprintLockedOut || mFingerprintLockedOutPermanent; } + public boolean isFaceLockedOut() { + return mFaceLockedOutPermanent; + } + /** * If biometrics hardware is available, not disabled, and user has enrolled templates. * This does NOT check if the device is encrypted or in lockdown. diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index 33538ec25fcd..a100cb8caf98 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -342,7 +342,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable { mDisplayManager.getDisplay(DEFAULT_DISPLAY).getMetrics(metrics); mDensity = metrics.density; - mExecutor.execute(() -> mTunerService.addTunable(this, SIZE)); + mMainExecutor.execute(() -> mTunerService.addTunable(this, SIZE)); // Watch color inversion and invert the overlay as needed. if (mColorInversionSetting == null) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index fd7ae323e88f..d20844143ad6 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -53,6 +53,7 @@ import android.view.accessibility.AccessibilityManager; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.LatencyTracker; import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.DozeReceiver; @@ -130,6 +131,7 @@ public class UdfpsController implements DozeReceiver { // Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple // sensors, this, in addition to a lot of the code here, will be updated. @VisibleForTesting final FingerprintSensorPropertiesInternal mSensorProps; + @NonNull private final ActivityLaunchAnimator mActivityLaunchAnimator; // Tracks the velocity of a touch to help filter out the touches that move too fast. @Nullable private VelocityTracker mVelocityTracker; @@ -198,7 +200,8 @@ public class UdfpsController implements DozeReceiver { mLockscreenShadeTransitionController, mConfigurationController, mSystemClock, mKeyguardStateController, mUnlockedScreenOffAnimationController, mSensorProps, mHbmProvider, - reason, callback, UdfpsController.this::onTouch))); + reason, callback, UdfpsController.this::onTouch, + mActivityLaunchAnimator))); } @Override @@ -487,7 +490,8 @@ public class UdfpsController implements DozeReceiver { @NonNull SystemClock systemClock, @NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController, @NonNull SystemUIDialogManager dialogManager, - @NonNull LatencyTracker latencyTracker) { + @NonNull LatencyTracker latencyTracker, + @NonNull ActivityLaunchAnimator activityLaunchAnimator) { mContext = context; mExecution = execution; mVibrator = vibrator; @@ -516,6 +520,7 @@ public class UdfpsController implements DozeReceiver { mSystemClock = systemClock; mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController; mLatencyTracker = latencyTracker; + mActivityLaunchAnimator = activityLaunchAnimator; mSensorProps = findFirstUdfps(); // At least one UDFPS sensor exists diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt index e0912508d178..590963b2ff48 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt @@ -41,6 +41,7 @@ import android.view.accessibility.AccessibilityManager.TouchExplorationStateChan import androidx.annotation.LayoutRes import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.R +import com.android.systemui.animation.ActivityLaunchAnimator import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.LockscreenShadeTransitionController @@ -81,7 +82,8 @@ class UdfpsControllerOverlay( private var hbmProvider: UdfpsHbmProvider, @ShowReason val requestReason: Int, private val controllerCallback: IUdfpsOverlayControllerCallback, - private val onTouch: (View, MotionEvent, Boolean) -> Boolean + private val onTouch: (View, MotionEvent, Boolean) -> Boolean, + private val activityLaunchAnimator: ActivityLaunchAnimator ) { /** The view, when [isShowing], or null. */ var overlayView: UdfpsView? = null @@ -200,7 +202,8 @@ class UdfpsControllerOverlay( keyguardStateController, unlockedScreenOffAnimationController, dialogManager, - controller + controller, + activityLaunchAnimator ) } BiometricOverlayConstants.REASON_AUTH_BP -> { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java index d90a746b0f79..8b7aa093600c 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java @@ -25,6 +25,7 @@ import android.view.MotionEvent; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.R; +import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.LockscreenShadeTransitionController; @@ -55,6 +56,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud @NonNull private final UdfpsController mUdfpsController; @NonNull private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; + @NonNull private final ActivityLaunchAnimator mActivityLaunchAnimator; private boolean mShowingUdfpsBouncer; private boolean mUdfpsRequested; @@ -66,6 +68,8 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud private long mLastUdfpsBouncerShowTime = -1; private float mStatusBarExpansion; private boolean mLaunchTransitionFadingAway; + private boolean mIsLaunchingActivity; + private float mActivityLaunchProgress; /** * hidden amount of pin/pattern/password bouncer @@ -88,7 +92,8 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud @NonNull KeyguardStateController keyguardStateController, @NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController, @NonNull SystemUIDialogManager systemUIDialogManager, - @NonNull UdfpsController udfpsController) { + @NonNull UdfpsController udfpsController, + @NonNull ActivityLaunchAnimator activityLaunchAnimator) { super(view, statusBarStateController, panelExpansionStateManager, systemUIDialogManager, dumpManager); mKeyguardViewManager = statusBarKeyguardViewManager; @@ -99,6 +104,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud mKeyguardStateController = keyguardStateController; mUdfpsController = udfpsController; mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController; + mActivityLaunchAnimator = activityLaunchAnimator; } @Override @@ -136,6 +142,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud mKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor); mLockScreenShadeTransitionController.setUdfpsKeyguardViewController(this); mUnlockedScreenOffAnimationController.addCallback(mUnlockedScreenOffCallback); + mActivityLaunchAnimator.addListener(mActivityLaunchAnimatorListener); } @Override @@ -153,6 +160,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud mLockScreenShadeTransitionController.setUdfpsKeyguardViewController(null); } mUnlockedScreenOffAnimationController.removeCallback(mUnlockedScreenOffCallback); + mActivityLaunchAnimator.removeListener(mActivityLaunchAnimatorListener); } @Override @@ -294,6 +302,12 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud 0f, 255f); if (!mShowingUdfpsBouncer) { alpha *= (1.0f - mTransitionToFullShadeProgress); + + // Fade out the icon if we are animating an activity launch over the lockscreen and the + // activity didn't request the UDFPS. + if (mIsLaunchingActivity && !mUdfpsRequested) { + alpha *= (1.0f - mActivityLaunchProgress); + } } mView.setUnpausedAlpha(alpha); } @@ -426,4 +440,26 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud private final UnlockedScreenOffAnimationController.Callback mUnlockedScreenOffCallback = (linear, eased) -> mStateListener.onDozeAmountChanged(linear, eased); + + private final ActivityLaunchAnimator.Listener mActivityLaunchAnimatorListener = + new ActivityLaunchAnimator.Listener() { + @Override + public void onLaunchAnimationStart() { + mIsLaunchingActivity = true; + mActivityLaunchProgress = 0f; + updateAlpha(); + } + + @Override + public void onLaunchAnimationEnd() { + mIsLaunchingActivity = false; + updateAlpha(); + } + + @Override + public void onLaunchAnimationProgress(float linearProgress) { + mActivityLaunchProgress = linearProgress; + updateAlpha(); + } + }; } diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt index 977e46ac3b44..d2ded71487dc 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt @@ -131,6 +131,12 @@ class ControlsProviderLifecycleManager( wrapper = null bindService(false) } + + override fun onNullBinding(name: ComponentName?) { + if (DEBUG) Log.d(TAG, "onNullBinding $name") + wrapper = null + context.unbindService(this) + } } private fun handlePendingServiceMethods() { diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java index cffb2f79ebfb..b23569241f59 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java @@ -28,6 +28,8 @@ import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerR import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender; import com.android.systemui.people.PeopleProvider; import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.unfold.FoldStateLogger; +import com.android.systemui.unfold.FoldStateLoggingProvider; import com.android.systemui.unfold.SysUIUnfoldComponent; import com.android.systemui.unfold.UnfoldLatencyTracker; import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider; @@ -139,6 +141,8 @@ public interface SysUIComponent { getMediaTttChipControllerReceiver(); getMediaTttCommandLineHelper(); getUnfoldLatencyTracker().init(); + getFoldStateLoggingProvider().ifPresent(FoldStateLoggingProvider::init); + getFoldStateLogger().ifPresent(FoldStateLogger::init); } /** @@ -166,6 +170,18 @@ public interface SysUIComponent { UnfoldLatencyTracker getUnfoldLatencyTracker(); /** + * Creates a FoldStateLoggingProvider. + */ + @SysUISingleton + Optional<FoldStateLoggingProvider> getFoldStateLoggingProvider(); + + /** + * Creates a FoldStateLogger. + */ + @SysUISingleton + Optional<FoldStateLogger> getFoldStateLogger(); + + /** * Main dependency providing module. */ @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java index 9dddbb1d67c6..c0da57f58043 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java @@ -16,6 +16,7 @@ package com.android.systemui.dagger; +import com.android.keyguard.KeyguardBiometricLockoutLogger; import com.android.systemui.CoreStartable; import com.android.systemui.LatencyTester; import com.android.systemui.ScreenDecorations; @@ -90,6 +91,13 @@ public abstract class SystemUIBinder { @ClassKey(KeyguardViewMediator.class) public abstract CoreStartable bindKeyguardViewMediator(KeyguardViewMediator sysui); + /** Inject into KeyguardBiometricLockoutLogger. */ + @Binds + @IntoMap + @ClassKey(KeyguardBiometricLockoutLogger.class) + public abstract CoreStartable bindKeyguardBiometricLockoutLogger( + KeyguardBiometricLockoutLogger sysui); + /** Inject into LatencyTests. */ @Binds @IntoMap diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index c4a58db4f41a..9bc3f176e91a 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -59,6 +59,7 @@ import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl; import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; +import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.people.PeopleHubModule; @@ -208,6 +209,7 @@ public abstract class SystemUIModule { NotificationInterruptStateProvider interruptionStateProvider, ZenModeController zenModeController, NotificationLockscreenUserManager notifUserManager, NotificationGroupManagerLegacy groupManager, NotificationEntryManager entryManager, + CommonNotifCollection notifCollection, NotifPipeline notifPipeline, SysUiState sysUiState, NotifPipelineFlags notifPipelineFlags, DumpManager dumpManager, @Main Executor sysuiMainExecutor) { @@ -216,7 +218,7 @@ public abstract class SystemUIModule { configurationController, statusBarService, notificationManager, visibilityProvider, interruptionStateProvider, zenModeController, notifUserManager, - groupManager, entryManager, notifPipeline, sysUiState, notifPipelineFlags, - dumpManager, sysuiMainExecutor)); + groupManager, entryManager, notifCollection, notifPipeline, sysUiState, + notifPipelineFlags, dumpManager, sysuiMainExecutor)); } } diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java index 726f8656767c..5d6c2a247df3 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java @@ -57,8 +57,10 @@ public class Flags { /***************************************/ // 200 - keyguard/lockscreen - public static final BooleanFlag KEYGUARD_LAYOUT = - new BooleanFlag(200, true); + + // ** Flag retired ** + // public static final BooleanFlag KEYGUARD_LAYOUT = + // new BooleanFlag(200, true); public static final BooleanFlag LOCKSCREEN_ANIMATIONS = new BooleanFlag(201, true); @@ -69,6 +71,12 @@ public class Flags { public static final ResourceBooleanFlag CHARGING_RIPPLE = new ResourceBooleanFlag(203, R.bool.flag_charging_ripple); + public static final ResourceBooleanFlag BOUNCER_USER_SWITCHER = + new ResourceBooleanFlag(204, R.bool.config_enableBouncerUserSwitcher); + + public static final ResourceBooleanFlag ACTIVE_UNLOCK = + new ResourceBooleanFlag(205, R.bool.flag_active_unlock); + /***************************************/ // 300 - power menu public static final BooleanFlag POWER_MENU_LITE = @@ -123,7 +131,8 @@ public class Flags { /***************************************/ // 900 - media public static final BooleanFlag MEDIA_TAP_TO_TRANSFER = new BooleanFlag(900, false); - public static final BooleanFlag MEDIA_SESSION_ACTIONS = new BooleanFlag(901, false); + public static final BooleanFlag MEDIA_SESSION_ACTIONS = new BooleanFlag(901, true); + public static final BooleanFlag MEDIA_SESSION_LAYOUT = new BooleanFlag(902, false); // Pay no attention to the reflection behind the curtain. // ========================== Curtain ========================== diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 4658a745b680..094b1927480d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -100,6 +100,7 @@ import com.android.keyguard.KeyguardViewController; import com.android.keyguard.ViewMediatorCallback; import com.android.keyguard.mediator.ScreenOnCoordinator; import com.android.systemui.CoreStartable; +import com.android.systemui.DejankUtils; import com.android.systemui.Dumpable; import com.android.systemui.animation.Interpolators; import com.android.systemui.broadcast.BroadcastDispatcher; @@ -2345,16 +2346,20 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, // Block the panel from expanding, in case we were doing a swipe to dismiss gesture. mKeyguardViewControllerLazy.get().blockPanelExpansionFromCurrentTouch(); final boolean wasShowing = mShowing; - onKeyguardExitFinished(); + InteractionJankMonitor.getInstance().end(CUJ_LOCKSCREEN_UNLOCK_ANIMATION); - if (mKeyguardStateController.isDismissingFromSwipe() || wasShowing) { - mKeyguardUnlockAnimationControllerLazy.get().hideKeyguardViewAfterRemoteAnimation(); - } + // Post layout changes to the next frame, so we don't hang at the end of the animation. + DejankUtils.postAfterTraversal(() -> { + onKeyguardExitFinished(); - finishSurfaceBehindRemoteAnimation(cancelled); - mSurfaceBehindRemoteAnimationRequested = false; - mKeyguardUnlockAnimationControllerLazy.get().notifyFinishedKeyguardExitAnimation(); - InteractionJankMonitor.getInstance().end(CUJ_LOCKSCREEN_UNLOCK_ANIMATION); + if (mKeyguardStateController.isDismissingFromSwipe() || wasShowing) { + mKeyguardUnlockAnimationControllerLazy.get().hideKeyguardViewAfterRemoteAnimation(); + } + + finishSurfaceBehindRemoteAnimation(cancelled); + mSurfaceBehindRemoteAnimationRequested = false; + mKeyguardUnlockAnimationControllerLazy.get().notifyFinishedKeyguardExitAnimation(); + }); } /** diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt index 29321b46328a..c404f7aa0fb4 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt @@ -418,7 +418,7 @@ class MediaCarouselController @Inject constructor( .elementAtOrNull(mediaCarouselScrollHandler.visibleMediaIndex) if (existingPlayer == null) { var newPlayer = mediaControlPanelFactory.get() - if (mediaFlags.areMediaSessionActionsEnabled()) { + if (mediaFlags.useMediaSessionLayout()) { newPlayer.attachPlayer( PlayerSessionViewHolder.create(LayoutInflater.from(context), mediaContent), MediaViewController.TYPE.PLAYER_SESSION) diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index 3c237223e31a..69a7ec3447aa 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -61,6 +61,7 @@ import com.android.systemui.util.animation.TransitionLayout; import com.android.systemui.util.time.SystemClock; import java.net.URISyntaxException; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executor; @@ -480,6 +481,24 @@ public class MediaControlPanel { List<MediaAction> actionIcons = data.getActions(); List<Integer> actionsWhenCollapsed = data.getActionsToShowInCompact(); + // If the session actions flag is enabled, but we're still using the regular layout, use + // the session actions anyways + if (mMediaFlags.areMediaSessionActionsEnabled() && data.getSemanticActions() != null) { + MediaButton semanticActions = data.getSemanticActions(); + + actionIcons = new ArrayList<MediaAction>(); + actionIcons.add(semanticActions.getStartCustom()); + actionIcons.add(semanticActions.getPrevOrCustom()); + actionIcons.add(semanticActions.getPlayOrPause()); + actionIcons.add(semanticActions.getNextOrCustom()); + actionIcons.add(semanticActions.getEndCustom()); + + actionsWhenCollapsed = new ArrayList<Integer>(); + actionsWhenCollapsed.add(1); + actionsWhenCollapsed.add(2); + actionsWhenCollapsed.add(3); + } + int i = 0; for (; i < actionIcons.size() && i < ACTION_IDS.length; i++) { int actionId = ACTION_IDS[i]; diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaFlags.kt b/packages/SystemUI/src/com/android/systemui/media/MediaFlags.kt index b4a4b42d1509..b9795f1265fa 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaFlags.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaFlags.kt @@ -29,4 +29,12 @@ class MediaFlags @Inject constructor(private val featureFlags: FeatureFlags) { fun areMediaSessionActionsEnabled(): Boolean { return featureFlags.isEnabled(Flags.MEDIA_SESSION_ACTIONS) } + + /** + * Check whether media controls should use the new session-based layout + */ + fun useMediaSessionLayout(): Boolean { + return featureFlags.isEnabled(Flags.MEDIA_SESSION_ACTIONS) && + featureFlags.isEnabled(Flags.MEDIA_SESSION_LAYOUT) + } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java index 5fbdd88b9f66..ac816ba9e8d5 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java @@ -16,6 +16,7 @@ package com.android.systemui.navigationbar; +import static android.inputmethodservice.InputMethodService.canImeRenderGesturalNavButtons; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED; @@ -185,6 +186,17 @@ public class NavigationBarView extends FrameLayout implements @Nullable private Rect mOrientedHandleSamplingRegion; + /** + * {@code true} if the IME can render the back button and the IME switcher button. + * + * <p>The value must be used when and only when + * {@link com.android.systemui.shared.system.QuickStepContract#isGesturalMode(int)} returns + * {@code true}</p> + * + * <p>Cache the value here for better performance.</p> + */ + private final boolean mImeCanRenderGesturalNavButtons = canImeRenderGesturalNavButtons(); + private class NavTransitionListener implements TransitionListener { private boolean mBackTransitioning; private boolean mHomeAppearing; @@ -760,9 +772,14 @@ public class NavigationBarView extends FrameLayout implements updateRecentsIcon(); + boolean isImeRenderingNavButtons = isGesturalMode(mNavBarMode) + && mImeCanRenderGesturalNavButtons; + // Update IME button visibility, a11y and rotate button always overrides the appearance - mContextualButtonGroup.setButtonVisibility(R.id.ime_switcher, - (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0); + boolean disableImeSwitcher = + (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) == 0 + || isImeRenderingNavButtons; + mContextualButtonGroup.setButtonVisibility(R.id.ime_switcher, !disableImeSwitcher); mBarTransitions.reapplyDarkIntensity(); @@ -777,7 +794,8 @@ public class NavigationBarView extends FrameLayout implements && ((mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0); boolean disableBack = !useAltBack && (mEdgeBackGestureHandler.isHandlingGestures() - || ((mDisabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0)); + || ((mDisabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0)) + || isImeRenderingNavButtons; // When screen pinning, don't hide back and home when connected service or back and // recents buttons when disconnected from launcher service in screen pinning mode, diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt b/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt index 11c49496fc1d..bd2f64bb9874 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt @@ -59,6 +59,7 @@ class TileRequestDialog( R.dimen.qs_tile_service_request_tile_width), context.resources.getDimensionPixelSize(R.dimen.qs_quick_tile_size) ) + isSelected = true } val spacing = 0 setView(ll, spacing, spacing, spacing, spacing / 2) @@ -68,12 +69,17 @@ class TileRequestDialog( val tile = QSTileViewImpl(context, QSIconViewImpl(context), true) val state = QSTile.BooleanState().apply { label = tileData.label + handlesLongClick = false icon = tileData.icon?.loadDrawable(context)?.let { QSTileImpl.DrawableIcon(it) } ?: ResourceIcon.get(R.drawable.android) } tile.onStateChanged(state) - tile.isSelected = true + tile.post { + tile.stateDescription = "" + tile.isClickable = false + tile.isSelected = true + } return tile } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java index 7cf0583206e1..2a21f421869b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java @@ -238,7 +238,7 @@ public abstract class AlertingNotificationManager implements NotificationLifetim * @param key the key to check if removable * @return true if the alert entry can be removed */ - protected boolean canRemoveImmediately(String key) { + public boolean canRemoveImmediately(String key) { AlertEntry alertEntry = mAlertEntries.get(key); return alertEntry == null || alertEntry.wasShownLongEnough() || alertEntry.mEntry.isRowDismissed(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt index 73d3e2afac7c..8dc01f014192 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.charging -import android.graphics.Color import android.graphics.PointF import android.graphics.RuntimeShader import android.util.MathUtils @@ -40,7 +39,7 @@ class DwellRippleShader internal constructor() : RuntimeShader(SHADER, false) { uniform float in_time; uniform float in_radius; uniform float in_blur; - uniform vec4 in_color; + layout(color) uniform vec4 in_color; uniform float in_phase1; uniform float in_phase2; uniform float in_distortion_strength;""" @@ -98,7 +97,7 @@ class DwellRippleShader internal constructor() : RuntimeShader(SHADER, false) { var origin: PointF = PointF() set(value) { field = value - setUniform("in_origin", floatArrayOf(value.x, value.y)) + setFloatUniform("in_origin", value.x, value.y) } /** @@ -107,9 +106,9 @@ class DwellRippleShader internal constructor() : RuntimeShader(SHADER, false) { var progress: Float = 0.0f set(value) { field = value - setUniform("in_radius", + setFloatUniform("in_radius", (1 - (1 - value) * (1 - value) * (1 - value))* maxRadius) - setUniform("in_blur", MathUtils.lerp(1f, 0.7f, value)) + setFloatUniform("in_blur", MathUtils.lerp(1f, 0.7f, value)) } /** @@ -118,7 +117,7 @@ class DwellRippleShader internal constructor() : RuntimeShader(SHADER, false) { var distortionStrength: Float = 0.0f set(value) { field = value - setUniform("in_distortion_strength", value) + setFloatUniform("in_distortion_strength", value) } /** @@ -127,9 +126,9 @@ class DwellRippleShader internal constructor() : RuntimeShader(SHADER, false) { var time: Float = 0.0f set(value) { field = value * 0.001f - setUniform("in_time", field) - setUniform("in_phase1", field * 3f + 0.367f) - setUniform("in_phase2", field * 7.2f * 1.531f) + setFloatUniform("in_time", field) + setFloatUniform("in_phase1", field * 3f + 0.367f) + setFloatUniform("in_phase2", field * 7.2f * 1.531f) } /** @@ -138,8 +137,6 @@ class DwellRippleShader internal constructor() : RuntimeShader(SHADER, false) { var color: Int = 0xffffff.toInt() set(value) { field = value - val color = Color.valueOf(value) - setUniform("in_color", floatArrayOf(color.red(), - color.green(), color.blue(), color.alpha())) + setColorUniform("in_color", value) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/RippleShader.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/RippleShader.kt index 5175977d4e81..22fbf9139f1d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/RippleShader.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/RippleShader.kt @@ -15,7 +15,6 @@ */ package com.android.systemui.statusbar.charging -import android.graphics.Color import android.graphics.PointF import android.graphics.RuntimeShader import android.util.MathUtils @@ -43,7 +42,7 @@ class RippleShader internal constructor() : RuntimeShader(SHADER, false) { uniform float in_fadeRing; uniform float in_blur; uniform float in_pixelDensity; - uniform vec4 in_color; + layout(color) uniform vec4 in_color; uniform float in_sparkle_strength;""" private const val SHADER_LIB = """float triangleNoise(vec2 n) { n = fract(n * vec2(5.3987, 5.4421)); @@ -122,7 +121,7 @@ class RippleShader internal constructor() : RuntimeShader(SHADER, false) { var radius: Float = 0.0f set(value) { field = value - setUniform("in_maxRadius", value) + setFloatUniform("in_maxRadius", value) } /** @@ -131,7 +130,7 @@ class RippleShader internal constructor() : RuntimeShader(SHADER, false) { var origin: PointF = PointF() set(value) { field = value - setUniform("in_origin", floatArrayOf(value.x, value.y)) + setFloatUniform("in_origin", value.x, value.y) } /** @@ -140,10 +139,10 @@ class RippleShader internal constructor() : RuntimeShader(SHADER, false) { var progress: Float = 0.0f set(value) { field = value - setUniform("in_progress", value) - setUniform("in_radius", + setFloatUniform("in_progress", value) + setFloatUniform("in_radius", (1 - (1 - value) * (1 - value) * (1 - value))* radius) - setUniform("in_blur", MathUtils.lerp(1.25f, 0.5f, value)) + setFloatUniform("in_blur", MathUtils.lerp(1.25f, 0.5f, value)) val fadeIn = subProgress(0f, 0.1f, value) val fadeOutNoise = subProgress(0.4f, 1f, value) @@ -153,9 +152,9 @@ class RippleShader internal constructor() : RuntimeShader(SHADER, false) { fadeCircle = subProgress(0f, 0.2f, value) fadeOutRipple = subProgress(0.3f, 1f, value) } - setUniform("in_fadeSparkle", Math.min(fadeIn, 1 - fadeOutNoise)) - setUniform("in_fadeCircle", 1 - fadeCircle) - setUniform("in_fadeRing", Math.min(fadeIn, 1 - fadeOutRipple)) + setFloatUniform("in_fadeSparkle", Math.min(fadeIn, 1 - fadeOutNoise)) + setFloatUniform("in_fadeCircle", 1 - fadeCircle) + setFloatUniform("in_fadeRing", Math.min(fadeIn, 1 - fadeOutRipple)) } /** @@ -164,7 +163,7 @@ class RippleShader internal constructor() : RuntimeShader(SHADER, false) { var time: Float = 0.0f set(value) { field = value - setUniform("in_time", value) + setFloatUniform("in_time", value) } /** @@ -173,9 +172,7 @@ class RippleShader internal constructor() : RuntimeShader(SHADER, false) { var color: Int = 0xffffff.toInt() set(value) { field = value - val color = Color.valueOf(value) - setUniform("in_color", floatArrayOf(color.red(), - color.green(), color.blue(), color.alpha())) + setColorUniform("in_color", value) } /** @@ -186,7 +183,7 @@ class RippleShader internal constructor() : RuntimeShader(SHADER, false) { var sparkleStrength: Float = 0.0f set(value) { field = value - setUniform("in_sparkle_strength", value) + setFloatUniform("in_sparkle_strength", value) } /** @@ -195,14 +192,14 @@ class RippleShader internal constructor() : RuntimeShader(SHADER, false) { var distortionStrength: Float = 0.0f set(value) { field = value - setUniform("in_distort_radial", 75 * progress * value) - setUniform("in_distort_xy", 75 * value) + setFloatUniform("in_distort_radial", 75 * progress * value) + setFloatUniform("in_distort_xy", 75 * value) } var pixelDensity: Float = 1.0f set(value) { field = value - setUniform("in_pixelDensity", value) + setFloatUniform("in_pixelDensity", value) } var shouldFadeOutRipple: Boolean = true diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java index ed9663e9c672..f9f0b9da8778 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java @@ -179,23 +179,26 @@ public class HeadsUpCoordinator implements Coordinator { @Override public boolean shouldExtendLifetime(@NonNull NotificationEntry entry, int reason) { - boolean isShowingHun = isCurrentlyShowingHun(entry); - if (isShowingHun) { + boolean extend = !mHeadsUpManager.canRemoveImmediately(entry.getKey()); + if (extend) { if (isSticky(entry)) { long removeAfterMillis = mHeadsUpManager.getEarliestRemovalTime(entry.getKey()); - if (removeAfterMillis <= 0) return false; mExecutor.executeDelayed(() -> { - // make sure that the entry was not updated - long removeAfterMillis2 = - mHeadsUpManager.getEarliestRemovalTime(entry.getKey()); - if (mNotifsExtendingLifetime.contains(entry) && removeAfterMillis2 <= 0) { - mHeadsUpManager.removeNotification(entry.getKey(), true); + if (mNotifsExtendingLifetime.contains(entry) + && mHeadsUpManager.canRemoveImmediately(entry.getKey())) { + mHeadsUpManager.removeNotification( + entry.getKey(), /* releaseImmediately */ true); } }, removeAfterMillis); + } else { + // remove as early as possible + mExecutor.execute( + () -> mHeadsUpManager.removeNotification( + entry.getKey(), /* releaseImmediately */ false)); } mNotifsExtendingLifetime.add(entry); } - return isShowingHun; + return extend; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt index 43a75a5d5da8..ad973927f21e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt @@ -24,16 +24,18 @@ import com.android.systemui.statusbar.notification.collection.ListEntry import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.stack.NotificationListContainer import com.android.systemui.util.traceSection -import javax.inject.Inject +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject /** * Responsible for building and applying the "shade node spec": the list (tree) of things that * currently populate the notification shade. */ -class ShadeViewManager constructor( +class ShadeViewManager @AssistedInject constructor( context: Context, - listContainer: NotificationListContainer, - private val stackController: NotifStackController, + @Assisted listContainer: NotificationListContainer, + @Assisted private val stackController: NotifStackController, mediaContainerController: MediaContainerController, featureManager: NotificationSectionsFeatureManager, logger: ShadeViewDifferLogger, @@ -68,20 +70,10 @@ class ShadeViewManager constructor( } } -class ShadeViewManagerFactory @Inject constructor( - private val context: Context, - private val logger: ShadeViewDifferLogger, - private val mediaContainerController: MediaContainerController, - private val sectionsFeatureManager: NotificationSectionsFeatureManager, - private val viewBarn: NotifViewBarn -) { - fun create(listContainer: NotificationListContainer, stackController: NotifStackController) = - ShadeViewManager( - context, - listContainer, - stackController, - mediaContainerController, - sectionsFeatureManager, - logger, - viewBarn) +@AssistedFactory +interface ShadeViewManagerFactory { + fun create( + listContainer: NotificationListContainer, + stackController: NotifStackController + ): ShadeViewManager } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java index 6218c7708020..624e7416d3ee 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java @@ -550,41 +550,32 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable { public void removeFromTransientContainerForAdditionTo(ViewGroup newParent) { final ViewParent parent = getParent(); final ViewGroup transientContainer = getTransientContainer(); - if (parent == null) { - // If this view has no parent, the add will succeed, so just make sure the tracked - // transient container is in sync with the lack of a parent. - if (transientContainer != null) { + if (parent == null || parent == newParent) { + // If this view's current parent is null or the same as the new parent, the add will + // succeed, so just make sure the tracked transient container is in sync with the + // current parent. + if (transientContainer != null && transientContainer != parent) { Log.w(TAG, "Expandable view " + this + " has transient container " + transientContainer - + " but no parent"); + + " but different parent" + parent); setTransientContainer(null); } return; } if (transientContainer == null) { - throw new IllegalStateException( - "Can't add view " + this + " to container " + newParent + "; current parent " - + parent + " is not a transient container"); + throw new IllegalStateException("Can't add view " + this + " to container " + newParent + + "; current parent " + parent + " is not a transient container"); } if (transientContainer != parent) { - String transientContainerOutOfSyncError = "Expandable view " + this + // Crash with details before addView() crashes without any; the view is being added + // to a different parent, and the transient container isn't the parent, so we can't + // even (safely) clean that up. + throw new IllegalStateException("Expandable view " + this + " has transient container " + transientContainer - + " but different parent " + parent; - if (parent != newParent) { - // Crash with details before addView() crashes without any; the view is being added - // to a different parent, and the transient container isn't the parent, so we can't - // even (safely) clean that up. - throw new IllegalStateException(transientContainerOutOfSyncError); - } else { - Log.w(TAG, transientContainerOutOfSyncError); - setTransientContainer(null); - return; - } - } - if (parent != newParent) { - Log.w(TAG, "Moving view " + this + " from transient container " - + transientContainer + " to parent " + newParent); + + " but different parent " + parent); } + Log.w(TAG, "Removing view " + this + " from transient container " + + transientContainer + " in preparation for moving to parent " + newParent); transientContainer.removeTransientView(this); setTransientContainer(null); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java index bfa4a245070c..dee1b334182a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java @@ -11,7 +11,7 @@ * 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 + * limitations under the License. */ package com.android.systemui.statusbar.phone; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java index 01acce302b30..2824ab85152f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java @@ -412,7 +412,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, } @Override - protected boolean canRemoveImmediately(@NonNull String key) { + public boolean canRemoveImmediately(@NonNull String key) { if (mSwipedOutKeys.contains(key)) { // We always instantly dismiss views being manually swiped out. mSwipedOutKeys.remove(key); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt index ec2d0364a3a6..571c10b3800f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt @@ -54,6 +54,7 @@ class KeyguardLiftController constructor( isListening = false updateListeningState() keyguardUpdateMonitor.requestFaceAuth(true) + keyguardUpdateMonitor.requestActiveUnlock() } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt index 4de78f5d6190..868efa027f40 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt @@ -34,7 +34,7 @@ class LSShadeTransitionLogger @Inject constructor( private val displayMetrics: DisplayMetrics ) { fun logUnSuccessfulDragDown(startingChild: View?) { - val entry = (startingChild as ExpandableNotificationRow?)?.entry + val entry = (startingChild as? ExpandableNotificationRow)?.entry buffer.log(TAG, LogLevel.INFO, { str1 = entry?.key ?: "no entry" }, { @@ -49,7 +49,7 @@ class LSShadeTransitionLogger @Inject constructor( } fun logDragDownStarted(startingChild: ExpandableView?) { - val entry = (startingChild as ExpandableNotificationRow?)?.entry + val entry = (startingChild as? ExpandableNotificationRow)?.entry buffer.log(TAG, LogLevel.INFO, { str1 = entry?.key ?: "no entry" }, { @@ -58,7 +58,7 @@ class LSShadeTransitionLogger @Inject constructor( } fun logDraggedDownLockDownShade(startingChild: View?) { - val entry = (startingChild as ExpandableNotificationRow?)?.entry + val entry = (startingChild as? ExpandableNotificationRow)?.entry buffer.log(TAG, LogLevel.INFO, { str1 = entry?.key ?: "no entry" }, { @@ -67,7 +67,7 @@ class LSShadeTransitionLogger @Inject constructor( } fun logDraggedDown(startingChild: View?, dragLengthY: Int) { - val entry = (startingChild as ExpandableNotificationRow?)?.entry + val entry = (startingChild as? ExpandableNotificationRow)?.entry buffer.log(TAG, LogLevel.INFO, { str1 = entry?.key ?: "no entry" }, { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 1cc5e12a4102..a9b3927acc88 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -1412,7 +1412,8 @@ public class StatusBar extends CoreStartable implements private void setUpPresenter() { // Set up the initial notification state. - mActivityLaunchAnimator.setCallback(mKeyguardHandler); + mActivityLaunchAnimator.setCallback(mActivityLaunchAnimatorCallback); + mActivityLaunchAnimator.addListener(mActivityLaunchAnimatorListener); mNotificationAnimationProvider = new NotificationLaunchAnimatorControllerProvider( mNotificationShadeWindowViewController, mStackScrollerController.getNotificationListContainer(), @@ -4393,7 +4394,7 @@ public class StatusBar extends CoreStartable implements } }; - private final ActivityLaunchAnimator.Callback mKeyguardHandler = + private final ActivityLaunchAnimator.Callback mActivityLaunchAnimatorCallback = new ActivityLaunchAnimator.Callback() { @Override public boolean isOnKeyguard() { @@ -4412,11 +4413,6 @@ public class StatusBar extends CoreStartable implements } @Override - public void setBlursDisabledForAppLaunch(boolean disabled) { - mKeyguardViewMediator.setBlursDisabledForAppLaunch(disabled); - } - - @Override public int getBackgroundColor(TaskInfo task) { if (!mStartingSurfaceOptional.isPresent()) { Log.w(TAG, "No starting surface, defaulting to SystemBGColor"); @@ -4427,6 +4423,19 @@ public class StatusBar extends CoreStartable implements } }; + private final ActivityLaunchAnimator.Listener mActivityLaunchAnimatorListener = + new ActivityLaunchAnimator.Listener() { + @Override + public void onLaunchAnimationStart() { + mKeyguardViewMediator.setBlursDisabledForAppLaunch(true); + } + + @Override + public void onLaunchAnimationEnd() { + mKeyguardViewMediator.setBlursDisabledForAppLaunch(false); + } + }; + private final DemoMode mDemoModeCallback = new DemoMode() { @Override public void onDemoModeFinished() { diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldStateLogger.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldStateLogger.kt new file mode 100644 index 000000000000..1451c03fefa0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldStateLogger.kt @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2022 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.unfold + +import com.android.internal.util.FrameworkStatsLog + +/** Logs fold state changes. */ +class FoldStateLogger(private val foldStateLoggingProvider: FoldStateLoggingProvider) : + FoldStateLoggingProvider.FoldStateLoggingListener { + + fun init() { + foldStateLoggingProvider.addCallback(this) + } + + override fun onFoldUpdate(foldStateUpdate: FoldStateChange) { + FrameworkStatsLog.write( + FrameworkStatsLog.FOLD_STATE_DURATION_REPORTED, + foldStateUpdate.previous, + foldStateUpdate.current, + foldStateUpdate.dtMillis) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt index f2c156108ac6..c2fd34c719a0 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt @@ -102,6 +102,15 @@ class UnfoldTransitionModule { @Provides @Singleton + fun providesFoldStateLogger( + optionalFoldStateLoggingProvider: Optional<FoldStateLoggingProvider> + ): Optional<FoldStateLogger> = + optionalFoldStateLoggingProvider.map { FoldStateLoggingProvider -> + FoldStateLogger(FoldStateLoggingProvider) + } + + @Provides + @Singleton fun provideUnfoldTransitionConfig(context: Context): UnfoldTransitionConfig = createConfig(context) diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java index 5e9579cb6a83..20c8bf38692b 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java @@ -74,6 +74,7 @@ import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.coordinator.BubbleCoordinator; import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; +import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; @@ -88,6 +89,7 @@ import com.android.wm.shell.bubbles.Bubbles; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Optional; @@ -111,8 +113,10 @@ public class BubblesManager implements Dumpable { private final INotificationManager mNotificationManager; private final NotificationVisibilityProvider mVisibilityProvider; private final NotificationInterruptStateProvider mNotificationInterruptStateProvider; + private final NotificationLockscreenUserManager mNotifUserManager; private final NotificationGroupManagerLegacy mNotificationGroupManager; private final NotificationEntryManager mNotificationEntryManager; + private final CommonNotifCollection mCommonNotifCollection; private final NotifPipeline mNotifPipeline; private final Executor mSysuiMainExecutor; @@ -139,6 +143,7 @@ public class BubblesManager implements Dumpable { NotificationLockscreenUserManager notifUserManager, NotificationGroupManagerLegacy groupManager, NotificationEntryManager entryManager, + CommonNotifCollection notifCollection, NotifPipeline notifPipeline, SysUiState sysUiState, NotifPipelineFlags notifPipelineFlags, @@ -150,8 +155,8 @@ public class BubblesManager implements Dumpable { configurationController, statusBarService, notificationManager, visibilityProvider, interruptionStateProvider, zenModeController, notifUserManager, - groupManager, entryManager, notifPipeline, sysUiState, notifPipelineFlags, - dumpManager, sysuiMainExecutor); + groupManager, entryManager, notifCollection, notifPipeline, sysUiState, + notifPipelineFlags, dumpManager, sysuiMainExecutor); } else { return null; } @@ -172,6 +177,7 @@ public class BubblesManager implements Dumpable { NotificationLockscreenUserManager notifUserManager, NotificationGroupManagerLegacy groupManager, NotificationEntryManager entryManager, + CommonNotifCollection notifCollection, NotifPipeline notifPipeline, SysUiState sysUiState, NotifPipelineFlags notifPipelineFlags, @@ -184,8 +190,10 @@ public class BubblesManager implements Dumpable { mNotificationManager = notificationManager; mVisibilityProvider = visibilityProvider; mNotificationInterruptStateProvider = interruptionStateProvider; + mNotifUserManager = notifUserManager; mNotificationGroupManager = groupManager; mNotificationEntryManager = entryManager; + mCommonNotifCollection = notifCollection; mNotifPipeline = notifPipeline; mSysuiMainExecutor = sysuiMainExecutor; @@ -264,8 +272,7 @@ public class BubblesManager implements Dumpable { @Override public void getPendingOrActiveEntry(String key, Consumer<BubbleEntry> callback) { sysuiMainExecutor.execute(() -> { - NotificationEntry entry = - mNotificationEntryManager.getPendingOrActiveNotif(key); + final NotificationEntry entry = mCommonNotifCollection.getEntry(key); callback.accept(entry == null ? null : notifToBubbleEntry(entry)); }); } @@ -275,11 +282,11 @@ public class BubblesManager implements Dumpable { Consumer<List<BubbleEntry>> callback) { sysuiMainExecutor.execute(() -> { List<BubbleEntry> result = new ArrayList<>(); - List<NotificationEntry> activeEntries = - mNotificationEntryManager.getActiveNotificationsForCurrentUser(); - for (int i = 0; i < activeEntries.size(); i++) { - NotificationEntry entry = activeEntries.get(i); - if (savedBubbleKeys.contains(entry.getKey()) + final Collection<NotificationEntry> activeEntries = + mCommonNotifCollection.getAllNotifs(); + for (NotificationEntry entry : activeEntries) { + if (mNotifUserManager.isCurrentProfile(entry.getSbn().getUserId()) + && savedBubbleKeys.contains(entry.getKey()) && mNotificationInterruptStateProvider.shouldBubbleUp(entry) && entry.isBubble()) { result.add(notifToBubbleEntry(entry)); @@ -292,8 +299,7 @@ public class BubblesManager implements Dumpable { @Override public void setNotificationInterruption(String key) { sysuiMainExecutor.execute(() -> { - final NotificationEntry entry = - mNotificationEntryManager.getPendingOrActiveNotif(key); + final NotificationEntry entry = mCommonNotifCollection.getEntry(key); if (entry != null && entry.getImportance() >= NotificationManager.IMPORTANCE_HIGH) { entry.setInterruption(); @@ -311,8 +317,7 @@ public class BubblesManager implements Dumpable { @Override public void notifyRemoveNotification(String key, int reason) { sysuiMainExecutor.execute(() -> { - final NotificationEntry entry = - mNotificationEntryManager.getPendingOrActiveNotif(key); + final NotificationEntry entry = mCommonNotifCollection.getEntry(key); if (entry != null) { for (NotifCallback cb : mCallbacks) { cb.removeNotification(entry, getDismissedByUserStats(entry, true), @@ -334,8 +339,7 @@ public class BubblesManager implements Dumpable { @Override public void notifyMaybeCancelSummary(String key) { sysuiMainExecutor.execute(() -> { - final NotificationEntry entry = - mNotificationEntryManager.getPendingOrActiveNotif(key); + final NotificationEntry entry = mCommonNotifCollection.getEntry(key); if (entry != null) { for (NotifCallback cb : mCallbacks) { cb.maybeCancelSummary(entry); @@ -347,8 +351,7 @@ public class BubblesManager implements Dumpable { @Override public void removeNotificationEntry(String key) { sysuiMainExecutor.execute(() -> { - final NotificationEntry entry = - mNotificationEntryManager.getPendingOrActiveNotif(key); + final NotificationEntry entry = mCommonNotifCollection.getEntry(key); if (entry != null) { mNotificationGroupManager.onEntryRemoved(entry); } @@ -358,8 +361,7 @@ public class BubblesManager implements Dumpable { @Override public void updateNotificationBubbleButton(String key) { sysuiMainExecutor.execute(() -> { - final NotificationEntry entry = - mNotificationEntryManager.getPendingOrActiveNotif(key); + final NotificationEntry entry = mCommonNotifCollection.getEntry(key); if (entry != null && entry.getRow() != null) { entry.getRow().updateBubbleButton(); } @@ -369,8 +371,7 @@ public class BubblesManager implements Dumpable { @Override public void updateNotificationSuppression(String key) { sysuiMainExecutor.execute(() -> { - final NotificationEntry entry = - mNotificationEntryManager.getPendingOrActiveNotif(key); + final NotificationEntry entry = mCommonNotifCollection.getEntry(key); if (entry != null) { mNotificationGroupManager.updateSuppression(entry); } @@ -402,8 +403,7 @@ public class BubblesManager implements Dumpable { @Override public void onUnbubbleConversation(String key) { sysuiMainExecutor.execute(() -> { - final NotificationEntry entry = - mNotificationEntryManager.getPendingOrActiveNotif(key); + final NotificationEntry entry = mCommonNotifCollection.getEntry(key); if (entry != null) { onUserChangedBubble(entry, false /* shouldBubble */); } @@ -580,7 +580,7 @@ public class BubblesManager implements Dumpable { HashMap<String, Pair<BubbleEntry, Boolean>> pendingOrActiveNotif = new HashMap<>(); for (int i = 0; i < orderedKeys.length; i++) { String key = orderedKeys[i]; - NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(key); + final NotificationEntry entry = mCommonNotifCollection.getEntry(key); BubbleEntry bubbleEntry = entry != null ? notifToBubbleEntry(entry) : null; diff --git a/packages/SystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java b/packages/SystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java index cbd6e8659e69..0369d5b32883 100644 --- a/packages/SystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java +++ b/packages/SystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java @@ -110,6 +110,17 @@ public class AAAPlusPlusVerifySysuiRequiredTestPropertiesTest extends SysuiTestC private Collection<String> getClassNamesFromClassPath() { ClassPathScanner scanner = new ClassPathScanner(mContext.getPackageCodePath()); + ChainedClassNameFilter filter = makeClassNameFilter(); + + try { + return scanner.getClassPathEntries(filter); + } catch (IOException e) { + Log.e(getTag(), "Failed to scan classes", e); + } + return Collections.emptyList(); + } + + protected ChainedClassNameFilter makeClassNameFilter() { ChainedClassNameFilter filter = new ChainedClassNameFilter(); filter.add(new ExternalClassNameFilter()); @@ -122,13 +133,7 @@ public class AAAPlusPlusVerifySysuiRequiredTestPropertiesTest extends SysuiTestC // the main SystemUI process. Therefore, exclude this package // from the base class whitelist. filter.add(s -> !s.startsWith("com.android.systemui.screenshot")); - - try { - return scanner.getClassPathEntries(filter); - } catch (IOException e) { - Log.e(TAG, "Failed to scan classes", e); - } - return Collections.emptyList(); + return filter; } private String getClsStr() { @@ -212,8 +217,12 @@ public class AAAPlusPlusVerifySysuiRequiredTestPropertiesTest extends SysuiTestC * as loggable to limit log spam during normal use. */ private void logDebug(String msg) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, msg); + if (Log.isLoggable(getTag(), Log.DEBUG)) { + Log.d(getTag(), msg); } } + + protected String getTag() { + return TAG; + } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt new file mode 100644 index 000000000000..6bc65054d830 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2022 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.keyguard + +import android.hardware.biometrics.BiometricSourceType +import org.mockito.Mockito.verify +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.internal.logging.UiEventLogger +import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT +import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE +import com.android.systemui.SysuiTestCase +import com.android.systemui.dump.DumpManager +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.Captor +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.Mock +import org.mockito.Mockito.verifyNoMoreInteractions +import org.mockito.Mockito.`when` as whenever +import org.mockito.MockitoAnnotations + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class KeyguardBiometricLockoutLoggerTest : SysuiTestCase() { + @Mock + lateinit var uiEventLogger: UiEventLogger + @Mock + lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor + @Mock + lateinit var dumpManager: DumpManager + @Mock + lateinit var strongAuthTracker: KeyguardUpdateMonitor.StrongAuthTracker + + @Captor + lateinit var updateMonitorCallbackCaptor: ArgumentCaptor<KeyguardUpdateMonitorCallback> + lateinit var updateMonitorCallback: KeyguardUpdateMonitorCallback + + lateinit var keyguardBiometricLockoutLogger: KeyguardBiometricLockoutLogger + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + whenever(keyguardUpdateMonitor.strongAuthTracker).thenReturn(strongAuthTracker) + keyguardBiometricLockoutLogger = KeyguardBiometricLockoutLogger( + mContext, + uiEventLogger, + keyguardUpdateMonitor, + dumpManager) + } + + @Test + fun test_logsOnStart() { + // GIVEN is encrypted / lockdown before start + whenever(keyguardUpdateMonitor.isEncryptedOrLockdown(anyInt())) + .thenReturn(true) + + // WHEN start + keyguardBiometricLockoutLogger.start() + + // THEN encrypted / lockdown state is logged + verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent + .PRIMARY_AUTH_REQUIRED_ENCRYPTED_OR_LOCKDOWN) + } + + @Test + fun test_logTimeoutChange() { + keyguardBiometricLockoutLogger.start() + captureUpdateMonitorCallback() + + // GIVEN primary auth required b/c timeout + whenever(strongAuthTracker.getStrongAuthForUser(anyInt())) + .thenReturn(STRONG_AUTH_REQUIRED_AFTER_TIMEOUT) + + // WHEN primary auth requirement changes + updateMonitorCallback.onStrongAuthStateChanged(0) + + // THEN primary auth required state is logged + verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent + .PRIMARY_AUTH_REQUIRED_TIMEOUT) + } + + @Test + fun test_logUnattendedUpdate() { + keyguardBiometricLockoutLogger.start() + captureUpdateMonitorCallback() + + // GIVEN primary auth required b/c unattended update + whenever(strongAuthTracker.getStrongAuthForUser(anyInt())) + .thenReturn(STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE) + + // WHEN primary auth requirement changes + updateMonitorCallback.onStrongAuthStateChanged(0) + + // THEN primary auth required state is logged + verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent + .PRIMARY_AUTH_REQUIRED_UNATTENDED_UPDATE) + } + + @Test + fun test_logMultipleChanges() { + keyguardBiometricLockoutLogger.start() + captureUpdateMonitorCallback() + + // GIVEN primary auth required b/c timeout + whenever(strongAuthTracker.getStrongAuthForUser(anyInt())) + .thenReturn(STRONG_AUTH_REQUIRED_AFTER_TIMEOUT + or STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE) + + // WHEN primary auth requirement changes + updateMonitorCallback.onStrongAuthStateChanged(0) + + // THEN primary auth required state is logged with all the reasons + verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent + .PRIMARY_AUTH_REQUIRED_TIMEOUT) + verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent + .PRIMARY_AUTH_REQUIRED_UNATTENDED_UPDATE) + + // WHEN onStrongAuthStateChanged is called again + updateMonitorCallback.onStrongAuthStateChanged(0) + + // THEN no more events are sent since there haven't been any changes + verifyNoMoreInteractions(uiEventLogger) + } + + @Test + fun test_logFaceLockout() { + keyguardBiometricLockoutLogger.start() + captureUpdateMonitorCallback() + + // GIVEN primary auth required b/c face lock + whenever(keyguardUpdateMonitor.isFaceLockedOut).thenReturn(true) + + // WHEN lockout state changes + updateMonitorCallback.onLockedOutStateChanged(BiometricSourceType.FACE) + + // THEN primary auth required state is logged + verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent + .PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT) + + // WHEN face lockout is reset + whenever(keyguardUpdateMonitor.isFaceLockedOut).thenReturn(false) + updateMonitorCallback.onLockedOutStateChanged(BiometricSourceType.FACE) + + // THEN primary auth required state is logged + verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent + .PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT_RESET) + } + + @Test + fun test_logFingerprintLockout() { + keyguardBiometricLockoutLogger.start() + captureUpdateMonitorCallback() + + // GIVEN primary auth required b/c fingerprint lock + whenever(keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(true) + + // WHEN lockout state changes + updateMonitorCallback.onLockedOutStateChanged(BiometricSourceType.FINGERPRINT) + + // THEN primary auth required state is logged + verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent + .PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT) + + // WHEN fingerprint lockout is reset + whenever(keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(false) + updateMonitorCallback.onLockedOutStateChanged(BiometricSourceType.FINGERPRINT) + + // THEN primary auth required state is logged + verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent + .PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT_RESET) + } + + fun captureUpdateMonitorCallback() { + verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallbackCaptor.capture()) + updateMonitorCallback = updateMonitorCallbackCaptor.value + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java index c873804bc82c..599e5474c564 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java @@ -48,6 +48,7 @@ import com.android.keyguard.KeyguardSecurityModel.SecurityMode; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.classifier.FalsingCollector; +import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -116,6 +117,8 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { @Mock private GlobalSettings mGlobalSettings; @Mock + private FeatureFlags mFeatureFlags; + @Mock private UserSwitcherController mUserSwitcherController; private Configuration mConfiguration; @@ -151,7 +154,7 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger, mKeyguardStateController, mKeyguardSecurityViewFlipperController, mConfigurationController, mFalsingCollector, mFalsingManager, - mUserSwitcherController, mGlobalSettings).create(mSecurityCallback); + mUserSwitcherController, mFeatureFlags, mGlobalSettings).create(mSecurityCallback); } @Test diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index 70792cfee301..7266e41ad7ca 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -88,6 +88,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.AuthController; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dump.DumpManager; +import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.phone.KeyguardBypassController; @@ -173,6 +174,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { private InteractionJankMonitor mInteractionJankMonitor; @Mock private LatencyTracker mLatencyTracker; + @Mock + private FeatureFlags mFeatureFlags; @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateListenerCaptor; // Direct executor @@ -1105,7 +1108,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mRingerModeTracker, mBackgroundExecutor, mMainExecutor, mStatusBarStateController, mLockPatternUtils, mAuthController, mTelephonyListenerManager, - mInteractionJankMonitor, mLatencyTracker); + mInteractionJankMonitor, mLatencyTracker, mFeatureFlags); setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java index 40549d69dc74..8c20b248d02c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java @@ -29,8 +29,10 @@ import com.android.systemui.utils.leaks.LeakCheckedTest; import com.android.systemui.utils.leaks.LeakCheckedTest.SysuiLeakCheck; import org.junit.After; +import org.junit.AfterClass; import org.junit.Before; import org.junit.Rule; +import org.mockito.Mockito; public abstract class SysuiBaseFragmentTest extends BaseFragmentTest { @@ -78,6 +80,11 @@ public abstract class SysuiBaseFragmentTest extends BaseFragmentTest { SystemUIFactory.cleanup(); } + @AfterClass + public static void mockitoTeardown() { + Mockito.framework().clearInlineMocks(); + } + @Override protected SysuiTestableContext getContext() { return new SysuiTestableContext(InstrumentationRegistry.getContext(), mLeakCheck); diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt index 1fe3d4417730..589eeb5e0761 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt @@ -21,13 +21,12 @@ import android.widget.LinearLayout import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.mockito.any -import com.android.systemui.util.mockito.eq import junit.framework.Assert.assertFalse import junit.framework.Assert.assertNotNull import junit.framework.Assert.assertNull import junit.framework.Assert.assertTrue import junit.framework.AssertionFailedError -import kotlin.concurrent.thread +import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test @@ -40,6 +39,7 @@ import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.Spy import org.mockito.junit.MockitoJUnit +import kotlin.concurrent.thread @SmallTest @RunWith(AndroidTestingRunner::class) @@ -48,6 +48,7 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() { private val launchContainer = LinearLayout(mContext) private val launchAnimator = LaunchAnimator(TEST_TIMINGS, TEST_INTERPOLATORS) @Mock lateinit var callback: ActivityLaunchAnimator.Callback + @Mock lateinit var listener: ActivityLaunchAnimator.Listener @Spy private val controller = TestLaunchAnimatorController(launchContainer) @Mock lateinit var iCallback: IRemoteAnimationFinishedCallback @Mock lateinit var failHandler: Log.TerribleFailureHandler @@ -59,6 +60,12 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() { fun setup() { activityLaunchAnimator = ActivityLaunchAnimator(launchAnimator) activityLaunchAnimator.callback = callback + activityLaunchAnimator.addListener(listener) + } + + @After + fun tearDown() { + activityLaunchAnimator.removeListener(listener) } private fun startIntentWithAnimation( @@ -177,7 +184,7 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() { val runner = activityLaunchAnimator.createRunner(controller) runner.onAnimationStart(0, arrayOf(fakeWindow()), emptyArray(), emptyArray(), iCallback) waitForIdleSync() - verify(callback).setBlursDisabledForAppLaunch(eq(true)) + verify(listener).onLaunchAnimationStart() verify(controller).onLaunchAnimationStart(anyBoolean()) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt index 84eb93556c86..066a866118dd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt @@ -37,6 +37,7 @@ import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.R import com.android.systemui.SysuiTestCase +import com.android.systemui.animation.ActivityLaunchAnimator import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.LockscreenShadeTransitionController @@ -89,6 +90,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { @Mock private lateinit var udfpsController: UdfpsController @Mock private lateinit var udfpsView: UdfpsView @Mock private lateinit var udfpsEnrollView: UdfpsEnrollView + @Mock private lateinit var activityLaunchAnimator: ActivityLaunchAnimator private val sensorProps = SensorLocationInternal("", 10, 100, 20) .asFingerprintSensorProperties() @@ -118,7 +120,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { keyguardUpdateMonitor, dialogManager, dumpManager, transitionController, configurationController, systemClock, keyguardStateController, unlockedScreenOffAnimationController, sensorProps, hbmProvider, reason, - controllerCallback, onTouch) + controllerCallback, onTouch, activityLaunchAnimator) block() } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index 2afcbda64fb5..159bdbab6d8d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -58,6 +58,7 @@ import com.android.internal.util.LatencyTracker; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.plugins.FalsingManager; @@ -180,6 +181,8 @@ public class UdfpsControllerTest extends SysuiTestCase { private TypedArray mBrightnessBacklight; @Mock private SystemUIDialogManager mSystemUIDialogManager; + @Mock + private ActivityLaunchAnimator mActivityLaunchAnimator; // Capture listeners so that they can be used to send events @Captor private ArgumentCaptor<IUdfpsOverlayController> mOverlayCaptor; @@ -252,7 +255,8 @@ public class UdfpsControllerTest extends SysuiTestCase { mSystemClock, mUnlockedScreenOffAnimationController, mSystemUIDialogManager, - mLatencyTracker); + mLatencyTracker, + mActivityLaunchAnimator); verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture()); mOverlayController = mOverlayCaptor.getValue(); verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java index 0ae3c39e659b..e9a4e15b9773 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java @@ -36,6 +36,7 @@ import androidx.test.filters.SmallTest; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.SysuiTestCase; +import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -96,6 +97,8 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase { private SystemUIDialogManager mDialogManager; @Mock private UdfpsController mUdfpsController; + @Mock + private ActivityLaunchAnimator mActivityLaunchAnimator; private FakeSystemClock mSystemClock = new FakeSystemClock(); private UdfpsKeyguardViewController mController; @@ -134,7 +137,8 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase { mKeyguardStateController, mUnlockedScreenOffAnimationController, mDialogManager, - mUdfpsController); + mUdfpsController, + mActivityLaunchAnimator); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt index 2d3757c29ebf..12096bc06748 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt @@ -17,6 +17,9 @@ package com.android.systemui.controls.controller import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.ServiceConnection import android.os.UserHandle import android.service.controls.IControlsActionCallback import android.service.controls.IControlsProvider @@ -43,6 +46,8 @@ import org.mockito.ArgumentMatchers.eq import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito.`when` +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @@ -57,8 +62,6 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() { private lateinit var subscriberService: IControlsSubscriber.Stub @Mock private lateinit var service: IControlsProvider.Stub - @Mock - private lateinit var loadCallback: ControlsBindingController.LoadCallback @Captor private lateinit var wrapperCaptor: ArgumentCaptor<ControlActionWrapper> @@ -75,7 +78,7 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() { fun setUp() { MockitoAnnotations.initMocks(this) - mContext.addMockService(componentName, service) + context.addMockService(componentName, service) executor = FakeExecutor(FakeSystemClock()) `when`(service.asBinder()).thenCallRealMethod() `when`(service.queryLocalInterface(ArgumentMatchers.anyString())).thenReturn(service) @@ -98,7 +101,36 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() { fun testBindService() { manager.bindService() executor.runAllReady() - assertTrue(mContext.isBound(componentName)) + assertTrue(context.isBound(componentName)) + } + + @Test + fun testNullBinding() { + val mockContext = mock(Context::class.java) + lateinit var serviceConnection: ServiceConnection + `when`(mockContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer { + val component = (it.arguments[0] as Intent).component + if (component == componentName) { + serviceConnection = it.arguments[1] as ServiceConnection + serviceConnection.onNullBinding(component) + true + } else { + false + } + } + + val nullManager = ControlsProviderLifecycleManager( + mockContext, + executor, + actionCallbackService, + UserHandle.of(0), + componentName + ) + + nullManager.bindService() + executor.runAllReady() + + verify(mockContext).unbindService(serviceConnection) } @Test @@ -109,7 +141,7 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() { manager.unbindService() executor.runAllReady() - assertFalse(mContext.isBound(componentName)) + assertFalse(context.isBound(componentName)) } @Test @@ -119,7 +151,7 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() { verify(service).load(subscriberService) - assertTrue(mContext.isBound(componentName)) + assertTrue(context.isBound(componentName)) } @Test @@ -129,7 +161,7 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() { manager.unbindService() executor.runAllReady() - assertFalse(mContext.isBound(componentName)) + assertFalse(context.isBound(componentName)) } @Test @@ -162,7 +194,7 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() { manager.maybeBindAndSubscribe(list, subscriberService) executor.runAllReady() - assertTrue(mContext.isBound(componentName)) + assertTrue(context.isBound(componentName)) verify(service).subscribe(list, subscriberService) } @@ -173,7 +205,7 @@ class ControlsProviderLifecycleManagerTest : SysuiTestCase() { manager.maybeBindAndSendAction(controlId, action) executor.runAllReady() - assertTrue(mContext.isBound(componentName)) + assertTrue(context.isBound(componentName)) verify(service).action(eq(controlId), capture(wrapperCaptor), eq(actionCallbackService)) assertEquals(action, wrapperCaptor.getValue().getWrappedAction()) diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java index 8cd8e4d7382d..c0b7271c6de7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java @@ -16,12 +16,15 @@ package com.android.systemui.dreams; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Intent; import android.os.IBinder; +import android.service.dreams.DreamService; import android.service.dreams.IDreamOverlay; import android.service.dreams.IDreamOverlayCallback; import android.testing.AndroidTestingRunner; @@ -195,4 +198,22 @@ public class DreamOverlayServiceTest extends SysuiTestCase { mService.onDestroy(); verify(mDreamOverlayStateController).removeCallback(callbackCapture.getValue()); } + + @Test + public void testShouldShowComplicationsTrueByDefault() { + assertThat(mService.shouldShowComplications()).isTrue(); + + mService.onBind(new Intent()); + + assertThat(mService.shouldShowComplications()).isTrue(); + } + + @Test + public void testShouldShowComplicationsSetByIntentExtra() { + final Intent intent = new Intent(); + intent.putExtra(DreamService.EXTRA_SHOW_COMPLICATIONS, false); + mService.onBind(intent); + + assertThat(mService.shouldShowComplications()).isFalse(); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt index 7763e75ae40b..140a395f1e27 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt @@ -202,6 +202,7 @@ public class MediaControlPanelTest : SysuiTestCase() { resumeAction = null) whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(false) + whenever(mediaFlags.useMediaSessionLayout()).thenReturn(false) } /** Mock view holder for the notification player */ @@ -301,8 +302,47 @@ public class MediaControlPanelTest : SysuiTestCase() { } @Test - fun bindSemanticActions() { + fun bindSemanticActionsOldLayout() { whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(true) + whenever(mediaFlags.useMediaSessionLayout()).thenReturn(false) + + val icon = Icon.createWithResource(context, android.R.drawable.ic_media_play) + val semanticActions = MediaButton( + playOrPause = MediaAction(icon, Runnable {}, "play"), + nextOrCustom = MediaAction(icon, Runnable {}, "next"), + startCustom = MediaAction(icon, null, "custom 1"), + endCustom = MediaAction(icon, null, "custom 2") + ) + val state = mediaData.copy(semanticActions = semanticActions) + + player.attachPlayer(holder, MediaViewController.TYPE.PLAYER) + player.bindPlayer(state, PACKAGE) + + verify(expandedSet).setVisibility(R.id.action0, ConstraintSet.VISIBLE) + assertThat(action0.contentDescription).isEqualTo("custom 1") + assertThat(action0.isEnabled()).isFalse() + + verify(expandedSet).setVisibility(R.id.action1, ConstraintSet.INVISIBLE) + assertThat(action1.isEnabled()).isFalse() + + verify(expandedSet).setVisibility(R.id.action2, ConstraintSet.VISIBLE) + assertThat(action2.isEnabled()).isTrue() + assertThat(action2.contentDescription).isEqualTo("play") + + verify(expandedSet).setVisibility(R.id.action3, ConstraintSet.VISIBLE) + assertThat(action3.isEnabled()).isTrue() + assertThat(action3.contentDescription).isEqualTo("next") + + verify(expandedSet).setVisibility(R.id.action4, ConstraintSet.VISIBLE) + assertThat(action4.contentDescription).isEqualTo("custom 2") + assertThat(action4.isEnabled()).isFalse() + } + + @Test + fun bindSemanticActionsNewLayout() { + whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(true) + whenever(mediaFlags.useMediaSessionLayout()).thenReturn(true) + val icon = Icon.createWithResource(context, android.R.drawable.ic_media_play) val semanticActions = MediaButton( playOrPause = MediaAction(icon, Runnable {}, "play"), diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogTest.kt index f56a185a19a5..2ad9c9400f65 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogTest.kt @@ -130,4 +130,37 @@ class TileRequestDialogTest : SysuiTestCase() { val tile = content.getChildAt(1) as QSTileView assertThat((tile.icon.iconView as ImageView).drawable).isNotNull() } + + @Test + fun setTileData_hasNoStateDescription() { + val icon = Icon.createWithResource(mContext, R.drawable.cloud) + val tileData = TileRequestDialog.TileData(APP_NAME, LABEL, icon) + + dialog.setTileData(tileData) + dialog.show() + + TestableLooper.get(this).processAllMessages() + + val content = dialog.requireViewById<ViewGroup>(TileRequestDialog.CONTENT_ID) + val tile = content.getChildAt(1) as QSTileView + + assertThat(tile.stateDescription).isEqualTo("") + } + + @Test + fun setTileData_tileNotClickable() { + val icon = Icon.createWithResource(mContext, R.drawable.cloud) + val tileData = TileRequestDialog.TileData(APP_NAME, LABEL, icon) + + dialog.setTileData(tileData) + dialog.show() + + TestableLooper.get(this).processAllMessages() + + val content = dialog.requireViewById<ViewGroup>(TileRequestDialog.CONTENT_ID) + val tile = content.getChildAt(1) as QSTileView + + assertThat(tile.isClickable).isFalse() + assertThat(tile.isLongClickable).isFalse() + } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/navigationbar/RegionSamplingHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/navigationbar/RegionSamplingHelperTest.kt new file mode 100644 index 000000000000..8bc438bce5cc --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/navigationbar/RegionSamplingHelperTest.kt @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2022 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.shared.navigationbar +import android.graphics.Rect +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper.RunWithLooper +import android.view.SurfaceControl +import android.view.View +import android.view.ViewRootImpl +import androidx.concurrent.futures.DirectExecutor +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.eq +import org.mockito.Mock +import org.mockito.Mockito.* +import org.mockito.junit.MockitoJUnit +import org.mockito.Mockito.`when` as whenever + +@RunWith(AndroidTestingRunner::class) +@SmallTest +@RunWithLooper +class RegionSamplingHelperTest : SysuiTestCase() { + + @Mock + lateinit var sampledView: View + @Mock + lateinit var samplingCallback: RegionSamplingHelper.SamplingCallback + @Mock + lateinit var compositionListener: RegionSamplingHelper.SysuiCompositionSamplingListener + @Mock + lateinit var viewRootImpl: ViewRootImpl + @Mock + lateinit var surfaceControl: SurfaceControl + @Mock + lateinit var wrappedSurfaceControl: SurfaceControl + @JvmField @Rule + var rule = MockitoJUnit.rule() + lateinit var regionSamplingHelper: RegionSamplingHelper + + @Before + fun setup() { + whenever(sampledView.isAttachedToWindow).thenReturn(true) + whenever(sampledView.viewRootImpl).thenReturn(viewRootImpl) + whenever(viewRootImpl.surfaceControl).thenReturn(surfaceControl) + whenever(surfaceControl.isValid).thenReturn(true) + whenever(wrappedSurfaceControl.isValid).thenReturn(true) + whenever(samplingCallback.isSamplingEnabled).thenReturn(true) + regionSamplingHelper = object : RegionSamplingHelper(sampledView, samplingCallback, + DirectExecutor.INSTANCE, DirectExecutor.INSTANCE, compositionListener) { + override fun wrap(stopLayerControl: SurfaceControl?): SurfaceControl { + return wrappedSurfaceControl + } + } + regionSamplingHelper.setWindowVisible(true) + } + + @Test + fun testStart_register() { + regionSamplingHelper.start(Rect(0, 0, 100, 100)) + verify(compositionListener).register(any(), anyInt(), eq(wrappedSurfaceControl), any()) + } + + @Test + fun testStart_unregister() { + regionSamplingHelper.start(Rect(0, 0, 100, 100)) + regionSamplingHelper.setWindowVisible(false) + verify(compositionListener).unregister(any()) + } + + @Test + fun testStart_hasBlur_neverRegisters() { + regionSamplingHelper.setWindowHasBlurs(true) + regionSamplingHelper.start(Rect(0, 0, 100, 100)) + verify(compositionListener, never()) + .register(any(), anyInt(), eq(wrappedSurfaceControl), any()) + } + + @Test + fun testStart_stopAndDestroy() { + regionSamplingHelper.start(Rect(0, 0, 100, 100)) + regionSamplingHelper.stopAndDestroy() + verify(compositionListener).unregister(any()) + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt new file mode 100644 index 000000000000..6971c63ed6d4 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt @@ -0,0 +1,44 @@ +package com.android.systemui.statusbar + +import android.testing.AndroidTestingRunner +import android.util.DisplayMetrics +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.log.LogBuffer +import com.android.systemui.statusbar.notification.row.ExpandableView +import com.android.systemui.statusbar.phone.LSShadeTransitionLogger +import com.android.systemui.statusbar.phone.LockscreenGestureLogger +import com.android.systemui.util.mockito.mock +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.junit.MockitoJUnit + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class LSShadeTransitionLoggerTest : SysuiTestCase() { + lateinit var logger: LSShadeTransitionLogger + @Mock + lateinit var gestureLogger: LockscreenGestureLogger + @Mock + lateinit var displayMetrics: DisplayMetrics + @JvmField @Rule + val mockito = MockitoJUnit.rule() + + @Before + fun setup() { + logger = LSShadeTransitionLogger( + LogBuffer("Test", 10, 10, mock()), + gestureLogger, + displayMetrics) + } + + @Test + fun testLogDragDownStarted() { + val view: ExpandableView = mock() + // log a non-null, non row, ensure no crash + logger.logDragDownStarted(view) + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java index a11b46df1218..b3ee5f8373e4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java @@ -140,10 +140,13 @@ public class HeadsUpCoordinatorTest extends SysuiTestCase { public void testCancelStickyNotification() { when(mHeadsUpManager.isSticky(anyString())).thenReturn(true); addHUN(mEntry); + when(mHeadsUpManager.canRemoveImmediately(anyString())).thenReturn(false, true); when(mHeadsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L, 0L); assertTrue(mNotifLifetimeExtender.shouldExtendLifetime(mEntry, 0)); mClock.advanceTime(1000L); mExecutor.runAllReady(); + verify(mHeadsUpManager, times(0)) + .removeNotification(anyString(), eq(false)); verify(mHeadsUpManager, times(1)) .removeNotification(anyString(), eq(true)); } @@ -157,6 +160,22 @@ public class HeadsUpCoordinatorTest extends SysuiTestCase { mClock.advanceTime(1000L); mExecutor.runAllReady(); verify(mHeadsUpManager, times(0)) + .removeNotification(anyString(), eq(false)); + verify(mHeadsUpManager, times(0)) + .removeNotification(anyString(), eq(true)); + } + + @Test + public void testCancelNotification() { + when(mHeadsUpManager.isSticky(anyString())).thenReturn(false); + addHUN(mEntry); + when(mHeadsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L, 500L); + assertTrue(mNotifLifetimeExtender.shouldExtendLifetime(mEntry, 0)); + mClock.advanceTime(1000L); + mExecutor.runAllReady(); + verify(mHeadsUpManager, times(1)) + .removeNotification(anyString(), eq(false)); + verify(mHeadsUpManager, times(0)) .removeNotification(anyString(), eq(true)); } @@ -189,6 +208,13 @@ public class HeadsUpCoordinatorTest extends SysuiTestCase { // GIVEN there is a HUN, mEntry addHUN(mEntry); + given(mHeadsUpManager.canRemoveImmediately(anyString())).willAnswer(i -> { + String key = i.getArgument(0); + for (NotificationEntry entry : mHuns) { + if (entry.getKey().equals(key)) return false; + } + return true; + }); // THEN only the current HUN, mEntry, should be lifetimeExtended assertTrue(mNotifLifetimeExtender.shouldExtendLifetime(mEntry, /* cancellationReason */ 0)); assertFalse(mNotifLifetimeExtender.shouldExtendLifetime( diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index d27a57077c9c..07351cf386ac 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -92,6 +92,7 @@ import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; +import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationTestHelper; @@ -111,6 +112,7 @@ import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.TaskViewTransitions; import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.bubbles.Bubble; +import com.android.wm.shell.bubbles.BubbleBadgeIconFactory; import com.android.wm.shell.bubbles.BubbleData; import com.android.wm.shell.bubbles.BubbleDataRepository; import com.android.wm.shell.bubbles.BubbleEntry; @@ -152,6 +154,8 @@ public class BubblesTest extends SysuiTestCase { @Mock private NotificationEntryManager mNotificationEntryManager; @Mock + private CommonNotifCollection mCommonNotifCollection; + @Mock private NotificationGroupManagerLegacy mNotificationGroupManager; @Mock private WindowManager mWindowManager; @@ -299,9 +303,8 @@ public class BubblesTest extends SysuiTestCase { mBubbleEntry2User11 = BubblesManager.notifToBubbleEntry( mNotificationTestHelper.createBubble(handle)); - // Return non-null notification data from the NEM - when(mNotificationEntryManager - .getActiveNotificationUnfiltered(mRow.getKey())).thenReturn(mRow); + // Return non-null notification data from the CommonNotifCollection + when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow); mZenModeConfig.suppressedVisualEffects = 0; when(mZenModeController.getConfig()).thenReturn(mZenModeConfig); @@ -371,12 +374,14 @@ public class BubblesTest extends SysuiTestCase { mLockscreenUserManager, mNotificationGroupManager, mNotificationEntryManager, + mCommonNotifCollection, mNotifPipeline, mSysUiState, mNotifPipelineFlags, mDumpManager, syncExecutor); + // XXX: Does *this* need to be changed? // Get a reference to the BubbleController's entry listener verify(mNotificationEntryManager, atLeastOnce()) .addNotificationEntryListener(mEntryListenerCaptor.capture()); @@ -421,10 +426,8 @@ public class BubblesTest extends SysuiTestCase { public void testPromoteBubble_autoExpand() throws Exception { mBubbleController.updateBubble(mBubbleEntry2); mBubbleController.updateBubble(mBubbleEntry); - when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getKey())) - .thenReturn(mRow); - when(mNotificationEntryManager.getPendingOrActiveNotif(mRow2.getKey())) - .thenReturn(mRow2); + when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow); + when(mCommonNotifCollection.getEntry(mRow2.getKey())).thenReturn(mRow2); mBubbleController.removeBubble( mRow.getKey(), Bubbles.DISMISS_USER_GESTURE); @@ -452,10 +455,8 @@ public class BubblesTest extends SysuiTestCase { mBubbleController.updateBubble(mBubbleEntry2); mBubbleController.updateBubble(mBubbleEntry, /* suppressFlyout */ false, /* showInShade */ true); - when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getKey())) - .thenReturn(mRow); - when(mNotificationEntryManager.getPendingOrActiveNotif(mRow2.getKey())) - .thenReturn(mRow2); + when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow); + when(mCommonNotifCollection.getEntry(mRow2.getKey())).thenReturn(mRow2); mBubbleController.removeBubble( mRow.getKey(), Bubbles.DISMISS_USER_GESTURE); @@ -958,12 +959,9 @@ public class BubblesTest extends SysuiTestCase { mBubbleEntry2, /* suppressFlyout */ false, /* showInShade */ false); mBubbleController.updateBubble( mBubbleEntry3, /* suppressFlyout */ false, /* showInShade */ false); - when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getKey())) - .thenReturn(mRow); - when(mNotificationEntryManager.getPendingOrActiveNotif(mRow2.getKey())) - .thenReturn(mRow2); - when(mNotificationEntryManager.getPendingOrActiveNotif(mRow3.getKey())) - .thenReturn(mRow3); + when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow); + when(mCommonNotifCollection.getEntry(mRow2.getKey())).thenReturn(mRow2); + when(mCommonNotifCollection.getEntry(mRow3.getKey())).thenReturn(mRow3); assertEquals(mBubbleData.getBubbles().size(), 3); mBubbleData.setMaxOverflowBubbles(1); @@ -1021,7 +1019,7 @@ public class BubblesTest extends SysuiTestCase { // GIVEN a group summary with a bubble child ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0); ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup(); - when(mNotificationEntryManager.getPendingOrActiveNotif(groupedBubble.getEntry().getKey())) + when(mCommonNotifCollection.getEntry(groupedBubble.getEntry().getKey())) .thenReturn(groupedBubble.getEntry()); mEntryListener.onPendingEntryAdded(groupedBubble.getEntry()); groupSummary.addChildNotification(groupedBubble); @@ -1046,7 +1044,7 @@ public class BubblesTest extends SysuiTestCase { ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0); ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup(); mEntryListener.onPendingEntryAdded(groupedBubble.getEntry()); - when(mNotificationEntryManager.getPendingOrActiveNotif(groupedBubble.getEntry().getKey())) + when(mCommonNotifCollection.getEntry(groupedBubble.getEntry().getKey())) .thenReturn(groupedBubble.getEntry()); groupSummary.addChildNotification(groupedBubble); assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey())); @@ -1069,7 +1067,7 @@ public class BubblesTest extends SysuiTestCase { // GIVEN a group summary with two (non-bubble) children and one bubble child ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(2); ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup(); - when(mNotificationEntryManager.getPendingOrActiveNotif(groupedBubble.getEntry().getKey())) + when(mCommonNotifCollection.getEntry(groupedBubble.getEntry().getKey())) .thenReturn(groupedBubble.getEntry()); mEntryListener.onPendingEntryAdded(groupedBubble.getEntry()); groupSummary.addChildNotification(groupedBubble); @@ -1220,6 +1218,7 @@ public class BubblesTest extends SysuiTestCase { mBubbleController, mBubbleController.getStackView(), new BubbleIconFactory(mContext), + new BubbleBadgeIconFactory(mContext), bubble, true /* skipInflation */); verify(userContext, times(1)).getPackageManager(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java index ad26f0112a8e..9eee83ccb6e2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java @@ -77,6 +77,7 @@ import com.android.systemui.statusbar.notification.NotificationFilter; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; +import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -133,6 +134,8 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { @Mock private NotificationEntryManager mNotificationEntryManager; @Mock + private CommonNotifCollection mCommonNotifCollection; + @Mock private NotificationGroupManagerLegacy mNotificationGroupManager; @Mock private BubblesManager.NotifCallback mNotifCallback; @@ -336,6 +339,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { mLockscreenUserManager, mNotificationGroupManager, mNotificationEntryManager, + mCommonNotifCollection, mNotifPipeline, mSysUiState, mNotifPipelineFlags, @@ -404,8 +408,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { public void testRemoveBubble_withDismissedNotif_notInOverflow() { mEntryListener.onEntryAdded(mRow); mBubbleController.updateBubble(mBubbleEntry); - when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getKey())) - .thenReturn(mRow); + when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow); assertTrue(mBubbleController.hasBubbles()); assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry); @@ -887,7 +890,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0); ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup(); mEntryListener.onEntryAdded(groupedBubble.getEntry()); - when(mNotificationEntryManager.getPendingOrActiveNotif(groupedBubble.getEntry().getKey())) + when(mCommonNotifCollection.getEntry(groupedBubble.getEntry().getKey())) .thenReturn(groupedBubble.getEntry()); groupSummary.addChildNotification(groupedBubble); assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey())); @@ -911,7 +914,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0); ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup(); mEntryListener.onEntryAdded(groupedBubble.getEntry()); - when(mNotificationEntryManager.getPendingOrActiveNotif(groupedBubble.getEntry().getKey())) + when(mCommonNotifCollection.getEntry(groupedBubble.getEntry().getKey())) .thenReturn(groupedBubble.getEntry()); groupSummary.addChildNotification(groupedBubble); assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey())); @@ -935,7 +938,7 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(2); ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup(); mEntryListener.onEntryAdded(groupedBubble.getEntry()); - when(mNotificationEntryManager.getPendingOrActiveNotif(groupedBubble.getEntry().getKey())) + when(mCommonNotifCollection.getEntry(groupedBubble.getEntry().getKey())) .thenReturn(groupedBubble.getEntry()); groupSummary.addChildNotification(groupedBubble); @@ -1011,10 +1014,9 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { */ @Test public void testOverflowLoadedOnce() { - when(mNotificationEntryManager.getPendingOrActiveNotif(mRow.getKey())) - .thenReturn(mRow); - when(mNotificationEntryManager.getPendingOrActiveNotif(mRow2.getKey())) - .thenReturn(mRow2); + // XXX + when(mCommonNotifCollection.getEntry(mRow.getKey())).thenReturn(mRow); + when(mCommonNotifCollection.getEntry(mRow2.getKey())).thenReturn(mRow2); mEntryListener.onEntryAdded(mRow); mEntryListener.onEntryAdded(mRow2); diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java index 09485d179e60..aba32ec465b5 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java @@ -667,6 +667,10 @@ public class AccessibilityWindowManager { return null; } + // Don't need to add the embedded hierarchy windows into the accessibility windows list. + if (mHostEmbeddedMap.size() > 0 && isEmbeddedHierarchyWindowsLocked(windowId)) { + return null; + } final AccessibilityWindowInfo reportedWindow = AccessibilityWindowInfo.obtain(); reportedWindow.setId(windowId); @@ -699,6 +703,21 @@ public class AccessibilityWindowManager { return reportedWindow; } + private boolean isEmbeddedHierarchyWindowsLocked(int windowId) { + final IBinder leashToken = mWindowIdMap.get(windowId); + if (leashToken == null) { + return false; + } + + for (int i = 0; i < mHostEmbeddedMap.size(); i++) { + if (mHostEmbeddedMap.keyAt(i).equals(leashToken)) { + return true; + } + } + + return false; + } + private int getTypeForWindowManagerWindowType(int windowType) { switch (windowType) { case WindowManager.LayoutParams.TYPE_APPLICATION: @@ -735,8 +754,7 @@ public class AccessibilityWindowManager { case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR: case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY: case WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY: - case WindowManager.LayoutParams.TYPE_SCREENSHOT: - case WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY: { + case WindowManager.LayoutParams.TYPE_SCREENSHOT: { return AccessibilityWindowInfo.TYPE_SYSTEM; } @@ -748,6 +766,10 @@ public class AccessibilityWindowManager { return AccessibilityWindowInfo.TYPE_ACCESSIBILITY_OVERLAY; } + case WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY: { + return AccessibilityWindowInfo.TYPE_MAGNIFICATION_OVERLAY; + } + default: { return -1; } diff --git a/services/companion/java/com/android/server/companion/AssociationCleanUpService.java b/services/companion/java/com/android/server/companion/AssociationCleanUpService.java new file mode 100644 index 000000000000..f1d98f09aba3 --- /dev/null +++ b/services/companion/java/com/android/server/companion/AssociationCleanUpService.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.companion; + +import static com.android.server.companion.CompanionDeviceManagerService.LOG_TAG; + +import android.app.job.JobInfo; +import android.app.job.JobParameters; +import android.app.job.JobScheduler; +import android.app.job.JobService; +import android.companion.AssociationRequest; +import android.content.ComponentName; +import android.content.Context; +import android.os.AsyncTask; +import android.util.Slog; + +import com.android.server.LocalServices; + +/** + * A Job Service responsible for clean up the Association. + * The job will be executed only if the device is charging and in idle mode due to the application + * will be killed if association/role are revoked. + */ +public class AssociationCleanUpService extends JobService { + private static final String TAG = LOG_TAG + ".AssociationCleanUpService"; + private static final int JOB_ID = AssociationCleanUpService.class.hashCode(); + private static final long ONE_DAY_INTERVAL = 3 * 24 * 60 * 60 * 1000; // 1 Day + private CompanionDeviceManagerServiceInternal mCdmServiceInternal = LocalServices.getService( + CompanionDeviceManagerServiceInternal.class); + + @Override + public boolean onStartJob(final JobParameters params) { + Slog.i(LOG_TAG, "Execute the Association CleanUp job"); + // Special policy for APP_STREAMING role that need to revoke associations if the device + // does not connect for 3 months. + AsyncTask.execute(() -> { + mCdmServiceInternal.associationCleanUp(AssociationRequest.DEVICE_PROFILE_APP_STREAMING); + jobFinished(params, false); + }); + return true; + } + + @Override + public boolean onStopJob(final JobParameters params) { + Slog.d(TAG, "Association cleanup job stopped; id=" + params.getJobId() + + ", reason=" + + JobParameters.getInternalReasonCodeDescription( + params.getInternalStopReasonCode())); + return false; + } + + static void schedule(Context context) { + Slog.i(LOG_TAG, "Scheduling the Association Cleanup job"); + final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class); + final JobInfo job = new JobInfo.Builder(JOB_ID, + new ComponentName(context, AssociationCleanUpService.class)) + .setRequiresCharging(true) + .setRequiresDeviceIdle(true) + .setPeriodic(ONE_DAY_INTERVAL) + .build(); + jobScheduler.schedule(job); + } +} diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index c18f5fa2c782..94a97d864f58 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -149,6 +149,9 @@ public class CompanionDeviceManagerService extends SystemService private static final String PREF_FILE_NAME = "companion_device_preferences.xml"; private static final String PREF_KEY_AUTO_REVOKE_GRANTS_DONE = "auto_revoke_grants_done"; + private static final long ASSOCIATION_CLEAN_UP_TIME_WINDOW = + 90L * 24 * 60 * 60 * 1000; // 3 months + private static DateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); static { sDateFormat.setTimeZone(TimeZone.getDefault()); @@ -179,6 +182,7 @@ public class CompanionDeviceManagerService extends SystemService new ArrayMap<>(); private final RemoteCallbackList<IOnAssociationsChangedListener> mListeners = new RemoteCallbackList<>(); + private final CompanionDeviceManagerServiceInternal mLocalService = new LocalService(this); final Handler mMainHandler = Handler.getMain(); private CompanionDevicePresenceController mCompanionDevicePresenceController; @@ -208,6 +212,8 @@ public class CompanionDeviceManagerService extends SystemService mPermissionControllerManager = requireNonNull( context.getSystemService(PermissionControllerManager.class)); mUserManager = context.getSystemService(UserManager.class); + + LocalServices.addService(CompanionDeviceManagerServiceInternal.class, mLocalService); } @Override @@ -250,6 +256,9 @@ public class CompanionDeviceManagerService extends SystemService } else { Slog.w(LOG_TAG, "No BluetoothAdapter available"); } + } else if (phase == PHASE_BOOT_COMPLETED) { + // Run the Association CleanUp job service daily. + AssociationCleanUpService.schedule(getContext()); } } @@ -294,6 +303,24 @@ public class CompanionDeviceManagerService extends SystemService return association; } + // Revoke associations if the selfManaged companion device does not connect for 3 + // months for specific profile. + private void associationCleanUp(String profile) { + for (AssociationInfo ai : mAssociationStore.getAssociations()) { + if (ai.isSelfManaged() + && profile.equals(ai.getDeviceProfile()) + && System.currentTimeMillis() - ai.getLastTimeConnectedMs() + >= ASSOCIATION_CLEAN_UP_TIME_WINDOW) { + Slog.d(LOG_TAG, "Removing the association for associationId: " + + ai.getId() + + " due to the device does not connect for 3 months." + + " Current time: " + + new Date(System.currentTimeMillis())); + disassociateInternal(ai.getId()); + } + } + } + void maybeGrantAutoRevokeExemptions() { Slog.d(LOG_TAG, "maybeGrantAutoRevokeExemptions()"); PackageManager pm = getContext().getPackageManager(); @@ -573,6 +600,9 @@ public class CompanionDeviceManagerService extends SystemService return; } + association.setLastTimeConnected(System.currentTimeMillis()); + mAssociationStore.updateAssociation(association); + mCompanionDevicePresenceController.onDeviceNotifyAppeared( association, getContext(), mMainHandler); } @@ -735,7 +765,8 @@ public class CompanionDeviceManagerService extends SystemService final long timestamp = System.currentTimeMillis(); final AssociationInfo association = new AssociationInfo(id, userId, packageName, - macAddress, displayName, deviceProfile, selfManaged, false, timestamp); + macAddress, displayName, deviceProfile, selfManaged, false, timestamp, + Long.MAX_VALUE); Slog.i(LOG_TAG, "New CDM association created=" + association); mAssociationStore.addAssociation(association); @@ -1297,4 +1328,17 @@ public class CompanionDeviceManagerService extends SystemService return Collections.unmodifiableMap(copy); } + + private final class LocalService extends CompanionDeviceManagerServiceInternal { + private final CompanionDeviceManagerService mService; + + LocalService(CompanionDeviceManagerService service) { + mService = service; + } + + @Override + public void associationCleanUp(String profile) { + mService.associationCleanUp(profile); + } + } } diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerServiceInternal.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerServiceInternal.java new file mode 100644 index 000000000000..326fefe1f8ea --- /dev/null +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerServiceInternal.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.companion; + +/** + * Companion Device Manager Local System Service Interface. + * + * @hide Only for use within the system server. + */ +public abstract class CompanionDeviceManagerServiceInternal { + /** + * @see CompanionDeviceManagerService#associationCleanUp + */ + public abstract void associationCleanUp(String profile); +} diff --git a/services/companion/java/com/android/server/companion/PersistentDataStore.java b/services/companion/java/com/android/server/companion/PersistentDataStore.java index 97ec3bb7127d..ef3aa7fea1b5 100644 --- a/services/companion/java/com/android/server/companion/PersistentDataStore.java +++ b/services/companion/java/com/android/server/companion/PersistentDataStore.java @@ -168,6 +168,7 @@ final class PersistentDataStore { private static final String XML_ATTR_SELF_MANAGED = "self_managed"; private static final String XML_ATTR_NOTIFY_DEVICE_NEARBY = "notify_device_nearby"; private static final String XML_ATTR_TIME_APPROVED = "time_approved"; + private static final String XML_ATTR_LAST_TIME_CONNECTED = "last_time_connected"; private static final String LEGACY_XML_ATTR_DEVICE = "device"; @@ -378,7 +379,7 @@ final class PersistentDataStore { out.add(new AssociationInfo(associationId, userId, appPackage, MacAddress.fromString(deviceAddress), null, profile, - /* managedByCompanionApp */false, notify, timeApproved)); + /* managedByCompanionApp */false, notify, timeApproved, Long.MAX_VALUE)); } private static void readAssociationsV1(@NonNull TypedXmlPullParser parser, @@ -408,9 +409,12 @@ final class PersistentDataStore { final boolean selfManaged = readBooleanAttribute(parser, XML_ATTR_SELF_MANAGED); final boolean notify = readBooleanAttribute(parser, XML_ATTR_NOTIFY_DEVICE_NEARBY); final long timeApproved = readLongAttribute(parser, XML_ATTR_TIME_APPROVED, 0L); + final long lastTimeConnected = readLongAttribute( + parser, XML_ATTR_LAST_TIME_CONNECTED, Long.MAX_VALUE); final AssociationInfo associationInfo = createAssociationInfoNoThrow(associationId, userId, - appPackage, macAddress, displayName, profile, selfManaged, notify, timeApproved); + appPackage, macAddress, displayName, profile, selfManaged, notify, timeApproved, + lastTimeConnected); if (associationInfo != null) { out.add(associationInfo); } @@ -464,6 +468,8 @@ final class PersistentDataStore { writeBooleanAttribute( serializer, XML_ATTR_NOTIFY_DEVICE_NEARBY, a.isNotifyOnDeviceNearby()); writeLongAttribute(serializer, XML_ATTR_TIME_APPROVED, a.getTimeApprovedMs()); + writeLongAttribute( + serializer, XML_ATTR_LAST_TIME_CONNECTED, a.getLastTimeConnectedMs()); serializer.endTag(null, XML_TAG_ASSOCIATION); } @@ -512,11 +518,11 @@ final class PersistentDataStore { private static AssociationInfo createAssociationInfoNoThrow(int associationId, @UserIdInt int userId, @NonNull String appPackage, @Nullable MacAddress macAddress, @Nullable CharSequence displayName, @Nullable String profile, boolean selfManaged, - boolean notify, long timeApproved) { + boolean notify, long timeApproved, long lastTimeConnected) { AssociationInfo associationInfo = null; try { associationInfo = new AssociationInfo(associationId, userId, appPackage, macAddress, - displayName, profile, selfManaged, notify, timeApproved); + displayName, profile, selfManaged, notify, timeApproved, lastTimeConnected); } catch (Exception e) { if (DEBUG) Slog.w(LOG_TAG, "Could not create AssociationInfo", e); } diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index bc8da8443a7d..262933dea27f 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -45,6 +45,7 @@ import android.bluetooth.IBluetoothManager; import android.bluetooth.IBluetoothManagerCallback; import android.bluetooth.IBluetoothProfileServiceConnection; import android.bluetooth.IBluetoothStateChangeCallback; +import android.bluetooth.IBluetoothLeCallControl; import android.content.ActivityNotFoundException; import android.content.AttributionSource; import android.content.BroadcastReceiver; @@ -1323,11 +1324,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { + bluetoothProfile); } - if (bluetoothProfile != BluetoothProfile.HEADSET) { + Intent intent; + if (bluetoothProfile == BluetoothProfile.HEADSET) { + intent = new Intent(IBluetoothHeadset.class.getName()); + } else if (bluetoothProfile== BluetoothProfile.LE_CALL_CONTROL) { + intent = new Intent(IBluetoothLeCallControl.class.getName()); + } else { return false; } - Intent intent = new Intent(IBluetoothHeadset.class.getName()); psc = new ProfileServiceConnections(intent); if (!psc.bindService()) { return false; diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index a2c2dbd407a5..39516802e93b 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -39,6 +39,8 @@ import static android.net.NetworkStats.STATS_PER_UID; import static android.net.NetworkStats.TAG_NONE; import static android.net.TrafficStats.UID_TETHERING; +import static com.android.net.module.util.NetworkStatsUtils.LIMIT_GLOBAL_ALERT; + import android.annotation.NonNull; import android.app.ActivityManager; import android.content.Context; @@ -133,12 +135,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub { private static final int MAX_UID_RANGES_PER_COMMAND = 10; - /** - * Name representing {@link #setGlobalAlert(long)} limit when delivered to - * {@link INetworkManagementEventObserver#limitReached(String, String)}. - */ - public static final String LIMIT_GLOBAL_ALERT = "globalAlert"; - static final int DAEMON_MSG_MOBILE_CONN_REAL_TIME_INFO = 1; static final boolean MODIFY_OPERATION_ADD = true; diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java index 1208cb7e3813..e7f4de2a0588 100644 --- a/services/core/java/com/android/server/SystemServiceManager.java +++ b/services/core/java/com/android/server/SystemServiceManager.java @@ -26,6 +26,7 @@ import android.os.Environment; import android.os.SystemClock; import android.os.Trace; import android.os.UserHandle; +import android.util.ArraySet; import android.util.EventLog; import android.util.IndentingPrintWriter; import android.util.Slog; @@ -45,6 +46,9 @@ import java.io.File; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @@ -91,7 +95,8 @@ public final class SystemServiceManager implements Dumpable { private long mRuntimeStartUptime; // Services that should receive lifecycle events. - private final ArrayList<SystemService> mServices = new ArrayList<SystemService>(); + private List<SystemService> mServices; + private Set<String> mServiceClassnames; private int mCurrentPhase = -1; @@ -113,11 +118,13 @@ public final class SystemServiceManager implements Dumpable { SystemServiceManager(Context context) { mContext = context; + mServices = new ArrayList<>(); + mServiceClassnames = new ArraySet<>(); // Disable using the thread pool for low ram devices sUseLifecycleThreadPool = sUseLifecycleThreadPool - && !ActivityManager.isLowRamDeviceStatic(); + && !ActivityManager.isLowRamDeviceStatic(); mNumUserPoolThreads = Math.min(Runtime.getRuntime().availableProcessors(), - DEFAULT_MAX_USER_POOL_THREADS); + DEFAULT_MAX_USER_POOL_THREADS); } /** @@ -207,8 +214,17 @@ public final class SystemServiceManager implements Dumpable { } public void startService(@NonNull final SystemService service) { + // Check if already started + String className = service.getClass().getName(); + if (mServiceClassnames.contains(className)) { + Slog.i(TAG, "Not starting an already started service " + className); + return; + } + mServiceClassnames.add(className); + // Register it. mServices.add(service); + // Start it. long time = SystemClock.elapsedRealtime(); try { @@ -220,11 +236,17 @@ public final class SystemServiceManager implements Dumpable { warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onStart"); } + /** Disallow starting new services after this call. */ + void sealStartedServices() { + mServiceClassnames = Collections.emptySet(); + mServices = Collections.unmodifiableList(mServices); + } + /** * Starts the specified boot phase for all system services that have been started up to * this point. * - * @param t trace logger + * @param t trace logger * @param phase The boot phase to start. */ public void startBootPhase(@NonNull TimingsTraceAndSlog t, int phase) { @@ -398,8 +420,8 @@ public final class SystemServiceManager implements Dumpable { // Limit the lifecycle parallelization to all users other than the system user // and only for the user start lifecycle phase for now. final boolean useThreadPool = sUseLifecycleThreadPool - && curUserId != UserHandle.USER_SYSTEM - && onWhat.equals(USER_STARTING); + && curUserId != UserHandle.USER_SYSTEM + && onWhat.equals(USER_STARTING); final ExecutorService threadPool = useThreadPool ? Executors.newFixedThreadPool(mNumUserPoolThreads) : null; for (int i = 0; i < serviceLen; i++) { @@ -419,7 +441,7 @@ public final class SystemServiceManager implements Dumpable { + serviceName + " because it's not supported (curUser: " + curUser + ", prevUser:" + prevUser + ")"); } else { - Slog.i(TAG, "Skipping " + onWhat + "User-" + curUserId + " on " + Slog.i(TAG, "Skipping " + onWhat + "User-" + curUserId + " on " + serviceName); } continue; @@ -516,6 +538,7 @@ public final class SystemServiceManager implements Dumpable { /** * Returns whether we are booting into safe mode. + * * @return safe mode flag */ public boolean isSafeMode() { @@ -559,9 +582,10 @@ public final class SystemServiceManager implements Dumpable { /** * Ensures that the system directory exist creating one if needed. + * + * @return The system directory. * @deprecated Use {@link Environment#getDataSystemCeDirectory()} * or {@link Environment#getDataSystemDeDirectory()} instead. - * @return The system directory. */ @Deprecated public static File ensureSystemDir() { @@ -578,7 +602,9 @@ public final class SystemServiceManager implements Dumpable { pw.printf("Current phase: %d\n", mCurrentPhase); synchronized (mTargetUsers) { if (mCurrentUser != null) { - pw.print("Current user: "); mCurrentUser.dump(pw); pw.println(); + pw.print("Current user: "); + mCurrentUser.dump(pw); + pw.println(); } else { pw.println("Current user not set!"); } diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java index f591b26f1770..297d28dadde3 100644 --- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java +++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java @@ -18,6 +18,7 @@ package com.android.server.adb; import static com.android.internal.util.dump.DumpUtils.writeStringIfNotNull; +import android.annotation.NonNull; import android.annotation.TestApi; import android.app.ActivityManager; import android.app.Notification; @@ -170,6 +171,12 @@ public class AdbDebuggingManager { mAdbConnectionInfo = new AdbConnectionInfo(); } + static void sendBroadcastWithDebugPermission(@NonNull Context context, @NonNull Intent intent, + @NonNull UserHandle userHandle) { + context.sendBroadcastAsUser(intent, userHandle, + android.Manifest.permission.MANAGE_DEBUGGING); + } + class PairingThread extends Thread implements NsdManager.RegistrationListener { private NsdManager mNsdManager; private String mPublicKey; @@ -1278,7 +1285,7 @@ public class AdbDebuggingManager { ? AdbManager.WIRELESS_STATUS_CONNECTED : AdbManager.WIRELESS_STATUS_DISCONNECTED); intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, UserHandle.ALL); } private void onAdbdWifiServerConnected(int port) { @@ -1350,7 +1357,8 @@ public class AdbDebuggingManager { if (publicKey == null) { Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION); intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, AdbManager.WIRELESS_STATUS_FAIL); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, + UserHandle.ALL); } else { Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION); intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, @@ -1366,7 +1374,8 @@ public class AdbDebuggingManager { device.guid = hostname; device.connected = false; intent.putExtra(AdbManager.WIRELESS_PAIR_DEVICE_EXTRA, device); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, + UserHandle.ALL); // Add the key into the keystore mAdbKeyStore.setLastConnectionTime(publicKey, System.currentTimeMillis()); @@ -1380,14 +1389,14 @@ public class AdbDebuggingManager { intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, AdbManager.WIRELESS_STATUS_CONNECTED); intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, UserHandle.ALL); } private void sendPairedDevicesToUI(Map<String, PairDevice> devices) { Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION); // Map is not serializable, so need to downcast intent.putExtra(AdbManager.WIRELESS_DEVICES_EXTRA, (HashMap) devices); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, UserHandle.ALL); } private void updateUIPairCode(String code) { @@ -1397,7 +1406,7 @@ public class AdbDebuggingManager { intent.putExtra(AdbManager.WIRELESS_PAIRING_CODE_EXTRA, code); intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, AdbManager.WIRELESS_STATUS_PAIRING_CODE); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, UserHandle.ALL); } } diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java index 7a4d2ce50cd3..2845fbfc6ebf 100644 --- a/services/core/java/com/android/server/adb/AdbService.java +++ b/services/core/java/com/android/server/adb/AdbService.java @@ -459,7 +459,7 @@ public class AdbService extends IAdbManager.Stub { ? AdbManager.WIRELESS_STATUS_CONNECTED : AdbManager.WIRELESS_STATUS_DISCONNECTED); intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, UserHandle.ALL); Slog.i(TAG, "sent port broadcast port=" + port); } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index da8c407a501d..70bd734a9ccd 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2333,7 +2333,7 @@ public class ActivityManagerService extends IActivityManager.Stub offloadConstants.SLOW_TIME = Integer.MAX_VALUE; mEnableOffloadQueue = SystemProperties.getBoolean( - "persist.device_config.activity_manager_native_boot.offload_queue_enabled", false); + "persist.device_config.activity_manager_native_boot.offload_queue_enabled", true); mFgBroadcastQueue = new BroadcastQueue(this, mHandler, "foreground", foreConstants, false); @@ -13378,7 +13378,12 @@ public class ActivityManagerService extends IActivityManager.Stub !intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP, false); final boolean fullUninstall = removed && !replacing; if (removed) { - if (!killProcess) { + if (killProcess) { + forceStopPackageLocked(ssp, UserHandle.getAppId( + intent.getIntExtra(Intent.EXTRA_UID, -1)), + false, true, true, false, fullUninstall, userId, + removed ? "pkg removed" : "pkg changed"); + } else { // Kill any app zygotes always, since they can't fork new // processes with references to the old code forceStopAppZygoteLocked(ssp, UserHandle.getAppId( diff --git a/services/core/java/com/android/server/am/AppNotRespondingDialog.java b/services/core/java/com/android/server/am/AppNotRespondingDialog.java index 878ef31f143d..5fe842752e81 100644 --- a/services/core/java/com/android/server/am/AppNotRespondingDialog.java +++ b/services/core/java/com/android/server/am/AppNotRespondingDialog.java @@ -163,7 +163,9 @@ final class AppNotRespondingDialog extends BaseErrorDialog implements View.OnCli synchronized (mService.mProcLock) { errState.setNotResponding(false); - errState.setNotRespondingReport(null); + // We're not clearing the ANR report here, in case we'd need to report + // it again when the ANR dialog shows again. + // errState.setNotRespondingReport(null); errState.getDialogController().clearAnrDialogs(); } mService.mServices.scheduleServiceTimeoutLocked(app); diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java index 336572f44be3..8561b61c2172 100644 --- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java +++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java @@ -16,6 +16,7 @@ package com.android.server.am; import android.annotation.Nullable; +import android.app.usage.NetworkStatsManager; import android.bluetooth.BluetoothActivityEnergyInfo; import android.bluetooth.BluetoothAdapter; import android.content.Context; @@ -712,8 +713,10 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { if (wifiInfo.isValid()) { final long wifiChargeUC = measuredEnergyDeltas != null ? measuredEnergyDeltas.wifiChargeUC : MeasuredEnergySnapshot.UNAVAILABLE; - mStats.updateWifiState( - extractDeltaLocked(wifiInfo), wifiChargeUC, elapsedRealtime, uptime); + final NetworkStatsManager networkStatsManager = mInjector.getSystemService( + NetworkStatsManager.class); + mStats.updateWifiState(extractDeltaLocked(wifiInfo), + wifiChargeUC, elapsedRealtime, uptime, networkStatsManager); } else { Slog.w(TAG, "wifi info is invalid: " + wifiInfo); } @@ -722,8 +725,10 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { if (modemInfo != null) { final long mobileRadioChargeUC = measuredEnergyDeltas != null ? measuredEnergyDeltas.mobileRadioChargeUC : MeasuredEnergySnapshot.UNAVAILABLE; + final NetworkStatsManager networkStatsManager = mInjector.getSystemService( + NetworkStatsManager.class); mStats.noteModemControllerActivity(modemInfo, mobileRadioChargeUC, elapsedRealtime, - uptime); + uptime, networkStatsManager); } if (updateFlags == UPDATE_ALL) { diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 8cb20404b3e1..9ffafe256033 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -22,6 +22,7 @@ import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE; import android.annotation.NonNull; import android.app.StatsManager; +import android.app.usage.NetworkStatsManager; import android.bluetooth.BluetoothActivityEnergyInfo; import android.content.ContentResolver; import android.content.Context; @@ -2029,8 +2030,11 @@ public final class BatteryStatsService extends IBatteryStats.Stub synchronized (mLock) { final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); + final NetworkStatsManager networkStatsManager = mContext.getSystemService( + NetworkStatsManager.class); mHandler.post(() -> { - mStats.updateWifiState(info, POWER_DATA_UNAVAILABLE, elapsedRealtime, uptime); + mStats.updateWifiState(info, POWER_DATA_UNAVAILABLE, elapsedRealtime, uptime, + networkStatsManager); }); } } @@ -2067,9 +2071,11 @@ public final class BatteryStatsService extends IBatteryStats.Stub synchronized (mLock) { final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); + final NetworkStatsManager networkStatsManager = mContext.getSystemService( + NetworkStatsManager.class); mHandler.post(() -> { mStats.noteModemControllerActivity(info, POWER_DATA_UNAVAILABLE, elapsedRealtime, - uptime); + uptime, networkStatsManager); }); } } diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java index c55bbe8e971b..c08cf6485855 100644 --- a/services/core/java/com/android/server/am/CachedAppOptimizer.java +++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java @@ -539,6 +539,8 @@ public final class CachedAppOptimizer { */ static private native void compactProcess(int pid, int compactionFlags); + static private native void cancelCompaction(); + /** * Reads the flag value from DeviceConfig to determine whether app compaction * should be enabled, and starts the freeze/compaction thread if needed. @@ -1049,6 +1051,26 @@ public final class CachedAppOptimizer { } } + @GuardedBy({"mService", "mProcLock"}) + void onOomAdjustChanged(int oldAdj, int newAdj, ProcessRecord app) { + // Cancel any currently executing compactions + // if the process moved out of cached state + if (DefaultProcessDependencies.mPidCompacting == app.mPid && newAdj < oldAdj + && newAdj < ProcessList.CACHED_APP_MIN_ADJ) { + cancelCompaction(); + } + + // Perform a minor compaction when a perceptible app becomes the prev/home app + // Perform a major compaction when any app enters cached + if (oldAdj <= ProcessList.PERCEPTIBLE_APP_ADJ + && (newAdj == ProcessList.PREVIOUS_APP_ADJ || newAdj == ProcessList.HOME_APP_ADJ)) { + compactAppSome(app); + } else if (newAdj >= ProcessList.CACHED_APP_MIN_ADJ + && newAdj <= ProcessList.CACHED_APP_MAX_ADJ) { + compactAppFull(app); + } + } + @VisibleForTesting static final class LastCompactionStats { private final long[] mRssAfterCompaction; @@ -1091,6 +1113,13 @@ public final class CachedAppOptimizer { name = proc.processName; opt.setHasPendingCompact(false); + if (mAm.mInternal.isPendingTopUid(proc.uid)) { + // In case the OOM Adjust has not yet been propagated we see if this is + // pending on becoming top app in which case we should not compact. + Slog.e(TAG_AM, "Skip compaction since UID is active for " + name); + return; + } + // don't compact if the process has returned to perceptible // and this is only a cached/home/prev compaction if ((pendingAction == COMPACT_PROCESS_SOME @@ -1500,6 +1529,8 @@ public final class CachedAppOptimizer { * Default implementation for ProcessDependencies, public vor visibility to OomAdjuster class. */ private static final class DefaultProcessDependencies implements ProcessDependencies { + public static int mPidCompacting = -1; + // Get memory RSS from process. @Override public long[] getRss(int pid) { @@ -1509,6 +1540,7 @@ public final class CachedAppOptimizer { // Compact process. @Override public void performCompaction(String action, int pid) throws IOException { + mPidCompacting = pid; if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_FULL])) { compactProcess(pid, COMPACT_ACTION_FILE_FLAG | COMPACT_ACTION_ANON_FLAG); } else if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_FILE])) { @@ -1516,6 +1548,7 @@ public final class CachedAppOptimizer { } else if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_ANON])) { compactProcess(pid, COMPACT_ACTION_ANON_FLAG); } + mPidCompacting = -1; } } } diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index af4ff58596a1..b1234962efc2 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -2486,18 +2486,9 @@ public class OomAdjuster { // don't compact during bootup if (mCachedAppOptimizer.useCompaction() && mService.mBooted) { // Cached and prev/home compaction + // reminder: here, setAdj is previous state, curAdj is upcoming state if (state.getCurAdj() != state.getSetAdj()) { - // Perform a minor compaction when a perceptible app becomes the prev/home app - // Perform a major compaction when any app enters cached - // reminder: here, setAdj is previous state, curAdj is upcoming state - if (state.getSetAdj() <= ProcessList.PERCEPTIBLE_APP_ADJ - && (state.getCurAdj() == ProcessList.PREVIOUS_APP_ADJ - || state.getCurAdj() == ProcessList.HOME_APP_ADJ)) { - mCachedAppOptimizer.compactAppSome(app); - } else if (state.getCurAdj() >= ProcessList.CACHED_APP_MIN_ADJ - && state.getCurAdj() <= ProcessList.CACHED_APP_MAX_ADJ) { - mCachedAppOptimizer.compactAppFull(app); - } + mCachedAppOptimizer.onOomAdjustChanged(state.getSetAdj(), state.getCurAdj(), app); } else if (mService.mWakefulness.get() != PowerManagerInternal.WAKEFULNESS_AWAKE && state.getSetAdj() < ProcessList.FOREGROUND_APP_ADJ // Because these can fire independent of oom_adj/procstate changes, we need diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 9b731d58153c..b3e46cd0b526 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -174,6 +174,9 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN boolean mFgsNotificationWasDeferred; // FGS notification was shown before the FGS finishes, or it wasn't deferred in the first place. boolean mFgsNotificationShown; + // Whether FGS package has permissions to show notifications. + // TODO(b/194833441): Output this field to logs in ActiveServices#logFGSStateChangeLocked. + boolean mFgsHasNotificationPermission; // allow the service becomes foreground service? Service started from background may not be // allowed to become a foreground service. @@ -968,6 +971,9 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN if (nm == null) { return; } + // Record whether the package has permission to notify the user + mFgsHasNotificationPermission = nm.areNotificationsEnabledForPackage( + localPackageName, appUid); Notification localForegroundNoti = _foregroundNoti; try { if (localForegroundNoti.getSmallIcon() == null) { diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java index 4d025c981ce9..9d4d1c1b0ff3 100644 --- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java +++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java @@ -223,10 +223,10 @@ public final class AppHibernationService extends SystemService { android.Manifest.permission.MANAGE_APP_HIBERNATION, "Caller does not have MANAGE_APP_HIBERNATION permission."); userId = handleIncomingUser(userId, methodName); - if (!checkUserStatesExist(userId, methodName)) { - return false; - } synchronized (mLock) { + if (!checkUserStatesExist(userId, methodName)) { + return false; + } final Map<String, UserLevelState> packageStates = mUserStates.get(userId); final UserLevelState pkgState = packageStates.get(packageName); if (pkgState == null) { @@ -278,10 +278,10 @@ public final class AppHibernationService extends SystemService { android.Manifest.permission.MANAGE_APP_HIBERNATION, "Caller does not have MANAGE_APP_HIBERNATION permission."); final int realUserId = handleIncomingUser(userId, methodName); - if (!checkUserStatesExist(realUserId, methodName)) { - return; - } synchronized (mLock) { + if (!checkUserStatesExist(realUserId, methodName)) { + return; + } final Map<String, UserLevelState> packageStates = mUserStates.get(realUserId); final UserLevelState pkgState = packageStates.get(packageName); if (pkgState == null) { @@ -365,10 +365,10 @@ public final class AppHibernationService extends SystemService { android.Manifest.permission.MANAGE_APP_HIBERNATION, "Caller does not have MANAGE_APP_HIBERNATION permission."); userId = handleIncomingUser(userId, methodName); - if (!checkUserStatesExist(userId, methodName)) { - return hibernatingPackages; - } synchronized (mLock) { + if (!checkUserStatesExist(userId, methodName)) { + return hibernatingPackages; + } Map<String, UserLevelState> userStates = mUserStates.get(userId); for (UserLevelState state : userStates.values()) { if (state.hibernated) { @@ -658,6 +658,14 @@ public final class AppHibernationService extends SystemService { } } + /** + * Check that user states exist. + * + * @param userId user to check + * @param methodName method name that is calling. Used for logging purposes. + * @return true if user states exist + */ + @GuardedBy("mLock") private boolean checkUserStatesExist(int userId, String methodName) { if (!mUserManager.isUserUnlockingOrUnlocked(userId)) { Slog.e(TAG, String.format( diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 0879bec665ba..805e45db7010 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -8541,7 +8541,7 @@ public class AudioService extends IAudioService.Stub /** @see Spatializer#getSpatializerCompatibleAudioDevices() */ public @NonNull List<AudioDeviceAttributes> getSpatializerCompatibleAudioDevices() { - enforceModifyAudioRoutingPermission(); + enforceModifyDefaultAudioEffectsPermission(); return mSpatializerHelper.getCompatibleAudioDevices(); } @@ -9319,8 +9319,6 @@ public class AudioService extends IAudioService.Stub protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; - mAudioSystem.dump(pw); - sLifecycleLogger.dump(pw); if (mAudioHandler != null) { pw.println("\nMessage handler (watch for unhandled messages):"); @@ -9400,6 +9398,8 @@ public class AudioService extends IAudioService.Stub pw.println("mHasSpatializerEffect:" + mHasSpatializerEffect); pw.println("isSpatializerEnabled:" + isSpatializerEnabled()); pw.println("isSpatialAudioEnabled:" + isSpatialAudioEnabled()); + + mAudioSystem.dump(pw); } private void dumpSupportedSystemUsage(PrintWriter pw) { diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java index ac212eee21e6..a2ba76b6fd6a 100644 --- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java +++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java @@ -524,11 +524,28 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback { * @param pw */ public void dump(PrintWriter pw) { + pw.println("\nAudioSystemAdapter:"); + pw.println(" mDevicesForStreamCache:"); + if (mDevicesForStreamCache != null) { + for (Integer stream : mDevicesForStreamCache.keySet()) { + pw.println("\t stream:" + stream + " device:" + + AudioSystem.getOutputDeviceName(mDevicesForStreamCache.get(stream))); + } + } + pw.println(" mDevicesForAttrCache:"); + if (mDevicesForAttrCache != null) { + for (AudioAttributes attr : mDevicesForAttrCache.keySet()) { + pw.println("\t" + attr); + for (AudioDeviceAttributes devAttr : mDevicesForAttrCache.get(attr)) { + pw.println("\t\t" + devAttr); + } + } + } + if (!ENABLE_GETDEVICES_STATS) { - // only stats in this dump + // only stats in the rest of this dump return; } - pw.println("\nAudioSystemAdapter:"); for (int i = 0; i < NB_MEASUREMENTS; i++) { pw.println(mMethodNames[i] + ": counter=" + mMethodCallCounter[i] diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index 9273a5d5cf9c..6ec983620698 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -102,8 +102,6 @@ public class BtHelper { /*package*/ static final int SCO_MODE_UNDEFINED = -1; // SCO audio mode is virtual voice call (BluetoothHeadset.startScoUsingVirtualVoiceCall()) /*package*/ static final int SCO_MODE_VIRTUAL_CALL = 0; - // SCO audio mode is raw audio (BluetoothHeadset.connectAudio()) - private static final int SCO_MODE_RAW = 1; // SCO audio mode is Voice Recognition (BluetoothHeadset.startVoiceRecognition()) private static final int SCO_MODE_VR = 2; // max valid SCO audio mode values @@ -122,8 +120,6 @@ public class BtHelper { return "SCO_MODE_UNDEFINED"; case SCO_MODE_VIRTUAL_CALL: return "SCO_MODE_VIRTUAL_CALL"; - case SCO_MODE_RAW: - return "SCO_MODE_RAW"; case SCO_MODE_VR: return "SCO_MODE_VR"; default: @@ -812,8 +808,6 @@ public class BtHelper { private static boolean disconnectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset, BluetoothDevice device, int scoAudioMode) { switch (scoAudioMode) { - case SCO_MODE_RAW: - return bluetoothHeadset.disconnectAudio(); case SCO_MODE_VIRTUAL_CALL: return bluetoothHeadset.stopScoUsingVirtualVoiceCall(); case SCO_MODE_VR: @@ -826,8 +820,6 @@ public class BtHelper { private static boolean connectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset, BluetoothDevice device, int scoAudioMode) { switch (scoAudioMode) { - case SCO_MODE_RAW: - return bluetoothHeadset.connectAudio(); case SCO_MODE_VIRTUAL_CALL: return bluetoothHeadset.startScoUsingVirtualVoiceCall(); case SCO_MODE_VR: diff --git a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java index 26bbb403f39f..b73e91173a43 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java +++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java @@ -16,8 +16,6 @@ package com.android.server.biometrics.sensors; -import static com.android.internal.annotations.VisibleForTesting.Visibility; - import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; @@ -50,6 +48,7 @@ public abstract class BaseClientMonitor extends LoggableMonitor * Interface that ClientMonitor holders should use to receive callbacks. */ public interface Callback { + /** * Invoked when the ClientMonitor operation has been started (e.g. reached the head of * the queue and becomes the current operation). @@ -204,8 +203,7 @@ public abstract class BaseClientMonitor extends LoggableMonitor } /** Signals this operation has completed its lifecycle and should no longer be used. */ - @VisibleForTesting(visibility = Visibility.PACKAGE) - public void destroy() { + void destroy() { mAlreadyDone = true; if (mToken != null) { try { diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java index 1f91c4d6803e..a358bc2bad55 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java @@ -17,14 +17,15 @@ package com.android.server.biometrics.sensors; import android.annotation.IntDef; -import android.annotation.MainThread; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.IBiometricService; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.os.Handler; import android.os.IBinder; +import android.os.Looper; import android.os.RemoteException; import android.os.ServiceManager; import android.util.Slog; @@ -54,7 +55,6 @@ import java.util.Locale; * We currently assume (and require) that each biometric sensor have its own instance of a * {@link BiometricScheduler}. See {@link CoexCoordinator}. */ -@MainThread public class BiometricScheduler { private static final String BASE_TAG = "BiometricScheduler"; @@ -110,6 +110,123 @@ public class BiometricScheduler { } } + /** + * Contains all the necessary information for a HAL operation. + */ + @VisibleForTesting + static final class Operation { + + /** + * The operation is added to the list of pending operations and waiting for its turn. + */ + static final int STATE_WAITING_IN_QUEUE = 0; + + /** + * The operation is added to the list of pending operations, but a subsequent operation + * has been added. This state only applies to {@link Interruptable} operations. When this + * operation reaches the head of the queue, it will send ERROR_CANCELED and finish. + */ + static final int STATE_WAITING_IN_QUEUE_CANCELING = 1; + + /** + * The operation has reached the front of the queue and has started. + */ + static final int STATE_STARTED = 2; + + /** + * The operation was started, but is now canceling. Operations should wait for the HAL to + * acknowledge that the operation was canceled, at which point it finishes. + */ + static final int STATE_STARTED_CANCELING = 3; + + /** + * The operation has reached the head of the queue but is waiting for BiometricService + * to acknowledge and start the operation. + */ + static final int STATE_WAITING_FOR_COOKIE = 4; + + /** + * The {@link BaseClientMonitor.Callback} has been invoked and the client is finished. + */ + static final int STATE_FINISHED = 5; + + @IntDef({STATE_WAITING_IN_QUEUE, + STATE_WAITING_IN_QUEUE_CANCELING, + STATE_STARTED, + STATE_STARTED_CANCELING, + STATE_WAITING_FOR_COOKIE, + STATE_FINISHED}) + @Retention(RetentionPolicy.SOURCE) + @interface OperationState {} + + @NonNull final BaseClientMonitor mClientMonitor; + @Nullable final BaseClientMonitor.Callback mClientCallback; + @OperationState int mState; + + Operation( + @NonNull BaseClientMonitor clientMonitor, + @Nullable BaseClientMonitor.Callback callback + ) { + this(clientMonitor, callback, STATE_WAITING_IN_QUEUE); + } + + protected Operation( + @NonNull BaseClientMonitor clientMonitor, + @Nullable BaseClientMonitor.Callback callback, + @OperationState int state + ) { + mClientMonitor = clientMonitor; + mClientCallback = callback; + mState = state; + } + + public boolean isHalOperation() { + return mClientMonitor instanceof HalClientMonitor<?>; + } + + /** + * @return true if the operation requires the HAL, and the HAL is null. + */ + public boolean isUnstartableHalOperation() { + if (isHalOperation()) { + final HalClientMonitor<?> client = (HalClientMonitor<?>) mClientMonitor; + if (client.getFreshDaemon() == null) { + return true; + } + } + return false; + } + + @Override + public String toString() { + return mClientMonitor + ", State: " + mState; + } + } + + /** + * Monitors an operation's cancellation. If cancellation takes too long, the watchdog will + * kill the current operation and forcibly start the next. + */ + private static final class CancellationWatchdog implements Runnable { + static final int DELAY_MS = 3000; + + final String tag; + final Operation operation; + CancellationWatchdog(String tag, Operation operation) { + this.tag = tag; + this.operation = operation; + } + + @Override + public void run() { + if (operation.mState != Operation.STATE_FINISHED) { + Slog.e(tag, "[Watchdog Triggered]: " + operation); + operation.mClientMonitor.mCallback + .onClientFinished(operation.mClientMonitor, false /* success */); + } + } + } + private static final class CrashState { static final int NUM_ENTRIES = 10; final String timestamp; @@ -146,9 +263,10 @@ public class BiometricScheduler { private final @SensorType int mSensorType; @Nullable private final GestureAvailabilityDispatcher mGestureAvailabilityDispatcher; @NonNull private final IBiometricService mBiometricService; - @NonNull protected final Handler mHandler; - @VisibleForTesting @NonNull final Deque<BiometricSchedulerOperation> mPendingOperations; - @VisibleForTesting @Nullable BiometricSchedulerOperation mCurrentOperation; + @NonNull protected final Handler mHandler = new Handler(Looper.getMainLooper()); + @NonNull private final InternalCallback mInternalCallback; + @VisibleForTesting @NonNull final Deque<Operation> mPendingOperations; + @VisibleForTesting @Nullable Operation mCurrentOperation; @NonNull private final ArrayDeque<CrashState> mCrashStates; private int mTotalOperationsHandled; @@ -159,7 +277,7 @@ public class BiometricScheduler { // Internal callback, notified when an operation is complete. Notifies the requester // that the operation is complete, before performing internal scheduler work (such as // starting the next client). - private final BaseClientMonitor.Callback mInternalCallback = new BaseClientMonitor.Callback() { + public class InternalCallback implements BaseClientMonitor.Callback { @Override public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) { Slog.d(getTag(), "[Started] " + clientMonitor); @@ -168,11 +286,16 @@ public class BiometricScheduler { mCoexCoordinator.addAuthenticationClient(mSensorType, (AuthenticationClient<?>) clientMonitor); } + + if (mCurrentOperation.mClientCallback != null) { + mCurrentOperation.mClientCallback.onClientStarted(clientMonitor); + } } @Override public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) { mHandler.post(() -> { + clientMonitor.destroy(); if (mCurrentOperation == null) { Slog.e(getTag(), "[Finishing] " + clientMonitor + " but current operation is null, success: " + success @@ -180,9 +303,9 @@ public class BiometricScheduler { return; } - if (!mCurrentOperation.isFor(clientMonitor)) { + if (clientMonitor != mCurrentOperation.mClientMonitor) { Slog.e(getTag(), "[Ignoring Finish] " + clientMonitor + " does not match" - + " current: " + mCurrentOperation); + + " current: " + mCurrentOperation.mClientMonitor); return; } @@ -192,33 +315,36 @@ public class BiometricScheduler { (AuthenticationClient<?>) clientMonitor); } + mCurrentOperation.mState = Operation.STATE_FINISHED; + + if (mCurrentOperation.mClientCallback != null) { + mCurrentOperation.mClientCallback.onClientFinished(clientMonitor, success); + } + if (mGestureAvailabilityDispatcher != null) { mGestureAvailabilityDispatcher.markSensorActive( - mCurrentOperation.getSensorId(), false /* active */); + mCurrentOperation.mClientMonitor.getSensorId(), false /* active */); } if (mRecentOperations.size() >= mRecentOperationsLimit) { mRecentOperations.remove(0); } - mRecentOperations.add(mCurrentOperation.getProtoEnum()); + mRecentOperations.add(mCurrentOperation.mClientMonitor.getProtoEnum()); mCurrentOperation = null; mTotalOperationsHandled++; startNextOperationIfIdle(); }); } - }; + } @VisibleForTesting - BiometricScheduler(@NonNull String tag, - @NonNull Handler handler, - @SensorType int sensorType, + BiometricScheduler(@NonNull String tag, @SensorType int sensorType, @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher, - @NonNull IBiometricService biometricService, - int recentOperationsLimit, + @NonNull IBiometricService biometricService, int recentOperationsLimit, @NonNull CoexCoordinator coexCoordinator) { mBiometricTag = tag; - mHandler = handler; mSensorType = sensorType; + mInternalCallback = new InternalCallback(); mGestureAvailabilityDispatcher = gestureAvailabilityDispatcher; mPendingOperations = new ArrayDeque<>(); mBiometricService = biometricService; @@ -230,26 +356,24 @@ public class BiometricScheduler { /** * Creates a new scheduler. - * * @param tag for the specific instance of the scheduler. Should be unique. - * @param handler handler for callbacks (all methods of this class must be called on the - * thread associated with this handler) * @param sensorType the sensorType that this scheduler is handling. * @param gestureAvailabilityDispatcher may be null if the sensor does not support gestures * (such as fingerprint swipe). */ public BiometricScheduler(@NonNull String tag, - @NonNull Handler handler, @SensorType int sensorType, @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) { - this(tag, handler, sensorType, gestureAvailabilityDispatcher, - IBiometricService.Stub.asInterface( - ServiceManager.getService(Context.BIOMETRIC_SERVICE)), - LOG_NUM_RECENT_OPERATIONS, CoexCoordinator.getInstance()); + this(tag, sensorType, gestureAvailabilityDispatcher, IBiometricService.Stub.asInterface( + ServiceManager.getService(Context.BIOMETRIC_SERVICE)), LOG_NUM_RECENT_OPERATIONS, + CoexCoordinator.getInstance()); } - @VisibleForTesting - public BaseClientMonitor.Callback getInternalCallback() { + /** + * @return A reference to the internal callback that should be invoked whenever the scheduler + * needs to (e.g. client started, client finished). + */ + @NonNull protected InternalCallback getInternalCallback() { return mInternalCallback; } @@ -268,46 +392,72 @@ public class BiometricScheduler { } mCurrentOperation = mPendingOperations.poll(); + final BaseClientMonitor currentClient = mCurrentOperation.mClientMonitor; Slog.d(getTag(), "[Polled] " + mCurrentOperation); // If the operation at the front of the queue has been marked for cancellation, send // ERROR_CANCELED. No need to start this client. - if (mCurrentOperation.isMarkedCanceling()) { + if (mCurrentOperation.mState == Operation.STATE_WAITING_IN_QUEUE_CANCELING) { Slog.d(getTag(), "[Now Cancelling] " + mCurrentOperation); - mCurrentOperation.cancel(mHandler, mInternalCallback); + if (!(currentClient instanceof Interruptable)) { + throw new IllegalStateException("Mis-implemented client or scheduler, " + + "trying to cancel non-interruptable operation: " + mCurrentOperation); + } + + final Interruptable interruptable = (Interruptable) currentClient; + interruptable.cancelWithoutStarting(getInternalCallback()); // Now we wait for the client to send its FinishCallback, which kicks off the next // operation. return; } - if (mGestureAvailabilityDispatcher != null && mCurrentOperation.isAcquisitionOperation()) { + if (mGestureAvailabilityDispatcher != null + && mCurrentOperation.mClientMonitor instanceof AcquisitionClient) { mGestureAvailabilityDispatcher.markSensorActive( - mCurrentOperation.getSensorId(), true /* active */); + mCurrentOperation.mClientMonitor.getSensorId(), + true /* active */); } // Not all operations start immediately. BiometricPrompt waits for its operation // to arrive at the head of the queue, before pinging it to start. - final int cookie = mCurrentOperation.isReadyToStart(); - if (cookie == 0) { - if (!mCurrentOperation.start(mInternalCallback)) { + final boolean shouldStartNow = currentClient.getCookie() == 0; + if (shouldStartNow) { + if (mCurrentOperation.isUnstartableHalOperation()) { + final HalClientMonitor<?> halClientMonitor = + (HalClientMonitor<?>) mCurrentOperation.mClientMonitor; // Note down current length of queue final int pendingOperationsLength = mPendingOperations.size(); - final BiometricSchedulerOperation lastOperation = mPendingOperations.peekLast(); + final Operation lastOperation = mPendingOperations.peekLast(); Slog.e(getTag(), "[Unable To Start] " + mCurrentOperation + ". Last pending operation: " + lastOperation); + // For current operations, 1) unableToStart, which notifies the caller-side, then + // 2) notify operation's callback, to notify applicable system service that the + // operation failed. + halClientMonitor.unableToStart(); + if (mCurrentOperation.mClientCallback != null) { + mCurrentOperation.mClientCallback.onClientFinished( + mCurrentOperation.mClientMonitor, false /* success */); + } + // Then for each operation currently in the pending queue at the time of this // failure, do the same as above. Otherwise, it's possible that something like // setActiveUser fails, but then authenticate (for the wrong user) is invoked. for (int i = 0; i < pendingOperationsLength; i++) { - final BiometricSchedulerOperation operation = mPendingOperations.pollFirst(); - if (operation != null) { - Slog.w(getTag(), "[Aborting Operation] " + operation); - operation.abort(); - } else { + final Operation operation = mPendingOperations.pollFirst(); + if (operation == null) { Slog.e(getTag(), "Null operation, index: " + i + ", expected length: " + pendingOperationsLength); + break; + } + if (operation.isHalOperation()) { + ((HalClientMonitor<?>) operation.mClientMonitor).unableToStart(); + } + if (operation.mClientCallback != null) { + operation.mClientCallback.onClientFinished(operation.mClientMonitor, + false /* success */); } + Slog.w(getTag(), "[Aborted Operation] " + operation); } // It's possible that during cleanup a new set of operations came in. We can try to @@ -315,20 +465,25 @@ public class BiometricScheduler { // actually be multiple operations (i.e. updateActiveUser + authenticate). mCurrentOperation = null; startNextOperationIfIdle(); + } else { + Slog.d(getTag(), "[Starting] " + mCurrentOperation); + currentClient.start(getInternalCallback()); + mCurrentOperation.mState = Operation.STATE_STARTED; } } else { try { - mBiometricService.onReadyForAuthentication(cookie); + mBiometricService.onReadyForAuthentication(currentClient.getCookie()); } catch (RemoteException e) { Slog.e(getTag(), "Remote exception when contacting BiometricService", e); } Slog.d(getTag(), "Waiting for cookie before starting: " + mCurrentOperation); + mCurrentOperation.mState = Operation.STATE_WAITING_FOR_COOKIE; } } /** * Starts the {@link #mCurrentOperation} if - * 1) its state is {@link BiometricSchedulerOperation#STATE_WAITING_FOR_COOKIE} and + * 1) its state is {@link Operation#STATE_WAITING_FOR_COOKIE} and * 2) its cookie matches this cookie * * This is currently only used by {@link com.android.server.biometrics.BiometricService}, which @@ -344,13 +499,45 @@ public class BiometricScheduler { Slog.e(getTag(), "Current operation is null"); return; } + if (mCurrentOperation.mState != Operation.STATE_WAITING_FOR_COOKIE) { + if (mCurrentOperation.mState == Operation.STATE_WAITING_IN_QUEUE_CANCELING) { + Slog.d(getTag(), "Operation was marked for cancellation, cancelling now: " + + mCurrentOperation); + // This should trigger the internal onClientFinished callback, which clears the + // operation and starts the next one. + final ErrorConsumer errorConsumer = + (ErrorConsumer) mCurrentOperation.mClientMonitor; + errorConsumer.onError(BiometricConstants.BIOMETRIC_ERROR_CANCELED, + 0 /* vendorCode */); + return; + } else { + Slog.e(getTag(), "Operation is in the wrong state: " + mCurrentOperation + + ", expected STATE_WAITING_FOR_COOKIE"); + return; + } + } + if (mCurrentOperation.mClientMonitor.getCookie() != cookie) { + Slog.e(getTag(), "Mismatched cookie for operation: " + mCurrentOperation + + ", received: " + cookie); + return; + } - if (mCurrentOperation.startWithCookie(mInternalCallback, cookie)) { - Slog.d(getTag(), "[Started] Prepared client: " + mCurrentOperation); - } else { + if (mCurrentOperation.isUnstartableHalOperation()) { Slog.e(getTag(), "[Unable To Start] Prepared client: " + mCurrentOperation); + // This is BiometricPrompt trying to auth but something's wrong with the HAL. + final HalClientMonitor<?> halClientMonitor = + (HalClientMonitor<?>) mCurrentOperation.mClientMonitor; + halClientMonitor.unableToStart(); + if (mCurrentOperation.mClientCallback != null) { + mCurrentOperation.mClientCallback.onClientFinished(mCurrentOperation.mClientMonitor, + false /* success */); + } mCurrentOperation = null; startNextOperationIfIdle(); + } else { + Slog.d(getTag(), "[Starting] Prepared client: " + mCurrentOperation); + mCurrentOperation.mState = Operation.STATE_STARTED; + mCurrentOperation.mClientMonitor.start(getInternalCallback()); } } @@ -375,13 +562,17 @@ public class BiometricScheduler { // pending clients as canceling. Once they reach the head of the queue, the scheduler will // send ERROR_CANCELED and skip the operation. if (clientMonitor.interruptsPrecedingClients()) { - for (BiometricSchedulerOperation operation : mPendingOperations) { - Slog.d(getTag(), "New client, marking pending op as canceling: " + operation); - operation.markCanceling(); + for (Operation operation : mPendingOperations) { + if (operation.mClientMonitor instanceof Interruptable + && operation.mState != Operation.STATE_WAITING_IN_QUEUE_CANCELING) { + Slog.d(getTag(), "New client incoming, marking pending client as canceling: " + + operation.mClientMonitor); + operation.mState = Operation.STATE_WAITING_IN_QUEUE_CANCELING; + } } } - mPendingOperations.add(new BiometricSchedulerOperation(clientMonitor, clientCallback)); + mPendingOperations.add(new Operation(clientMonitor, clientCallback)); Slog.d(getTag(), "[Added] " + clientMonitor + ", new queue size: " + mPendingOperations.size()); @@ -389,34 +580,67 @@ public class BiometricScheduler { // cancellable, start the cancellation process. if (clientMonitor.interruptsPrecedingClients() && mCurrentOperation != null - && mCurrentOperation.isInterruptable() - && mCurrentOperation.isStarted()) { + && mCurrentOperation.mClientMonitor instanceof Interruptable + && mCurrentOperation.mState == Operation.STATE_STARTED) { Slog.d(getTag(), "[Cancelling Interruptable]: " + mCurrentOperation); - mCurrentOperation.cancel(mHandler, mInternalCallback); - } else { + cancelInternal(mCurrentOperation); + } + + startNextOperationIfIdle(); + } + + private void cancelInternal(Operation operation) { + if (operation != mCurrentOperation) { + Slog.e(getTag(), "cancelInternal invoked on non-current operation: " + operation); + return; + } + if (!(operation.mClientMonitor instanceof Interruptable)) { + Slog.w(getTag(), "Operation not interruptable: " + operation); + return; + } + if (operation.mState == Operation.STATE_STARTED_CANCELING) { + Slog.w(getTag(), "Cancel already invoked for operation: " + operation); + return; + } + if (operation.mState == Operation.STATE_WAITING_FOR_COOKIE) { + Slog.w(getTag(), "Skipping cancellation for non-started operation: " + operation); + // We can set it to null immediately, since the HAL was never notified to start. + if (mCurrentOperation != null) { + mCurrentOperation.mClientMonitor.destroy(); + } + mCurrentOperation = null; startNextOperationIfIdle(); + return; } + Slog.d(getTag(), "[Cancelling] Current client: " + operation.mClientMonitor); + final Interruptable interruptable = (Interruptable) operation.mClientMonitor; + interruptable.cancel(); + operation.mState = Operation.STATE_STARTED_CANCELING; + + // Add a watchdog. If the HAL does not acknowledge within the timeout, we will + // forcibly finish this client. + mHandler.postDelayed(new CancellationWatchdog(getTag(), operation), + CancellationWatchdog.DELAY_MS); } /** * Requests to cancel enrollment. * @param token from the caller, should match the token passed in when requesting enrollment */ - public void cancelEnrollment(IBinder token, long requestId) { - Slog.d(getTag(), "cancelEnrollment, requestId: " + requestId); - - if (mCurrentOperation != null - && canCancelEnrollOperation(mCurrentOperation, token, requestId)) { - Slog.d(getTag(), "Cancelling enrollment op: " + mCurrentOperation); - mCurrentOperation.cancel(mHandler, mInternalCallback); - } else { - for (BiometricSchedulerOperation operation : mPendingOperations) { - if (canCancelEnrollOperation(operation, token, requestId)) { - Slog.d(getTag(), "Cancelling pending enrollment op: " + operation); - operation.markCanceling(); - } - } + public void cancelEnrollment(IBinder token) { + if (mCurrentOperation == null) { + Slog.e(getTag(), "Unable to cancel enrollment, null operation"); + return; + } + final boolean isEnrolling = mCurrentOperation.mClientMonitor instanceof EnrollClient; + final boolean tokenMatches = mCurrentOperation.mClientMonitor.getToken() == token; + if (!isEnrolling || !tokenMatches) { + Slog.w(getTag(), "Not cancelling enrollment, isEnrolling: " + isEnrolling + + " tokenMatches: " + tokenMatches); + return; } + + cancelInternal(mCurrentOperation); } /** @@ -425,42 +649,62 @@ public class BiometricScheduler { * @param requestId the id returned when requesting authentication */ public void cancelAuthenticationOrDetection(IBinder token, long requestId) { - Slog.d(getTag(), "cancelAuthenticationOrDetection, requestId: " + requestId); + Slog.d(getTag(), "cancelAuthenticationOrDetection, requestId: " + requestId + + " current: " + mCurrentOperation + + " stack size: " + mPendingOperations.size()); if (mCurrentOperation != null && canCancelAuthOperation(mCurrentOperation, token, requestId)) { - Slog.d(getTag(), "Cancelling auth/detect op: " + mCurrentOperation); - mCurrentOperation.cancel(mHandler, mInternalCallback); + Slog.d(getTag(), "Cancelling: " + mCurrentOperation); + cancelInternal(mCurrentOperation); } else { - for (BiometricSchedulerOperation operation : mPendingOperations) { + // Look through the current queue for all authentication clients for the specified + // token, and mark them as STATE_WAITING_IN_QUEUE_CANCELING. Note that we're marking + // all of them, instead of just the first one, since the API surface currently doesn't + // allow us to distinguish between multiple authentication requests from the same + // process. However, this generally does not happen anyway, and would be a class of + // bugs on its own. + for (Operation operation : mPendingOperations) { if (canCancelAuthOperation(operation, token, requestId)) { - Slog.d(getTag(), "Cancelling pending auth/detect op: " + operation); - operation.markCanceling(); + Slog.d(getTag(), "Marking " + operation + + " as STATE_WAITING_IN_QUEUE_CANCELING"); + operation.mState = Operation.STATE_WAITING_IN_QUEUE_CANCELING; } } } } - private static boolean canCancelEnrollOperation(BiometricSchedulerOperation operation, - IBinder token, long requestId) { - return operation.isEnrollOperation() - && operation.isMatchingToken(token) - && operation.isMatchingRequestId(requestId); + private static boolean canCancelAuthOperation(Operation operation, IBinder token, + long requestId) { + // TODO: restrict callers that can cancel without requestId (negative value)? + return isAuthenticationOrDetectionOperation(operation) + && operation.mClientMonitor.getToken() == token + && isMatchingRequestId(operation, requestId); + } + + // By default, monitors are not associated with a request id to retain the original + // behavior (i.e. if no requestId is explicitly set then assume it matches) + private static boolean isMatchingRequestId(Operation operation, long requestId) { + return !operation.mClientMonitor.hasRequestId() + || operation.mClientMonitor.getRequestId() == requestId; } - private static boolean canCancelAuthOperation(BiometricSchedulerOperation operation, - IBinder token, long requestId) { - // TODO: restrict callers that can cancel without requestId (negative value)? - return operation.isAuthenticationOrDetectionOperation() - && operation.isMatchingToken(token) - && operation.isMatchingRequestId(requestId); + private static boolean isAuthenticationOrDetectionOperation(@NonNull Operation operation) { + final boolean isAuthentication = + operation.mClientMonitor instanceof AuthenticationConsumer; + final boolean isDetection = + operation.mClientMonitor instanceof DetectionConsumer; + return isAuthentication || isDetection; } /** * @return the current operation */ public BaseClientMonitor getCurrentClient() { - return mCurrentOperation != null ? mCurrentOperation.getClientMonitor() : null; + if (mCurrentOperation == null) { + return null; + } + return mCurrentOperation.mClientMonitor; } public int getCurrentPendingCount() { @@ -475,7 +719,7 @@ public class BiometricScheduler { new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US); final String timestamp = dateFormat.format(new Date(System.currentTimeMillis())); final List<String> pendingOperations = new ArrayList<>(); - for (BiometricSchedulerOperation operation : mPendingOperations) { + for (Operation operation : mPendingOperations) { pendingOperations.add(operation.toString()); } @@ -491,7 +735,7 @@ public class BiometricScheduler { pw.println("Type: " + mSensorType); pw.println("Current operation: " + mCurrentOperation); pw.println("Pending operations: " + mPendingOperations.size()); - for (BiometricSchedulerOperation operation : mPendingOperations) { + for (Operation operation : mPendingOperations) { pw.println("Pending operation: " + operation); } for (CrashState crashState : mCrashStates) { @@ -502,7 +746,7 @@ public class BiometricScheduler { public byte[] dumpProtoState(boolean clearSchedulerBuffer) { final ProtoOutputStream proto = new ProtoOutputStream(); proto.write(BiometricSchedulerProto.CURRENT_OPERATION, mCurrentOperation != null - ? mCurrentOperation.getProtoEnum() : BiometricsProto.CM_NONE); + ? mCurrentOperation.mClientMonitor.getProtoEnum() : BiometricsProto.CM_NONE); proto.write(BiometricSchedulerProto.TOTAL_OPERATIONS, mTotalOperationsHandled); if (!mRecentOperations.isEmpty()) { @@ -527,7 +771,6 @@ public class BiometricScheduler { * HAL dies. */ public void reset() { - Slog.d(getTag(), "Resetting scheduler"); mPendingOperations.clear(); mCurrentOperation = null; } diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java deleted file mode 100644 index a8cce153dc70..000000000000 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java +++ /dev/null @@ -1,419 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.biometrics.sensors; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.hardware.biometrics.BiometricConstants; -import android.os.Handler; -import android.os.IBinder; -import android.util.Slog; - -import com.android.internal.annotations.VisibleForTesting; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Contains all the necessary information for a HAL operation. - */ -public class BiometricSchedulerOperation { - protected static final String TAG = "BiometricSchedulerOperation"; - - /** - * The operation is added to the list of pending operations and waiting for its turn. - */ - protected static final int STATE_WAITING_IN_QUEUE = 0; - - /** - * The operation is added to the list of pending operations, but a subsequent operation - * has been added. This state only applies to {@link Interruptable} operations. When this - * operation reaches the head of the queue, it will send ERROR_CANCELED and finish. - */ - protected static final int STATE_WAITING_IN_QUEUE_CANCELING = 1; - - /** - * The operation has reached the front of the queue and has started. - */ - protected static final int STATE_STARTED = 2; - - /** - * The operation was started, but is now canceling. Operations should wait for the HAL to - * acknowledge that the operation was canceled, at which point it finishes. - */ - protected static final int STATE_STARTED_CANCELING = 3; - - /** - * The operation has reached the head of the queue but is waiting for BiometricService - * to acknowledge and start the operation. - */ - protected static final int STATE_WAITING_FOR_COOKIE = 4; - - /** - * The {@link BaseClientMonitor.Callback} has been invoked and the client is finished. - */ - protected static final int STATE_FINISHED = 5; - - @IntDef({STATE_WAITING_IN_QUEUE, - STATE_WAITING_IN_QUEUE_CANCELING, - STATE_STARTED, - STATE_STARTED_CANCELING, - STATE_WAITING_FOR_COOKIE, - STATE_FINISHED}) - @Retention(RetentionPolicy.SOURCE) - protected @interface OperationState {} - - private static final int CANCEL_WATCHDOG_DELAY_MS = 3000; - - @NonNull - private final BaseClientMonitor mClientMonitor; - @Nullable - private final BaseClientMonitor.Callback mClientCallback; - @OperationState - private int mState; - @VisibleForTesting - @NonNull - final Runnable mCancelWatchdog; - - BiometricSchedulerOperation( - @NonNull BaseClientMonitor clientMonitor, - @Nullable BaseClientMonitor.Callback callback - ) { - this(clientMonitor, callback, STATE_WAITING_IN_QUEUE); - } - - protected BiometricSchedulerOperation( - @NonNull BaseClientMonitor clientMonitor, - @Nullable BaseClientMonitor.Callback callback, - @OperationState int state - ) { - mClientMonitor = clientMonitor; - mClientCallback = callback; - mState = state; - mCancelWatchdog = () -> { - if (!isFinished()) { - Slog.e(TAG, "[Watchdog Triggered]: " + this); - getWrappedCallback().onClientFinished(mClientMonitor, false /* success */); - } - }; - } - - /** - * Zero if this operation is ready to start or has already started. A non-zero cookie - * is returned if the operation has not started and is waiting on - * {@link android.hardware.biometrics.IBiometricService#onReadyForAuthentication(int)}. - * - * @return cookie or 0 if ready/started - */ - public int isReadyToStart() { - if (mState == STATE_WAITING_FOR_COOKIE || mState == STATE_WAITING_IN_QUEUE) { - final int cookie = mClientMonitor.getCookie(); - if (cookie != 0) { - mState = STATE_WAITING_FOR_COOKIE; - } - return cookie; - } - - return 0; - } - - /** - * Start this operation without waiting for a cookie - * (i.e. {@link #isReadyToStart() returns zero} - * - * @param callback lifecycle callback - * @return if this operation started - */ - public boolean start(@NonNull BaseClientMonitor.Callback callback) { - checkInState("start", - STATE_WAITING_IN_QUEUE, - STATE_WAITING_FOR_COOKIE, - STATE_WAITING_IN_QUEUE_CANCELING); - - if (mClientMonitor.getCookie() != 0) { - throw new IllegalStateException("operation requires cookie"); - } - - return doStart(callback); - } - - /** - * Start this operation after receiving the given cookie. - * - * @param callback lifecycle callback - * @param cookie cookie indicting the operation should begin - * @return if this operation started - */ - public boolean startWithCookie(@NonNull BaseClientMonitor.Callback callback, int cookie) { - checkInState("start", - STATE_WAITING_IN_QUEUE, - STATE_WAITING_FOR_COOKIE, - STATE_WAITING_IN_QUEUE_CANCELING); - - if (mClientMonitor.getCookie() != cookie) { - Slog.e(TAG, "Mismatched cookie for operation: " + this + ", received: " + cookie); - return false; - } - - return doStart(callback); - } - - private boolean doStart(@NonNull BaseClientMonitor.Callback callback) { - final BaseClientMonitor.Callback cb = getWrappedCallback(callback); - - if (mState == STATE_WAITING_IN_QUEUE_CANCELING) { - Slog.d(TAG, "Operation marked for cancellation, cancelling now: " + this); - - cb.onClientFinished(mClientMonitor, true /* success */); - if (mClientMonitor instanceof ErrorConsumer) { - final ErrorConsumer errorConsumer = (ErrorConsumer) mClientMonitor; - errorConsumer.onError(BiometricConstants.BIOMETRIC_ERROR_CANCELED, - 0 /* vendorCode */); - } else { - Slog.w(TAG, "monitor cancelled but does not implement ErrorConsumer"); - } - - return false; - } - - if (isUnstartableHalOperation()) { - Slog.v(TAG, "unable to start: " + this); - ((HalClientMonitor<?>) mClientMonitor).unableToStart(); - cb.onClientFinished(mClientMonitor, false /* success */); - return false; - } - - mState = STATE_STARTED; - mClientMonitor.start(cb); - - Slog.v(TAG, "started: " + this); - return true; - } - - /** - * Abort a pending operation. - * - * This is similar to cancel but the operation must not have been started. It will - * immediately abort the operation and notify the client that it has finished unsuccessfully. - */ - public void abort() { - checkInState("cannot abort a non-pending operation", - STATE_WAITING_IN_QUEUE, - STATE_WAITING_FOR_COOKIE, - STATE_WAITING_IN_QUEUE_CANCELING); - - if (isHalOperation()) { - ((HalClientMonitor<?>) mClientMonitor).unableToStart(); - } - getWrappedCallback().onClientFinished(mClientMonitor, false /* success */); - - Slog.v(TAG, "Aborted: " + this); - } - - /** Flags this operation as canceled, but does not cancel it until started. */ - public void markCanceling() { - if (mState == STATE_WAITING_IN_QUEUE && isInterruptable()) { - mState = STATE_WAITING_IN_QUEUE_CANCELING; - Slog.v(TAG, "Marked cancelling: " + this); - } - } - - /** - * Cancel the operation now. - * - * @param handler handler to use for the cancellation watchdog - * @param callback lifecycle callback (only used if this operation hasn't started, otherwise - * the callback used from {@link #start(BaseClientMonitor.Callback)} is used) - */ - public void cancel(@NonNull Handler handler, @NonNull BaseClientMonitor.Callback callback) { - checkNotInState("cancel", STATE_FINISHED); - - final int currentState = mState; - if (!isInterruptable()) { - Slog.w(TAG, "Cannot cancel - operation not interruptable: " + this); - return; - } - if (currentState == STATE_STARTED_CANCELING) { - Slog.w(TAG, "Cannot cancel - already invoked for operation: " + this); - return; - } - - mState = STATE_STARTED_CANCELING; - if (currentState == STATE_WAITING_IN_QUEUE - || currentState == STATE_WAITING_IN_QUEUE_CANCELING - || currentState == STATE_WAITING_FOR_COOKIE) { - Slog.d(TAG, "[Cancelling] Current client (without start): " + mClientMonitor); - ((Interruptable) mClientMonitor).cancelWithoutStarting(getWrappedCallback(callback)); - } else { - Slog.d(TAG, "[Cancelling] Current client: " + mClientMonitor); - ((Interruptable) mClientMonitor).cancel(); - } - - // forcibly finish this client if the HAL does not acknowledge within the timeout - handler.postDelayed(mCancelWatchdog, CANCEL_WATCHDOG_DELAY_MS); - } - - @NonNull - private BaseClientMonitor.Callback getWrappedCallback() { - return getWrappedCallback(null); - } - - @NonNull - private BaseClientMonitor.Callback getWrappedCallback( - @Nullable BaseClientMonitor.Callback callback) { - final BaseClientMonitor.Callback destroyCallback = new BaseClientMonitor.Callback() { - @Override - public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, - boolean success) { - mClientMonitor.destroy(); - mState = STATE_FINISHED; - } - }; - return new BaseClientMonitor.CompositeCallback(destroyCallback, callback, mClientCallback); - } - - /** {@link BaseClientMonitor#getSensorId()}. */ - public int getSensorId() { - return mClientMonitor.getSensorId(); - } - - /** {@link BaseClientMonitor#getProtoEnum()}. */ - public int getProtoEnum() { - return mClientMonitor.getProtoEnum(); - } - - /** {@link BaseClientMonitor#getTargetUserId()}. */ - public int getTargetUserId() { - return mClientMonitor.getTargetUserId(); - } - - /** If the given clientMonitor is the same as the one in the constructor. */ - public boolean isFor(@NonNull BaseClientMonitor clientMonitor) { - return mClientMonitor == clientMonitor; - } - - /** If this operation is {@link Interruptable}. */ - public boolean isInterruptable() { - return mClientMonitor instanceof Interruptable; - } - - private boolean isHalOperation() { - return mClientMonitor instanceof HalClientMonitor<?>; - } - - private boolean isUnstartableHalOperation() { - if (isHalOperation()) { - final HalClientMonitor<?> client = (HalClientMonitor<?>) mClientMonitor; - if (client.getFreshDaemon() == null) { - return true; - } - } - return false; - } - - /** If this operation is an enrollment. */ - public boolean isEnrollOperation() { - return mClientMonitor instanceof EnrollClient; - } - - /** If this operation is authentication. */ - public boolean isAuthenticateOperation() { - return mClientMonitor instanceof AuthenticationClient; - } - - /** If this operation is authentication or detection. */ - public boolean isAuthenticationOrDetectionOperation() { - final boolean isAuthentication = mClientMonitor instanceof AuthenticationConsumer; - final boolean isDetection = mClientMonitor instanceof DetectionConsumer; - return isAuthentication || isDetection; - } - - /** If this operation performs acquisition {@link AcquisitionClient}. */ - public boolean isAcquisitionOperation() { - return mClientMonitor instanceof AcquisitionClient; - } - - /** - * If this operation matches the original requestId. - * - * By default, monitors are not associated with a request id to retain the original - * behavior (i.e. if no requestId is explicitly set then assume it matches) - * - * @param requestId a unique id {@link BaseClientMonitor#setRequestId(long)}. - */ - public boolean isMatchingRequestId(long requestId) { - return !mClientMonitor.hasRequestId() - || mClientMonitor.getRequestId() == requestId; - } - - /** If the token matches */ - public boolean isMatchingToken(@Nullable IBinder token) { - return mClientMonitor.getToken() == token; - } - - /** If this operation has started. */ - public boolean isStarted() { - return mState == STATE_STARTED; - } - - /** If this operation is cancelling but has not yet completed. */ - public boolean isCanceling() { - return mState == STATE_STARTED_CANCELING; - } - - /** If this operation has finished and completed its lifecycle. */ - public boolean isFinished() { - return mState == STATE_FINISHED; - } - - /** If {@link #markCanceling()} was called but the operation hasn't been canceled. */ - public boolean isMarkedCanceling() { - return mState == STATE_WAITING_IN_QUEUE_CANCELING; - } - - /** - * The monitor passed to the constructor. - * @deprecated avoid using and move to encapsulate within the operation - */ - @Deprecated - public BaseClientMonitor getClientMonitor() { - return mClientMonitor; - } - - private void checkNotInState(String message, @OperationState int... states) { - for (int state : states) { - if (mState == state) { - throw new IllegalStateException(message + ": illegal state= " + state); - } - } - } - - private void checkInState(String message, @OperationState int... states) { - for (int state : states) { - if (mState == state) { - return; - } - } - throw new IllegalStateException(message + ": illegal state= " + mState); - } - - @Override - public String toString() { - return mClientMonitor + ", State: " + mState; - } -} diff --git a/services/core/java/com/android/server/biometrics/sensors/Interruptable.java b/services/core/java/com/android/server/biometrics/sensors/Interruptable.java index d5093c756415..fab98b6581a3 100644 --- a/services/core/java/com/android/server/biometrics/sensors/Interruptable.java +++ b/services/core/java/com/android/server/biometrics/sensors/Interruptable.java @@ -32,11 +32,6 @@ public interface Interruptable { * {@link BaseClientMonitor#start(BaseClientMonitor.Callback)} was invoked. This usually happens * if the client is still waiting in the pending queue and got notified that a subsequent * operation is preempting it. - * - * This method must invoke - * {@link BaseClientMonitor.Callback#onClientFinished(BaseClientMonitor, boolean)} on the - * given callback (with success). - * * @param callback invoked when the operation is completed. */ void cancelWithoutStarting(@NonNull BaseClientMonitor.Callback callback); diff --git a/services/core/java/com/android/server/biometrics/sensors/LockoutResetDispatcher.java b/services/core/java/com/android/server/biometrics/sensors/LockoutResetDispatcher.java index f4997d4abe24..92218b1023c4 100644 --- a/services/core/java/com/android/server/biometrics/sensors/LockoutResetDispatcher.java +++ b/services/core/java/com/android/server/biometrics/sensors/LockoutResetDispatcher.java @@ -25,7 +25,11 @@ import android.os.PowerManager; import android.os.RemoteException; import android.util.Slog; +import com.android.internal.annotations.VisibleForTesting; + import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; /** * Allows clients (such as keyguard) to register for notifications on when biometric lockout @@ -37,7 +41,8 @@ public class LockoutResetDispatcher implements IBinder.DeathRecipient { private static final String TAG = "LockoutResetTracker"; private final Context mContext; - private final ArrayList<ClientCallback> mClientCallbacks; + @VisibleForTesting + final List<ClientCallback> mClientCallbacks = new ArrayList<>(); private static class ClientCallback { private static final long WAKELOCK_TIMEOUT_MS = 2000; @@ -81,7 +86,6 @@ public class LockoutResetDispatcher implements IBinder.DeathRecipient { public LockoutResetDispatcher(Context context) { mContext = context; - mClientCallbacks = new ArrayList<>(); } public void addCallback(IBiometricServiceLockoutResetCallback callback, String opPackageName) { @@ -106,11 +110,13 @@ public class LockoutResetDispatcher implements IBinder.DeathRecipient { @Override public void binderDied(IBinder who) { Slog.e(TAG, "Callback binder died: " + who); - for (ClientCallback callback : mClientCallbacks) { + final Iterator<ClientCallback> iterator = mClientCallbacks.iterator(); + while (iterator.hasNext()) { + final ClientCallback callback = iterator.next(); if (callback.mCallback.asBinder().equals(who)) { Slog.e(TAG, "Removing dead callback for: " + callback.mOpPackageName); callback.releaseWakelock(); - mClientCallbacks.remove(callback); + iterator.remove(); } } } diff --git a/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java index 19eaa178c7c9..b056bf897b5c 100644 --- a/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java +++ b/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java @@ -16,13 +16,10 @@ package com.android.server.biometrics.sensors; -import static com.android.server.biometrics.sensors.BiometricSchedulerOperation.STATE_STARTED; - import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.hardware.biometrics.IBiometricService; -import android.os.Handler; import android.os.ServiceManager; import android.os.UserHandle; import android.util.Slog; @@ -71,8 +68,9 @@ public class UserAwareBiometricScheduler extends BiometricScheduler { return; } - Slog.d(getTag(), "[Client finished] " + clientMonitor + ", success: " + success); - if (mCurrentOperation != null && mCurrentOperation.isFor(mOwner)) { + Slog.d(getTag(), "[Client finished] " + + clientMonitor + ", success: " + success); + if (mCurrentOperation != null && mCurrentOperation.mClientMonitor == mOwner) { mCurrentOperation = null; startNextOperationIfIdle(); } else { @@ -85,31 +83,26 @@ public class UserAwareBiometricScheduler extends BiometricScheduler { } @VisibleForTesting - UserAwareBiometricScheduler(@NonNull String tag, - @NonNull Handler handler, - @SensorType int sensorType, + UserAwareBiometricScheduler(@NonNull String tag, @SensorType int sensorType, @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher, @NonNull IBiometricService biometricService, @NonNull CurrentUserRetriever currentUserRetriever, @NonNull UserSwitchCallback userSwitchCallback, @NonNull CoexCoordinator coexCoordinator) { - super(tag, handler, sensorType, gestureAvailabilityDispatcher, biometricService, + super(tag, sensorType, gestureAvailabilityDispatcher, biometricService, LOG_NUM_RECENT_OPERATIONS, coexCoordinator); mCurrentUserRetriever = currentUserRetriever; mUserSwitchCallback = userSwitchCallback; } - public UserAwareBiometricScheduler(@NonNull String tag, - @NonNull Handler handler, - @SensorType int sensorType, + public UserAwareBiometricScheduler(@NonNull String tag, @SensorType int sensorType, @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher, @NonNull CurrentUserRetriever currentUserRetriever, @NonNull UserSwitchCallback userSwitchCallback) { - this(tag, handler, sensorType, gestureAvailabilityDispatcher, - IBiometricService.Stub.asInterface( - ServiceManager.getService(Context.BIOMETRIC_SERVICE)), - currentUserRetriever, userSwitchCallback, CoexCoordinator.getInstance()); + this(tag, sensorType, gestureAvailabilityDispatcher, IBiometricService.Stub.asInterface( + ServiceManager.getService(Context.BIOMETRIC_SERVICE)), currentUserRetriever, + userSwitchCallback, CoexCoordinator.getInstance()); } @Override @@ -129,7 +122,7 @@ public class UserAwareBiometricScheduler extends BiometricScheduler { } final int currentUserId = mCurrentUserRetriever.getCurrentUserId(); - final int nextUserId = mPendingOperations.getFirst().getTargetUserId(); + final int nextUserId = mPendingOperations.getFirst().mClientMonitor.getTargetUserId(); if (nextUserId == currentUserId) { super.startNextOperationIfIdle(); @@ -140,8 +133,8 @@ public class UserAwareBiometricScheduler extends BiometricScheduler { new ClientFinishedCallback(startClient); Slog.d(getTag(), "[Starting User] " + startClient); - mCurrentOperation = new BiometricSchedulerOperation( - startClient, finishedCallback, STATE_STARTED); + mCurrentOperation = new Operation( + startClient, finishedCallback, Operation.STATE_STARTED); startClient.start(finishedCallback); } else { if (mStopUserClient != null) { @@ -154,8 +147,8 @@ public class UserAwareBiometricScheduler extends BiometricScheduler { Slog.d(getTag(), "[Stopping User] current: " + currentUserId + ", next: " + nextUserId + ". " + mStopUserClient); - mCurrentOperation = new BiometricSchedulerOperation( - mStopUserClient, finishedCallback, STATE_STARTED); + mCurrentOperation = new Operation( + mStopUserClient, finishedCallback, Operation.STATE_STARTED); mStopUserClient.start(finishedCallback); } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java index 039b08e805c1..675ee545a14f 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java @@ -213,7 +213,7 @@ public class FaceService extends SystemService { } @Override // Binder call - public long enroll(int userId, final IBinder token, final byte[] hardwareAuthToken, + public void enroll(int userId, final IBinder token, final byte[] hardwareAuthToken, final IFaceServiceReceiver receiver, final String opPackageName, final int[] disabledFeatures, Surface previewSurface, boolean debugConsent) { Utils.checkPermission(getContext(), MANAGE_BIOMETRIC); @@ -221,24 +221,23 @@ public class FaceService extends SystemService { final Pair<Integer, ServiceProvider> provider = getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for enroll"); - return -1; + return; } - return provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId, + provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId, receiver, opPackageName, disabledFeatures, previewSurface, debugConsent); } @Override // Binder call - public long enrollRemotely(int userId, final IBinder token, final byte[] hardwareAuthToken, + public void enrollRemotely(int userId, final IBinder token, final byte[] hardwareAuthToken, final IFaceServiceReceiver receiver, final String opPackageName, final int[] disabledFeatures) { Utils.checkPermission(getContext(), MANAGE_BIOMETRIC); // TODO(b/145027036): Implement this. - return -1; } @Override // Binder call - public void cancelEnrollment(final IBinder token, long requestId) { + public void cancelEnrollment(final IBinder token) { Utils.checkPermission(getContext(), MANAGE_BIOMETRIC); final Pair<Integer, ServiceProvider> provider = getSingleProvider(); @@ -247,7 +246,7 @@ public class FaceService extends SystemService { return; } - provider.second.cancelEnrollment(provider.first, token, requestId); + provider.second.cancelEnrollment(provider.first, token); } @Override // Binder call @@ -625,7 +624,7 @@ public class FaceService extends SystemService { private void addHidlProviders(@NonNull List<FaceSensorPropertiesInternal> hidlSensors) { for (FaceSensorPropertiesInternal hidlSensor : hidlSensors) { mServiceProviders.add( - Face10.newInstance(getContext(), hidlSensor, mLockoutResetDispatcher)); + new Face10(getContext(), hidlSensor, mLockoutResetDispatcher)); } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java index 77e431c81192..e099ba372b05 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java @@ -94,12 +94,12 @@ public interface ServiceProvider { void scheduleRevokeChallenge(int sensorId, int userId, @NonNull IBinder token, @NonNull String opPackageName, long challenge); - long scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken, + void scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName, @NonNull int[] disabledFeatures, @Nullable Surface previewSurface, boolean debugConsent); - void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId); + void cancelEnrollment(int sensorId, @NonNull IBinder token); long scheduleFaceDetect(int sensorId, @NonNull IBinder token, int userId, @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName, diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java index aae4fbe9b0d7..a806277ed45e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java @@ -82,14 +82,13 @@ public class FaceEnrollClient extends EnrollClient<ISession> { FaceEnrollClient(@NonNull Context context, @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId, - @NonNull byte[] hardwareAuthToken, @NonNull String opPackageName, long requestId, + @NonNull byte[] hardwareAuthToken, @NonNull String opPackageName, @NonNull BiometricUtils<Face> utils, @NonNull int[] disabledFeatures, int timeoutSec, @Nullable Surface previewSurface, int sensorId, int maxTemplatesPerUser, boolean debugConsent) { super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, opPackageName, utils, timeoutSec, BiometricsProtoEnums.MODALITY_FACE, sensorId, false /* shouldVibrate */); - setRequestId(requestId); mEnrollIgnoreList = getContext().getResources() .getIntArray(R.array.config_face_acquire_enroll_ignorelist); mEnrollIgnoreListVendor = getContext().getResources() diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java index ae507abea537..4bae7756abe0 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java @@ -327,18 +327,17 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { } @Override - public long scheduleEnroll(int sensorId, @NonNull IBinder token, + public void scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName, @NonNull int[] disabledFeatures, @Nullable Surface previewSurface, boolean debugConsent) { - final long id = mRequestCounter.incrementAndGet(); mHandler.post(() -> { final int maxTemplatesPerUser = mSensors.get( sensorId).getSensorProperties().maxEnrollmentsPerUser; final FaceEnrollClient client = new FaceEnrollClient(mContext, mSensors.get(sensorId).getLazySession(), token, new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken, - opPackageName, id, FaceUtils.getInstance(sensorId), disabledFeatures, + opPackageName, FaceUtils.getInstance(sensorId), disabledFeatures, ENROLL_TIMEOUT_SEC, previewSurface, sensorId, maxTemplatesPerUser, debugConsent); scheduleForSensor(sensorId, client, new BaseClientMonitor.Callback() { @@ -352,13 +351,11 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { } }); }); - return id; } @Override - public void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId) { - mHandler.post(() -> - mSensors.get(sensorId).getScheduler().cancelEnrollment(token, requestId)); + public void cancelEnrollment(int sensorId, @NonNull IBinder token) { + mHandler.post(() -> mSensors.get(sensorId).getScheduler().cancelEnrollment(token)); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java index 39270430c21d..206b8f0779e8 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java @@ -494,7 +494,7 @@ public class Sensor { mToken = new Binder(); mHandler = handler; mSensorProperties = sensorProperties; - mScheduler = new UserAwareBiometricScheduler(tag, mHandler, + mScheduler = new UserAwareBiometricScheduler(tag, BiometricScheduler.SENSOR_TYPE_FACE, null /* gestureAvailabilityDispatcher */, () -> mCurrentSession != null ? mCurrentSession.mUserId : UserHandle.USER_NULL, new UserAwareBiometricScheduler.UserSwitchCallback() { diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java index 493c0a05e379..f4dcbbba21d7 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java @@ -333,13 +333,12 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { Face10(@NonNull Context context, @NonNull FaceSensorPropertiesInternal sensorProps, @NonNull LockoutResetDispatcher lockoutResetDispatcher, - @NonNull Handler handler, @NonNull BiometricScheduler scheduler) { mSensorProperties = sensorProps; mContext = context; mSensorId = sensorProps.sensorId; mScheduler = scheduler; - mHandler = handler; + mHandler = new Handler(Looper.getMainLooper()); mUsageStats = new UsageStats(context); mAuthenticatorIds = new HashMap<>(); mLazyDaemon = Face10.this::getDaemon; @@ -358,12 +357,10 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { } } - public static Face10 newInstance(@NonNull Context context, - @NonNull FaceSensorPropertiesInternal sensorProps, + public Face10(@NonNull Context context, @NonNull FaceSensorPropertiesInternal sensorProps, @NonNull LockoutResetDispatcher lockoutResetDispatcher) { - final Handler handler = new Handler(Looper.getMainLooper()); - return new Face10(context, sensorProps, lockoutResetDispatcher, handler, - new BiometricScheduler(TAG, handler, BiometricScheduler.SENSOR_TYPE_FACE, + this(context, sensorProps, lockoutResetDispatcher, + new BiometricScheduler(TAG, BiometricScheduler.SENSOR_TYPE_FACE, null /* gestureAvailabilityTracker */)); } @@ -576,11 +573,10 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { } @Override - public long scheduleEnroll(int sensorId, @NonNull IBinder token, + public void scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName, @NonNull int[] disabledFeatures, @Nullable Surface previewSurface, boolean debugConsent) { - final long id = mRequestCounter.incrementAndGet(); mHandler.post(() -> { scheduleUpdateActiveUserWithoutHandler(userId); @@ -588,7 +584,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { final FaceEnrollClient client = new FaceEnrollClient(mContext, mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken, - opPackageName, id, FaceUtils.getLegacyInstance(mSensorId), disabledFeatures, + opPackageName, FaceUtils.getLegacyInstance(mSensorId), disabledFeatures, ENROLL_TIMEOUT_SEC, previewSurface, mSensorId); mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() { @@ -602,12 +598,13 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { } }); }); - return id; } @Override - public void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId) { - mHandler.post(() -> mScheduler.cancelEnrollment(token, requestId)); + public void cancelEnrollment(int sensorId, @NonNull IBinder token) { + mHandler.post(() -> { + mScheduler.cancelEnrollment(token); + }); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java index 31e5c86103fb..80828cced4e8 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java @@ -53,13 +53,12 @@ public class FaceEnrollClient extends EnrollClient<IBiometricsFace> { FaceEnrollClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId, - @NonNull byte[] hardwareAuthToken, @NonNull String owner, long requestId, + @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils<Face> utils, @NonNull int[] disabledFeatures, int timeoutSec, @Nullable Surface previewSurface, int sensorId) { super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils, timeoutSec, BiometricsProtoEnums.MODALITY_FACE, sensorId, false /* shouldVibrate */); - setRequestId(requestId); mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length); mEnrollIgnoreList = getContext().getResources() .getIntArray(R.array.config_face_acquire_enroll_ignorelist); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java index 6366e19ef191..3e70ee52ff1b 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java @@ -249,7 +249,7 @@ public class FingerprintService extends SystemService { } @Override // Binder call - public long enroll(final IBinder token, @NonNull final byte[] hardwareAuthToken, + public void enroll(final IBinder token, @NonNull final byte[] hardwareAuthToken, final int userId, final IFingerprintServiceReceiver receiver, final String opPackageName, @FingerprintManager.EnrollReason int enrollReason) { Utils.checkPermission(getContext(), MANAGE_FINGERPRINT); @@ -257,15 +257,15 @@ public class FingerprintService extends SystemService { final Pair<Integer, ServiceProvider> provider = getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for enroll"); - return -1; + return; } - return provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId, + provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId, receiver, opPackageName, enrollReason); } @Override // Binder call - public void cancelEnrollment(final IBinder token, long requestId) { + public void cancelEnrollment(final IBinder token) { Utils.checkPermission(getContext(), MANAGE_FINGERPRINT); final Pair<Integer, ServiceProvider> provider = getSingleProvider(); @@ -274,7 +274,7 @@ public class FingerprintService extends SystemService { return; } - provider.second.cancelEnrollment(provider.first, token, requestId); + provider.second.cancelEnrollment(provider.first, token); } @SuppressWarnings("deprecation") @@ -818,7 +818,7 @@ public class FingerprintService extends SystemService { mLockoutResetDispatcher, mGestureAvailabilityDispatcher); } else { fingerprint21 = Fingerprint21.newInstance(getContext(), - mFingerprintStateCallback, hidlSensor, mHandler, + mFingerprintStateCallback, hidlSensor, mLockoutResetDispatcher, mGestureAvailabilityDispatcher); } mServiceProviders.add(fingerprint21); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java index 535705c63cab..1772f814dd10 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java @@ -88,11 +88,11 @@ public interface ServiceProvider { /** * Schedules fingerprint enrollment. */ - long scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken, + void scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken, int userId, @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName, @FingerprintManager.EnrollReason int enrollReason); - void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId); + void cancelEnrollment(int sensorId, @NonNull IBinder token); long scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId, @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName, diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java index 67507ccbbbfe..ccb34aad3198 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java @@ -57,7 +57,7 @@ class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps { private boolean mIsPointerDown; FingerprintEnrollClient(@NonNull Context context, - @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token, long requestId, + @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int sensorId, @@ -69,7 +69,6 @@ class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps { super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils, 0 /* timeoutSec */, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId, !sensorProps.isAnyUdfpsType() /* shouldVibrate */); - setRequestId(requestId); mSensorProps = sensorProps; mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController); mMaxTemplatesPerUser = maxTemplatesPerUser; diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index eb16c763dea6..734b1737dfbc 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -347,16 +347,15 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi } @Override - public long scheduleEnroll(int sensorId, @NonNull IBinder token, + public void scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken, int userId, @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName, @FingerprintManager.EnrollReason int enrollReason) { - final long id = mRequestCounter.incrementAndGet(); mHandler.post(() -> { final int maxTemplatesPerUser = mSensors.get(sensorId).getSensorProperties() .maxEnrollmentsPerUser; final FingerprintEnrollClient client = new FingerprintEnrollClient(mContext, - mSensors.get(sensorId).getLazySession(), token, id, + mSensors.get(sensorId).getLazySession(), token, new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken, opPackageName, FingerprintUtils.getInstance(sensorId), sensorId, mSensors.get(sensorId).getSensorProperties(), @@ -379,13 +378,11 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi } }); }); - return id; } @Override - public void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId) { - mHandler.post(() -> - mSensors.get(sensorId).getScheduler().cancelEnrollment(token, requestId)); + public void cancelEnrollment(int sensorId, @NonNull IBinder token) { + mHandler.post(() -> mSensors.get(sensorId).getScheduler().cancelEnrollment(token)); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java index 256761a61a72..59e4b582ca84 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java @@ -449,7 +449,7 @@ class Sensor { mHandler = handler; mSensorProperties = sensorProperties; mLockoutCache = new LockoutCache(); - mScheduler = new UserAwareBiometricScheduler(tag, handler, + mScheduler = new UserAwareBiometricScheduler(tag, BiometricScheduler.sensorTypeFromFingerprintProperties(mSensorProperties), gestureAvailabilityDispatcher, () -> mCurrentSession != null ? mCurrentSession.mUserId : UserHandle.USER_NULL, diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java index d352cda609e3..5f2f4cf6ef3c 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java @@ -42,6 +42,7 @@ import android.hardware.fingerprint.IUdfpsOverlayController; import android.os.Handler; import android.os.IBinder; import android.os.IHwBinder; +import android.os.Looper; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; @@ -319,8 +320,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider Fingerprint21(@NonNull Context context, @NonNull FingerprintStateCallback fingerprintStateCallback, @NonNull FingerprintSensorPropertiesInternal sensorProps, - @NonNull BiometricScheduler scheduler, - @NonNull Handler handler, + @NonNull BiometricScheduler scheduler, @NonNull Handler handler, @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull HalResultController controller) { mContext = context; @@ -356,15 +356,16 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider public static Fingerprint21 newInstance(@NonNull Context context, @NonNull FingerprintStateCallback fingerprintStateCallback, @NonNull FingerprintSensorPropertiesInternal sensorProps, - @NonNull Handler handler, @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) { + final Handler handler = new Handler(Looper.getMainLooper()); final BiometricScheduler scheduler = - new BiometricScheduler(TAG, handler, + new BiometricScheduler(TAG, BiometricScheduler.sensorTypeFromFingerprintProperties(sensorProps), gestureAvailabilityDispatcher); final HalResultController controller = new HalResultController(sensorProps.sensorId, - context, handler, scheduler); + context, handler, + scheduler); return new Fingerprint21(context, fingerprintStateCallback, sensorProps, scheduler, handler, lockoutResetDispatcher, controller); } @@ -557,20 +558,18 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider } @Override - public long scheduleEnroll(int sensorId, @NonNull IBinder token, + public void scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken, int userId, @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName, @FingerprintManager.EnrollReason int enrollReason) { - final long id = mRequestCounter.incrementAndGet(); mHandler.post(() -> { scheduleUpdateActiveUserWithoutHandler(userId); final FingerprintEnrollClient client = new FingerprintEnrollClient(mContext, - mLazyDaemon, token, id, new ClientMonitorCallbackConverter(receiver), - userId, hardwareAuthToken, opPackageName, - FingerprintUtils.getLegacyInstance(mSensorId), ENROLL_TIMEOUT_SEC, - mSensorProperties.sensorId, mUdfpsOverlayController, mSidefpsController, - enrollReason); + mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, + hardwareAuthToken, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId), + ENROLL_TIMEOUT_SEC, mSensorProperties.sensorId, mUdfpsOverlayController, + mSidefpsController, enrollReason); mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() { @Override public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) { @@ -589,12 +588,13 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider } }); }); - return id; } @Override - public void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId) { - mHandler.post(() -> mScheduler.cancelEnrollment(token, requestId)); + public void cancelEnrollment(int sensorId, @NonNull IBinder token) { + mHandler.post(() -> { + mScheduler.cancelEnrollment(token); + }); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java index 20dab5552df9..dd68b4d37e2a 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java @@ -26,6 +26,7 @@ import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback; import android.hardware.fingerprint.FingerprintManager.AuthenticationResult; import android.hardware.fingerprint.FingerprintSensorProperties; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; +import android.hardware.fingerprint.FingerprintStateListener; import android.hardware.fingerprint.IUdfpsOverlayController; import android.os.Handler; import android.os.IBinder; @@ -134,17 +135,43 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage @NonNull private final RestartAuthRunnable mRestartAuthRunnable; private static class TestableBiometricScheduler extends BiometricScheduler { + @NonNull private final TestableInternalCallback mInternalCallback; @NonNull private Fingerprint21UdfpsMock mFingerprint21; - TestableBiometricScheduler(@NonNull String tag, @NonNull Handler handler, + TestableBiometricScheduler(@NonNull String tag, @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) { - super(tag, handler, BiometricScheduler.SENSOR_TYPE_FP_OTHER, + super(tag, BiometricScheduler.SENSOR_TYPE_FP_OTHER, gestureAvailabilityDispatcher); + mInternalCallback = new TestableInternalCallback(); + } + + class TestableInternalCallback extends InternalCallback { + @Override + public void onClientStarted(BaseClientMonitor clientMonitor) { + super.onClientStarted(clientMonitor); + Slog.d(TAG, "Client started: " + clientMonitor); + mFingerprint21.setDebugMessage("Started: " + clientMonitor); + } + + @Override + public void onClientFinished(BaseClientMonitor clientMonitor, boolean success) { + super.onClientFinished(clientMonitor, success); + Slog.d(TAG, "Client finished: " + clientMonitor); + mFingerprint21.setDebugMessage("Finished: " + clientMonitor); + } } void init(@NonNull Fingerprint21UdfpsMock fingerprint21) { mFingerprint21 = fingerprint21; } + + /** + * Expose the internal finish callback so it can be used for testing + */ + @Override + @NonNull protected InternalCallback getInternalCallback() { + return mInternalCallback; + } } /** @@ -253,7 +280,7 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage final Handler handler = new Handler(Looper.getMainLooper()); final TestableBiometricScheduler scheduler = - new TestableBiometricScheduler(TAG, handler, gestureAvailabilityDispatcher); + new TestableBiometricScheduler(TAG, gestureAvailabilityDispatcher); final MockHalResultController controller = new MockHalResultController(sensorProps.sensorId, context, handler, scheduler); return new Fingerprint21UdfpsMock(context, fingerprintStateCallback, sensorProps, scheduler, diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java index cc50bdfb59ae..1ebf44ca707f 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java @@ -55,7 +55,7 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint FingerprintEnrollClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token, - long requestId, @NonNull ClientMonitorCallbackConverter listener, int userId, + @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int timeoutSec, int sensorId, @Nullable IUdfpsOverlayController udfpsOverlayController, @@ -64,7 +64,6 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils, timeoutSec, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId, true /* shouldVibrate */); - setRequestId(requestId); mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController); mEnrollReason = enrollReason; diff --git a/services/core/java/com/android/server/communal/CommunalManagerService.java b/services/core/java/com/android/server/communal/CommunalManagerService.java index 2a6456dfc602..600313bbad64 100644 --- a/services/core/java/com/android/server/communal/CommunalManagerService.java +++ b/services/core/java/com/android/server/communal/CommunalManagerService.java @@ -19,10 +19,7 @@ package com.android.server.communal; import android.Manifest; import android.annotation.RequiresPermission; import android.app.communal.ICommunalManager; -import android.app.communal.ICommunalModeListener; import android.content.Context; -import android.os.RemoteCallbackList; -import android.os.RemoteException; import com.android.internal.annotations.VisibleForTesting; import com.android.server.SystemService; @@ -36,9 +33,6 @@ public final class CommunalManagerService extends SystemService { private final Context mContext; private final AtomicBoolean mCommunalViewIsShowing = new AtomicBoolean(false); private final BinderService mBinderService; - private final RemoteCallbackList<ICommunalModeListener> mListeners = - new RemoteCallbackList<>(); - public CommunalManagerService(Context context) { super(context); @@ -56,27 +50,6 @@ public final class CommunalManagerService extends SystemService { publishBinderService(Context.COMMUNAL_SERVICE, mBinderService); } - private void dispatchCommunalMode(boolean isShowing) { - synchronized (mListeners) { - int i = mListeners.beginBroadcast(); - while (i > 0) { - i--; - try { - mListeners.getBroadcastItem(i).onCommunalModeChanged(isShowing); - } catch (RemoteException e) { - // Handled by the RemoteCallbackList. - } - } - mListeners.finishBroadcast(); - } - } - - private void enforceReadPermission() { - mContext.enforceCallingPermission(Manifest.permission.READ_COMMUNAL_STATE, - Manifest.permission.READ_COMMUNAL_STATE - + "permission required to read communal state."); - } - private final class BinderService extends ICommunalManager.Stub { /** * Sets whether or not we are in communal mode. @@ -91,39 +64,6 @@ public final class CommunalManagerService extends SystemService { return; } mCommunalViewIsShowing.set(isShowing); - dispatchCommunalMode(isShowing); - } - - /** - * Checks whether or not we are in communal mode. - */ - @RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE) - @Override - public boolean isCommunalMode() { - enforceReadPermission(); - return mCommunalViewIsShowing.get(); - } - - /** - * Adds a callback to execute when communal state changes. - */ - @RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE) - public void addCommunalModeListener(ICommunalModeListener listener) { - enforceReadPermission(); - synchronized (mListeners) { - mListeners.register(listener); - } - } - - /** - * Removes an added callback that execute when communal state changes. - */ - @RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE) - public void removeCommunalModeListener(ICommunalModeListener listener) { - enforceReadPermission(); - synchronized (mListeners) { - mListeners.unregister(listener); - } } } } diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 6a716cbc2816..066c263fa83b 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -1223,8 +1223,11 @@ public class Vpn { for (RouteInfo route : mConfig.routes) { lp.addRoute(route); InetAddress address = route.getDestination().getAddress(); - allowIPv4 |= address instanceof Inet4Address; - allowIPv6 |= address instanceof Inet6Address; + + if (route.getType() == RouteInfo.RTN_UNICAST) { + allowIPv4 |= address instanceof Inet4Address; + allowIPv6 |= address instanceof Inet6Address; + } } } diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java index f84b68e7c556..9b0b7829005d 100644 --- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java +++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java @@ -35,6 +35,7 @@ import android.os.HandlerThread; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.ServiceSpecificException; import android.util.Log; import android.util.Pair; @@ -435,7 +436,7 @@ public abstract class IContextHubWrapper { public void onHostEndpointConnected(HostEndpointInfo info) { try { mHub.onHostEndpointConnected(info); - } catch (RemoteException e) { + } catch (RemoteException | ServiceSpecificException e) { Log.e(TAG, "RemoteException in onHostEndpointConnected"); } } @@ -444,7 +445,7 @@ public abstract class IContextHubWrapper { public void onHostEndpointDisconnected(short hostEndpointId) { try { mHub.onHostEndpointDisconnected((char) hostEndpointId); - } catch (RemoteException e) { + } catch (RemoteException | ServiceSpecificException e) { Log.e(TAG, "RemoteException in onHostEndpointDisconnected"); } } @@ -457,7 +458,7 @@ public abstract class IContextHubWrapper { mHub.sendMessageToHub(contextHubId, ContextHubServiceUtil.createAidlContextHubMessage(hostEndpointId, message)); return ContextHubTransaction.RESULT_SUCCESS; - } catch (RemoteException e) { + } catch (RemoteException | ServiceSpecificException e) { return ContextHubTransaction.RESULT_FAILED_UNKNOWN; } } @@ -470,7 +471,7 @@ public abstract class IContextHubWrapper { try { mHub.loadNanoapp(contextHubId, aidlNanoAppBinary, transactionId); return ContextHubTransaction.RESULT_SUCCESS; - } catch (RemoteException e) { + } catch (RemoteException | ServiceSpecificException e) { return ContextHubTransaction.RESULT_FAILED_UNKNOWN; } } @@ -481,7 +482,7 @@ public abstract class IContextHubWrapper { try { mHub.unloadNanoapp(contextHubId, nanoappId, transactionId); return ContextHubTransaction.RESULT_SUCCESS; - } catch (RemoteException e) { + } catch (RemoteException | ServiceSpecificException e) { return ContextHubTransaction.RESULT_FAILED_UNKNOWN; } } @@ -492,7 +493,7 @@ public abstract class IContextHubWrapper { try { mHub.enableNanoapp(contextHubId, nanoappId, transactionId); return ContextHubTransaction.RESULT_SUCCESS; - } catch (RemoteException e) { + } catch (RemoteException | ServiceSpecificException e) { return ContextHubTransaction.RESULT_FAILED_UNKNOWN; } } @@ -503,7 +504,7 @@ public abstract class IContextHubWrapper { try { mHub.disableNanoapp(contextHubId, nanoappId, transactionId); return ContextHubTransaction.RESULT_SUCCESS; - } catch (RemoteException e) { + } catch (RemoteException | ServiceSpecificException e) { return ContextHubTransaction.RESULT_FAILED_UNKNOWN; } } @@ -513,21 +514,25 @@ public abstract class IContextHubWrapper { try { mHub.queryNanoapps(contextHubId); return ContextHubTransaction.RESULT_SUCCESS; - } catch (RemoteException e) { + } catch (RemoteException | ServiceSpecificException e) { return ContextHubTransaction.RESULT_FAILED_UNKNOWN; } } public void registerCallback(int contextHubId, ICallback callback) throws RemoteException { mAidlCallbackMap.put(contextHubId, new ContextHubAidlCallback(contextHubId, callback)); - mHub.registerCallback(contextHubId, mAidlCallbackMap.get(contextHubId)); + try { + mHub.registerCallback(contextHubId, mAidlCallbackMap.get(contextHubId)); + } catch (RemoteException | ServiceSpecificException e) { + Log.e(TAG, "Exception while registering callback: " + e.getMessage()); + } } private void onSettingChanged(byte setting, boolean enabled) { try { mHub.onSettingChanged(setting, enabled); - } catch (RemoteException e) { - Log.e(TAG, "RemoteException while sending setting update"); + } catch (RemoteException | ServiceSpecificException e) { + Log.e(TAG, "Exception while sending setting update: " + e.getMessage()); } } } diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index 4822d6a62ac7..96391ac62530 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -52,7 +52,6 @@ import android.os.Process; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.SystemClock; -import android.text.TextUtils; import android.util.Log; import android.view.KeyEvent; @@ -643,22 +642,24 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR } private void pushQueueUpdate() { - ParceledListSlice<QueueItem> parcelableQueue; + ArrayList<QueueItem> toSend; synchronized (mLock) { if (mDestroyed) { return; } - if (mQueue == null) { - parcelableQueue = null; - } else { - parcelableQueue = new ParceledListSlice<>(mQueue); - // Limit the size of initial Parcel to prevent binder buffer overflow - // as onQueueChanged is an async binder call. - parcelableQueue.setInlineCountLimit(1); + toSend = new ArrayList<>(); + if (mQueue != null) { + toSend.ensureCapacity(mQueue.size()); + toSend.addAll(mQueue); } } Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null; for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) { + ParceledListSlice<QueueItem> parcelableQueue = new ParceledListSlice<>(toSend); + // Limit the size of initial Parcel to prevent binder buffer overflow + // as onQueueChanged is an async binder call. + parcelableQueue.setInlineCountLimit(1); + try { holder.mCallback.onQueueChanged(parcelableQueue); } catch (DeadObjectException e) { diff --git a/services/core/java/com/android/server/media/OWNERS b/services/core/java/com/android/server/media/OWNERS index 2e2d812c058e..8097f4e9b329 100644 --- a/services/core/java/com/android/server/media/OWNERS +++ b/services/core/java/com/android/server/media/OWNERS @@ -1,8 +1,6 @@ +# Bug component: 137631 elaurent@google.com -hdmoon@google.com -insun@google.com -jaewan@google.com -jinpark@google.com -klhyun@google.com lajos@google.com -sungsoo@google.com + +# go/android-fwk-media-solutions for info on areas of ownership. +include platform/frameworks/av:/media/janitors/media_solutions_OWNERS diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 65c5b88d846a..5660951a9efa 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -130,7 +130,7 @@ import static com.android.internal.util.XmlUtils.writeIntArrayXml; import static com.android.internal.util.XmlUtils.writeIntAttribute; import static com.android.internal.util.XmlUtils.writeLongAttribute; import static com.android.internal.util.XmlUtils.writeStringAttribute; -import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT; +import static com.android.net.module.util.NetworkStatsUtils.LIMIT_GLOBAL_ALERT; import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_UPDATED; import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; diff --git a/services/core/java/com/android/server/notification/CountdownConditionProvider.java b/services/core/java/com/android/server/notification/CountdownConditionProvider.java index 471c9b97200f..4b70e2e31888 100644 --- a/services/core/java/com/android/server/notification/CountdownConditionProvider.java +++ b/services/core/java/com/android/server/notification/CountdownConditionProvider.java @@ -94,7 +94,8 @@ public class CountdownConditionProvider extends SystemConditionProviderService { @Override public void onConnected() { if (DEBUG) Slog.d(TAG, "onConnected"); - mContext.registerReceiver(mReceiver, new IntentFilter(ACTION)); + mContext.registerReceiver(mReceiver, new IntentFilter(ACTION), + Context.RECEIVER_EXPORTED_UNAUDITED); mConnected = true; } diff --git a/services/core/java/com/android/server/notification/EventConditionProvider.java b/services/core/java/com/android/server/notification/EventConditionProvider.java index 4be4f0a1e7f0..4fe7a27c64c6 100644 --- a/services/core/java/com/android/server/notification/EventConditionProvider.java +++ b/services/core/java/com/android/server/notification/EventConditionProvider.java @@ -306,7 +306,8 @@ public class EventConditionProvider extends SystemConditionProviderService { filter.addAction(Intent.ACTION_TIME_CHANGED); filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); filter.addAction(ACTION_EVALUATE); - registerReceiver(mReceiver, filter); + registerReceiver(mReceiver, filter, + Context.RECEIVER_EXPORTED_UNAUDITED); } else { unregisterReceiver(mReceiver); } diff --git a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java index 8a3329913142..a9b2570a3dda 100644 --- a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java +++ b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java @@ -100,7 +100,8 @@ public class NotificationHistoryDatabase { IntentFilter deletionFilter = new IntentFilter(ACTION_HISTORY_DELETION); deletionFilter.addDataScheme(SCHEME_DELETION); - mContext.registerReceiver(mFileCleanupReceiver, deletionFilter); + mContext.registerReceiver(mFileCleanupReceiver, deletionFilter, + Context.RECEIVER_EXPORTED_UNAUDITED); } public void init() { diff --git a/services/core/java/com/android/server/notification/NotificationManagerInternal.java b/services/core/java/com/android/server/notification/NotificationManagerInternal.java index 0528b95d1a6e..c548e7edc3cf 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerInternal.java +++ b/services/core/java/com/android/server/notification/NotificationManagerInternal.java @@ -36,4 +36,10 @@ public interface NotificationManagerInternal { void removeForegroundServiceFlagFromNotification(String pkg, int notificationId, int userId); void onConversationRemoved(String pkg, int uid, Set<String> shortcuts); + + /** Get the number of notification channels for a given package */ + int getNumNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted); + + /** Does the specified package/uid have permission to post notifications? */ + boolean areNotificationsEnabledForPackage(String pkg, int uid); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 755c50df1bef..399ae5347e6e 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -133,6 +133,7 @@ import android.annotation.WorkerThread; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.ActivityManagerInternal.ServiceNotificationPolicy; +import android.app.ActivityTaskManager; import android.app.AlarmManager; import android.app.AppGlobals; import android.app.AppOpsManager; @@ -296,6 +297,7 @@ import com.android.server.notification.toast.TextToastRecord; import com.android.server.notification.toast.ToastRecord; import com.android.server.pm.PackageManagerService; import com.android.server.pm.permission.PermissionManagerServiceInternal; +import com.android.server.policy.PermissionPolicyInternal; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.uri.UriGrantsManagerInternal; import com.android.server.utils.quota.MultiRateLimiter; @@ -480,6 +482,7 @@ public class NotificationManagerService extends SystemService { private IPackageManager mPackageManager; private PackageManager mPackageManagerClient; private PackageManagerInternal mPackageManagerInternal; + private PermissionPolicyInternal mPermissionPolicyInternal; AudioManager mAudioManager; AudioManagerInternal mAudioManagerInternal; // Can be null for wear @@ -2106,6 +2109,7 @@ public class NotificationManagerService extends SystemService { mPackageManager = packageManager; mPackageManagerClient = packageManagerClient; mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); + mPermissionPolicyInternal = LocalServices.getService(PermissionPolicyInternal.class); mAppOps = appOps; mAppOpsService = iAppOps; try { @@ -2330,7 +2334,8 @@ public class NotificationManagerService extends SystemService { IntentFilter timeoutFilter = new IntentFilter(ACTION_NOTIFICATION_TIMEOUT); timeoutFilter.addDataScheme(SCHEME_TIMEOUT); - getContext().registerReceiver(mNotificationTimeoutReceiver, timeoutFilter); + getContext().registerReceiver(mNotificationTimeoutReceiver, timeoutFilter, + Context.RECEIVER_EXPORTED_UNAUDITED); IntentFilter settingsRestoredFilter = new IntentFilter(Intent.ACTION_SETTING_RESTORED); getContext().registerReceiver(mRestoreReceiver, settingsRestoredFilter); @@ -2766,7 +2771,7 @@ public class NotificationManagerService extends SystemService { } } - private void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group, + void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group, boolean fromApp, boolean fromListener) { Objects.requireNonNull(group); Objects.requireNonNull(pkg); @@ -3674,9 +3679,19 @@ public class NotificationManagerService extends SystemService { private void createNotificationChannelsImpl(String pkg, int uid, ParceledListSlice channelsList) { + createNotificationChannelsImpl(pkg, uid, channelsList, + ActivityTaskManager.INVALID_TASK_ID); + } + + private void createNotificationChannelsImpl(String pkg, int uid, + ParceledListSlice channelsList, int startingTaskId) { List<NotificationChannel> channels = channelsList.getList(); final int channelsSize = channels.size(); + ParceledListSlice<NotificationChannel> oldChannels = + mPreferencesHelper.getNotificationChannels(pkg, uid, true); + final boolean hadChannel = oldChannels != null && !oldChannels.getList().isEmpty(); boolean needsPolicyFileChange = false; + boolean hasRequestedNotificationPermission = false; for (int i = 0; i < channelsSize; i++) { final NotificationChannel channel = channels.get(i); Objects.requireNonNull(channel, "channel in list is null"); @@ -3690,6 +3705,19 @@ public class NotificationManagerService extends SystemService { mPreferencesHelper.getNotificationChannel(pkg, uid, channel.getId(), false), NOTIFICATION_CHANNEL_OR_GROUP_ADDED); + boolean hasChannel = hadChannel || hasRequestedNotificationPermission; + if (!hasChannel) { + ParceledListSlice<NotificationChannel> currChannels = + mPreferencesHelper.getNotificationChannels(pkg, uid, true); + hasChannel = currChannels != null && !currChannels.getList().isEmpty(); + } + if (!hadChannel && hasChannel && !hasRequestedNotificationPermission + && startingTaskId != ActivityTaskManager.INVALID_TASK_ID) { + hasRequestedNotificationPermission = true; + mHandler.post(new ShowNotificationPermissionPromptRunnable(pkg, + UserHandle.getUserId(uid), startingTaskId, + mPermissionPolicyInternal)); + } } } if (needsPolicyFileChange) { @@ -3698,10 +3726,29 @@ public class NotificationManagerService extends SystemService { } @Override - public void createNotificationChannels(String pkg, - ParceledListSlice channelsList) { + public void createNotificationChannels(String pkg, ParceledListSlice channelsList) { checkCallerIsSystemOrSameApp(pkg); - createNotificationChannelsImpl(pkg, Binder.getCallingUid(), channelsList); + int taskId = ActivityTaskManager.INVALID_TASK_ID; + try { + int uid = mPackageManager.getPackageUid(pkg, 0, + UserHandle.getUserId(Binder.getCallingUid())); + List<ActivityManager.AppTask> tasks = mAtm.getAppTasks(pkg, uid); + for (int i = 0; i < tasks.size(); i++) { + ActivityManager.RecentTaskInfo task = tasks.get(i).getTaskInfo(); + if (mPermissionPolicyInternal == null) { + mPermissionPolicyInternal = + LocalServices.getService(PermissionPolicyInternal.class); + } + if (mPermissionPolicyInternal != null + && mPermissionPolicyInternal.canShowPermissionPromptForTask(task)) { + taskId = task.taskId; + break; + } + } + } catch (RemoteException e) { + // Do nothing + } + createNotificationChannelsImpl(pkg, Binder.getCallingUid(), channelsList, taskId); } @Override @@ -3822,7 +3869,8 @@ public class NotificationManagerService extends SystemService { final int callingUid = Binder.getCallingUid(); NotificationChannelGroup groupToDelete = - mPreferencesHelper.getNotificationChannelGroup(groupId, pkg, callingUid); + mPreferencesHelper.getNotificationChannelGroupWithChannels( + pkg, callingUid, groupId, false); if (groupToDelete != null) { // Preflight for allowability final int userId = UserHandle.getUserId(callingUid); @@ -3884,8 +3932,8 @@ public class NotificationManagerService extends SystemService { public int getNumNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted) { enforceSystemOrSystemUI("getNumNotificationChannelsForPackage"); - return mPreferencesHelper.getNotificationChannels(pkg, uid, includeDeleted) - .getList().size(); + return NotificationManagerService.this + .getNumNotificationChannelsForPackage(pkg, uid, includeDeleted); } @Override @@ -6150,8 +6198,25 @@ public class NotificationManagerService extends SystemService { // initially *and* force remove FLAG_FOREGROUND_SERVICE. sbn.getNotification().flags = (r.mOriginalFlags & ~FLAG_FOREGROUND_SERVICE); } + + @Override + public int getNumNotificationChannelsForPackage(String pkg, int uid, + boolean includeDeleted) { + return NotificationManagerService.this + .getNumNotificationChannelsForPackage(pkg, uid, includeDeleted); + } + + @Override + public boolean areNotificationsEnabledForPackage(String pkg, int uid) { + return areNotificationsEnabledForPackageInt(pkg, uid); + } }; + int getNumNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted) { + return mPreferencesHelper.getNotificationChannels(pkg, uid, includeDeleted).getList() + .size(); + } + void cancelNotificationInternal(String pkg, String opPkg, int callingUid, int callingPid, String tag, int id, int userId) { userId = ActivityManager.handleIncomingUser(callingPid, @@ -6973,6 +7038,44 @@ public class NotificationManagerService extends SystemService { } } + protected static class ShowNotificationPermissionPromptRunnable implements Runnable { + private final String mPkgName; + private final int mUserId; + private final int mTaskId; + private final PermissionPolicyInternal mPpi; + + ShowNotificationPermissionPromptRunnable(String pkg, int user, int task, + PermissionPolicyInternal pPi) { + mPkgName = pkg; + mUserId = user; + mTaskId = task; + mPpi = pPi; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof ShowNotificationPermissionPromptRunnable)) { + return false; + } + + ShowNotificationPermissionPromptRunnable other = + (ShowNotificationPermissionPromptRunnable) o; + + return Objects.equals(mPkgName, other.mPkgName) && mUserId == other.mUserId + && mTaskId == other.mTaskId; + } + + @Override + public int hashCode() { + return Objects.hash(mPkgName, mUserId, mTaskId); + } + + @Override + public void run() { + mPpi.showNotificationPromptIfNeeded(mPkgName, mUserId, mTaskId); + } + } + protected class EnqueueNotificationRunnable implements Runnable { private final NotificationRecord r; private final int userId; diff --git a/services/core/java/com/android/server/notification/PermissionHelper.java b/services/core/java/com/android/server/notification/PermissionHelper.java index 24008d0f64ca..0cbdbc18ad39 100644 --- a/services/core/java/com/android/server/notification/PermissionHelper.java +++ b/services/core/java/com/android/server/notification/PermissionHelper.java @@ -16,6 +16,7 @@ package com.android.server.notification; +import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET; import static android.content.pm.PackageManager.GET_PERMISSIONS; import static android.permission.PermissionManager.PERMISSION_GRANTED; @@ -159,13 +160,21 @@ public final class PermissionHelper { } /** + * @see setNotificationPermission(String, int, boolean, boolean, boolean) + */ + public void setNotificationPermission(String packageName, @UserIdInt int userId, boolean grant, + boolean userSet) { + setNotificationPermission(packageName, userId, grant, userSet, false); + } + + /** * Grants or revokes the notification permission for a given package/user. UserSet should * only be true if this method is being called to migrate existing user choice, because it * can prevent the user from seeing the in app permission dialog. Must not be called * with a lock held. */ public void setNotificationPermission(String packageName, @UserIdInt int userId, boolean grant, - boolean userSet) { + boolean userSet, boolean reviewRequired) { assertFlag(); final long callingId = Binder.clearCallingIdentity(); try { @@ -177,7 +186,12 @@ public final class PermissionHelper { } if (userSet) { mPermManager.updatePermissionFlags(packageName, NOTIFICATION_PERMISSION, - FLAG_PERMISSION_USER_SET, FLAG_PERMISSION_USER_SET, true, userId); + FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_REVIEW_REQUIRED, + FLAG_PERMISSION_USER_SET, true, userId); + } else if (reviewRequired) { + mPermManager.updatePermissionFlags(packageName, NOTIFICATION_PERMISSION, + FLAG_PERMISSION_REVIEW_REQUIRED, FLAG_PERMISSION_REVIEW_REQUIRED, true, + userId); } } catch (RemoteException e) { Slog.e(TAG, "Could not reach system server", e); @@ -186,15 +200,17 @@ public final class PermissionHelper { } } + /** + * Set the notification permission state upon phone version upgrade from S- to T+, or upon + * restoring a pre-T backup on a T+ device + */ public void setNotificationPermission(PackagePermission pkgPerm) { assertFlag(); - final long callingId = Binder.clearCallingIdentity(); - try { - setNotificationPermission( - pkgPerm.packageName, pkgPerm.userId, pkgPerm.granted, pkgPerm.userSet); - } finally { - Binder.restoreCallingIdentity(callingId); + if (pkgPerm == null || pkgPerm.packageName == null) { + return; } + setNotificationPermission(pkgPerm.packageName, pkgPerm.userId, pkgPerm.granted, + pkgPerm.userSet, !pkgPerm.userSet); } public boolean isPermissionFixed(String packageName, @UserIdInt int userId) { diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index 258ae8c1c849..0246c0c851cf 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -848,6 +848,9 @@ public class PreferencesHelper implements RankingConfig { if (r == null) { throw new IllegalArgumentException("Invalid package"); } + if (fromTargetApp) { + group.setBlocked(false); + } final NotificationChannelGroup oldGroup = r.groups.get(group.getId()); if (oldGroup != null) { group.setChannels(oldGroup.getChannels()); diff --git a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java index 92cdce7ddceb..737353dc5151 100644 --- a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java +++ b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java @@ -261,7 +261,8 @@ public class ScheduleConditionProvider extends SystemConditionProviderService { filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); filter.addAction(ACTION_EVALUATE); filter.addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED); - registerReceiver(mReceiver, filter); + registerReceiver(mReceiver, filter, + Context.RECEIVER_EXPORTED_UNAUDITED); } else { unregisterReceiver(mReceiver); } diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java index 4500bbcd250f..7f265df3f416 100644 --- a/services/core/java/com/android/server/notification/SnoozeHelper.java +++ b/services/core/java/com/android/server/notification/SnoozeHelper.java @@ -116,7 +116,8 @@ public class SnoozeHelper { mContext = context; IntentFilter filter = new IntentFilter(REPOST_ACTION); filter.addDataScheme(REPOST_SCHEME); - mContext.registerReceiver(mBroadcastReceiver, filter); + mContext.registerReceiver(mBroadcastReceiver, filter, + Context.RECEIVER_EXPORTED_UNAUDITED); mAm = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); mCallback = callback; mUserProfiles = userProfiles; diff --git a/services/core/java/com/android/server/notification/VibratorHelper.java b/services/core/java/com/android/server/notification/VibratorHelper.java index be5f2194997a..8acc8572453b 100644 --- a/services/core/java/com/android/server/notification/VibratorHelper.java +++ b/services/core/java/com/android/server/notification/VibratorHelper.java @@ -125,10 +125,10 @@ public final class VibratorHelper { private static VibrationEffect createChirpVibration(int rampDuration, boolean insistent) { VibrationEffect.WaveformBuilder waveformBuilder = VibrationEffect.startWaveform() - .addStep(/* amplitude= */ 0, /* frequency= */ -0.85f, /* duration= */ 0) - .addRamp(/* amplitude= */ 1, /* frequency= */ -0.25f, rampDuration) - .addStep(/* amplitude= */ 1, /* frequency= */ -0.25f, CHIRP_LEVEL_DURATION_MILLIS) - .addRamp(/* amplitude= */ 0, /* frequency= */ -0.85f, rampDuration); + .addStep(/* amplitude= */ 0, /* frequencyHz= */ 60f, /* duration= */ 0) + .addRamp(/* amplitude= */ 1, /* frequencyHz= */ 120f, rampDuration) + .addStep(/* amplitude= */ 1, /* frequencyHz= */ 120f, CHIRP_LEVEL_DURATION_MILLIS) + .addRamp(/* amplitude= */ 0, /* frequencyHz= */ 60f, rampDuration); if (insistent) { return waveformBuilder diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java index 05567480e6bf..c285e2795510 100644 --- a/services/core/java/com/android/server/pm/ApexManager.java +++ b/services/core/java/com/android/server/pm/ApexManager.java @@ -799,7 +799,7 @@ public abstract class ApexManager { throw new RuntimeException(re); } catch (Exception e) { throw new PackageManagerException( - PackageInstaller.SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, + PackageInstaller.SessionInfo.SESSION_VERIFICATION_FAILED, "apexd verification failed : " + e.getMessage()); } } @@ -826,7 +826,7 @@ public abstract class ApexManager { throw new RuntimeException(re); } catch (Exception e) { throw new PackageManagerException( - PackageInstaller.SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, + PackageInstaller.SessionInfo.SESSION_VERIFICATION_FAILED, "Failed to mark apexd session as ready : " + e.getMessage()); } } diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java index 05a51ccfaca7..31df0a53eaa9 100644 --- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java +++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java @@ -896,10 +896,10 @@ public final class BackgroundDexOptService { synchronized (mLock) { if (!mFinishedPostBootUpdate) { mFinishedPostBootUpdate = true; - JobScheduler js = mInjector.getJobScheduler(); - js.cancel(JOB_POST_BOOT_UPDATE); } } + // Safe to do this outside lock. + mInjector.getJobScheduler().cancel(JOB_POST_BOOT_UPDATE); } private void notifyPinService(ArraySet<String> updatedPackages) { diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index cfcf199d01a4..8ebd254d03d3 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -70,6 +70,7 @@ import android.text.format.DateUtils; import android.util.ArraySet; import android.util.AtomicFile; import android.util.ExceptionUtils; +import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; @@ -121,7 +122,7 @@ import java.util.function.Supplier; public class PackageInstallerService extends IPackageInstaller.Stub implements PackageSessionProvider { private static final String TAG = "PackageInstaller"; - private static final boolean LOGD = false; + private static final boolean LOGD = Log.isLoggable(TAG, Log.DEBUG); private static final boolean DEBUG = Build.IS_DEBUGGABLE; @@ -135,7 +136,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements /** Automatically destroy sessions older than this */ private static final long MAX_AGE_MILLIS = 3 * DateUtils.DAY_IN_MILLIS; /** Automatically destroy staged sessions that have not changed state in this time */ - private static final long MAX_TIME_SINCE_UPDATE_MILLIS = 7 * DateUtils.DAY_IN_MILLIS; + private static final long MAX_TIME_SINCE_UPDATE_MILLIS = 21 * DateUtils.DAY_IN_MILLIS; /** Upper bound on number of active sessions for a UID that has INSTALL_PACKAGES */ private static final long MAX_ACTIVE_SESSIONS_WITH_PERMISSION = 1024; /** Upper bound on number of active sessions for a UID without INSTALL_PACKAGES */ @@ -330,7 +331,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements StagingManager.StagedSession stagedSession = session.mStagedSession; if (!stagedSession.isInTerminalState() && stagedSession.hasParentSessionId() && getSession(stagedSession.getParentSessionId()) == null) { - stagedSession.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, + stagedSession.setSessionFailed(SessionInfo.SESSION_ACTIVATION_FAILED, "An orphan staged session " + stagedSession.sessionId() + " is found, " + "parent " + stagedSession.getParentSessionId() + " is missing"); continue; @@ -510,6 +511,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements valid = true; } if (!valid) { + Slog.w(TAG, "Remove old session: " + session.sessionId); // Remove expired sessions as well as child sessions if any mSessions.remove(session.sessionId); // Since this is early during boot we don't send @@ -841,7 +843,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements mSilentUpdatePolicy, mInstallThread.getLooper(), mStagingManager, sessionId, userId, callingUid, installSource, params, createdMillis, 0L, stageDir, stageCid, null, null, false, false, false, false, null, SessionInfo.INVALID_ID, - false, false, false, SessionInfo.STAGED_SESSION_NO_ERROR, ""); + false, false, false, SessionInfo.SESSION_NO_ERROR, ""); synchronized (mSessions) { mSessions.put(sessionId, session); @@ -850,6 +852,9 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements mCallbacks.notifySessionCreated(session.sessionId, session.userId); mSettingsWriteRequest.schedule(); + if (LOGD) { + Slog.d(TAG, "Created session id=" + sessionId + " staged=" + params.isStaged); + } return sessionId; } diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index f45e54b04e54..304ad72c6359 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -460,7 +460,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @GuardedBy("mLock") private boolean mSessionFailed; @GuardedBy("mLock") - private int mSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR; + private int mSessionErrorCode = SessionInfo.SESSION_NO_ERROR; @GuardedBy("mLock") private String mSessionErrorMessage; @@ -2092,7 +2092,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { if (isStaged()) { // This will clean up the session when it reaches the terminal state mStagedSession.setSessionFailed( - SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, msgWithErrorCode); + SessionInfo.SESSION_VERIFICATION_FAILED, msgWithErrorCode); mStagedSession.notifyEndPreRebootVerification(); } else { // Session is sealed and committed but could not be verified, we need to destroy it. @@ -2547,7 +2547,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { if (isStaged()) { mSessionProvider.getSessionVerifier().verifyStaged(mStagedSession, (error, msg) -> { mStagedSession.notifyEndPreRebootVerification(); - if (error == SessionInfo.STAGED_SESSION_NO_ERROR) { + if (error == SessionInfo.SESSION_NO_ERROR) { mStagingManager.commitSession(mStagedSession); } }); @@ -4168,7 +4168,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mSessionReady = true; mSessionApplied = false; mSessionFailed = false; - mSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR; + mSessionErrorCode = SessionInfo.SESSION_NO_ERROR; mSessionErrorMessage = ""; } mCallback.onSessionChanged(this); @@ -4196,7 +4196,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mSessionReady = false; mSessionApplied = true; mSessionFailed = false; - mSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR; + mSessionErrorCode = SessionInfo.SESSION_NO_ERROR; mSessionErrorMessage = ""; Slog.d(TAG, "Marking session " + sessionId + " as applied"); } @@ -4705,7 +4705,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { final boolean isFailed = in.getAttributeBoolean(null, ATTR_IS_FAILED, false); final boolean isApplied = in.getAttributeBoolean(null, ATTR_IS_APPLIED, false); final int sessionErrorCode = in.getAttributeInt(null, ATTR_SESSION_ERROR_CODE, - SessionInfo.STAGED_SESSION_NO_ERROR); + SessionInfo.SESSION_NO_ERROR); final String sessionErrorMessage = readStringAttribute(in, ATTR_SESSION_ERROR_MESSAGE); if (!isStagedSessionStateValid(isReady, isApplied, isFailed)) { diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 0564e858c84c..be2bdaac13e4 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -2816,7 +2816,7 @@ class PackageManagerShellCommand extends ShellCommand { case UserManager.REMOVE_RESULT_REMOVED: getOutPrintWriter().printf("Success: user %d removed\n", userId); return 0; - case UserManager.REMOVE_RESULT_SET_EPHEMERAL: + case UserManager.REMOVE_RESULT_DEFERRED: getOutPrintWriter().printf("Success: user %d set as ephemeral\n", userId); return 0; case UserManager.REMOVE_RESULT_ALREADY_BEING_REMOVED: diff --git a/services/core/java/com/android/server/pm/PackageSessionVerifier.java b/services/core/java/com/android/server/pm/PackageSessionVerifier.java index a532fe3a3d4d..ccabce719402 100644 --- a/services/core/java/com/android/server/pm/PackageSessionVerifier.java +++ b/services/core/java/com/android/server/pm/PackageSessionVerifier.java @@ -202,7 +202,7 @@ final class PackageSessionVerifier { } private void onVerificationSuccess(StagingManager.StagedSession session, Callback callback) { - callback.onResult(SessionInfo.STAGED_SESSION_NO_ERROR, null); + callback.onResult(SessionInfo.SESSION_NO_ERROR, null); } private void onVerificationFailure(StagingManager.StagedSession session, Callback callback, @@ -298,7 +298,7 @@ final class PackageSessionVerifier { // Failed to get hold of StorageManager Slog.e(TAG, "Failed to get hold of StorageManager", e); throw new PackageManagerException( - SessionInfo.STAGED_SESSION_UNKNOWN, + SessionInfo.SESSION_UNKNOWN_ERROR, "Failed to get hold of StorageManager"); } // Proactively mark session as ready before calling apexd. Although this call order @@ -336,7 +336,7 @@ final class PackageSessionVerifier { final ParseResult<SigningDetails> newResult = ApkSignatureVerifier.verify( input.reset(), apexPath, minSignatureScheme); if (newResult.isError()) { - throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, + throw new PackageManagerException(SessionInfo.SESSION_VERIFICATION_FAILED, "Failed to parse APEX package " + apexPath + " : " + newResult.getException(), newResult.getException()); } @@ -355,7 +355,7 @@ final class PackageSessionVerifier { input.reset(), existingApexPkg.applicationInfo.sourceDir, SigningDetails.SignatureSchemeVersion.JAR); if (existingResult.isError()) { - throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, + throw new PackageManagerException(SessionInfo.SESSION_VERIFICATION_FAILED, "Failed to parse APEX package " + existingApexPkg.applicationInfo.sourceDir + " : " + existingResult.getException(), existingResult.getException()); } @@ -369,7 +369,7 @@ final class PackageSessionVerifier { return; } - throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, + throw new PackageManagerException(SessionInfo.SESSION_VERIFICATION_FAILED, "APK-container signature of APEX package " + packageName + " with version " + newApexPkg.versionCodeMajor + " and path " + apexPath + " is not" + " compatible with the one currently installed on device"); @@ -412,11 +412,11 @@ final class PackageSessionVerifier { packageInfo = PackageInfoWithoutStateUtils.generate(parsedPackage, apexInfo, flags); if (packageInfo == null) { throw new PackageManagerException( - SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, + SessionInfo.SESSION_VERIFICATION_FAILED, "Unable to generate package info: " + apexInfo.modulePath); } } catch (PackageManagerException e) { - throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, + throw new PackageManagerException(SessionInfo.SESSION_VERIFICATION_FAILED, "Failed to parse APEX package " + apexInfo.modulePath + " : " + e, e); } result.add(packageInfo); @@ -438,7 +438,7 @@ final class PackageSessionVerifier { } } throw new PackageManagerException( - SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, + SessionInfo.SESSION_VERIFICATION_FAILED, "Could not find rollback id for commit session: " + sessionId); } @@ -546,7 +546,7 @@ final class PackageSessionVerifier { try { checkActiveSessions(PackageHelper.getStorageManager().supportsCheckpoint()); } catch (RemoteException e) { - throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, + throw new PackageManagerException(SessionInfo.SESSION_VERIFICATION_FAILED, "Can't query fs-checkpoint status : " + e); } } @@ -562,7 +562,7 @@ final class PackageSessionVerifier { } if (!supportsCheckpoint && activeSessions > 1) { throw new PackageManagerException( - SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, + SessionInfo.SESSION_VERIFICATION_FAILED, "Cannot stage multiple sessions without checkpoint support"); } } @@ -593,13 +593,13 @@ final class PackageSessionVerifier { // will be deleted. } stagedSession.setSessionFailed( - SessionInfo.STAGED_SESSION_CONFLICT, + SessionInfo.SESSION_CONFLICT, "Session was failed by rollback session: " + session.sessionId()); Slog.i(TAG, "Session " + stagedSession.sessionId() + " is marked failed due to " + "rollback session: " + session.sessionId()); } else if (!isRollback(session) && isRollback(stagedSession)) { throw new PackageManagerException( - SessionInfo.STAGED_SESSION_CONFLICT, + SessionInfo.SESSION_CONFLICT, "Session was failed by rollback session: " + stagedSession.sessionId()); } @@ -622,7 +622,7 @@ final class PackageSessionVerifier { final String packageName = child.getPackageName(); if (packageName == null) { throw new PackageManagerException( - SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, + SessionInfo.SESSION_VERIFICATION_FAILED, "Cannot stage session " + child.sessionId() + " with package name null"); } for (StagingManager.StagedSession stagedSession : mStagedSessions) { @@ -634,14 +634,14 @@ final class PackageSessionVerifier { if (stagedSession.getCommittedMillis() < parent.getCommittedMillis()) { // Fail the session committed later when there are overlapping packages throw new PackageManagerException( - SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, + SessionInfo.SESSION_VERIFICATION_FAILED, "Package: " + packageName + " in session: " + child.sessionId() + " has been staged already by session: " + stagedSession.sessionId()); } else { stagedSession.setSessionFailed( - SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, + SessionInfo.SESSION_VERIFICATION_FAILED, "Package: " + packageName + " in session: " + stagedSession.sessionId() + " has been staged already by session: " diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 8a6ef6bfcb41..29de5551cb27 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -284,7 +284,7 @@ public class StagingManager { String packageName = apexSession.getPackageName(); String errorMsg = mApexManager.getApkInApexInstallError(packageName); if (errorMsg != null) { - throw new PackageManagerException(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, + throw new PackageManagerException(SessionInfo.SESSION_ACTIVATION_FAILED, "Failed to install apk-in-apex of " + packageName + " : " + errorMsg); } } @@ -397,7 +397,7 @@ public class StagingManager { revertMsg += " Reason for revert: " + reasonForRevert; } Slog.d(TAG, revertMsg); - session.setSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN, revertMsg); + session.setSessionFailed(SessionInfo.SESSION_UNKNOWN_ERROR, revertMsg); return; } @@ -484,7 +484,7 @@ public class StagingManager { for (String apkInApex : mApexManager.getApksInApex(packageName)) { if (!apkNames.add(apkInApex)) { throw new PackageManagerException( - SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, + SessionInfo.SESSION_ACTIVATION_FAILED, "Package: " + packageName + " in session: " + apexSession.sessionId() + " has duplicate apk-in-apex: " + apkInApex, null); @@ -511,7 +511,7 @@ public class StagingManager { Slog.e(TAG, "Failure to install APK staged session " + session.sessionId() + " [" + errorMessage + "]"); throw new PackageManagerException( - SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMessage); + SessionInfo.SESSION_ACTIVATION_FAILED, errorMessage); } } @@ -665,7 +665,7 @@ public class StagingManager { // is upgrading. Fail all the sessions and exit early. for (int i = 0; i < sessions.size(); i++) { StagedSession session = sessions.get(i); - session.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, + session.setSessionFailed(SessionInfo.SESSION_ACTIVATION_FAILED, "Build fingerprint has changed"); } return; @@ -705,7 +705,7 @@ public class StagingManager { final ApexSessionInfo apexSession = apexSessions.get(session.sessionId()); if (apexSession == null || apexSession.isUnknown) { hasFailedApexSession = true; - session.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, "apexd did " + session.setSessionFailed(SessionInfo.SESSION_ACTIVATION_FAILED, "apexd did " + "not know anything about a staged session supposed to be activated"); continue; } else if (isApexSessionFailed(apexSession)) { @@ -721,7 +721,7 @@ public class StagingManager { errorMsg += " Error: " + apexSession.errorMessage; } Slog.d(TAG, errorMsg); - session.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMsg); + session.setSessionFailed(SessionInfo.SESSION_ACTIVATION_FAILED, errorMsg); continue; } else if (apexSession.isActivated || apexSession.isSuccess) { hasAppliedApexSession = true; @@ -730,13 +730,13 @@ public class StagingManager { // Apexd did not apply the session for some unknown reason. There is no guarantee // that apexd will install it next time. Safer to proactively mark it as failed. hasFailedApexSession = true; - session.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, + session.setSessionFailed(SessionInfo.SESSION_ACTIVATION_FAILED, "Staged session " + session.sessionId() + " at boot didn't activate nor " + "fail. Marking it as failed anyway."); } else { Slog.w(TAG, "Apex session " + session.sessionId() + " is in impossible state"); hasFailedApexSession = true; - session.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, + session.setSessionFailed(SessionInfo.SESSION_ACTIVATION_FAILED, "Impossible state"); } } @@ -756,7 +756,7 @@ public class StagingManager { // Session has been already failed in the loop above. continue; } - session.setSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, + session.setSessionFailed(SessionInfo.SESSION_ACTIVATION_FAILED, "Another apex session failed"); } return; @@ -772,7 +772,7 @@ public class StagingManager { } catch (Exception e) { Slog.e(TAG, "Staged install failed due to unhandled exception", e); onInstallationFailure(session, new PackageManagerException( - SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, + SessionInfo.SESSION_ACTIVATION_FAILED, "Staged install failed due to unhandled exception: " + e), supportsCheckpoint, needsCheckpoint); } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 167ad3ba7d0e..fb3ca91003ea 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -3537,8 +3537,11 @@ public class UserManagerService extends IUserManager.Stub { Slog.wtf(LOG_TAG, "Seeing both legacy and current local restrictions in xml"); } } else if (legacyLocalRestrictions != null) { - mDevicePolicyLocalUserRestrictions.put(id, - new RestrictionsSet(id, legacyLocalRestrictions)); + RestrictionsSet legacyLocalRestrictionsSet = + legacyLocalRestrictions.isEmpty() + ? new RestrictionsSet() + : new RestrictionsSet(id, legacyLocalRestrictions); + mDevicePolicyLocalUserRestrictions.put(id, legacyLocalRestrictionsSet); } if (globalRestrictions != null) { mDevicePolicyGlobalUserRestrictions.updateRestrictions(id, @@ -4478,7 +4481,7 @@ public class UserManagerService extends IUserManager.Stub { userData.info.flags |= UserInfo.FLAG_EPHEMERAL; writeUserLP(userData); - return UserManager.REMOVE_RESULT_SET_EPHEMERAL; + return UserManager.REMOVE_RESULT_DEFERRED; } } finally { Binder.restoreCallingIdentity(ident); diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index af524db74341..60d2fc1ab498 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -1378,7 +1378,9 @@ final class DefaultPermissionGrantPolicy { pm.grantPermission(permission, pkg, user); } - pm.updatePermissionFlags(permission, pkg, newFlags, newFlags, user); + // clear the REVIEW_REQUIRED flag, if set + int flagMask = newFlags | PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; + pm.updatePermissionFlags(permission, pkg, flagMask, newFlags, user); } // If a component gets a permission for being the default handler A diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index a3b6b8221843..1cfcdf51f5b8 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -66,9 +66,11 @@ import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.Preconditions; import com.android.internal.util.function.TriFunction; import com.android.server.LocalServices; import com.android.server.pm.UserManagerInternal; +import com.android.server.pm.UserManagerService; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.permission.PermissionManagerServiceInternal.HotwordDetectionServiceProvider; @@ -384,7 +386,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { @Override public void startOneTimePermissionSession(String packageName, @UserIdInt int userId, long timeoutMillis, int importanceToResetTimer, int importanceToKeepSessionAlive) { - mContext.enforceCallingPermission(Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS, + mContext.enforceCallingOrSelfPermission( + Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS, "Must hold " + Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS + " to register permissions as one time."); Objects.requireNonNull(packageName); @@ -549,6 +552,18 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @Override + public void revokePostNotificationPermissionWithoutKillForTest(String packageName, int userId) { + mPermissionManagerServiceImpl.revokePostNotificationPermissionWithoutKillForTest( + packageName, userId); + } + + @Override + public void selfRevokePermissions(@NonNull String packageName, + @NonNull List<String> permissions) { + mPermissionManagerServiceImpl.selfRevokePermissions(packageName, permissions); + } + + @Override public boolean shouldShowRequestPermissionRationale(String packageName, String permissionName, int userId) { return mPermissionManagerServiceImpl.shouldShowRequestPermissionRationale(packageName, @@ -678,8 +693,23 @@ public class PermissionManagerService extends IPermissionManager.Stub { @Override public void onPackageInstalled(@NonNull AndroidPackage pkg, int previousAppId, - @NonNull PackageInstalledParams params, @UserIdInt int userId) { - mPermissionManagerServiceImpl.onPackageInstalled(pkg, previousAppId, params, userId); + @NonNull PackageInstalledParams params, @UserIdInt int rawUserId) { + Objects.requireNonNull(pkg, "pkg"); + Objects.requireNonNull(params, "params"); + Preconditions.checkArgument(rawUserId >= UserHandle.USER_SYSTEM + || rawUserId == UserHandle.USER_ALL, "userId"); + + mPermissionManagerServiceImpl.onPackageInstalled(pkg, previousAppId, params, rawUserId); + final int[] userIds = rawUserId == UserHandle.USER_ALL ? getAllUserIds() + : new int[] { rawUserId }; + for (final int userId : userIds) { + final int autoRevokePermissionsMode = params.getAutoRevokePermissionsMode(); + if (autoRevokePermissionsMode == AppOpsManager.MODE_ALLOWED + || autoRevokePermissionsMode == AppOpsManager.MODE_IGNORED) { + setAutoRevokeExemptedInternal(pkg, + autoRevokePermissionsMode == AppOpsManager.MODE_IGNORED, userId); + } + } } @Override @@ -780,6 +810,15 @@ public class PermissionManagerService extends IPermissionManager.Stub { } /** + * Returns all relevant user ids. This list include the current set of created user ids as well + * as pre-created user ids. + * @return user ids for created users and pre-created users + */ + private int[] getAllUserIds() { + return UserManagerService.getInstance().getUserIdsIncludingPreCreated(); + } + + /** * Interface to intercept permission checks and optionally pass through to the original * implementation. */ diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java index c9fd12219562..7833c4341a94 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java @@ -19,8 +19,6 @@ package com.android.server.pm.permission; import static android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY; import static android.Manifest.permission.READ_EXTERNAL_STORAGE; import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; -import static android.app.AppOpsManager.MODE_ALLOWED; -import static android.app.AppOpsManager.MODE_IGNORED; import static android.content.pm.PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT; import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION; import static android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKED; @@ -63,7 +61,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManager; -import android.app.AppOpsManager; import android.app.IActivityManager; import android.app.admin.DevicePolicyManagerInternal; import android.compat.annotation.ChangeId; @@ -170,6 +167,10 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt private static final String TAG = "PackageManager"; private static final String LOG_TAG = PermissionManagerServiceImpl.class.getSimpleName(); + private static final String SKIP_KILL_APP_REASON_NOTIFICATION_TEST = "skip permission revoke " + + "app kill for notification test"; + + private static final long BACKUP_TIMEOUT_MILLIS = SECONDS.toMillis(60); // For automotive products, CarService enforces allow-listing of the privileged permissions @@ -198,14 +199,19 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt /** All nearby devices permissions */ private static final List<String> NEARBY_DEVICES_PERMISSIONS = new ArrayList<>(); - // TODO: This is a placeholder. Replace with actual implementation - private static final List<String> NOTIFICATION_PERMISSIONS = new ArrayList<>(); - /** - * All permissions that should be granted with the REVOKE_WHEN_REQUESTED flag, if they are - * implicitly added to a package + * All notification permissions. + * Notification permission state is treated differently from other permissions. Notification + * permission get the REVIEW_REQUIRED flag set for S- apps, or for T+ apps on updating to T or + * restoring a pre-T backup. The permission and app op remain denied. The flag will be read by + * the notification system, and allow apps to send notifications, until cleared. + * The flag is cleared for S- apps by the system showing a permission request prompt, and the + * user clicking "allow" or "deny" in the dialog. For T+ apps, the flag is cleared upon the + * first activity launch. + * + * @see PermissionPolicyInternal#showNotificationPromptIfNeeded(String, int, int) */ - private static final List<String> IMPLICIT_GRANTED_PERMISSIONS = new ArrayList<>(); + private static final List<String> NOTIFICATION_PERMISSIONS = new ArrayList<>(); /** If the permission of the value is granted, so is the key */ private static final Map<String, String> FULLER_PERMISSION_MAP = new HashMap<>(); @@ -221,7 +227,7 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.BLUETOOTH_ADVERTISE); NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.BLUETOOTH_CONNECT); NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.BLUETOOTH_SCAN); - IMPLICIT_GRANTED_PERMISSIONS.add(Manifest.permission.POST_NOTIFICATIONS); + NOTIFICATION_PERMISSIONS.add(Manifest.permission.POST_NOTIFICATIONS); } /** Set of source package names for Privileged Permission Allowlist */ @@ -244,9 +250,6 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt /** Permission controller: User space permission management */ private PermissionControllerManager mPermissionControllerManager; - /** App ops manager */ - private final AppOpsManager mAppOpsManager; - /** * Built-in permissions. Read from system configuration files. Mapping is from * UID to permission name. @@ -321,11 +324,15 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt mPackageManagerInt.writeSettings(true); } @Override - public void onPermissionRevoked(int uid, int userId, String reason) { + public void onPermissionRevoked(int uid, int userId, String reason, boolean overrideKill) { mOnPermissionChangeListeners.onPermissionsChanged(uid); // Critical; after this call the application should never have the permission mPackageManagerInt.writeSettings(false); + if (overrideKill) { + return; + } + final int appId = UserHandle.getAppId(uid); if (reason == null) { mHandler.post(() -> killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED)); @@ -374,7 +381,6 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt mContext = context; mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class); mUserManagerInt = LocalServices.getService(UserManagerInternal.class); - mAppOpsManager = context.getSystemService(AppOpsManager.class); mPrivilegedPermissionAllowlistSourcePackageNames.add(PLATFORM_PACKAGE_NAME); // PackageManager.hasSystemFeature() is not used here because PackageManagerService @@ -1440,9 +1446,29 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt reason, mDefaultPermissionCallback); } + @Override + public void revokePostNotificationPermissionWithoutKillForTest(String packageName, int userId) { + final int callingUid = Binder.getCallingUid(); + final boolean overridePolicy = + checkUidPermission(callingUid, ADJUST_RUNTIME_PERMISSIONS_POLICY) + == PackageManager.PERMISSION_GRANTED; + mContext.enforceCallingPermission( + android.Manifest.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL, ""); + revokeRuntimePermissionInternal(packageName, Manifest.permission.POST_NOTIFICATIONS, + overridePolicy, true, callingUid, userId, + SKIP_KILL_APP_REASON_NOTIFICATION_TEST, mDefaultPermissionCallback); + } + private void revokeRuntimePermissionInternal(String packageName, String permName, - boolean overridePolicy, int callingUid, final int userId, String reason, - PermissionCallback callback) { + boolean overridePolicy, int callingUid, final int userId, + String reason, PermissionCallback callback) { + revokeRuntimePermissionInternal(packageName, permName, overridePolicy, false, callingUid, + userId, reason, callback); + } + + private void revokeRuntimePermissionInternal(String packageName, String permName, + boolean overridePolicy, boolean overrideKill, int callingUid, final int userId, + String reason, PermissionCallback callback) { if (PermissionManager.DEBUG_TRACE_PERMISSION_UPDATES && PermissionManager.shouldTraceGrant(packageName, permName, userId)) { Log.i(TAG, "System is revoking " + packageName + " " @@ -1554,7 +1580,7 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt if (callback != null) { if (isRuntimePermission) { callback.onPermissionRevoked(UserHandle.getUid(userId, pkg.getUid()), userId, - reason); + reason, overrideKill); } else { mDefaultPermissionCallback.onInstallPermissionRevoked(); } @@ -1565,6 +1591,25 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt } } + @Override + public void selfRevokePermissions(String packageName, List<String> permissions) { + final int callingUid = Binder.getCallingUid(); + int callingUserId = UserHandle.getUserId(callingUid); + int targetPackageUid = mPackageManagerInt.getPackageUid(packageName, 0, callingUserId); + if (targetPackageUid != callingUid) { + throw new SecurityException("uid " + callingUid + + " cannot revoke permissions for package " + packageName + " with uid " + + targetPackageUid); + } + for (String permName : permissions) { + if (!checkCallingOrSelfPermission(permName)) { + throw new SecurityException("uid " + callingUid + " cannot revoke permission " + + permName + " because it does not hold that permission"); + } + } + mPermissionControllerManager.selfRevokePermissions(packageName, permissions); + } + private boolean mayManageRolePermission(int uid) { final PackageManager packageManager = mContext.getPackageManager(); final String[] packageNames = packageManager.getPackagesForUid(uid); @@ -2594,9 +2639,12 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt // Cache newImplicitPermissions before modifing permissionsState as for the // shared uids the original and new state are the same object + // TODO(205888750): remove the line for LEGACY_REVIEW once propagated through + // droidfood if (!origState.hasPermissionState(permName) && (pkg.getImplicitPermissions().contains(permName) - || (permName.equals(Manifest.permission.ACTIVITY_RECOGNITION)))) { + || (permName.equals(Manifest.permission.ACTIVITY_RECOGNITION))) + || NOTIFICATION_PERMISSIONS.contains(permName)) { if (pkg.getImplicitPermissions().contains(permName)) { // If permName is an implicit permission, try to auto-grant newImplicitPermissions.add(permName); @@ -2754,9 +2802,11 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt } // Remove review flag as it is not necessary anymore - if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0) { - flags &= ~FLAG_PERMISSION_REVIEW_REQUIRED; - wasChanged = true; + if (!NOTIFICATION_PERMISSIONS.contains(perm)) { + if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0) { + flags &= ~FLAG_PERMISSION_REVIEW_REQUIRED; + wasChanged = true; + } } if ((flags & FLAG_PERMISSION_REVOKED_COMPAT) != 0 @@ -3117,28 +3167,37 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt inheritPermissionStateToNewImplicitPermissionLocked(sourcePerms, newPerm, ps, pkg); } - } else if (IMPLICIT_GRANTED_PERMISSIONS.contains(newPerm) - && !origPs.hasPermissionState(newPerm)) { + } else if (NOTIFICATION_PERMISSIONS.contains(newPerm)) { + //&& (origPs.getPermissionState(newPerm) == null) { + // TODO(b/205888750): add back line about origPs once propagated through droidfood Permission bp = mRegistry.getPermission(newPerm); if (bp == null) { throw new IllegalStateException("Unknown new permission " + newPerm); } - if ((ps.getPermissionState(newPerm).getFlags() - & FLAG_PERMISSION_REVIEW_REQUIRED) != 0) { - // No need to grant if review is required - continue; + // TODO(b/205888750): remove the line for REVOKE_WHEN_REQUESTED once propagated + // through droidfood + if (!isUserSetOrPregrantedOrFixed(ps.getPermissionFlags(newPerm))) { + updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId); + ps.updatePermissionFlags(bp, PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED + | FLAG_PERMISSION_REVOKE_WHEN_REQUESTED, + PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED); + // TODO(b/205888750): remove revoke once propagated through droidfood + if (ps.isPermissionGranted(newPerm)) { + ps.revokePermission(bp); + } } - updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId); - ps.updatePermissionFlags(bp, - FLAG_PERMISSION_REVOKE_WHEN_REQUESTED, - FLAG_PERMISSION_REVOKE_WHEN_REQUESTED); - ps.grantPermission(bp); } } return updatedUserIds; } + private boolean isUserSetOrPregrantedOrFixed(int flags) { + return (flags & (FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED + | FLAG_PERMISSION_POLICY_FIXED | FLAG_PERMISSION_SYSTEM_FIXED + | FLAG_PERMISSION_GRANTED_BY_DEFAULT | FLAG_PERMISSION_GRANTED_BY_ROLE)) != 0; + } + @NonNull @Override public List<SplitPermissionInfoParcelable> getSplitPermissions() { @@ -4323,9 +4382,9 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt updateAllPermissions(StorageManager.UUID_PRIVATE_INTERNAL, false) ); - mSystemReady = true; - synchronized (mLock) { + mSystemReady = true; + if (mPrivappPermissionsViolations != null) { throw new IllegalStateException("Signature|privileged permissions not in " + "privapp-permissions allowlist: " + mPrivappPermissionsViolations); @@ -4905,15 +4964,6 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt addAllowlistedRestrictedPermissionsInternal(pkg, params.getAllowlistedRestrictedPermissions(), FLAG_PERMISSION_WHITELIST_INSTALLER, userId); - final int autoRevokePermissionsMode = params.getAutoRevokePermissionsMode(); - if (autoRevokePermissionsMode == AppOpsManager.MODE_ALLOWED - || autoRevokePermissionsMode == AppOpsManager.MODE_IGNORED) { - // TODO: theianchen Bug: 182523293 - // We should move this portion of code that's calling - // setAutoRevokeExemptedInternal() into the old PMS - setAutoRevokeExemptedInternal(pkg, - autoRevokePermissionsMode == AppOpsManager.MODE_IGNORED, userId); - } grantRequestedRuntimePermissionsInternal(pkg, params.getGrantedPermissions(), userId); } } @@ -5182,25 +5232,6 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt onPackageUninstalledInternal(packageName, appId, pkg, sharedUserPkgs, userIds); } - private boolean setAutoRevokeExemptedInternal(@NonNull AndroidPackage pkg, boolean exempted, - @UserIdInt int userId) { - final int packageUid = UserHandle.getUid(userId, pkg.getUid()); - if (mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_AUTO_REVOKE_MANAGED_BY_INSTALLER, - packageUid, pkg.getPackageName()) != MODE_ALLOWED) { - // Allowlist user set - don't override - return false; - } - - final long identity = Binder.clearCallingIdentity(); - try { - mAppOpsManager.setMode(AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, packageUid, - pkg.getPackageName(), exempted ? MODE_IGNORED : MODE_ALLOWED); - } finally { - Binder.restoreCallingIdentity(identity); - } - return true; - } - /** * Callbacks invoked when interesting actions have been taken on a permission. * <p> @@ -5213,7 +5244,11 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt public void onPermissionChanged() {} public void onPermissionGranted(int uid, @UserIdInt int userId) {} public void onInstallPermissionGranted() {} - public void onPermissionRevoked(int uid, @UserIdInt int userId, String reason) {} + public void onPermissionRevoked(int uid, @UserIdInt int userId, String reason) { + onPermissionRevoked(uid, userId, reason, false); + } + public void onPermissionRevoked(int uid, @UserIdInt int userId, String reason, + boolean overrideKill) {} public void onInstallPermissionRevoked() {} public void onPermissionUpdated(@UserIdInt int[] updatedUserIds, boolean sync) {} public void onPermissionUpdatedNotifyListener(@UserIdInt int[] updatedUserIds, boolean sync, diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java index d2018f26d04a..c582f9efa7a0 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java @@ -318,6 +318,35 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte String reason); /** + * Revoke the POST_NOTIFICATIONS permission, without killing the app. This method must ONLY BE + * USED in CTS or local tests. + * + * @param packageName The package to be revoked + * @param userId The user for which to revoke + */ + void revokePostNotificationPermissionWithoutKillForTest(String packageName, int userId); + + /** + * Triggers the revocation of one or more permissions for a package, under the following + * conditions: + * <ul> + * <li>The package {@code packageName} must be under the same UID as the calling process + * (typically, the target package is the calling package). + * <li>Each permission in {@code permissions} must be granted to the package + * {@code packageName}. + * <li>Each permission in {@code permissions} must be a runtime permission. + * </ul> + * <p> + * For every permission in {@code permissions}, the entire permission group it belongs to will + * be revoked. This revocation happens asynchronously and kills all processes running in the + * same UID as {@code packageName}. It will be triggered once it is safe to do so. + * + * @param packageName The name of the package for which the permissions will be revoked. + * @param permissions List of permissions to be revoked. + */ + void selfRevokePermissions(String packageName, List<String> permissions); + + /** * Get whether you should show UI with rationale for requesting a permission. You should do this * only if you do not have the permission and the context in which the permission is requested * does not clearly communicate to the user what would be the benefit from grating this diff --git a/services/core/java/com/android/server/policy/PermissionPolicyInternal.java b/services/core/java/com/android/server/policy/PermissionPolicyInternal.java index 6084c6718577..20b7ccd39287 100644 --- a/services/core/java/com/android/server/policy/PermissionPolicyInternal.java +++ b/services/core/java/com/android/server/policy/PermissionPolicyInternal.java @@ -19,6 +19,7 @@ package com.android.server.policy; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.app.TaskInfo; import android.content.Intent; /** @@ -52,6 +53,25 @@ public abstract class PermissionPolicyInternal { @Nullable String callingPackage); /** + * Check whether a notification permission prompt should be shown for the given package. A + * prompt should be shown if the app targets S-, is currently running in a visible, focused + * task, has the REVIEW_REQUIRED flag set on its implicit notification permission, and has + * created at least one notification channel (even if it has since been deleted). + * @param packageName The package whose permission is being checked + * @param userId The user for whom the package is being started + * @param taskId The task the notification prompt should be attached to + */ + public abstract void showNotificationPromptIfNeeded(@NonNull String packageName, int userId, + int taskId); + + /** + * Determine if a particular task is in the proper state to show a system-triggered permission + * prompt. A prompt can be shown if the task is focused, visible, and running. + * @param taskInfo The task to be checked + */ + public abstract boolean canShowPermissionPromptForTask(@Nullable TaskInfo taskInfo); + + /** * @return Whether the policy is initialized for a user. */ public abstract boolean isInitialized(@UserIdInt int userId); diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java index ad435146f579..c9a8701ec7af 100644 --- a/services/core/java/com/android/server/policy/PermissionPolicyService.java +++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java @@ -16,6 +16,7 @@ package com.android.server.policy; +import static android.Manifest.permission.POST_NOTIFICATIONS; import static android.app.AppOpsManager.MODE_ALLOWED; import static android.app.AppOpsManager.MODE_FOREGROUND; import static android.app.AppOpsManager.MODE_IGNORED; @@ -25,16 +26,24 @@ import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT; import static android.content.pm.PackageManager.GET_PERMISSIONS; +import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.app.ActivityOptions; +import android.app.ActivityTaskManager; import android.app.AppOpsManager; import android.app.AppOpsManagerInternal; +import android.app.TaskInfo; +import android.app.compat.CompatChanges; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledSince; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -43,6 +52,7 @@ import android.content.pm.PackageManagerInternal; import android.content.pm.PackageManagerInternal.PackageListObserver; import android.content.pm.PermissionInfo; import android.os.Build; +import android.os.Bundle; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; @@ -53,6 +63,7 @@ import android.provider.Telephony; import android.telecom.TelecomManager; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.Log; import android.util.LongSparseLongArray; import android.util.Pair; import android.util.Slog; @@ -67,17 +78,21 @@ import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.SystemService; +import com.android.server.notification.NotificationManagerInternal; import com.android.server.pm.UserManagerInternal; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.policy.PermissionPolicyInternal.OnInitializedCallback; import com.android.server.utils.TimingsTraceAndSlog; +import com.android.server.wm.ActivityInterceptorCallback; +import com.android.server.wm.ActivityTaskManagerInternal; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.concurrent.ExecutionException; /** @@ -89,11 +104,15 @@ import java.util.concurrent.ExecutionException; */ public final class PermissionPolicyService extends SystemService { private static final String LOG_TAG = PermissionPolicyService.class.getSimpleName(); + private static final String SYSTEM_PKG = "android"; private static final boolean DEBUG = false; private static final long USER_SENSITIVE_UPDATE_DELAY_MS = 60000; private final Object mLock = new Object(); + @GuardedBy("mLock") + private boolean mBootCompleted = false; + private IAppOpsCallback mAppOpsCallback; /** Whether the user is started but not yet stopped */ @@ -118,24 +137,39 @@ public final class PermissionPolicyService extends SystemService { @GuardedBy("mLock") private final SparseBooleanArray mIsUidSyncScheduled = new SparseBooleanArray(); + /** + * This change reflects the presence of the new Notification Permission + */ + @ChangeId + @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) + private static final long NOTIFICATION_PERM_CHANGE_ID = 194833441L; + private List<String> mAppOpPermissions; + private Context mContext; + private PackageManagerInternal mPackageManagerInternal; + private NotificationManagerInternal mNotificationManager; + private PermissionManagerServiceInternal mPermissionManagerService; + private final PackageManager mPackageManager; + public PermissionPolicyService(@NonNull Context context) { super(context); + mContext = context; + mPackageManager = context.getPackageManager(); LocalServices.addService(PermissionPolicyInternal.class, new Internal()); } @Override public void onStart() { - final PackageManagerInternal packageManagerInternal = LocalServices.getService( + mPackageManagerInternal = LocalServices.getService( PackageManagerInternal.class); - final PermissionManagerServiceInternal permissionManagerInternal = LocalServices.getService( + PermissionManagerServiceInternal permissionManagerInternal = LocalServices.getService( PermissionManagerServiceInternal.class); final IAppOpsService appOpsService = IAppOpsService.Stub.asInterface( ServiceManager.getService(Context.APP_OPS_SERVICE)); - packageManagerInternal.getPackageList(new PackageListObserver() { + mPackageManagerInternal.getPackageList(new PackageListObserver() { @Override public void onPackageAdded(String packageName, int uid) { final int userId = UserHandle.getUserId(uid); @@ -207,10 +241,10 @@ public final class PermissionPolicyService extends SystemService { final PermissionInfo appOpPermissionInfo = appOpPermissionInfos.get(i); switch (appOpPermissionInfo.name) { - case android.Manifest.permission.ACCESS_NOTIFICATIONS: - case android.Manifest.permission.MANAGE_IPSEC_TUNNELS: + case Manifest.permission.ACCESS_NOTIFICATIONS: + case Manifest.permission.MANAGE_IPSEC_TUNNELS: continue; - case android.Manifest.permission.REQUEST_INSTALL_PACKAGES: + case Manifest.permission.REQUEST_INSTALL_PACKAGES: // Settings allows the user to control the app op if it's not in the default // mode, regardless of whether the app has requested the permission, so we // should not reset it. @@ -251,7 +285,7 @@ public final class PermissionPolicyService extends SystemService { } int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); // If there is no valid package for the given UID, return immediately - if (packageManagerInternal.getPackage(uid) == null) { + if (mPackageManagerInternal.getPackage(uid) == null) { return; } @@ -343,6 +377,18 @@ public final class PermissionPolicyService extends SystemService { } } } + + if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) { + ((Internal) LocalServices.getService(PermissionPolicyInternal.class)) + .onActivityManagerReady(); + } + + if (phase == SystemService.PHASE_BOOT_COMPLETED) { + synchronized (mLock) { + mBootCompleted = true; + } + } + } /** @@ -748,10 +794,12 @@ public final class PermissionPolicyService extends SystemService { String permissionName = permissionInfo.name; String packageName = packageInfo.packageName; + UserHandle user = UserHandle.getUserHandleForUid(packageInfo.applicationInfo.uid); int permissionFlags = mPackageManager.getPermissionFlags(permissionName, packageName, mContext.getUser()); boolean isReviewRequired = (permissionFlags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0; - if (isReviewRequired) { + if (isReviewRequired && !CompatChanges.isChangeEnabled( + NOTIFICATION_PERM_CHANGE_ID, packageName, user)) { return; } @@ -953,6 +1001,33 @@ public final class PermissionPolicyService extends SystemService { private class Internal extends PermissionPolicyInternal { + private ActivityInterceptorCallback mActivityInterceptorCallback = + new ActivityInterceptorCallback() { + @Nullable + @Override + public ActivityInterceptorCallback.ActivityInterceptResult intercept( + ActivityInterceptorInfo info) { + return null; + } + + @Override + public void onActivityLaunched(TaskInfo taskInfo, ActivityInfo activityInfo) { + super.onActivityLaunched(taskInfo, activityInfo); + clearNotificationReviewFlagsIfNeeded(activityInfo.packageName, + UserHandle.of(taskInfo.userId)); + showNotificationPromptIfNeeded(activityInfo.packageName, + taskInfo.userId, taskInfo.taskId); + } + }; + + private void onActivityManagerReady() { + ActivityTaskManagerInternal atm = + LocalServices.getService(ActivityTaskManagerInternal.class); + atm.registerActivityStartInterceptor( + ActivityInterceptorCallback.PERMISSION_POLICY_ORDERED_ID, + mActivityInterceptorCallback); + } + @Override public boolean checkStartActivity(@NonNull Intent intent, int callingUid, @Nullable String callingPackage) { @@ -962,9 +1037,53 @@ public final class PermissionPolicyService extends SystemService { + callingPackage + " (uid=" + callingUid + ")"); return false; } + + if (PackageManager.ACTION_REQUEST_PERMISSIONS_FOR_OTHER.equals(intent.getAction()) + && (callingUid != Process.SYSTEM_UID || !SYSTEM_PKG.equals(callingPackage))) { + return false; + } + return true; } + public void showNotificationPromptIfNeeded(@NonNull String packageName, int userId, + int taskId) { + UserHandle user = UserHandle.of(userId); + if (packageName == null || taskId == ActivityTaskManager.INVALID_TASK_ID + || !shouldForceShowNotificationPermissionRequest(packageName, user)) { + return; + } + + launchNotificationPermissionRequestDialog(packageName, user, taskId); + } + + private void clearNotificationReviewFlagsIfNeeded(String packageName, UserHandle userId) { + if (!CompatChanges.isChangeEnabled(NOTIFICATION_PERM_CHANGE_ID, packageName, userId)) { + return; + } + mPackageManager.updatePermissionFlags(POST_NOTIFICATIONS, packageName, + FLAG_PERMISSION_REVIEW_REQUIRED, 0, userId); + } + + private void launchNotificationPermissionRequestDialog(String pkgName, UserHandle user, + int taskId) { + Intent grantPermission = mPackageManager + .buildRequestPermissionsIntent(new String[] { POST_NOTIFICATIONS }); + grantPermission.setAction( + PackageManager.ACTION_REQUEST_PERMISSIONS_FOR_OTHER); + grantPermission.putExtra(Intent.EXTRA_PACKAGE_NAME, pkgName); + + ActivityOptions options = new ActivityOptions(new Bundle()); + options.setTaskOverlay(true, false); + options.setLaunchTaskId(taskId); + try { + mContext.startActivityAsUser(grantPermission, options.toBundle(), user); + } catch (Exception e) { + Log.e(LOG_TAG, "couldn't start grant permission dialog" + + "for other package " + pkgName, e); + } + } + @Override public boolean isInitialized(int userId) { return isStarted(userId); @@ -977,6 +1096,12 @@ public final class PermissionPolicyService extends SystemService { } } + @Override + public boolean canShowPermissionPromptForTask(@Nullable TaskInfo taskInfo) { + return taskInfo != null && taskInfo.isFocused && taskInfo.isVisible + && taskInfo.isRunning; + } + /** * Check if the intent action is removed for the calling package (often based on target SDK * version). If the action is removed, we'll silently cancel the activity launch. @@ -1010,5 +1135,49 @@ public final class PermissionPolicyService extends SystemService { return false; } } + + private boolean shouldForceShowNotificationPermissionRequest(@NonNull String pkgName, + @NonNull UserHandle user) { + AndroidPackage pkg = mPackageManagerInternal.getPackage(pkgName); + if (pkg == null || pkg.getPackageName() == null + || Objects.equals(pkgName, mPackageManager.getPermissionControllerPackageName()) + || pkg.getTargetSdkVersion() < Build.VERSION_CODES.M) { + Slog.w(LOG_TAG, "Cannot check for Notification prompt, no package for " + + pkgName + " or pkg is Permission Controller"); + return false; + } + + synchronized (mLock) { + if (!mBootCompleted) { + return false; + } + } + + try { + if (Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.NOTIFICATION_PERMISSION_ENABLED, UserHandle.USER_SYSTEM) + == 0) { + return false; + } + } catch (Settings.SettingNotFoundException e) { + return false; + } + + if (!pkg.getRequestedPermissions().contains(POST_NOTIFICATIONS) + || CompatChanges.isChangeEnabled(NOTIFICATION_PERM_CHANGE_ID, + pkg.getPackageName(), user)) { + return false; + } + + int uid = user.getUid(pkg.getUid()); + if (mNotificationManager == null) { + mNotificationManager = LocalServices.getService(NotificationManagerInternal.class); + } + boolean hasCreatedNotificationChannels = mNotificationManager + .getNumNotificationChannelsForPackage(pkg.getPackageName(), uid, true) > 0; + boolean needsReview = (mPackageManager.getPermissionFlags(POST_NOTIFICATIONS, pkgName, + user) & FLAG_PERMISSION_REVIEW_REQUIRED) != 0; + return hasCreatedNotificationChannels && needsReview; + } } } diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java index 70a804b8135b..73ec2cd66ac1 100644 --- a/services/core/java/com/android/server/power/Notifier.java +++ b/services/core/java/com/android/server/power/Notifier.java @@ -545,7 +545,7 @@ public class Notifier { /** * Called when there has been user activity. */ - public void onUserActivity(int event, int uid) { + public void onUserActivity(int displayGroupId, int event, int uid) { if (DEBUG) { Slog.d(TAG, "onUserActivity: event=" + event + ", uid=" + uid); } @@ -560,7 +560,8 @@ public class Notifier { if (!mUserActivityPending) { mUserActivityPending = true; Message msg = mHandler.obtainMessage(MSG_USER_ACTIVITY); - msg.arg1 = event; + msg.arg1 = displayGroupId; + msg.arg2 = event; msg.setAsynchronous(true); mHandler.sendMessage(msg); } @@ -633,14 +634,15 @@ public class Notifier { /** * Called when the screen policy changes. */ - public void onScreenPolicyUpdate(int newPolicy) { + public void onScreenPolicyUpdate(int displayGroupId, int newPolicy) { if (DEBUG) { Slog.d(TAG, "onScreenPolicyUpdate: newPolicy=" + newPolicy); } synchronized (mLock) { Message msg = mHandler.obtainMessage(MSG_SCREEN_POLICY); - msg.arg1 = newPolicy; + msg.arg1 = displayGroupId; + msg.arg2 = newPolicy; msg.setAsynchronous(true); mHandler.sendMessage(msg); } @@ -675,7 +677,7 @@ public class Notifier { mSuspendBlocker.release(); } - private void sendUserActivity(int event) { + private void sendUserActivity(int displayGroupId, int event) { synchronized (mLock) { if (!mUserActivityPending) { return; @@ -686,7 +688,7 @@ public class Notifier { tm.notifyUserActivity(); mPolicy.userActivity(); mFaceDownDetector.userActivity(event); - mScreenUndimDetector.userActivity(); + mScreenUndimDetector.userActivity(displayGroupId); } void postEnhancedDischargePredictionBroadcast(long delayMs) { @@ -840,8 +842,8 @@ public class Notifier { mSuspendBlocker.release(); } - private void screenPolicyChanging(int screenPolicy) { - mScreenUndimDetector.recordScreenPolicy(screenPolicy); + private void screenPolicyChanging(int displayGroupId, int screenPolicy) { + mScreenUndimDetector.recordScreenPolicy(displayGroupId, screenPolicy); } private void lockProfile(@UserIdInt int userId) { @@ -866,7 +868,7 @@ public class Notifier { public void handleMessage(Message msg) { switch (msg.what) { case MSG_USER_ACTIVITY: - sendUserActivity(msg.arg1); + sendUserActivity(msg.arg1, msg.arg2); break; case MSG_BROADCAST: sendNextBroadcast(); @@ -885,7 +887,7 @@ public class Notifier { showWiredChargingStarted(msg.arg1); break; case MSG_SCREEN_POLICY: - screenPolicyChanging(msg.arg1); + screenPolicyChanging(msg.arg1, msg.arg2); break; } } diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 1455326b12f8..4b2770cef476 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -49,6 +49,7 @@ import android.content.res.Resources; import android.database.ContentObserver; import android.hardware.SensorManager; import android.hardware.SystemSensorManager; +import android.hardware.devicestate.DeviceStateManager; import android.hardware.display.AmbientDisplayConfiguration; import android.hardware.display.DisplayManagerInternal; import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; @@ -60,6 +61,7 @@ import android.os.BatteryManagerInternal; import android.os.BatterySaverPolicyConfig; import android.os.Binder; import android.os.Handler; +import android.os.HandlerExecutor; import android.os.IBinder; import android.os.IPowerManager; import android.os.Looper; @@ -1151,6 +1153,9 @@ public final class PowerManagerService extends SystemService PowerManager.GO_TO_SLEEP_REASON_QUIESCENT, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID); } + + mContext.getSystemService(DeviceStateManager.class).registerCallback( + new HandlerExecutor(mHandler), new DeviceStateListener()); } } } @@ -1792,7 +1797,7 @@ public final class PowerManagerService extends SystemService mLastInteractivePowerHintTime = eventTime; } - mNotifier.onUserActivity(event, uid); + mNotifier.onUserActivity(powerGroup.getGroupId(), event, uid); mAttentionDetector.onUserActivity(eventTime, event); if (mUserInactiveOverrideFromWindowManager) { @@ -3372,7 +3377,7 @@ public final class PowerManagerService extends SystemService final boolean ready = mDisplayManagerInternal.requestPowerState(groupId, displayPowerRequest, mRequestWaitForNegativeProximity); - mNotifier.onScreenPolicyUpdate(displayPowerRequest.policy); + mNotifier.onScreenPolicyUpdate(groupId, displayPowerRequest.policy); if (DEBUG_SPEW) { Slog.d(TAG, "updateDisplayPowerStateLocked: displayReady=" + ready @@ -6349,4 +6354,25 @@ public final class PowerManagerService extends SystemService return interceptPowerKeyDownInternal(event); } } + + /** + * Listens to changes in device state and updates the interactivity time. + * Any changes to the device state are treated as user interactions. + */ + class DeviceStateListener implements DeviceStateManager.DeviceStateCallback { + private int mDeviceState = DeviceStateManager.INVALID_DEVICE_STATE; + + @Override + public void onStateChanged(int deviceState) { + if (mDeviceState != deviceState) { + mDeviceState = deviceState; + // Device-state interactions are applied to the default display so that they + // are reflected only with the default power group. + userActivityInternal(Display.DEFAULT_DISPLAY, mClock.uptimeMillis(), + PowerManager.USER_ACTIVITY_EVENT_DEVICE_STATE, /* flags= */0, + Process.SYSTEM_UID); + } + } + }; + } diff --git a/services/core/java/com/android/server/power/ScreenUndimDetector.java b/services/core/java/com/android/server/power/ScreenUndimDetector.java index 951bc1f76e6d..c4929c210e2c 100644 --- a/services/core/java/com/android/server/power/ScreenUndimDetector.java +++ b/services/core/java/com/android/server/power/ScreenUndimDetector.java @@ -28,6 +28,7 @@ import android.os.PowerManager; import android.os.SystemClock; import android.provider.DeviceConfig; import android.util.Slog; +import android.view.Display; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FrameworkStatsLog; @@ -123,8 +124,8 @@ public class ScreenUndimDetector { * Launches a message that figures out the screen transitions and detects user undims. Must be * called by the parent that is trying to update the screen policy. */ - public void recordScreenPolicy(int newPolicy) { - if (newPolicy == mCurrentScreenPolicy) { + public void recordScreenPolicy(int displayGroupId, int newPolicy) { + if (displayGroupId != Display.DEFAULT_DISPLAY_GROUP || newPolicy == mCurrentScreenPolicy) { return; } @@ -268,7 +269,10 @@ public class ScreenUndimDetector { * The user interacted with the screen after an undim, indicating the phone is in use. * We use this event for logging. */ - public void userActivity() { + public void userActivity(int displayGroupId) { + if (displayGroupId != Display.DEFAULT_DISPLAY) { + return; + } if (mUndimOccurredTime != 1 && mInteractionAfterUndimTime == -1) { mInteractionAfterUndimTime = mClock.getCurrentTime(); } diff --git a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java index ef0079e0c01f..ca675973b2fd 100644 --- a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java +++ b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java @@ -35,7 +35,6 @@ import android.util.proto.ProtoInputStream; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; - import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper; import com.android.server.powerstats.ProtoStreamUtils.ChannelUtils; import com.android.server.powerstats.ProtoStreamUtils.EnergyConsumerResultUtils; @@ -313,12 +312,12 @@ public final class PowerStatsLogger extends Handler { return mStartWallTime; } - public PowerStatsLogger(Context context, File dataStoragePath, + public PowerStatsLogger(Context context, Looper looper, File dataStoragePath, String meterFilename, String meterCacheFilename, String modelFilename, String modelCacheFilename, String residencyFilename, String residencyCacheFilename, IPowerStatsHALWrapper powerStatsHALWrapper) { - super(Looper.getMainLooper()); + super(looper); mStartWallTime = currentTimeMillis() - SystemClock.elapsedRealtime(); if (DEBUG) Slog.d(TAG, "mStartWallTime: " + mStartWallTime); mPowerStatsHALWrapper = powerStatsHALWrapper; diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java index bb52c1dc1a29..9953ca8f9b65 100644 --- a/services/core/java/com/android/server/powerstats/PowerStatsService.java +++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java @@ -28,6 +28,7 @@ import android.os.Binder; import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; +import android.os.Looper; import android.os.UserHandle; import android.power.PowerStatsInternal; import android.util.Slog; @@ -79,6 +80,9 @@ public class PowerStatsService extends SystemService { private StatsPullAtomCallbackImpl mPullAtomCallback; @Nullable private PowerStatsInternal mPowerStatsInternal; + @Nullable + @GuardedBy("this") + private Looper mLooper; @VisibleForTesting static class Injector { @@ -127,12 +131,12 @@ public class PowerStatsService extends SystemService { } } - PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath, - String meterFilename, String meterCacheFilename, + PowerStatsLogger createPowerStatsLogger(Context context, Looper looper, + File dataStoragePath, String meterFilename, String meterCacheFilename, String modelFilename, String modelCacheFilename, String residencyFilename, String residencyCacheFilename, IPowerStatsHALWrapper powerStatsHALWrapper) { - return new PowerStatsLogger(context, dataStoragePath, + return new PowerStatsLogger(context, looper, dataStoragePath, meterFilename, meterCacheFilename, modelFilename, modelCacheFilename, residencyFilename, residencyCacheFilename, @@ -229,11 +233,11 @@ public class PowerStatsService extends SystemService { mDataStoragePath = mInjector.createDataStoragePath(); // Only start logger and triggers if initialization is successful. - mPowerStatsLogger = mInjector.createPowerStatsLogger(mContext, mDataStoragePath, - mInjector.createMeterFilename(), mInjector.createMeterCacheFilename(), - mInjector.createModelFilename(), mInjector.createModelCacheFilename(), - mInjector.createResidencyFilename(), mInjector.createResidencyCacheFilename(), - getPowerStatsHal()); + mPowerStatsLogger = mInjector.createPowerStatsLogger(mContext, getLooper(), + mDataStoragePath, mInjector.createMeterFilename(), + mInjector.createMeterCacheFilename(), mInjector.createModelFilename(), + mInjector.createModelCacheFilename(), mInjector.createResidencyFilename(), + mInjector.createResidencyCacheFilename(), getPowerStatsHal()); mBatteryTrigger = mInjector.createBatteryTrigger(mContext, mPowerStatsLogger); mTimerTrigger = mInjector.createTimerTrigger(mContext, mPowerStatsLogger); } else { @@ -245,6 +249,17 @@ public class PowerStatsService extends SystemService { return mInjector.getPowerStatsHALWrapperImpl(); } + private Looper getLooper() { + synchronized (this) { + if (mLooper == null) { + HandlerThread thread = new HandlerThread(TAG); + thread.start(); + return thread.getLooper(); + } + return mLooper; + } + } + public PowerStatsService(Context context) { this(context, new Injector()); } @@ -260,9 +275,7 @@ public class PowerStatsService extends SystemService { private final Handler mHandler; LocalService() { - HandlerThread thread = new HandlerThread(TAG); - thread.start(); - mHandler = new Handler(thread.getLooper()); + mHandler = new Handler(getLooper()); } diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index b1cc51768754..7f50cd6ddb4f 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -28,18 +28,11 @@ import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.net.NetworkIdentity.OEM_PAID; import static android.net.NetworkIdentity.OEM_PRIVATE; -import static android.net.NetworkStats.DEFAULT_NETWORK_ALL; -import static android.net.NetworkStats.METERED_ALL; import static android.net.NetworkStats.METERED_YES; -import static android.net.NetworkStats.ROAMING_ALL; import static android.net.NetworkTemplate.MATCH_ETHERNET; -import static android.net.NetworkTemplate.MATCH_MOBILE_WILDCARD; -import static android.net.NetworkTemplate.MATCH_WIFI_WILDCARD; -import static android.net.NetworkTemplate.NETWORK_TYPE_ALL; +import static android.net.NetworkTemplate.MATCH_MOBILE; +import static android.net.NetworkTemplate.MATCH_WIFI; import static android.net.NetworkTemplate.OEM_MANAGED_ALL; -import static android.net.NetworkTemplate.buildTemplateMobileWildcard; -import static android.net.NetworkTemplate.buildTemplateMobileWithRatType; -import static android.net.NetworkTemplate.buildTemplateWifiWildcard; import static android.net.NetworkTemplate.getAllCollapsedRatTypes; import static android.os.Debug.getIonHeapsSizeKb; import static android.os.Process.LAST_SHARED_APPLICATION_GID; @@ -1179,9 +1172,10 @@ public class StatsPullAtomService extends SystemService { } case FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED: { final NetworkStats wifiStats = getUidNetworkStatsSnapshotForTemplate( - buildTemplateWifiWildcard(), /*includeTags=*/true); + new NetworkTemplate.Builder(MATCH_WIFI).build(), /*includeTags=*/true); final NetworkStats cellularStats = getUidNetworkStatsSnapshotForTemplate( - buildTemplateMobileWildcard(), /*includeTags=*/true); + new NetworkTemplate.Builder(MATCH_MOBILE) + .setMeteredness(METERED_YES).build(), /*includeTags=*/true); if (wifiStats != null && cellularStats != null) { final NetworkStats stats = wifiStats.add(cellularStats); ret.add(new NetworkStatsExt(sliceNetworkStatsByUidTagAndMetered(stats), @@ -1333,8 +1327,8 @@ public class StatsPullAtomService extends SystemService { @NonNull private List<NetworkStatsExt> getDataUsageBytesTransferSnapshotForOemManaged() { final List<Pair<Integer, Integer>> matchRulesAndTransports = List.of( new Pair(MATCH_ETHERNET, TRANSPORT_ETHERNET), - new Pair(MATCH_MOBILE_WILDCARD, TRANSPORT_CELLULAR), - new Pair(MATCH_WIFI_WILDCARD, TRANSPORT_WIFI) + new Pair(MATCH_MOBILE, TRANSPORT_CELLULAR), + new Pair(MATCH_WIFI, TRANSPORT_WIFI) ); final int[] oemManagedTypes = new int[] {OEM_PAID | OEM_PRIVATE, OEM_PAID, OEM_PRIVATE}; @@ -1343,12 +1337,11 @@ public class StatsPullAtomService extends SystemService { for (Pair<Integer, Integer> ruleAndTransport : matchRulesAndTransports) { final Integer matchRule = ruleAndTransport.first; for (final int oemManaged : oemManagedTypes) { - /* A null subscriberId will set wildcard=true, since we aren't trying to select a - specific ssid or subscriber. */ - final NetworkTemplate template = new NetworkTemplate(matchRule, - /*subscriberId=*/null, /*matchSubscriberIds=*/null, /*networkId=*/null, - METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, - oemManaged); + // Subscriber Ids and Wifi Network Keys will not be set since the purpose is to + // slice statistics of different OEM managed networks among all network types. + // Thus, specifying networks through their identifiers are not needed. + final NetworkTemplate template = new NetworkTemplate.Builder(matchRule) + .setOemManaged(oemManaged).build(); final NetworkStats stats = getUidNetworkStatsSnapshotForTemplate(template, false); final Integer transport = ruleAndTransport.second; if (stats != null) { @@ -1367,10 +1360,18 @@ public class StatsPullAtomService extends SystemService { * Create a snapshot of NetworkStats for a given transport. */ @Nullable private NetworkStats getUidNetworkStatsSnapshotForTransport(int transport) { - final NetworkTemplate template = (transport == TRANSPORT_CELLULAR) - ? NetworkTemplate.buildTemplateMobileWithRatType( - /*subscriptionId=*/null, NETWORK_TYPE_ALL, METERED_YES) - : NetworkTemplate.buildTemplateWifiWildcard(); + NetworkTemplate template = null; + switch (transport) { + case TRANSPORT_CELLULAR: + template = new NetworkTemplate.Builder(MATCH_MOBILE) + .setMeteredness(METERED_YES).build(); + break; + case TRANSPORT_WIFI: + template = new NetworkTemplate.Builder(MATCH_WIFI).build(); + break; + default: + Log.wtf(TAG, "Unexpected transport."); + } return getUidNetworkStatsSnapshotForTemplate(template, /*includeTags=*/false); } @@ -1409,8 +1410,10 @@ public class StatsPullAtomService extends SystemService { final List<NetworkStatsExt> ret = new ArrayList<>(); for (final int ratType : getAllCollapsedRatTypes()) { final NetworkTemplate template = - buildTemplateMobileWithRatType(subInfo.subscriberId, ratType, - METERED_YES); + new NetworkTemplate.Builder(MATCH_MOBILE) + .setSubscriberIds(Set.of(subInfo.subscriberId)) + .setRatType(ratType) + .setMeteredness(METERED_YES).build(); final NetworkStats stats = getUidNetworkStatsSnapshotForTemplate(template, /*includeTags=*/false); if (stats != null) { diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java index e98fa28634a4..fe7416782262 100644 --- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java +++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java @@ -528,12 +528,13 @@ public final class TextClassificationManagerService extends ITextClassifierServi callback.onFailure(); return; } - textClassifierServiceConsumer.accept(serviceState.mService); + consumeServiceNoExceptLocked(textClassifierServiceConsumer, serviceState.mService); } else { serviceState.mPendingRequests.add( new PendingRequest( methodName, - () -> textClassifierServiceConsumer.accept(serviceState.mService), + () -> consumeServiceNoExceptLocked( + textClassifierServiceConsumer, serviceState.mService), callback::onFailure, callback.asBinder(), this, serviceState, @@ -542,6 +543,16 @@ public final class TextClassificationManagerService extends ITextClassifierServi } } + private static void consumeServiceNoExceptLocked( + @NonNull ThrowingConsumer<ITextClassifierService> textClassifierServiceConsumer, + @Nullable ITextClassifierService service) { + try { + textClassifierServiceConsumer.accept(service); + } catch (RuntimeException | Error e) { + Slog.e(LOG_TAG, "Exception when consume textClassifierService: " + e); + } + } + private static ITextClassifierCallback wrap(ITextClassifierCallback orig) { return new CallbackWrapper(orig); } diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index 568e4b8de3ba..e066ca3e9dfc 100755 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -69,6 +69,7 @@ import android.media.tv.TvInputManager; import android.media.tv.TvInputService; import android.media.tv.TvStreamConfig; import android.media.tv.TvTrackInfo; +import android.media.tv.tunerresourcemanager.TunerResourceManager; import android.net.Uri; import android.os.Binder; import android.os.Bundle; @@ -2538,6 +2539,31 @@ public final class TvInputManagerService extends SystemService { } @Override + public int getClientPriority(int useCase, String sessionId) { + final int callingPid = Binder.getCallingPid(); + final long identity = Binder.clearCallingIdentity(); + try { + int clientPid = TvInputManager.UNKNOWN_CLIENT_PID; + if (sessionId != null) { + synchronized (mLock) { + try { + clientPid = getClientPidLocked(sessionId); + } catch (ClientPidNotFoundException e) { + Slog.e(TAG, "error in getClientPriority", e); + } + } + } else { + clientPid = callingPid; + } + TunerResourceManager trm = (TunerResourceManager) + mContext.getSystemService(Context.TV_TUNER_RESOURCE_MGR_SERVICE); + return trm.getClientPriority(useCase, clientPid); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override public List<TunedInfo> getCurrentTunedInfos(@UserIdInt int userId) { if (mContext.checkCallingPermission(android.Manifest.permission.ACCESS_TUNED_INFO) != PackageManager.PERMISSION_GRANTED) { @@ -2585,9 +2611,9 @@ public final class TvInputManagerService extends SystemService { @GuardedBy("mLock") private int getClientPidLocked(String sessionId) - throws IllegalStateException { + throws ClientPidNotFoundException { if (mSessionIdToSessionStateMap.get(sessionId) == null) { - throw new IllegalStateException("Client Pid not found with sessionId " + throw new ClientPidNotFoundException("Client Pid not found with sessionId " + sessionId); } return mSessionIdToSessionStateMap.get(sessionId).callingPid; diff --git a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java index a2bf2fe7df5e..a4732c1a0038 100644 --- a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java +++ b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java @@ -906,6 +906,94 @@ public class TvIAppManagerService extends SystemService { } @Override + public void notifyVideoAvailable(IBinder sessionToken, int userId) { + final int callingUid = Binder.getCallingUid(); + final int callingPid = Binder.getCallingPid(); + final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId, + "notifyVideoAvailable"); + final long identity = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + try { + SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, + resolvedUserId); + getSessionLocked(sessionState).notifyVideoAvailable(); + } catch (RemoteException | SessionNotFoundException e) { + Slogf.e(TAG, "error in notifyVideoAvailable", e); + } + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public void notifyVideoUnavailable(IBinder sessionToken, int reason, int userId) { + final int callingUid = Binder.getCallingUid(); + final int callingPid = Binder.getCallingPid(); + final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId, + "notifyVideoUnavailable"); + final long identity = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + try { + SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, + resolvedUserId); + getSessionLocked(sessionState).notifyVideoUnavailable(reason); + } catch (RemoteException | SessionNotFoundException e) { + Slogf.e(TAG, "error in notifyVideoUnavailable", e); + } + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public void notifyContentAllowed(IBinder sessionToken, int userId) { + final int callingUid = Binder.getCallingUid(); + final int callingPid = Binder.getCallingPid(); + final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId, + "notifyContentAllowed"); + final long identity = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + try { + SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, + resolvedUserId); + getSessionLocked(sessionState).notifyContentAllowed(); + } catch (RemoteException | SessionNotFoundException e) { + Slogf.e(TAG, "error in notifyContentAllowed", e); + } + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public void notifyContentBlocked(IBinder sessionToken, String rating, int userId) { + final int callingUid = Binder.getCallingUid(); + final int callingPid = Binder.getCallingPid(); + final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId, + "notifyContentBlocked"); + final long identity = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + try { + SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, + resolvedUserId); + getSessionLocked(sessionState).notifyContentBlocked(rating); + } catch (RemoteException | SessionNotFoundException e) { + Slogf.e(TAG, "error in notifyContentBlocked", e); + } + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override public void startIApp(IBinder sessionToken, int userId) { if (DEBUG) { Slogf.d(TAG, "BinderService#start(userId=%d)", userId); diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java index 63f4c68b11f6..af705d597af2 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java @@ -289,6 +289,23 @@ public class TunerResourceManagerService extends SystemService implements IBinde } @Override + public boolean transferOwner(int resourceType, int currentOwnerId, int newOwnerId) { + enforceTunerAccessPermission("transferOwner"); + enforceTrmAccessPermission("transferOwner"); + synchronized (mLock) { + if (!checkClientExists(currentOwnerId)) { + Slog.e(TAG, "currentOwnerId:" + currentOwnerId + " does not exit"); + return false; + } + if (!checkClientExists(newOwnerId)) { + Slog.e(TAG, "newOwnerId:" + newOwnerId + " does not exit"); + return false; + } + return transferOwnerInternal(resourceType, currentOwnerId, newOwnerId); + } + } + + @Override public boolean requestDemux(@NonNull TunerDemuxRequest request, @NonNull int[] demuxHandle) throws RemoteException { enforceTunerAccessPermission("requestDemux"); @@ -388,7 +405,11 @@ public class TunerResourceManagerService extends SystemService implements IBinde if (fe == null) { throw new RemoteException("Releasing frontend does not exist."); } - if (fe.getOwnerClientId() != clientId) { + int ownerClientId = fe.getOwnerClientId(); + ClientProfile ownerProfile = getClientProfile(ownerClientId); + if (ownerClientId != clientId + && (ownerProfile != null + && !ownerProfile.getShareFeClientIds().contains(clientId))) { throw new RemoteException( "Client is not the current owner of the releasing fe."); } @@ -619,6 +640,21 @@ public class TunerResourceManagerService extends SystemService implements IBinde } } + @Override + public int getClientPriority(int useCase, int pid) throws RemoteException { + enforceTrmAccessPermission("getClientPriority"); + synchronized (mLock) { + return TunerResourceManagerService.this.getClientPriority( + useCase, checkIsForeground(pid)); + } + } + @Override + public int getConfigPriority(int useCase, boolean isForeground) throws RemoteException { + enforceTrmAccessPermission("getConfigPriority"); + synchronized (mLock) { + return TunerResourceManagerService.this.getClientPriority(useCase, isForeground); + } + } } /** @@ -976,6 +1012,83 @@ public class TunerResourceManagerService extends SystemService implements IBinde getClientProfile(targetClientId).shareFrontend(selfClientId); } + private boolean transferFeOwner(int currentOwnerId, int newOwnerId) { + ClientProfile currentOwnerProfile = getClientProfile(currentOwnerId); + ClientProfile newOwnerProfile = getClientProfile(newOwnerId); + // change the owner of all the inUse frontend + newOwnerProfile.shareFrontend(currentOwnerId); + currentOwnerProfile.stopSharingFrontend(newOwnerId); + for (int inUseHandle : newOwnerProfile.getInUseFrontendHandles()) { + getFrontendResource(inUseHandle).setOwner(newOwnerId); + } + // double check there is no other resources tied to the previous owner + for (int inUseHandle : currentOwnerProfile.getInUseFrontendHandles()) { + int ownerId = getFrontendResource(inUseHandle).getOwnerClientId(); + if (ownerId != newOwnerId) { + Slog.e(TAG, "something is wrong in transferFeOwner:" + inUseHandle + + ", " + ownerId + ", " + newOwnerId); + return false; + } + } + return true; + } + + private boolean transferFeCiCamOwner(int currentOwnerId, int newOwnerId) { + ClientProfile currentOwnerProfile = getClientProfile(currentOwnerId); + ClientProfile newOwnerProfile = getClientProfile(newOwnerId); + + // link ciCamId to the new profile + int ciCamId = currentOwnerProfile.getInUseCiCamId(); + newOwnerProfile.useCiCam(ciCamId); + + // set the new owner Id + CiCamResource ciCam = getCiCamResource(ciCamId); + ciCam.setOwner(newOwnerId); + + // unlink cicam resource from the original owner profile + currentOwnerProfile.releaseCiCam(); + return true; + } + + private boolean transferLnbOwner(int currentOwnerId, int newOwnerId) { + ClientProfile currentOwnerProfile = getClientProfile(currentOwnerId); + ClientProfile newOwnerProfile = getClientProfile(newOwnerId); + + Set<Integer> inUseLnbHandles = new HashSet<>(); + for (Integer lnbHandle : currentOwnerProfile.getInUseLnbHandles()) { + // link lnb handle to the new profile + newOwnerProfile.useLnb(lnbHandle); + + // set new owner Id + LnbResource lnb = getLnbResource(lnbHandle); + lnb.setOwner(newOwnerId); + + inUseLnbHandles.add(lnbHandle); + } + + // unlink lnb handles from the original owner + for (Integer lnbHandle : inUseLnbHandles) { + currentOwnerProfile.releaseLnb(lnbHandle); + } + + return true; + } + + @VisibleForTesting + protected boolean transferOwnerInternal(int resourceType, int currentOwnerId, int newOwnerId) { + switch (resourceType) { + case TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND: + return transferFeOwner(currentOwnerId, newOwnerId); + case TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM: + return transferFeCiCamOwner(currentOwnerId, newOwnerId); + case TunerResourceManager.TUNER_RESOURCE_TYPE_LNB: + return transferLnbOwner(currentOwnerId, newOwnerId); + default: + Slog.e(TAG, "transferOwnerInternal. unsupported resourceType: " + resourceType); + return false; + } + } + @VisibleForTesting protected boolean requestLnbInternal(TunerLnbRequest request, int[] lnbHandle) { if (DEBUG) { diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java index bd8d13b87125..6db25b7ed583 100644 --- a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java +++ b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java @@ -20,6 +20,8 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_TEST; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; +import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN; +import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED; import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_ANY; import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK; @@ -44,7 +46,7 @@ import com.android.internal.annotations.VisibleForTesting.Visibility; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import com.android.server.vcn.VcnContext; -import java.util.LinkedHashSet; +import java.util.List; import java.util.Set; /** @hide */ @@ -76,7 +78,7 @@ class NetworkPriorityClassifier { public static int calculatePriorityClass( VcnContext vcnContext, UnderlyingNetworkRecord networkRecord, - LinkedHashSet<VcnUnderlyingNetworkTemplate> underlyingNetworkPriorities, + List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, ParcelUuid subscriptionGroup, TelephonySubscriptionSnapshot snapshot, UnderlyingNetworkRecord currentlySelected, @@ -94,7 +96,7 @@ class NetworkPriorityClassifier { } int priorityIndex = 0; - for (VcnUnderlyingNetworkTemplate nwPriority : underlyingNetworkPriorities) { + for (VcnUnderlyingNetworkTemplate nwPriority : underlyingNetworkTemplates) { if (checkMatchesPriorityRule( vcnContext, nwPriority, @@ -122,7 +124,11 @@ class NetworkPriorityClassifier { // TODO: Check Network Quality reported by metric monitors/probers. final NetworkCapabilities caps = networkRecord.networkCapabilities; - if (!networkPriority.allowMetered() && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)) { + + final int meteredMatch = networkPriority.getMetered(); + final boolean isMetered = !caps.hasCapability(NET_CAPABILITY_NOT_METERED); + if (meteredMatch == MATCH_REQUIRED && !isMetered + || meteredMatch == MATCH_FORBIDDEN && isMetered) { return false; } @@ -171,7 +177,8 @@ class NetworkPriorityClassifier { return false; } - if (networkPriority.getSsid() != null && networkPriority.getSsid() != caps.getSsid()) { + if (!networkPriority.getSsids().isEmpty() + && !networkPriority.getSsids().contains(caps.getSsid())) { return false; } @@ -226,26 +233,31 @@ class NetworkPriorityClassifier { .getSystemService(TelephonyManager.class) .createForSubscriptionId(subId); - if (!networkPriority.getAllowedOperatorPlmnIds().isEmpty()) { + if (!networkPriority.getOperatorPlmnIds().isEmpty()) { final String plmnId = subIdSpecificTelephonyMgr.getNetworkOperator(); - if (!networkPriority.getAllowedOperatorPlmnIds().contains(plmnId)) { + if (!networkPriority.getOperatorPlmnIds().contains(plmnId)) { return false; } } - if (!networkPriority.getAllowedSpecificCarrierIds().isEmpty()) { + if (!networkPriority.getSimSpecificCarrierIds().isEmpty()) { final int carrierId = subIdSpecificTelephonyMgr.getSimSpecificCarrierId(); - if (!networkPriority.getAllowedSpecificCarrierIds().contains(carrierId)) { + if (!networkPriority.getSimSpecificCarrierIds().contains(carrierId)) { return false; } } - if (!networkPriority.allowRoaming() && !caps.hasCapability(NET_CAPABILITY_NOT_ROAMING)) { + final int roamingMatch = networkPriority.getRoaming(); + final boolean isRoaming = !caps.hasCapability(NET_CAPABILITY_NOT_ROAMING); + if (roamingMatch == MATCH_REQUIRED && !isRoaming + || roamingMatch == MATCH_FORBIDDEN && isRoaming) { return false; } - if (networkPriority.requireOpportunistic()) { - if (!isOpportunistic(snapshot, caps.getSubscriptionIds())) { + final int opportunisticMatch = networkPriority.getOpportunistic(); + final boolean isOpportunistic = isOpportunistic(snapshot, caps.getSubscriptionIds()); + if (opportunisticMatch == MATCH_REQUIRED) { + if (!isOpportunistic) { return false; } @@ -265,6 +277,8 @@ class NetworkPriorityClassifier { .contains(SubscriptionManager.getActiveDataSubscriptionId())) { return false; } + } else if (opportunisticMatch == MATCH_FORBIDDEN && !isOpportunistic) { + return false; } return true; diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java index df2f0d58565e..c0488b18cb65 100644 --- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java +++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java @@ -32,7 +32,7 @@ import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscription import com.android.server.vcn.VcnContext; import java.util.Comparator; -import java.util.LinkedHashSet; +import java.util.List; import java.util.Objects; /** @@ -77,7 +77,7 @@ public class UnderlyingNetworkRecord { static Comparator<UnderlyingNetworkRecord> getComparator( VcnContext vcnContext, - LinkedHashSet<VcnUnderlyingNetworkTemplate> underlyingNetworkPriorities, + List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, ParcelUuid subscriptionGroup, TelephonySubscriptionSnapshot snapshot, UnderlyingNetworkRecord currentlySelected, @@ -87,7 +87,7 @@ public class UnderlyingNetworkRecord { NetworkPriorityClassifier.calculatePriorityClass( vcnContext, left, - underlyingNetworkPriorities, + underlyingNetworkTemplates, subscriptionGroup, snapshot, currentlySelected, @@ -96,7 +96,7 @@ public class UnderlyingNetworkRecord { NetworkPriorityClassifier.calculatePriorityClass( vcnContext, right, - underlyingNetworkPriorities, + underlyingNetworkTemplates, subscriptionGroup, snapshot, currentlySelected, @@ -133,7 +133,7 @@ public class UnderlyingNetworkRecord { void dump( VcnContext vcnContext, IndentingPrintWriter pw, - LinkedHashSet<VcnUnderlyingNetworkTemplate> underlyingNetworkPriorities, + List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, ParcelUuid subscriptionGroup, TelephonySubscriptionSnapshot snapshot, UnderlyingNetworkRecord currentlySelected, @@ -145,7 +145,7 @@ public class UnderlyingNetworkRecord { NetworkPriorityClassifier.calculatePriorityClass( vcnContext, this, - underlyingNetworkPriorities, + underlyingNetworkTemplates, subscriptionGroup, snapshot, currentlySelected, diff --git a/services/core/java/com/android/server/vibrator/ClippingAmplitudeAndFrequencyAdapter.java b/services/core/java/com/android/server/vibrator/ClippingAmplitudeAndFrequencyAdapter.java index 0690d3be3db1..8189e74f922c 100644 --- a/services/core/java/com/android/server/vibrator/ClippingAmplitudeAndFrequencyAdapter.java +++ b/services/core/java/com/android/server/vibrator/ClippingAmplitudeAndFrequencyAdapter.java @@ -21,18 +21,16 @@ import android.os.vibrator.RampSegment; import android.os.vibrator.StepSegment; import android.os.vibrator.VibrationEffectSegment; import android.util.MathUtils; +import android.util.Range; import java.util.List; /** - * Adapter that clips frequency values to {@link VibratorInfo#getFrequencyRange()} and + * Adapter that clips frequency values to {@link VibratorInfo#getFrequencyRangeHz()} and * amplitude values to respective {@link VibratorInfo#getMaxAmplitude}. * - * <p>Devices with no frequency control will collapse all frequencies to zero and leave - * amplitudes unchanged. - * - * <p>The frequency value returned in segments will be absolute, converted with - * {@link VibratorInfo#getAbsoluteFrequency(float)}. + * <p>Devices with no frequency control will collapse all frequencies to the resonant frequency and + * leave amplitudes unchanged. */ final class ClippingAmplitudeAndFrequencyAdapter implements VibrationEffectAdapters.SegmentsAdapter<VibratorInfo> { @@ -52,29 +50,39 @@ final class ClippingAmplitudeAndFrequencyAdapter } private StepSegment apply(StepSegment segment, VibratorInfo info) { - float clampedFrequency = clampFrequency(info, segment.getFrequency()); + float clampedFrequency = clampFrequency(info, segment.getFrequencyHz()); return new StepSegment( clampAmplitude(info, clampedFrequency, segment.getAmplitude()), - info.getAbsoluteFrequency(clampedFrequency), + clampedFrequency, (int) segment.getDuration()); } private RampSegment apply(RampSegment segment, VibratorInfo info) { - float clampedStartFrequency = clampFrequency(info, segment.getStartFrequency()); - float clampedEndFrequency = clampFrequency(info, segment.getEndFrequency()); + float clampedStartFrequency = clampFrequency(info, segment.getStartFrequencyHz()); + float clampedEndFrequency = clampFrequency(info, segment.getEndFrequencyHz()); return new RampSegment( clampAmplitude(info, clampedStartFrequency, segment.getStartAmplitude()), clampAmplitude(info, clampedEndFrequency, segment.getEndAmplitude()), - info.getAbsoluteFrequency(clampedStartFrequency), - info.getAbsoluteFrequency(clampedEndFrequency), + clampedStartFrequency, + clampedEndFrequency, (int) segment.getDuration()); } - private float clampFrequency(VibratorInfo info, float frequency) { - return info.getFrequencyRange().clamp(frequency); + private float clampFrequency(VibratorInfo info, float frequencyHz) { + Range<Float> frequencyRangeHz = info.getFrequencyRangeHz(); + if (frequencyHz == 0 || frequencyRangeHz == null) { + return info.getResonantFrequency(); + } + return frequencyRangeHz.clamp(frequencyHz); } - private float clampAmplitude(VibratorInfo info, float frequency, float amplitude) { - return MathUtils.min(amplitude, info.getMaxAmplitude(frequency)); + private float clampAmplitude(VibratorInfo info, float frequencyHz, float amplitude) { + Range<Float> frequencyRangeHz = info.getFrequencyRangeHz(); + if (frequencyRangeHz == null) { + // No frequency range was specified, leave amplitude unchanged, the frequency will be + // clamped to the device's resonant frequency. + return amplitude; + } + return MathUtils.min(amplitude, info.getMaxAmplitude(frequencyHz)); } } diff --git a/services/core/java/com/android/server/vibrator/RampDownAdapter.java b/services/core/java/com/android/server/vibrator/RampDownAdapter.java index e97ed4ce6e7c..8fec162bb587 100644 --- a/services/core/java/com/android/server/vibrator/RampDownAdapter.java +++ b/services/core/java/com/android/server/vibrator/RampDownAdapter.java @@ -90,13 +90,13 @@ final class RampDownAdapter implements VibrationEffectAdapters.SegmentsAdapter<V if (previousSegment instanceof StepSegment) { float previousAmplitude = ((StepSegment) previousSegment).getAmplitude(); - float previousFrequency = ((StepSegment) previousSegment).getFrequency(); + float previousFrequency = ((StepSegment) previousSegment).getFrequencyHz(); replacementSegments = createStepsDown(previousAmplitude, previousFrequency, offDuration); } else if (previousSegment instanceof RampSegment) { float previousAmplitude = ((RampSegment) previousSegment).getEndAmplitude(); - float previousFrequency = ((RampSegment) previousSegment).getEndFrequency(); + float previousFrequency = ((RampSegment) previousSegment).getEndFrequencyHz(); if (offDuration <= mRampDownDuration) { // Replace the zero amplitude segment with a ramp down of same duration, to @@ -177,12 +177,12 @@ final class RampDownAdapter implements VibrationEffectAdapters.SegmentsAdapter<V repeatIndex++; if (lastSegment instanceof StepSegment) { float previousAmplitude = ((StepSegment) lastSegment).getAmplitude(); - float previousFrequency = ((StepSegment) lastSegment).getFrequency(); + float previousFrequency = ((StepSegment) lastSegment).getFrequencyHz(); segments.addAll(createStepsDown(previousAmplitude, previousFrequency, Math.min(offDuration, mRampDownDuration))); } else if (lastSegment instanceof RampSegment) { float previousAmplitude = ((RampSegment) lastSegment).getEndAmplitude(); - float previousFrequency = ((RampSegment) lastSegment).getEndFrequency(); + float previousFrequency = ((RampSegment) lastSegment).getEndFrequencyHz(); segments.add(createRampDown(previousAmplitude, previousFrequency, Math.min(offDuration, mRampDownDuration))); } @@ -214,10 +214,10 @@ final class RampDownAdapter implements VibrationEffectAdapters.SegmentsAdapter<V if (segment instanceof RampSegment) { RampSegment ramp = (RampSegment) segment; return new RampSegment(ramp.getStartAmplitude(), ramp.getEndAmplitude(), - ramp.getStartFrequency(), ramp.getEndFrequency(), (int) newDuration); + ramp.getStartFrequencyHz(), ramp.getEndFrequencyHz(), (int) newDuration); } else if (segment instanceof StepSegment) { StepSegment step = (StepSegment) segment; - return new StepSegment(step.getAmplitude(), step.getFrequency(), (int) newDuration); + return new StepSegment(step.getAmplitude(), step.getFrequencyHz(), (int) newDuration); } return segment; } diff --git a/services/core/java/com/android/server/vibrator/RampToStepAdapter.java b/services/core/java/com/android/server/vibrator/RampToStepAdapter.java index 64624a28c5da..c592a7072407 100644 --- a/services/core/java/com/android/server/vibrator/RampToStepAdapter.java +++ b/services/core/java/com/android/server/vibrator/RampToStepAdapter.java @@ -21,6 +21,7 @@ import android.os.VibratorInfo; import android.os.vibrator.RampSegment; import android.os.vibrator.StepSegment; import android.os.vibrator.VibrationEffectSegment; +import android.util.MathUtils; import java.util.ArrayList; import java.util.Arrays; @@ -52,7 +53,7 @@ final class RampToStepAdapter implements VibrationEffectAdapters.SegmentsAdapter if (!(segment instanceof RampSegment)) { continue; } - List<StepSegment> steps = apply((RampSegment) segment); + List<StepSegment> steps = apply(info, (RampSegment) segment); segments.remove(i); segments.addAll(i, steps); int addedSegments = steps.size() - 1; @@ -65,11 +66,12 @@ final class RampToStepAdapter implements VibrationEffectAdapters.SegmentsAdapter return repeatIndex; } - private List<StepSegment> apply(RampSegment ramp) { + private List<StepSegment> apply(VibratorInfo info, RampSegment ramp) { if (Float.compare(ramp.getStartAmplitude(), ramp.getEndAmplitude()) == 0) { // Amplitude is the same, so return a single step to simulate this ramp. return Arrays.asList( - new StepSegment(ramp.getStartAmplitude(), ramp.getStartFrequency(), + new StepSegment(ramp.getStartAmplitude(), + fillEmptyFrequency(info, ramp.getStartFrequencyHz()), (int) ramp.getDuration())); } @@ -77,17 +79,21 @@ final class RampToStepAdapter implements VibrationEffectAdapters.SegmentsAdapter int stepCount = (int) (ramp.getDuration() + mStepDuration - 1) / mStepDuration; for (int i = 0; i < stepCount - 1; i++) { float pos = (float) i / stepCount; + // Fill zero frequency values with the device resonant frequency before interpolating. + float startFrequencyHz = fillEmptyFrequency(info, ramp.getStartFrequencyHz()); + float endFrequencyHz = fillEmptyFrequency(info, ramp.getEndFrequencyHz()); steps.add(new StepSegment( - interpolate(ramp.getStartAmplitude(), ramp.getEndAmplitude(), pos), - interpolate(ramp.getStartFrequency(), ramp.getEndFrequency(), pos), + MathUtils.lerp(ramp.getStartAmplitude(), ramp.getEndAmplitude(), pos), + MathUtils.lerp(startFrequencyHz, endFrequencyHz, pos), mStepDuration)); } int duration = (int) ramp.getDuration() - mStepDuration * (stepCount - 1); - steps.add(new StepSegment(ramp.getEndAmplitude(), ramp.getEndFrequency(), duration)); + float endFrequencyHz = fillEmptyFrequency(info, ramp.getEndFrequencyHz()); + steps.add(new StepSegment(ramp.getEndAmplitude(), endFrequencyHz, duration)); return steps; } - private static float interpolate(float start, float end, float position) { - return start + position * (end - start); + private static float fillEmptyFrequency(VibratorInfo info, float frequencyHz) { + return frequencyHz == 0 ? info.getResonantFrequency() : frequencyHz; } } diff --git a/services/core/java/com/android/server/vibrator/StepToRampAdapter.java b/services/core/java/com/android/server/vibrator/StepToRampAdapter.java index 6f5adac33ae6..5ace3896f387 100644 --- a/services/core/java/com/android/server/vibrator/StepToRampAdapter.java +++ b/services/core/java/com/android/server/vibrator/StepToRampAdapter.java @@ -21,6 +21,7 @@ import android.os.VibratorInfo; import android.os.vibrator.RampSegment; import android.os.vibrator.StepSegment; import android.os.vibrator.VibrationEffectSegment; +import android.util.MathUtils; import java.util.ArrayList; import java.util.List; @@ -41,18 +42,18 @@ final class StepToRampAdapter implements VibrationEffectAdapters.SegmentsAdapter // The vibrator does not have PWLE capability, so keep the segments unchanged. return repeatIndex; } - convertStepsToRamps(segments); + convertStepsToRamps(info, segments); repeatIndex = splitLongRampSegments(info, segments, repeatIndex); return repeatIndex; } - private void convertStepsToRamps(List<VibrationEffectSegment> segments) { + private void convertStepsToRamps(VibratorInfo info, List<VibrationEffectSegment> segments) { int segmentCount = segments.size(); // Convert steps that require frequency control to ramps. for (int i = 0; i < segmentCount; i++) { VibrationEffectSegment segment = segments.get(i); - if (isStep(segment) && ((StepSegment) segment).getFrequency() != 0) { - segments.set(i, convertStepToRamp((StepSegment) segment)); + if (isStep(segment) && ((StepSegment) segment).getFrequencyHz() != 0) { + segments.set(i, convertStepToRamp(info, (StepSegment) segment)); } } // Convert steps that are next to ramps to also become ramps, so they can be composed @@ -60,10 +61,10 @@ final class StepToRampAdapter implements VibrationEffectAdapters.SegmentsAdapter for (int i = 0; i < segmentCount; i++) { if (segments.get(i) instanceof RampSegment) { for (int j = i - 1; j >= 0 && isStep(segments.get(j)); j--) { - segments.set(j, convertStepToRamp((StepSegment) segments.get(j))); + segments.set(j, convertStepToRamp(info, (StepSegment) segments.get(j))); } for (int j = i + 1; j < segmentCount && isStep(segments.get(j)); j++) { - segments.set(j, convertStepToRamp((StepSegment) segments.get(j))); + segments.set(j, convertStepToRamp(info, (StepSegment) segments.get(j))); } } } @@ -92,7 +93,7 @@ final class StepToRampAdapter implements VibrationEffectAdapters.SegmentsAdapter continue; } segments.remove(i); - segments.addAll(i, splitRampSegment(ramp, splits)); + segments.addAll(i, splitRampSegment(info, ramp, splits)); int addedSegments = splits - 1; if (repeatIndex > i) { repeatIndex += addedSegments; @@ -104,31 +105,40 @@ final class StepToRampAdapter implements VibrationEffectAdapters.SegmentsAdapter return repeatIndex; } - private static RampSegment convertStepToRamp(StepSegment segment) { + private static RampSegment convertStepToRamp(VibratorInfo info, StepSegment segment) { + float frequencyHz = fillEmptyFrequency(info, segment.getFrequencyHz()); return new RampSegment(segment.getAmplitude(), segment.getAmplitude(), - segment.getFrequency(), segment.getFrequency(), (int) segment.getDuration()); + frequencyHz, frequencyHz, (int) segment.getDuration()); } - private static List<RampSegment> splitRampSegment(RampSegment ramp, int splits) { + private static List<RampSegment> splitRampSegment(VibratorInfo info, RampSegment ramp, + int splits) { List<RampSegment> ramps = new ArrayList<>(splits); + float startFrequencyHz = fillEmptyFrequency(info, ramp.getStartFrequencyHz()); + float endFrequencyHz = fillEmptyFrequency(info, ramp.getEndFrequencyHz()); long splitDuration = ramp.getDuration() / splits; float previousAmplitude = ramp.getStartAmplitude(); - float previousFrequency = ramp.getStartFrequency(); + float previousFrequency = startFrequencyHz; long accumulatedDuration = 0; for (int i = 1; i < splits; i++) { accumulatedDuration += splitDuration; + float durationRatio = (float) accumulatedDuration / ramp.getDuration(); + float interpolatedFrequency = + MathUtils.lerp(startFrequencyHz, endFrequencyHz, durationRatio); + float interpolatedAmplitude = + MathUtils.lerp(ramp.getStartAmplitude(), ramp.getEndAmplitude(), durationRatio); RampSegment rampSplit = new RampSegment( - previousAmplitude, interpolateAmplitude(ramp, accumulatedDuration), - previousFrequency, interpolateFrequency(ramp, accumulatedDuration), + previousAmplitude, interpolatedAmplitude, + previousFrequency, interpolatedFrequency, (int) splitDuration); ramps.add(rampSplit); previousAmplitude = rampSplit.getEndAmplitude(); - previousFrequency = rampSplit.getEndFrequency(); + previousFrequency = rampSplit.getEndFrequencyHz(); } ramps.add(new RampSegment(previousAmplitude, ramp.getEndAmplitude(), previousFrequency, - ramp.getEndFrequency(), (int) (ramp.getDuration() - accumulatedDuration))); + endFrequencyHz, (int) (ramp.getDuration() - accumulatedDuration))); return ramps; } @@ -137,18 +147,7 @@ final class StepToRampAdapter implements VibrationEffectAdapters.SegmentsAdapter return segment instanceof StepSegment; } - private static float interpolateAmplitude(RampSegment ramp, long duration) { - return interpolate(ramp.getStartAmplitude(), ramp.getEndAmplitude(), duration, - ramp.getDuration()); - } - - private static float interpolateFrequency(RampSegment ramp, long duration) { - return interpolate(ramp.getStartFrequency(), ramp.getEndFrequency(), duration, - ramp.getDuration()); - } - - private static float interpolate(float start, float end, long duration, long totalDuration) { - float position = (float) duration / totalDuration; - return start + position * (end - start); + private static float fillEmptyFrequency(VibratorInfo info, float frequencyHz) { + return frequencyHz == 0 ? info.getResonantFrequency() : frequencyHz; } } diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java index 1d6e1585872d..0c15ee723dd3 100644 --- a/services/core/java/com/android/server/vibrator/Vibration.java +++ b/services/core/java/com/android/server/vibrator/Vibration.java @@ -373,7 +373,7 @@ final class Vibration { final long token = proto.start(fieldId); proto.write(StepSegmentProto.DURATION, segment.getDuration()); proto.write(StepSegmentProto.AMPLITUDE, segment.getAmplitude()); - proto.write(StepSegmentProto.FREQUENCY, segment.getFrequency()); + proto.write(StepSegmentProto.FREQUENCY, segment.getFrequencyHz()); proto.end(token); } @@ -382,8 +382,8 @@ final class Vibration { proto.write(RampSegmentProto.DURATION, segment.getDuration()); proto.write(RampSegmentProto.START_AMPLITUDE, segment.getStartAmplitude()); proto.write(RampSegmentProto.END_AMPLITUDE, segment.getEndAmplitude()); - proto.write(RampSegmentProto.START_FREQUENCY, segment.getStartFrequency()); - proto.write(RampSegmentProto.END_FREQUENCY, segment.getEndFrequency()); + proto.write(RampSegmentProto.START_FREQUENCY, segment.getStartFrequencyHz()); + proto.write(RampSegmentProto.END_FREQUENCY, segment.getEndFrequencyHz()); proto.end(token); } diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java index 1ee115dd28f2..df6ffa2bd009 100644 --- a/services/core/java/com/android/server/vibrator/VibrationSettings.java +++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java @@ -16,13 +16,16 @@ package com.android.server.vibrator; +import static android.os.VibrationAttributes.USAGE_ACCESSIBILITY; import static android.os.VibrationAttributes.USAGE_ALARM; import static android.os.VibrationAttributes.USAGE_COMMUNICATION_REQUEST; import static android.os.VibrationAttributes.USAGE_HARDWARE_FEEDBACK; +import static android.os.VibrationAttributes.USAGE_MEDIA; import static android.os.VibrationAttributes.USAGE_NOTIFICATION; import static android.os.VibrationAttributes.USAGE_PHYSICAL_EMULATION; import static android.os.VibrationAttributes.USAGE_RINGTONE; import static android.os.VibrationAttributes.USAGE_TOUCH; +import static android.os.VibrationAttributes.USAGE_UNKNOWN; import android.annotation.Nullable; import android.app.ActivityManager; @@ -44,8 +47,11 @@ import android.os.UserHandle; import android.os.VibrationAttributes; import android.os.VibrationEffect; import android.os.Vibrator; +import android.os.Vibrator.VibrationIntensity; +import android.os.vibrator.VibrationConfig; import android.provider.Settings; import android.util.SparseArray; +import android.util.SparseIntArray; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; @@ -109,53 +115,31 @@ final class VibrationSettings { private final List<OnVibratorSettingsChanged> mListeners = new ArrayList<>(); private final SparseArray<VibrationEffect> mFallbackEffects; - private final int mRampStepDuration; - private final int mRampDownDuration; + private final VibrationConfig mVibrationConfig; @GuardedBy("mLock") @Nullable - private Vibrator mVibrator; - @GuardedBy("mLock") - @Nullable private AudioManager mAudioManager; @GuardedBy("mLock") private boolean mVibrateInputDevices; @GuardedBy("mLock") - private boolean mVibrateWhenRinging; - @GuardedBy("mLock") - private boolean mApplyRampingRinger; - @GuardedBy("mLock") - private int mHapticFeedbackIntensity; - @GuardedBy("mLock") - private int mHardwareFeedbackIntensity; - @GuardedBy("mLock") - private int mNotificationIntensity; - @GuardedBy("mLock") - private int mRingIntensity; + private SparseIntArray mCurrentVibrationIntensities = new SparseIntArray(); @GuardedBy("mLock") private boolean mBatterySaverMode; VibrationSettings(Context context, Handler handler) { - this(context, handler, - context.getResources().getInteger( - com.android.internal.R.integer.config_vibrationWaveformRampDownDuration), - context.getResources().getInteger( - com.android.internal.R.integer.config_vibrationWaveformRampStepDuration)); + this(context, handler, new VibrationConfig(context.getResources())); } @VisibleForTesting - VibrationSettings(Context context, Handler handler, int rampDownDuration, - int rampStepDuration) { + VibrationSettings(Context context, Handler handler, VibrationConfig config) { mContext = context; + mVibrationConfig = config; mSettingObserver = new SettingsObserver(handler); mUidObserver = new UidObserver(); mUserReceiver = new UserObserver(); - // TODO(b/191150049): move these to vibrator static config file - mRampDownDuration = rampDownDuration; - mRampStepDuration = rampStepDuration; - VibrationEffect clickEffect = createEffectFromResource( com.android.internal.R.array.config_virtualKeyVibePattern); VibrationEffect doubleClickEffect = createEffectFromResource( @@ -179,7 +163,6 @@ final class VibrationSettings { public void onSystemReady() { synchronized (mLock) { - mVibrator = mContext.getSystemService(Vibrator.class); mAudioManager = mContext.getSystemService(AudioManager.class); } try { @@ -214,12 +197,21 @@ final class VibrationSettings { IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED); mContext.registerReceiver(mUserReceiver, filter, Context.RECEIVER_NOT_EXPORTED); + // Listen to all settings that might affect the result of Vibrator.getVibrationIntensity. registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES)); registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_WHEN_RINGING)); registerSettingsObserver(Settings.System.getUriFor(Settings.System.APPLY_RAMPING_RINGER)); + registerSettingsObserver(Settings.System.getUriFor( + Settings.System.HAPTIC_FEEDBACK_ENABLED)); + registerSettingsObserver( + Settings.System.getUriFor(Settings.System.ALARM_VIBRATION_INTENSITY)); registerSettingsObserver( Settings.System.getUriFor(Settings.System.HAPTIC_FEEDBACK_INTENSITY)); registerSettingsObserver( + Settings.System.getUriFor(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY)); + registerSettingsObserver( + Settings.System.getUriFor(Settings.System.MEDIA_VIBRATION_INTENSITY)); + registerSettingsObserver( Settings.System.getUriFor(Settings.System.NOTIFICATION_VIBRATION_INTENSITY)); registerSettingsObserver( Settings.System.getUriFor(Settings.System.RING_VIBRATION_INTENSITY)); @@ -253,7 +245,7 @@ final class VibrationSettings { * devices without PWLE support. */ public int getRampStepDuration() { - return mRampStepDuration; + return mVibrationConfig.getRampStepDurationMs(); } /** @@ -261,7 +253,7 @@ final class VibrationSettings { * when a vibration is cancelled or finished at non-zero amplitude. */ public int getRampDownDuration() { - return mRampDownDuration; + return mVibrationConfig.getRampDownDurationMs(); } /** @@ -270,25 +262,8 @@ final class VibrationSettings { * @param usageHint one of VibrationAttributes.USAGE_* * @return The vibration intensity, one of Vibrator.VIBRATION_INTENSITY_* */ - public int getDefaultIntensity(int usageHint) { - if (usageHint == USAGE_ALARM) { - return Vibrator.VIBRATION_INTENSITY_HIGH; - } - synchronized (mLock) { - if (mVibrator != null) { - switch (usageHint) { - case USAGE_RINGTONE: - return mVibrator.getDefaultRingVibrationIntensity(); - case USAGE_NOTIFICATION: - return mVibrator.getDefaultNotificationVibrationIntensity(); - case USAGE_TOUCH: - case USAGE_HARDWARE_FEEDBACK: - case USAGE_PHYSICAL_EMULATION: - return mVibrator.getDefaultHapticFeedbackIntensity(); - } - } - } - return Vibrator.VIBRATION_INTENSITY_MEDIUM; + public int getDefaultIntensity(@VibrationAttributes.Usage int usageHint) { + return mVibrationConfig.getDefaultVibrationIntensity(usageHint); } /** @@ -297,23 +272,10 @@ final class VibrationSettings { * @param usageHint one of VibrationAttributes.USAGE_* * @return The vibration intensity, one of Vibrator.VIBRATION_INTENSITY_* */ - public int getCurrentIntensity(int usageHint) { + public int getCurrentIntensity(@VibrationAttributes.Usage int usageHint) { + int defaultIntensity = getDefaultIntensity(usageHint); synchronized (mLock) { - switch (usageHint) { - case USAGE_RINGTONE: - return mRingIntensity; - case USAGE_NOTIFICATION: - return mNotificationIntensity; - case USAGE_TOUCH: - return mHapticFeedbackIntensity; - case USAGE_HARDWARE_FEEDBACK: - case USAGE_PHYSICAL_EMULATION: - return mHardwareFeedbackIntensity; - case USAGE_ALARM: - return Vibrator.VIBRATION_INTENSITY_HIGH; - default: - return Vibrator.VIBRATION_INTENSITY_MEDIUM; - } + return mCurrentVibrationIntensities.get(usageHint, defaultIntensity); } } @@ -371,7 +333,7 @@ final class VibrationSettings { * for touch and ringtone usages only. All other usages are allowed by this method. */ @GuardedBy("mLock") - private boolean shouldVibrateForRingerModeLocked(int usageHint) { + private boolean shouldVibrateForRingerModeLocked(@VibrationAttributes.Usage int usageHint) { // If audio manager was not loaded yet then assume most restrictive mode. int ringerMode = (mAudioManager == null) ? AudioManager.RINGER_MODE_SILENT @@ -379,18 +341,9 @@ final class VibrationSettings { switch (usageHint) { case USAGE_TOUCH: - // Touch feedback disabled when phone is on silent mode. - return ringerMode != AudioManager.RINGER_MODE_SILENT; case USAGE_RINGTONE: - switch (ringerMode) { - case AudioManager.RINGER_MODE_SILENT: - return false; - case AudioManager.RINGER_MODE_VIBRATE: - return true; - default: - // Ringtone vibrations also depend on 2 other settings: - return mVibrateWhenRinging || mApplyRampingRinger; - } + // Touch feedback and ringtone disabled when phone is on silent mode. + return ringerMode != AudioManager.RINGER_MODE_SILENT; default: // All other usages ignore ringer mode settings. return true; @@ -401,64 +354,89 @@ final class VibrationSettings { @VisibleForTesting void updateSettings() { synchronized (mLock) { - mVibrateWhenRinging = getSystemSetting(Settings.System.VIBRATE_WHEN_RINGING, 0) != 0; - mApplyRampingRinger = getSystemSetting(Settings.System.APPLY_RAMPING_RINGER, 0) != 0; - mHapticFeedbackIntensity = getSystemSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, - getDefaultIntensity(USAGE_TOUCH)); - mHardwareFeedbackIntensity = getSystemSetting( - Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, - getHardwareFeedbackIntensityWhenSettingIsMissing(mHapticFeedbackIntensity)); - mNotificationIntensity = getSystemSetting( - Settings.System.NOTIFICATION_VIBRATION_INTENSITY, - getDefaultIntensity(USAGE_NOTIFICATION)); - mRingIntensity = getSystemSetting(Settings.System.RING_VIBRATION_INTENSITY, + mVibrateInputDevices = loadSystemSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0) > 0; + + int alarmIntensity = toIntensity( + loadSystemSetting(Settings.System.ALARM_VIBRATION_INTENSITY, -1), + getDefaultIntensity(USAGE_ALARM)); + int defaultHapticFeedbackIntensity = getDefaultIntensity(USAGE_TOUCH); + int hapticFeedbackIntensity = toIntensity( + loadSystemSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, -1), + defaultHapticFeedbackIntensity); + int positiveHapticFeedbackIntensity = toPositiveIntensity( + hapticFeedbackIntensity, defaultHapticFeedbackIntensity); + int hardwareFeedbackIntensity = toIntensity( + loadSystemSetting(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, -1), + positiveHapticFeedbackIntensity); + int mediaIntensity = toIntensity( + loadSystemSetting(Settings.System.MEDIA_VIBRATION_INTENSITY, -1), + getDefaultIntensity(USAGE_MEDIA)); + int defaultNotificationIntensity = getDefaultIntensity(USAGE_NOTIFICATION); + int notificationIntensity = toIntensity( + loadSystemSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, -1), + defaultNotificationIntensity); + int positiveNotificationIntensity = toPositiveIntensity( + notificationIntensity, defaultNotificationIntensity); + int ringIntensity = toIntensity( + loadSystemSetting(Settings.System.RING_VIBRATION_INTENSITY, -1), getDefaultIntensity(USAGE_RINGTONE)); - mVibrateInputDevices = getSystemSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0) > 0; - } - notifyListeners(); - } - /** - * Return the value to be used for {@link Settings.System#HARDWARE_HAPTIC_FEEDBACK_INTENSITY} - * when the value was not set by the user. - * - * <p>This should adapt the behavior preceding the introduction of this new setting key, which - * is to apply {@link Settings.System#HAPTIC_FEEDBACK_INTENSITY} unless it's disabled. - */ - private int getHardwareFeedbackIntensityWhenSettingIsMissing(int hapticFeedbackIntensity) { - if (hapticFeedbackIntensity == Vibrator.VIBRATION_INTENSITY_OFF) { - return getDefaultIntensity(USAGE_HARDWARE_FEEDBACK); + + mCurrentVibrationIntensities.clear(); + mCurrentVibrationIntensities.put(USAGE_ALARM, alarmIntensity); + mCurrentVibrationIntensities.put(USAGE_NOTIFICATION, notificationIntensity); + mCurrentVibrationIntensities.put(USAGE_MEDIA, mediaIntensity); + mCurrentVibrationIntensities.put(USAGE_UNKNOWN, mediaIntensity); + + // Communication request is not disabled by the notification setting. + mCurrentVibrationIntensities.put(USAGE_COMMUNICATION_REQUEST, + positiveNotificationIntensity); + + if (!loadBooleanSetting(Settings.System.VIBRATE_WHEN_RINGING) + && !loadBooleanSetting(Settings.System.APPLY_RAMPING_RINGER)) { + // Make sure deprecated boolean setting still disables ringtone vibrations. + mCurrentVibrationIntensities.put(USAGE_RINGTONE, Vibrator.VIBRATION_INTENSITY_OFF); + } else { + mCurrentVibrationIntensities.put(USAGE_RINGTONE, ringIntensity); + } + + // This should adapt the behavior preceding the introduction of this new setting + // key, which is to apply HAPTIC_FEEDBACK_INTENSITY, unless it's disabled. + mCurrentVibrationIntensities.put(USAGE_HARDWARE_FEEDBACK, hardwareFeedbackIntensity); + mCurrentVibrationIntensities.put(USAGE_PHYSICAL_EMULATION, hardwareFeedbackIntensity); + + if (!loadBooleanSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED)) { + // Make sure deprecated boolean setting still disables touch vibrations. + mCurrentVibrationIntensities.put(USAGE_TOUCH, Vibrator.VIBRATION_INTENSITY_OFF); + } else { + mCurrentVibrationIntensities.put(USAGE_TOUCH, hapticFeedbackIntensity); + } + + // A11y is not disabled by any haptic feedback setting. + mCurrentVibrationIntensities.put(USAGE_ACCESSIBILITY, positiveHapticFeedbackIntensity); } - return hapticFeedbackIntensity; + notifyListeners(); } @Override public String toString() { synchronized (mLock) { + StringBuilder vibrationIntensitiesString = new StringBuilder("{"); + for (int i = 0; i < mCurrentVibrationIntensities.size(); i++) { + int usage = mCurrentVibrationIntensities.keyAt(i); + int intensity = mCurrentVibrationIntensities.valueAt(i); + vibrationIntensitiesString.append(VibrationAttributes.usageToString(usage)) + .append("=(").append(intensityToString(intensity)) + .append(",default:").append(intensityToString(getDefaultIntensity(usage))) + .append("), "); + } + vibrationIntensitiesString.append('}'); return "VibrationSettings{" - + "mVibrateInputDevices=" + mVibrateInputDevices - + ", mVibrateWhenRinging=" + mVibrateWhenRinging - + ", mApplyRampingRinger=" + mApplyRampingRinger + + "mVibratorConfig=" + mVibrationConfig + + ", mVibrateInputDevices=" + mVibrateInputDevices + ", mBatterySaverMode=" + mBatterySaverMode + ", mProcStatesCache=" + mUidObserver.mProcStatesCache - + ", mHapticChannelMaxVibrationAmplitude=" - + getHapticChannelMaxVibrationAmplitude() - + ", mRampStepDuration=" + mRampStepDuration - + ", mRampDownDuration=" + mRampDownDuration - + ", mHardwareHapticFeedbackIntensity=" - + intensityToString(getCurrentIntensity(USAGE_HARDWARE_FEEDBACK)) - + ", mHapticFeedbackIntensity=" - + intensityToString(getCurrentIntensity(USAGE_TOUCH)) - + ", mHapticFeedbackDefaultIntensity=" - + intensityToString(getDefaultIntensity(USAGE_TOUCH)) - + ", mNotificationIntensity=" - + intensityToString(getCurrentIntensity(USAGE_NOTIFICATION)) - + ", mNotificationDefaultIntensity=" - + intensityToString(getDefaultIntensity(USAGE_NOTIFICATION)) - + ", mRingIntensity=" - + intensityToString(getCurrentIntensity(USAGE_RINGTONE)) - + ", mRingDefaultIntensity=" - + intensityToString(getDefaultIntensity(USAGE_RINGTONE)) + + ", mVibrationIntensities=" + vibrationIntensitiesString + '}'; } } @@ -466,16 +444,28 @@ final class VibrationSettings { /** Write current settings into given {@link ProtoOutputStream}. */ public void dumpProto(ProtoOutputStream proto) { synchronized (mLock) { + proto.write(VibratorManagerServiceDumpProto.ALARM_INTENSITY, + getCurrentIntensity(USAGE_ALARM)); + proto.write(VibratorManagerServiceDumpProto.ALARM_DEFAULT_INTENSITY, + getDefaultIntensity(USAGE_ALARM)); + proto.write(VibratorManagerServiceDumpProto.HARDWARE_FEEDBACK_INTENSITY, + getCurrentIntensity(USAGE_HARDWARE_FEEDBACK)); + proto.write(VibratorManagerServiceDumpProto.HARDWARE_FEEDBACK_DEFAULT_INTENSITY, + getDefaultIntensity(USAGE_HARDWARE_FEEDBACK)); proto.write(VibratorManagerServiceDumpProto.HAPTIC_FEEDBACK_INTENSITY, - mHapticFeedbackIntensity); + getCurrentIntensity(USAGE_TOUCH)); proto.write(VibratorManagerServiceDumpProto.HAPTIC_FEEDBACK_DEFAULT_INTENSITY, getDefaultIntensity(USAGE_TOUCH)); + proto.write(VibratorManagerServiceDumpProto.MEDIA_INTENSITY, + getCurrentIntensity(USAGE_MEDIA)); + proto.write(VibratorManagerServiceDumpProto.MEDIA_DEFAULT_INTENSITY, + getDefaultIntensity(USAGE_MEDIA)); proto.write(VibratorManagerServiceDumpProto.NOTIFICATION_INTENSITY, - mNotificationIntensity); + getCurrentIntensity(USAGE_NOTIFICATION)); proto.write(VibratorManagerServiceDumpProto.NOTIFICATION_DEFAULT_INTENSITY, getDefaultIntensity(USAGE_NOTIFICATION)); proto.write(VibratorManagerServiceDumpProto.RING_INTENSITY, - mRingIntensity); + getCurrentIntensity(USAGE_RINGTONE)); proto.write(VibratorManagerServiceDumpProto.RING_DEFAULT_INTENSITY, getDefaultIntensity(USAGE_RINGTONE)); } @@ -506,13 +496,29 @@ final class VibrationSettings { } } - private float getHapticChannelMaxVibrationAmplitude() { - synchronized (mLock) { - return mVibrator == null ? Float.NaN : mVibrator.getHapticChannelMaximumAmplitude(); + @VibrationIntensity + private int toPositiveIntensity(int value, @VibrationIntensity int defaultValue) { + if (value == Vibrator.VIBRATION_INTENSITY_OFF) { + return defaultValue; + } + return toIntensity(value, defaultValue); + } + + @VibrationIntensity + private int toIntensity(int value, @VibrationIntensity int defaultValue) { + if ((value < Vibrator.VIBRATION_INTENSITY_OFF) + || (value > Vibrator.VIBRATION_INTENSITY_HIGH)) { + return defaultValue; } + return value; + } + + private boolean loadBooleanSetting(String settingKey) { + return Settings.System.getIntForUser(mContext.getContentResolver(), + settingKey, 0, UserHandle.USER_CURRENT) != 0; } - private int getSystemSetting(String settingName, int defaultValue) { + private int loadSystemSetting(String settingName, int defaultValue) { return Settings.System.getIntForUser(mContext.getContentResolver(), settingName, defaultValue, UserHandle.USER_CURRENT); } diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java index 4a1b95bd4596..47b3e1a2294c 100644 --- a/services/core/java/com/android/server/vibrator/VibratorController.java +++ b/services/core/java/com/android/server/vibrator/VibratorController.java @@ -36,8 +36,6 @@ import libcore.util.NativeAllocationRegistry; /** Controls a single vibrator. */ final class VibratorController { private static final String TAG = "VibratorController"; - // TODO(b/167947076): load suggested range from config - private static final int SUGGESTED_FREQUENCY_SAFE_RANGE = 200; private final Object mLock = new Object(); @@ -74,8 +72,7 @@ final class VibratorController { mNativeWrapper = nativeWrapper; mNativeWrapper.init(vibratorId, listener); VibratorInfo.Builder vibratorInfoBuilder = new VibratorInfo.Builder(vibratorId); - mVibratorInfoLoadSuccessful = mNativeWrapper.getInfo(SUGGESTED_FREQUENCY_SAFE_RANGE, - vibratorInfoBuilder); + mVibratorInfoLoadSuccessful = mNativeWrapper.getInfo(vibratorInfoBuilder); mVibratorInfo = vibratorInfoBuilder.build(); if (!mVibratorInfoLoadSuccessful) { @@ -126,8 +123,7 @@ final class VibratorController { } int vibratorId = mVibratorInfo.getId(); VibratorInfo.Builder vibratorInfoBuilder = new VibratorInfo.Builder(vibratorId); - mVibratorInfoLoadSuccessful = mNativeWrapper.getInfo(SUGGESTED_FREQUENCY_SAFE_RANGE, - vibratorInfoBuilder); + mVibratorInfoLoadSuccessful = mNativeWrapper.getInfo(vibratorInfoBuilder); mVibratorInfo = vibratorInfoBuilder.build(); if (!mVibratorInfoLoadSuccessful) { Slog.e(TAG, "Failed retry of HAL getInfo for vibrator " + vibratorId); @@ -419,8 +415,7 @@ final class VibratorController { private static native void alwaysOnDisable(long nativePtr, long id); - private static native boolean getInfo(long nativePtr, float suggestedFrequencyRange, - VibratorInfo.Builder infoBuilder); + private static native boolean getInfo(long nativePtr, VibratorInfo.Builder infoBuilder); private long mNativePtr = 0; @@ -490,8 +485,8 @@ final class VibratorController { /** * Loads device vibrator metadata and returns true if all metadata was loaded successfully. */ - public boolean getInfo(float suggestedFrequencyRange, VibratorInfo.Builder infoBuilder) { - return getInfo(mNativePtr, suggestedFrequencyRange, infoBuilder); + public boolean getInfo(VibratorInfo.Builder infoBuilder) { + return getInfo(mNativePtr, infoBuilder); } } } diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java index 478e86e5f710..27566b301a6e 100644 --- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java +++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java @@ -1756,17 +1756,23 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } if (hasFrequencies) { frequencies.add(Float.parseFloat(getNextArgRequired())); - } else { - frequencies.add(0f); } } VibrationEffect.WaveformBuilder waveform = VibrationEffect.startWaveform(); for (int i = 0; i < durations.size(); i++) { if (isContinuous) { - waveform.addRamp(amplitudes.get(i), frequencies.get(i), durations.get(i)); + if (hasFrequencies) { + waveform.addRamp(amplitudes.get(i), frequencies.get(i), durations.get(i)); + } else { + waveform.addRamp(amplitudes.get(i), durations.get(i)); + } } else { - waveform.addStep(amplitudes.get(i), frequencies.get(i), durations.get(i)); + if (hasFrequencies) { + waveform.addStep(amplitudes.get(i), frequencies.get(i), durations.get(i)); + } else { + waveform.addStep(amplitudes.get(i), durations.get(i)); + } } } composition.addEffect(waveform.build(repeat), delay); @@ -1865,7 +1871,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { pw.println(" If -c is provided, the waveform is continuous and will ramp"); pw.println(" between values; otherwise each entry is a fixed step."); pw.println(" Duration is in milliseconds; amplitude is a scale of 1-255;"); - pw.println(" frequency is a relative value around resonant frequency 0;"); + pw.println(" frequency is an absolute value in hertz;"); pw.println(" prebaked [-w delay] [-b] <effect-id>"); pw.println(" Vibrates with prebaked effect; ignored when device is on DND "); pw.println(" (Do Not Disturb) mode; touch feedback strength user setting "); diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index 0396a1101c64..8f703c5c7761 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -45,7 +45,6 @@ import static com.android.server.accessibility.AccessibilityTraceProto.WINDOW_MA import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowTracing.WINSCOPE_EXT; -import static com.android.server.wm.utils.RegionUtils.forEachRect; import android.accessibilityservice.AccessibilityTrace; import android.animation.ObjectAnimator; @@ -101,6 +100,7 @@ import com.android.internal.util.TraceBuffer; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.LocalServices; import com.android.server.policy.WindowManagerPolicy; +import com.android.server.wm.AccessibilityWindowsPopulator.AccessibilityWindow; import com.android.server.wm.WindowManagerInternal.AccessibilityControllerInternal; import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks; import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallback; @@ -133,19 +133,22 @@ final class AccessibilityController { private static final Rect EMPTY_RECT = new Rect(); private static final float[] sTempFloats = new float[9]; - private SparseArray<DisplayMagnifier> mDisplayMagnifiers = new SparseArray<>(); - private SparseArray<WindowsForAccessibilityObserver> mWindowsForAccessibilityObserver = + private final SparseArray<DisplayMagnifier> mDisplayMagnifiers = new SparseArray<>(); + private final SparseArray<WindowsForAccessibilityObserver> mWindowsForAccessibilityObserver = new SparseArray<>(); private SparseArray<IBinder> mFocusedWindow = new SparseArray<>(); private int mFocusedDisplay = -1; private boolean mIsImeVisible = false; // Set to true if initializing window population complete. private boolean mAllObserversInitialized = true; + private final AccessibilityWindowsPopulator mAccessibilityWindowsPopulator; AccessibilityController(WindowManagerService service) { mService = service; mAccessibilityTracing = AccessibilityController.getAccessibilityControllerInternal(service); + + mAccessibilityWindowsPopulator = new AccessibilityWindowsPopulator(mService, this); } boolean setMagnificationCallbacks(int displayId, MagnificationCallbacks callbacks) { @@ -209,7 +212,9 @@ final class AccessibilityController { } mWindowsForAccessibilityObserver.remove(displayId); } - observer = new WindowsForAccessibilityObserver(mService, displayId, callback); + mAccessibilityWindowsPopulator.setWindowsNotification(true); + observer = new WindowsForAccessibilityObserver(mService, displayId, callback, + mAccessibilityWindowsPopulator); mWindowsForAccessibilityObserver.put(displayId, observer); mAllObserversInitialized &= observer.mInitialized; } else { @@ -224,6 +229,10 @@ final class AccessibilityController { } } mWindowsForAccessibilityObserver.remove(displayId); + + if (mWindowsForAccessibilityObserver.size() <= 0) { + mAccessibilityWindowsPopulator.setWindowsNotification(false); + } } } @@ -309,11 +318,6 @@ final class AccessibilityController { if (displayMagnifier != null) { displayMagnifier.onDisplaySizeChanged(displayContent); } - final WindowsForAccessibilityObserver windowsForA11yObserver = - mWindowsForAccessibilityObserver.get(displayId); - if (windowsForA11yObserver != null) { - windowsForA11yObserver.scheduleComputeChangedWindows(); - } } void onAppWindowTransition(int displayId, int transition) { @@ -341,11 +345,6 @@ final class AccessibilityController { if (displayMagnifier != null) { displayMagnifier.onWindowTransition(windowState, transition); } - final WindowsForAccessibilityObserver windowsForA11yObserver = - mWindowsForAccessibilityObserver.get(displayId); - if (windowsForA11yObserver != null) { - windowsForA11yObserver.scheduleComputeChangedWindows(); - } } void onWindowFocusChangedNot(int displayId) { @@ -455,6 +454,19 @@ final class AccessibilityController { return null; } + boolean getMagnificationSpecForDisplay(int displayId, MagnificationSpec outSpec) { + if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { + mAccessibilityTracing.logTrace(TAG + ".getMagnificationSpecForDisplay", + FLAGS_MAGNIFICATION_CALLBACK, "displayId=" + displayId); + } + final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); + if (displayMagnifier == null) { + return false; + } + + return displayMagnifier.getMagnificationSpec(outSpec); + } + boolean hasCallbacks() { if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) { @@ -756,6 +768,25 @@ final class AccessibilityController { return spec; } + boolean getMagnificationSpec(MagnificationSpec outSpec) { + if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { + mAccessibilityTracing.logTrace(LOG_TAG + ".getMagnificationSpec", + FLAGS_MAGNIFICATION_CALLBACK); + } + MagnificationSpec spec = mMagnifedViewport.getMagnificationSpec(); + if (spec == null) { + return false; + } + + outSpec.setTo(spec); + if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { + mAccessibilityTracing.logTrace(LOG_TAG + ".getMagnificationSpec", + FLAGS_MAGNIFICATION_CALLBACK, "outSpec={" + outSpec + "}"); + } + + return true; + } + void getMagnificationRegion(Region outMagnificationRegion) { if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { mAccessibilityTracing.logTrace(LOG_TAG + ".getMagnificationRegion", @@ -1403,20 +1434,18 @@ final class AccessibilityController { private static final boolean DEBUG = false; - private final SparseArray<WindowState> mTempWindowStates = new SparseArray<>(); + private final List<AccessibilityWindow> mTempA11yWindows = new ArrayList<>(); private final Set<IBinder> mTempBinderSet = new ArraySet<>(); - private final RectF mTempRectF = new RectF(); - - private final Matrix mTempMatrix = new Matrix(); - private final Point mTempPoint = new Point(); private final Region mTempRegion = new Region(); private final Region mTempRegion1 = new Region(); + private final Region mTempRegion2 = new Region(); + private final WindowManagerService mService; private final Handler mHandler; @@ -1431,10 +1460,11 @@ final class AccessibilityController { // Set to true if initializing window population complete. private boolean mInitialized; + private final AccessibilityWindowsPopulator mA11yWindowsPopulator; WindowsForAccessibilityObserver(WindowManagerService windowManagerService, - int displayId, - WindowsForAccessibilityCallback callback) { + int displayId, WindowsForAccessibilityCallback callback, + AccessibilityWindowsPopulator accessibilityWindowsPopulator) { mService = windowManagerService; mCallback = callback; mDisplayId = displayId; @@ -1443,6 +1473,7 @@ final class AccessibilityController { AccessibilityController.getAccessibilityControllerInternal(mService); mRecurringAccessibilityEventsIntervalMillis = ViewConfiguration .getSendRecurringAccessibilityEventsInterval(); + mA11yWindowsPopulator = accessibilityWindowsPopulator; computeChangedWindows(true); } @@ -1466,52 +1497,6 @@ final class AccessibilityController { } } - boolean shellRootIsAbove(WindowState windowState, ShellRoot shellRoot) { - int wsLayer = mService.mPolicy.getWindowLayerLw(windowState); - int shellLayer = mService.mPolicy.getWindowLayerFromTypeLw(shellRoot.getWindowType(), - true); - return shellLayer >= wsLayer; - } - - int addShellRootsIfAbove(WindowState windowState, ArrayList<ShellRoot> shellRoots, - int shellRootIndex, List<WindowInfo> windows, Set<IBinder> addedWindows, - Region unaccountedSpace, boolean focusedWindowAdded) { - while (shellRootIndex < shellRoots.size() - && shellRootIsAbove(windowState, shellRoots.get(shellRootIndex))) { - ShellRoot shellRoot = shellRoots.get(shellRootIndex); - shellRootIndex++; - final WindowInfo info = shellRoot.getWindowInfo(); - if (info == null) { - continue; - } - - info.layer = addedWindows.size(); - windows.add(info); - addedWindows.add(info.token); - unaccountedSpace.op(info.regionInScreen, unaccountedSpace, - Region.Op.REVERSE_DIFFERENCE); - if (unaccountedSpace.isEmpty() && focusedWindowAdded) { - break; - } - } - return shellRootIndex; - } - - private ArrayList<ShellRoot> getSortedShellRoots( - SparseArray<ShellRoot> originalShellRoots) { - ArrayList<ShellRoot> sortedShellRoots = new ArrayList<>(originalShellRoots.size()); - for (int i = originalShellRoots.size() - 1; i >= 0; --i) { - sortedShellRoots.add(originalShellRoots.valueAt(i)); - } - - sortedShellRoots.sort((left, right) -> - mService.mPolicy.getWindowLayerFromTypeLw(right.getWindowType(), true) - - mService.mPolicy.getWindowLayerFromTypeLw(left.getWindowType(), - true)); - - return sortedShellRoots; - } - /** * Check if windows have changed, and send them to the accessibility subsystem if they have. * @@ -1561,44 +1546,29 @@ final class AccessibilityController { Region unaccountedSpace = mTempRegion; unaccountedSpace.set(0, 0, screenWidth, screenHeight); - final SparseArray<WindowState> visibleWindows = mTempWindowStates; - populateVisibleWindowsOnScreen(visibleWindows); + final List<AccessibilityWindow> visibleWindows = mTempA11yWindows; + mA11yWindowsPopulator.populateVisibleWindowsOnScreenLocked( + mDisplayId, visibleWindows); Set<IBinder> addedWindows = mTempBinderSet; addedWindows.clear(); boolean focusedWindowAdded = false; final int visibleWindowCount = visibleWindows.size(); - ArrayList<TaskFragment> skipRemainingWindowsForTaskFragments = new ArrayList<>(); - - ArrayList<ShellRoot> shellRoots = getSortedShellRoots(dc.mShellRoots); // Iterate until we figure out what is touchable for the entire screen. - int shellRootIndex = 0; - for (int i = visibleWindowCount - 1; i >= 0; i--) { - final WindowState windowState = visibleWindows.valueAt(i); - int prevShellRootIndex = shellRootIndex; - shellRootIndex = addShellRootsIfAbove(windowState, shellRoots, shellRootIndex, - windows, addedWindows, unaccountedSpace, focusedWindowAdded); - - // If a Shell Root was added, it could have accounted for all the space already. - if (shellRootIndex > prevShellRootIndex && unaccountedSpace.isEmpty() - && focusedWindowAdded) { - break; - } - - final Region regionInScreen = new Region(); - computeWindowRegionInScreen(windowState, regionInScreen); - if (windowMattersToAccessibility(windowState, - regionInScreen, unaccountedSpace, - skipRemainingWindowsForTaskFragments)) { - addPopulatedWindowInfo(windowState, regionInScreen, windows, addedWindows); - if (windowMattersToUnaccountedSpaceComputation(windowState)) { - updateUnaccountedSpace(windowState, regionInScreen, unaccountedSpace, - skipRemainingWindowsForTaskFragments); + for (int i = 0; i < visibleWindowCount; i++) { + final AccessibilityWindow a11yWindow = visibleWindows.get(i); + final Region regionInWindow = new Region(); + a11yWindow.getTouchableRegionInWindow(regionInWindow); + if (windowMattersToAccessibility(a11yWindow, regionInWindow, + unaccountedSpace)) { + addPopulatedWindowInfo(a11yWindow, regionInWindow, windows, addedWindows); + if (windowMattersToUnaccountedSpaceComputation(a11yWindow)) { + updateUnaccountedSpace(a11yWindow, unaccountedSpace); } - focusedWindowAdded |= windowState.isFocused(); - } else if (isUntouchableNavigationBar(windowState, mTempRegion1)) { + focusedWindowAdded |= a11yWindow.isFocused(); + } else if (a11yWindow.isUntouchableNavigationBar()) { // If this widow is navigation bar without touchable region, accounting the // region of navigation bar inset because all touch events from this region // would be received by launcher, i.e. this region is a un-touchable one @@ -1647,47 +1617,39 @@ final class AccessibilityController { // Some windows should be excluded from unaccounted space computation, though they still // should be reported - private boolean windowMattersToUnaccountedSpaceComputation(WindowState windowState) { + private boolean windowMattersToUnaccountedSpaceComputation(AccessibilityWindow a11yWindow) { // Do not account space of trusted non-touchable windows, except the split-screen // divider. // If it's not trusted, touch events are not sent to the windows behind it. - if (((windowState.mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) - && (windowState.mAttrs.type != TYPE_DOCK_DIVIDER) - && windowState.isTrustedOverlay()) { + if (((a11yWindow.getFlags() & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) + && (a11yWindow.getType() != TYPE_DOCK_DIVIDER) + && a11yWindow.isTrustedOverlay()) { return false; } - if (windowState.mAttrs.type - == WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) { + if (a11yWindow.getType() == WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) { return false; } return true; } - private boolean windowMattersToAccessibility(WindowState windowState, - Region regionInScreen, Region unaccountedSpace, - ArrayList<TaskFragment> skipRemainingWindowsForTaskFragments) { - final RecentsAnimationController controller = mService.getRecentsAnimationController(); - if (controller != null && controller.shouldIgnoreForAccessibility(windowState)) { + private boolean windowMattersToAccessibility(AccessibilityWindow a11yWindow, + Region regionInScreen, Region unaccountedSpace) { + if (a11yWindow.ignoreRecentsAnimationForAccessibility()) { return false; } - if (windowState.isFocused()) { + if (a11yWindow.isFocused()) { return true; } - // If the window is part of a task that we're finished with - ignore. - final TaskFragment taskFragment = windowState.getTaskFragment(); - if (taskFragment != null - && skipRemainingWindowsForTaskFragments.contains(taskFragment)) { - return false; - } - // Ignore non-touchable windows, except the split-screen divider, which is // occasionally non-touchable but still useful for identifying split-screen - // mode. - if (((windowState.mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) - && (windowState.mAttrs.type != TYPE_DOCK_DIVIDER)) { + // mode and the PIP menu. + if (((a11yWindow.getFlags() + & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) + && (a11yWindow.getType() != TYPE_DOCK_DIVIDER + && !a11yWindow.isPIPMenu())) { return false; } @@ -1697,88 +1659,36 @@ final class AccessibilityController { } // Add windows of certain types not covered by modal windows. - if (isReportedWindowType(windowState.mAttrs.type)) { + if (isReportedWindowType(a11yWindow.getType())) { return true; } return false; } - private void updateUnaccountedSpace(WindowState windowState, Region regionInScreen, - Region unaccountedSpace, - ArrayList<TaskFragment> skipRemainingWindowsForTaskFragments) { - // Account for the space this window takes if the window - // is not an accessibility overlay which does not change - // the reported windows. - unaccountedSpace.op(regionInScreen, unaccountedSpace, - Region.Op.REVERSE_DIFFERENCE); - - // If a window is modal it prevents other windows from being touched - if ((windowState.mAttrs.flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) { - if (!windowState.hasTapExcludeRegion()) { - // Account for all space in the task, whether the windows in it are - // touchable or not. The modal window blocks all touches from the task's - // area. - unaccountedSpace.op(windowState.getDisplayFrame(), unaccountedSpace, - Region.Op.REVERSE_DIFFERENCE); - } else { - // If a window has tap exclude region, we need to account it. - final Region displayRegion = new Region(windowState.getDisplayFrame()); - final Region tapExcludeRegion = new Region(); - windowState.getTapExcludeRegion(tapExcludeRegion); - displayRegion.op(tapExcludeRegion, displayRegion, - Region.Op.REVERSE_DIFFERENCE); - unaccountedSpace.op(displayRegion, unaccountedSpace, - Region.Op.REVERSE_DIFFERENCE); - } - - final TaskFragment taskFragment = windowState.getTaskFragment(); - if (taskFragment != null) { - // If the window is associated with a particular task, we can skip the - // rest of the windows for that task. - skipRemainingWindowsForTaskFragments.add(taskFragment); - } else if (!windowState.hasTapExcludeRegion()) { - // If the window is not associated with a particular task, then it is - // globally modal. In this case we can skip all remaining windows when - // it doesn't has tap exclude region. - unaccountedSpace.setEmpty(); - } - } - - // Account for the space of letterbox. - if (windowState.areAppWindowBoundsLetterboxed()) { - unaccountedSpace.op(getLetterboxBounds(windowState), unaccountedSpace, + private void updateUnaccountedSpace(AccessibilityWindow a11yWindow, + Region unaccountedSpace) { + if (a11yWindow.getType() + != WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) { + // Account for the space this window takes if the window + // is not an accessibility overlay which does not change + // the reported windows. + final Region touchableRegion = mTempRegion2; + a11yWindow.getTouchableRegionInScreen(touchableRegion); + unaccountedSpace.op(touchableRegion, unaccountedSpace, Region.Op.REVERSE_DIFFERENCE); + // Account for the space of letterbox. + final Region letterboxBounds = mTempRegion1; + if (a11yWindow.setLetterBoxBoundsIfNeeded(letterboxBounds)) { + unaccountedSpace.op(letterboxBounds, + unaccountedSpace, Region.Op.REVERSE_DIFFERENCE); + } } } - private void computeWindowRegionInScreen(WindowState windowState, Region outRegion) { - // Get the touchable frame. - Region touchableRegion = mTempRegion1; - windowState.getTouchableRegion(touchableRegion); - - // Map the frame to get what appears on the screen. - Matrix matrix = mTempMatrix; - populateTransformationMatrix(windowState, matrix); - - forEachRect(touchableRegion, rect -> { - // Move to origin as all transforms are captured by the matrix. - RectF windowFrame = mTempRectF; - windowFrame.set(rect); - windowFrame.offset(-windowState.getFrame().left, -windowState.getFrame().top); - - matrix.mapRect(windowFrame); - - // Union all rects. - outRegion.union(new Rect((int) windowFrame.left, (int) windowFrame.top, - (int) windowFrame.right, (int) windowFrame.bottom)); - }); - } - - private static void addPopulatedWindowInfo(WindowState windowState, Region regionInScreen, - List<WindowInfo> out, Set<IBinder> tokenOut) { - final WindowInfo window = windowState.getWindowInfo(); + private static void addPopulatedWindowInfo(AccessibilityWindow a11yWindow, + Region regionInScreen, List<WindowInfo> out, Set<IBinder> tokenOut) { + final WindowInfo window = a11yWindow.getWindowInfo(); window.regionInScreen.set(regionInScreen); window.layer = tokenOut.size(); out.add(window); @@ -1805,23 +1715,6 @@ final class AccessibilityController { && windowType != WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION); } - private void populateVisibleWindowsOnScreen(SparseArray<WindowState> outWindows) { - final List<WindowState> tempWindowStatesList = new ArrayList<>(); - final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId); - if (dc == null) { - return; - } - - dc.forAllWindows(w -> { - if (w.isVisible()) { - tempWindowStatesList.add(w); - } - }, false /* traverseTopToBottom */); - for (int i = 0; i < tempWindowStatesList.size(); i++) { - outWindows.put(i, tempWindowStatesList.get(i)); - } - } - private WindowState getTopFocusWindow() { return mService.mRoot.getTopFocusedDisplayContent().mCurrentFocus; } diff --git a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java new file mode 100644 index 000000000000..f31ae06f62e9 --- /dev/null +++ b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java @@ -0,0 +1,625 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; + +import static com.android.server.wm.utils.RegionUtils.forEachRect; + +import android.annotation.NonNull; +import android.graphics.Matrix; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Region; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.util.Slog; +import android.util.SparseArray; +import android.view.IWindow; +import android.view.InputWindowHandle; +import android.view.MagnificationSpec; +import android.view.WindowInfo; +import android.view.WindowManager; +import android.window.WindowInfosListener; + +import com.android.internal.annotations.GuardedBy; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class is the accessibility windows population adapter. + */ +public final class AccessibilityWindowsPopulator extends WindowInfosListener { + + private static final String TAG = AccessibilityWindowsPopulator.class.getSimpleName(); + // If the surface flinger callback is not coming within in 2 frames time, i.e. about + // 35ms, then assuming the windows become stable. + private static final int SURFACE_FLINGER_CALLBACK_WINDOWS_STABLE_TIMES_MS = 35; + // To avoid the surface flinger callbacks always comes within in 2 frames, then no windows + // are reported to the A11y framework, and the animation duration time is 500ms, so setting + // this value as the max timeout value to force computing changed windows. + private static final int WINDOWS_CHANGED_NOTIFICATION_MAX_DURATION_TIMES_MS = 500; + + private static final float[] sTempFloats = new float[9]; + + private final WindowManagerService mService; + private final AccessibilityController mAccessibilityController; + @GuardedBy("mLock") + private final SparseArray<List<InputWindowHandle>> mInputWindowHandlesOnDisplays = + new SparseArray<>(); + @GuardedBy("mLock") + private final SparseArray<Matrix> mMagnificationSpecInverseMatrix = new SparseArray<>(); + @GuardedBy("mLock") + private final SparseArray<DisplayInfo> mDisplayInfos = new SparseArray<>(); + @GuardedBy("mLock") + private final List<InputWindowHandle> mVisibleWindows = new ArrayList<>(); + @GuardedBy("mLock") + private boolean mWindowsNotificationEnabled = false; + private final Object mLock = new Object(); + private final Handler mHandler; + + AccessibilityWindowsPopulator(WindowManagerService service, + AccessibilityController accessibilityController) { + mService = service; + mAccessibilityController = accessibilityController; + mHandler = new MyHandler(mService.mH.getLooper()); + + register(); + } + + /** + * Gets the visible windows list with the window layer on the specified display. + * + * @param displayId The display. + * @param outWindows The visible windows list. The z-order of each window in the list + * is from the top to bottom. + */ + public void populateVisibleWindowsOnScreenLocked(int displayId, + List<AccessibilityWindow> outWindows) { + List<InputWindowHandle> inputWindowHandles; + final Matrix inverseMatrix = new Matrix(); + final Matrix displayMatrix = new Matrix(); + + synchronized (mLock) { + inputWindowHandles = mInputWindowHandlesOnDisplays.get(displayId); + if (inputWindowHandles == null) { + outWindows.clear(); + + return; + } + inverseMatrix.set(mMagnificationSpecInverseMatrix.get(displayId)); + + final DisplayInfo displayInfo = mDisplayInfos.get(displayId); + if (displayInfo != null) { + displayMatrix.set(displayInfo.mTransform); + } else { + Slog.w(TAG, "The displayInfo of this displayId (" + displayId + ") called " + + "back from the surface fligner is null"); + } + } + + final DisplayContent dc = mService.mRoot.getDisplayContent(displayId); + final ShellRoot shellroot = dc.mShellRoots.get(WindowManager.SHELL_ROOT_LAYER_PIP); + final IBinder pipMenuIBinder = + shellroot != null ? shellroot.getAccessibilityWindowToken() : null; + + for (final InputWindowHandle windowHandle : inputWindowHandles) { + final AccessibilityWindow accessibilityWindow = + AccessibilityWindow.initializeData(mService, windowHandle, inverseMatrix, + pipMenuIBinder, displayMatrix); + + outWindows.add(accessibilityWindow); + } + } + + @Override + public void onWindowInfosChanged(InputWindowHandle[] windowHandles, + DisplayInfo[] displayInfos) { + synchronized (mLock) { + mVisibleWindows.clear(); + for (InputWindowHandle window : windowHandles) { + if (window.visible && window.getWindow() != null) { + mVisibleWindows.add(window); + } + } + + mDisplayInfos.clear(); + for (final DisplayInfo displayInfo : displayInfos) { + mDisplayInfos.put(displayInfo.mDisplayId, displayInfo); + } + + if (mWindowsNotificationEnabled) { + if (!mHandler.hasMessages( + MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_TIMEOUT)) { + mHandler.sendEmptyMessageDelayed( + MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_TIMEOUT, + WINDOWS_CHANGED_NOTIFICATION_MAX_DURATION_TIMES_MS); + } + populateVisibleWindowHandlesAndNotifyWindowsChangeIfNeededLocked(); + } + } + } + + /** + * Sets to notify the accessibilityController to compute changed windows on + * the display after populating the visible windows if the windows reported + * from the surface flinger changes. + * + * @param register {@code true} means starting windows population. + */ + public void setWindowsNotification(boolean register) { + synchronized (mLock) { + if (mWindowsNotificationEnabled == register) { + return; + } + mWindowsNotificationEnabled = register; + if (mWindowsNotificationEnabled) { + populateVisibleWindowHandlesAndNotifyWindowsChangeIfNeededLocked(); + } else { + releaseResources(); + } + } + } + + private void populateVisibleWindowHandlesAndNotifyWindowsChangeIfNeededLocked() { + final SparseArray<List<InputWindowHandle>> tempWindowHandleList = new SparseArray<>(); + + for (final InputWindowHandle windowHandle : mVisibleWindows) { + List<InputWindowHandle> inputWindowHandles = tempWindowHandleList.get( + windowHandle.displayId); + + if (inputWindowHandles == null) { + inputWindowHandles = new ArrayList<>(); + tempWindowHandleList.put(windowHandle.displayId, inputWindowHandles); + generateMagnificationSpecInverseMatrixLocked(windowHandle.displayId); + } + inputWindowHandles.add(windowHandle); + } + + final List<Integer> displayIdsForWindowsChanged = new ArrayList<>(); + + getDisplaysForWindowsChangedLocked(displayIdsForWindowsChanged, tempWindowHandleList, + mInputWindowHandlesOnDisplays); + // Clones all windows from the callback of the surface flinger. + mInputWindowHandlesOnDisplays.clear(); + for (int i = 0; i < tempWindowHandleList.size(); i++) { + final int displayId = tempWindowHandleList.keyAt(i); + mInputWindowHandlesOnDisplays.put(displayId, tempWindowHandleList.get(displayId)); + } + + if (displayIdsForWindowsChanged.size() > 0) { + if (!mHandler.hasMessages(MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED)) { + mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED, + displayIdsForWindowsChanged).sendToTarget(); + } + + return; + } + mHandler.removeMessages(MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_UI_STABLE); + mHandler.sendEmptyMessageDelayed(MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_UI_STABLE, + SURFACE_FLINGER_CALLBACK_WINDOWS_STABLE_TIMES_MS); + } + + private void getDisplaysForWindowsChangedLocked(List<Integer> outDisplayIdsForWindowsChanged, + SparseArray<List<InputWindowHandle>> newWindowsList, + SparseArray<List<InputWindowHandle>> oldWindowsList) { + for (int i = 0; i < newWindowsList.size(); i++) { + final int displayId = newWindowsList.keyAt(i); + final List<InputWindowHandle> newWindows = newWindowsList.get(displayId); + final List<InputWindowHandle> oldWindows = oldWindowsList.get(displayId); + + if (hasWindowsChangedLocked(newWindows, oldWindows)) { + outDisplayIdsForWindowsChanged.add(displayId); + } + } + } + + private boolean hasWindowsChangedLocked(List<InputWindowHandle> newWindows, + List<InputWindowHandle> oldWindows) { + if (oldWindows == null || oldWindows.size() != newWindows.size()) { + return true; + } + + final int windowsCount = newWindows.size(); + // Since we always traverse windows from high to low layer, + // the old and new windows at the same index should be the + // same, otherwise something changed. + for (int i = 0; i < windowsCount; i++) { + final InputWindowHandle newWindow = newWindows.get(i); + final InputWindowHandle oldWindow = oldWindows.get(i); + + if (!newWindow.getWindow().asBinder().equals(oldWindow.getWindow().asBinder())) { + return true; + } + } + + return false; + } + + private void generateMagnificationSpecInverseMatrixLocked(int displayId) { + MagnificationSpec spec = new MagnificationSpec(); + if (!mAccessibilityController.getMagnificationSpecForDisplay(displayId, spec)) { + return; + } + sTempFloats[Matrix.MSCALE_X] = spec.scale; + sTempFloats[Matrix.MSKEW_Y] = 0; + sTempFloats[Matrix.MSKEW_X] = 0; + sTempFloats[Matrix.MSCALE_Y] = spec.scale; + sTempFloats[Matrix.MTRANS_X] = spec.offsetX; + sTempFloats[Matrix.MTRANS_Y] = spec.offsetY; + sTempFloats[Matrix.MPERSP_0] = 0; + sTempFloats[Matrix.MPERSP_1] = 0; + sTempFloats[Matrix.MPERSP_2] = 1; + + final Matrix tempMatrix = new Matrix(); + tempMatrix.setValues(sTempFloats); + + final Matrix inverseMatrix = new Matrix(); + final boolean result = tempMatrix.invert(inverseMatrix); + + if (!result) { + Slog.e(TAG, "Can't inverse the magnification spec matrix with the " + + "magnification spec = " + spec + " on the displayId = " + displayId); + return; + } + mMagnificationSpecInverseMatrix.set(displayId, inverseMatrix); + } + + private void notifyWindowsChanged(@NonNull List<Integer> displayIdsForWindowsChanged) { + mHandler.removeMessages(MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_TIMEOUT); + + for (int i = 0; i < displayIdsForWindowsChanged.size(); i++) { + mAccessibilityController.performComputeChangedWindowsNot( + displayIdsForWindowsChanged.get(i), false); + } + } + + private void forceUpdateWindows() { + final List<Integer> displayIdsForWindowsChanged = new ArrayList<>(); + + synchronized (mLock) { + for (int i = 0; i < mInputWindowHandlesOnDisplays.size(); i++) { + final int displayId = mInputWindowHandlesOnDisplays.keyAt(i); + displayIdsForWindowsChanged.add(displayId); + } + } + notifyWindowsChanged(displayIdsForWindowsChanged); + } + + @GuardedBy("mLock") + private void releaseResources() { + mInputWindowHandlesOnDisplays.clear(); + mMagnificationSpecInverseMatrix.clear(); + mVisibleWindows.clear(); + mDisplayInfos.clear(); + mWindowsNotificationEnabled = false; + mHandler.removeCallbacksAndMessages(null); + } + + private class MyHandler extends Handler { + public static final int MESSAGE_NOTIFY_WINDOWS_CHANGED = 1; + public static final int MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_UI_STABLE = 2; + public static final int MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_TIMEOUT = 3; + + MyHandler(Looper looper) { + super(looper, null, false); + } + + @Override + public void handleMessage(Message message) { + switch (message.what) { + case MESSAGE_NOTIFY_WINDOWS_CHANGED: { + final List<Integer> displayIdsForWindowsChanged = (List<Integer>) message.obj; + notifyWindowsChanged(displayIdsForWindowsChanged); + } break; + + case MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_UI_STABLE: { + forceUpdateWindows(); + } break; + + case MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_TIMEOUT: { + Slog.w(TAG, "Windows change within in 2 frames continuously over 500 ms " + + "and notify windows changed immediately"); + mHandler.removeMessages( + MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_UI_STABLE); + + forceUpdateWindows(); + } break; + } + } + } + + /** + * This class represents information about a window from the + * surface flinger to the accessibility framework. + */ + public static class AccessibilityWindow { + private static final Region TEMP_REGION = new Region(); + private static final RectF TEMP_RECTF = new RectF(); + // Data + private IWindow mWindow; + private int mDisplayId; + private int mFlags; + private int mType; + private int mPrivateFlags; + private boolean mIsPIPMenu; + private boolean mIsFocused; + private boolean mShouldMagnify; + private boolean mIgnoreDuetoRecentsAnimation; + private boolean mIsTrustedOverlay; + private final Region mTouchableRegionInScreen = new Region(); + private final Region mTouchableRegionInWindow = new Region(); + private final Region mLetterBoxBounds = new Region(); + private WindowInfo mWindowInfo; + + /** + * Returns the instance after initializing the internal data. + * @param service The window manager service. + * @param inputWindowHandle The window from the surface flinger. + * @param inverseMatrix The magnification spec inverse matrix. + */ + public static AccessibilityWindow initializeData(WindowManagerService service, + InputWindowHandle inputWindowHandle, Matrix inverseMatrix, IBinder pipIBinder, + Matrix displayMatrix) { + final IWindow window = inputWindowHandle.getWindow(); + final WindowState windowState = window != null ? service.mWindowMap.get( + window.asBinder()) : null; + + final AccessibilityWindow instance = new AccessibilityWindow(); + + instance.mWindow = inputWindowHandle.getWindow(); + instance.mDisplayId = inputWindowHandle.displayId; + instance.mFlags = inputWindowHandle.layoutParamsFlags; + instance.mType = inputWindowHandle.layoutParamsType; + instance.mIsPIPMenu = inputWindowHandle.getWindow().asBinder().equals(pipIBinder); + + // TODO (b/199357848): gets the private flag of the window from other way. + instance.mPrivateFlags = windowState != null ? windowState.mAttrs.privateFlags : 0; + // TODO (b/199358208) : using new way to implement the focused window. + instance.mIsFocused = windowState != null && windowState.isFocused(); + instance.mShouldMagnify = windowState == null || windowState.shouldMagnify(); + + final RecentsAnimationController controller = service.getRecentsAnimationController(); + instance.mIgnoreDuetoRecentsAnimation = windowState != null && controller != null + && controller.shouldIgnoreForAccessibility(windowState); + instance.mIsTrustedOverlay = inputWindowHandle.trustedOverlay; + + // TODO (b/199358388) : gets the letterbox bounds of the window from other way. + if (windowState != null && windowState.areAppWindowBoundsLetterboxed()) { + getLetterBoxBounds(windowState, instance.mLetterBoxBounds); + } + + final Rect windowFrame = new Rect(inputWindowHandle.frameLeft, + inputWindowHandle.frameTop, inputWindowHandle.frameRight, + inputWindowHandle.frameBottom); + getTouchableRegionInWindow(instance.mShouldMagnify, inputWindowHandle.touchableRegion, + instance.mTouchableRegionInWindow, windowFrame, inverseMatrix, displayMatrix); + getUnMagnifiedTouchableRegion(instance.mShouldMagnify, + inputWindowHandle.touchableRegion, instance.mTouchableRegionInScreen, + inverseMatrix, displayMatrix); + instance.mWindowInfo = windowState != null + ? windowState.getWindowInfo() : getWindowInfoForWindowlessWindows(instance); + + return instance; + } + + /** + * Returns the touchable region in the screen. + * @param outRegion The touchable region. + */ + public void getTouchableRegionInScreen(Region outRegion) { + outRegion.set(mTouchableRegionInScreen); + } + + /** + * Returns the touchable region in the window. + * @param outRegion The touchable region. + */ + public void getTouchableRegionInWindow(Region outRegion) { + outRegion.set(mTouchableRegionInWindow); + } + + /** + * @return the layout parameter flag {@link android.view.WindowManager.LayoutParams#flags}. + */ + public int getFlags() { + return mFlags; + } + + /** + * @return the layout parameter type {@link android.view.WindowManager.LayoutParams#type}. + */ + public int getType() { + return mType; + } + + /** + * @return the layout parameter private flag + * {@link android.view.WindowManager.LayoutParams#privateFlags}. + */ + public int getPrivateFlag() { + return mPrivateFlags; + } + + /** + * @return the windowInfo {@link WindowInfo}. + */ + public WindowInfo getWindowInfo() { + return mWindowInfo; + } + + /** + * Gets the letter box bounds if activity bounds are letterboxed + * or letterboxed for display cutout. + * + * @return {@code true} there's a letter box bounds. + */ + public Boolean setLetterBoxBoundsIfNeeded(Region outBounds) { + if (mLetterBoxBounds.isEmpty()) { + return false; + } + + outBounds.set(mLetterBoxBounds); + return true; + } + + /** + * @return true if this window should be magnified. + */ + public boolean shouldMagnify() { + return mShouldMagnify; + } + + /** + * @return true if this window is focused. + */ + public boolean isFocused() { + return mIsFocused; + } + + /** + * @return true if it's running the recent animation but not the target app. + */ + public boolean ignoreRecentsAnimationForAccessibility() { + return mIgnoreDuetoRecentsAnimation; + } + + /** + * @return true if this window is the trusted overlay. + */ + public boolean isTrustedOverlay() { + return mIsTrustedOverlay; + } + + /** + * @return true if this window is the navigation bar with the gesture mode. + */ + public boolean isUntouchableNavigationBar() { + if (mType != WindowManager.LayoutParams.TYPE_NAVIGATION_BAR) { + return false; + } + + return mTouchableRegionInScreen.isEmpty(); + } + + /** + * @return true if this window is PIP menu. + */ + public boolean isPIPMenu() { + return mIsPIPMenu; + } + + private static void getTouchableRegionInWindow(boolean shouldMagnify, Region inRegion, + Region outRegion, Rect frame, Matrix inverseMatrix, Matrix displayMatrix) { + // Some modal windows, like the activity with Theme.dialog, has the full screen + // as its touchable region, but its window frame is smaller than the touchable + // region. The region we report should be the touchable area in the window frame + // for the consistency and match developers expectation. + // So we need to make the intersection between the frame and touchable region to + // obtain the real touch region in the screen. + Region touchRegion = TEMP_REGION; + touchRegion.set(inRegion); + touchRegion.op(frame, Region.Op.INTERSECT); + + getUnMagnifiedTouchableRegion(shouldMagnify, touchRegion, outRegion, inverseMatrix, + displayMatrix); + } + + /** + * Gets the un-magnified touchable region. If this window can be magnified and magnifying, + * we will transform the input touchable region by applying the inverse matrix of the + * magnification spec to get the un-magnified touchable region. + * @param shouldMagnify The window can be magnified. + * @param inRegion The touchable region of this window. + * @param outRegion The un-magnified touchable region of this window. + * @param inverseMatrix The inverse matrix of the magnification spec. + * @param displayMatrix The display transform matrix which takes display coordinates to + * logical display coordinates. + */ + private static void getUnMagnifiedTouchableRegion(boolean shouldMagnify, Region inRegion, + Region outRegion, Matrix inverseMatrix, Matrix displayMatrix) { + if ((!shouldMagnify || inverseMatrix.isIdentity()) && displayMatrix.isIdentity()) { + outRegion.set(inRegion); + return; + } + + forEachRect(inRegion, rect -> { + // Move to origin as all transforms are captured by the matrix. + RectF windowFrame = TEMP_RECTF; + windowFrame.set(rect); + + inverseMatrix.mapRect(windowFrame); + displayMatrix.mapRect(windowFrame); + // Union all rects. + outRegion.union(new Rect((int) windowFrame.left, (int) windowFrame.top, + (int) windowFrame.right, (int) windowFrame.bottom)); + }); + } + + private static WindowInfo getWindowInfoForWindowlessWindows(AccessibilityWindow window) { + WindowInfo windowInfo = WindowInfo.obtain(); + windowInfo.displayId = window.mDisplayId; + windowInfo.type = window.mType; + windowInfo.token = window.mWindow.asBinder(); + windowInfo.hasFlagWatchOutsideTouch = (window.mFlags + & WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH) != 0; + windowInfo.inPictureInPicture = false; + + // There only are two windowless windows now, one is split window, and the other + // one is PIP. + if (windowInfo.type == TYPE_DOCK_DIVIDER) { + windowInfo.title = "Splitscreen Divider"; + } else if (window.mIsPIPMenu) { + windowInfo.title = "Picture-in-Picture menu"; + } + return windowInfo; + } + + private static void getLetterBoxBounds(WindowState windowState, Region outRegion) { + final Rect letterboxInsets = windowState.mActivityRecord.getLetterboxInsets(); + final Rect nonLetterboxRect = windowState.getBounds(); + + nonLetterboxRect.inset(letterboxInsets); + outRegion.set(windowState.getBounds()); + outRegion.op(nonLetterboxRect, Region.Op.DIFFERENCE); + } + + @Override + public String toString() { + String builder = "A11yWindow=[" + mWindow.asBinder() + + ", displayId=" + mDisplayId + + ", flag=0x" + Integer.toHexString(mFlags) + + ", type=" + mType + + ", privateFlag=0x" + Integer.toHexString(mPrivateFlags) + + ", focused=" + mIsFocused + + ", shouldMagnify=" + mShouldMagnify + + ", ignoreDuetoRecentsAnimation=" + mIgnoreDuetoRecentsAnimation + + ", isTrustedOverlay=" + mIsTrustedOverlay + + ", regionInScreen=" + mTouchableRegionInScreen + + ", touchableRegion=" + mTouchableRegionInWindow + + ", letterBoxBounds=" + mLetterBoxBounds + + ", isPIPMenu=" + mIsPIPMenu + + ", windowInfo=" + mWindowInfo + + "]"; + + return builder; + } + } +} diff --git a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java index 30cd3c4ab8bf..1bb9ca770c45 100644 --- a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java +++ b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java @@ -54,6 +54,7 @@ public abstract class ActivityInterceptorCallback { @IntDef(suffix = { "_ORDERED_ID" }, value = { FIRST_ORDERED_ID, COMMUNAL_MODE_ORDERED_ID, + PERMISSION_POLICY_ORDERED_ID, LAST_ORDERED_ID // Update this when adding new ids }) @Retention(RetentionPolicy.SOURCE) @@ -70,10 +71,15 @@ public abstract class ActivityInterceptorCallback { public static final int COMMUNAL_MODE_ORDERED_ID = 1; /** + * The identifier for {@link com.android.server.policy.PermissionPolicyService} interceptor + */ + public static final int PERMISSION_POLICY_ORDERED_ID = 2; + + /** * The final id, used by the framework to determine the valid range of ids. Update this when * adding new ids. */ - static final int LAST_ORDERED_ID = COMMUNAL_MODE_ORDERED_ID; + static final int LAST_ORDERED_ID = PERMISSION_POLICY_ORDERED_ID; /** * Data class for storing the various arguments needed for activity interception. diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 0b0b70481589..c929cbbac263 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -2134,7 +2134,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return activityInfo != null ? activityInfo.applicationInfo : null; }); - final int typeParameter = mWmService.mStartingSurfaceController + final int typeParameter = StartingSurfaceController .makeStartingWindowTypeParameter(newTask, taskSwitch, processRunning, allowTaskSnapshot, activityCreated, useEmpty, useLegacy, activityAllDrawn); @@ -6616,13 +6616,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return splashScreenThemeResId; } + void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch, + boolean startActivity, ActivityRecord sourceRecord) { + showStartingWindow(prev, newTask, taskSwitch, isProcessRunning(), startActivity, + sourceRecord); + } + /** * @param prev Previous activity which contains a starting window. + * @param processRunning Whether the client process is running. * @param startActivity Whether this activity is just created from starter. * @param sourceRecord The source activity which start this activity. */ void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch, - boolean startActivity, ActivityRecord sourceRecord) { + boolean processRunning, boolean startActivity, ActivityRecord sourceRecord) { if (mTaskOverlay) { // We don't show starting window for overlay activities. return; @@ -6647,7 +6654,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A && task.getActivity((r) -> !r.finishing && r != this) == null; final boolean scheduled = addStartingWindow(packageName, resolvedTheme, - prev, newTask || newSingleActivity, taskSwitch, isProcessRunning(), + prev, newTask || newSingleActivity, taskSwitch, processRunning, allowTaskSnapshot(), activityCreated, mSplashScreenStyleEmpty, allDrawn); if (DEBUG_STARTING_WINDOW_VERBOSE && scheduled) { Slog.d(TAG, "Scheduled starting window for " + this); diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java index ecc85878353a..87fb2902dddb 100644 --- a/services/core/java/com/android/server/wm/ActivityStartController.java +++ b/services/core/java/com/android/server/wm/ActivityStartController.java @@ -455,6 +455,10 @@ public class ActivityStartController { // Lock the loop to ensure the activities launched in a sequence. synchronized (mService.mGlobalLock) { mService.deferWindowLayout(); + // To avoid creating multiple starting window when creating starting multiples + // activities, we defer the creation of the starting window once all start request + // are processed + mService.mWindowManager.mStartingSurfaceController.beginDeferAddStartingWindow(); try { for (int i = 0; i < starters.length; i++) { final int startResult = starters[i].setResultTo(resultTo) @@ -480,6 +484,7 @@ public class ActivityStartController { } } } finally { + mService.mWindowManager.mStartingSurfaceController.endDeferAddStartingWindow(); mService.continueWindowLayout(); } } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index a9142ef85608..23508d961b63 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -687,4 +687,7 @@ public abstract class ActivityTaskManagerInternal { /** Get the most recent task excluding the first running task (the one on the front most). */ public abstract ActivityManager.RecentTaskInfo getMostRecentTaskFromBackground(); + + /** Get the app tasks for a package */ + public abstract List<ActivityManager.AppTask> getAppTasks(String pkgName, int uid); } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 15ebe2846513..ddd624d115c3 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -135,6 +135,7 @@ import android.app.Dialog; import android.app.IActivityClientController; import android.app.IActivityController; import android.app.IActivityTaskManager; +import android.app.IAppTask; import android.app.IApplicationThread; import android.app.IAssistDataReceiver; import android.app.INotificationManager; @@ -436,9 +437,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { private static final long START_AS_CALLER_TOKEN_EXPIRED_TIMEOUT = START_AS_CALLER_TOKEN_TIMEOUT_IMPL + 20 * MINUTE_IN_MILLIS; - // Activity tokens of system activities that are delegating their call to - // #startActivityByCaller, keyed by the permissionToken granted to the delegate. - final HashMap<IBinder, IBinder> mStartActivitySources = new HashMap<>(); + // The component name of the delegated activities that are allowed to call + // #startActivityAsCaller with the one-time used permission token. + final HashMap<IBinder, ComponentName> mStartActivitySources = new HashMap<>(); // Permission tokens that have expired, but we remember for error reporting. final ArrayList<IBinder> mExpiredStartAsCallerTokens = new ArrayList<>(); @@ -1512,7 +1513,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public IBinder requestStartActivityPermissionToken(IBinder delegatorToken) { + public IBinder requestStartActivityPermissionToken(ComponentName componentName) { int callingUid = Binder.getCallingUid(); if (UserHandle.getAppId(callingUid) != SYSTEM_UID) { throw new SecurityException("Only the system process can request a permission token, " @@ -1520,7 +1521,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } IBinder permissionToken = new Binder(); synchronized (mGlobalLock) { - mStartActivitySources.put(permissionToken, delegatorToken); + mStartActivitySources.put(permissionToken, componentName); } Message expireMsg = PooledLambda.obtainMessage( @@ -1545,7 +1546,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // 1) The caller is an activity that is part of the core framework, and then only when it // is running as the system. // 2) The caller provides a valid permissionToken. Permission tokens are one-time use and - // can only be requested by a system activity, which may then delegate this call to + // can only be requested from system uid, which may then delegate this call to // another app. final ActivityRecord sourceRecord; final int targetUid; @@ -1556,18 +1557,26 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (resultTo == null) { throw new SecurityException("Must be called from an activity"); } - final IBinder sourceToken; + + sourceRecord = ActivityRecord.isInAnyTask(resultTo); + if (sourceRecord == null) { + throw new SecurityException("Called with bad activity token: " + resultTo); + } + if (sourceRecord.app == null) { + throw new SecurityException("Called without a process attached to activity"); + } + + final ComponentName componentName; if (permissionToken != null) { // To even attempt to use a permissionToken, an app must also have this signature // permission. mAmInternal.enforceCallingPermission( android.Manifest.permission.START_ACTIVITY_AS_CALLER, "startActivityAsCaller"); - // If called with a permissionToken, we want the sourceRecord from the delegator - // activity that requested this token. - sourceToken = mStartActivitySources.remove(permissionToken); - if (sourceToken == null) { - // Invalid permissionToken, check if it recently expired. + // If called with a permissionToken, the caller must be the same component that + // was allowed to use the permissionToken. + componentName = mStartActivitySources.remove(permissionToken); + if (!sourceRecord.mActivityComponent.equals(componentName)) { if (mExpiredStartAsCallerTokens.contains(permissionToken)) { throw new SecurityException("Called with expired permission token: " + permissionToken); @@ -1577,33 +1586,21 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } } else { - // This method was called directly by the source. - sourceToken = resultTo; - } - - sourceRecord = ActivityRecord.isInAnyTask(sourceToken); - if (sourceRecord == null) { - throw new SecurityException("Called with bad activity token: " + sourceToken); - } - if (sourceRecord.app == null) { - throw new SecurityException("Called without a process attached to activity"); - } - - // Whether called directly or from a delegate, the source activity must be from the - // android package. - if (!sourceRecord.info.packageName.equals("android")) { - throw new SecurityException("Must be called from an activity that is " - + "declared in the android package"); - } - - if (UserHandle.getAppId(sourceRecord.app.mUid) != SYSTEM_UID) { - // This is still okay, as long as this activity is running under the - // uid of the original calling activity. - if (sourceRecord.app.mUid != sourceRecord.launchedFromUid) { - throw new SecurityException( - "Calling activity in uid " + sourceRecord.app.mUid - + " must be system uid or original calling uid " - + sourceRecord.launchedFromUid); + // Whether called directly or from a delegate, the source activity must be from the + // android package. + if (!sourceRecord.info.packageName.equals("android")) { + throw new SecurityException("Must be called from an activity that is " + + "declared in the android package"); + } + if (UserHandle.getAppId(sourceRecord.app.mUid) != SYSTEM_UID) { + // This is still okay, as long as this activity is running under the + // uid of the original calling activity. + if (sourceRecord.app.mUid != sourceRecord.launchedFromUid) { + throw new SecurityException( + "Calling activity in uid " + sourceRecord.app.mUid + + " must be system uid or original calling uid " + + sourceRecord.launchedFromUid); + } } } if (ignoreTargetSecurity) { @@ -2558,12 +2555,15 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public List<IBinder> getAppTasks(String callingPackage) { - int callingUid = Binder.getCallingUid(); assertPackageMatchesCallingUid(callingPackage); + return getAppTasks(callingPackage, Binder.getCallingUid()); + } + + private List<IBinder> getAppTasks(String pkgName, int uid) { final long ident = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { - return mRecentTasks.getAppTasksList(callingUid, callingPackage); + return mRecentTasks.getAppTasksList(uid, pkgName); } } finally { Binder.restoreCallingIdentity(ident); @@ -6668,5 +6668,16 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } return targetTask; } + + @Override + public List<ActivityManager.AppTask> getAppTasks(String pkgName, int uid) { + ArrayList<ActivityManager.AppTask> tasks = new ArrayList<>(); + List<IBinder> appTasks = ActivityTaskManagerService.this.getAppTasks(pkgName, uid); + int numAppTasks = appTasks.size(); + for (int i = 0; i < numAppTasks; i++) { + tasks.add(new ActivityManager.AppTask(IAppTask.Stub.asInterface(appTasks.get(i)))); + } + return tasks; + } } } diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index 3d37278cd400..475a9fb36f92 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -799,19 +799,18 @@ public class AppTransitionController { } /** - * Returns {@code true} if a given {@link WindowContainer} is an embedded Task. + * Returns {@code true} if a given {@link WindowContainer} is an embedded Task in + * {@link com.android.wm.shell.TaskView}. * * Note that this is a short term workaround to support Android Auto until it migrate to * ShellTransition. This should only be used by {@link #getAnimationTargets}. * * TODO(b/213312721): Remove this predicate and its callers once ShellTransition is enabled. */ - private static boolean isEmbeddedTask(WindowContainer wc) { + private static boolean isTaskViewTask(WindowContainer wc) { // We use Task#mRemoveWithTaskOrganizer to identify an embedded Task, but this is a hack and // it is not guaranteed to work this logic in the future version. - return !WindowManagerService.sEnableShellTransitions - && wc instanceof Task - && ((Task) wc).mRemoveWithTaskOrganizer; + return wc instanceof Task && ((Task) wc).mRemoveWithTaskOrganizer; } /** @@ -860,7 +859,7 @@ public class AppTransitionController { siblings.add(current); boolean canPromote = true; - if (isEmbeddedTask(current)) { + if (isTaskViewTask(current)) { // Don't animate an embedded Task in app transition. This is a short term workaround // to prevent conflict of surface hierarchy changes between legacy app transition // and TaskView (b/205189147). @@ -909,7 +908,7 @@ public class AppTransitionController { for (int j = 0; j < parent.getChildCount(); ++j) { final WindowContainer sibling = parent.getChildAt(j); if (candidates.remove(sibling)) { - if (!isEmbeddedTask(sibling)) { + if (!isTaskViewTask(sibling)) { // Don't animate an embedded Task in app transition. This is a short // term workaround to prevent conflict of surface hierarchy changes // between legacy app transition and TaskView (b/205189147). diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java index 47622bc83417..9661e8d30b22 100644 --- a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java @@ -24,13 +24,11 @@ import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; -import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER; import static android.window.DisplayAreaOrganizer.FEATURE_FULLSCREEN_MAGNIFICATION; import static android.window.DisplayAreaOrganizer.FEATURE_HIDE_DISPLAY_CUTOUT; import static android.window.DisplayAreaOrganizer.FEATURE_IME_PLACEHOLDER; import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED; -import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED_BACKGROUND_PANEL; import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION; import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature; @@ -134,11 +132,6 @@ public abstract class DisplayAreaPolicy { .except(TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL, TYPE_STATUS_BAR, TYPE_NOTIFICATION_SHADE) .build()) - .addFeature(new Feature.Builder(wmService.mPolicy, - "OneHandedBackgroundPanel", - FEATURE_ONE_HANDED_BACKGROUND_PANEL) - .upTo(TYPE_WALLPAPER) - .build()) .addFeature(new Feature.Builder(wmService.mPolicy, "OneHanded", FEATURE_ONE_HANDED) .all() diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index c740f7b7c2e2..8e0435f1908f 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -218,7 +218,6 @@ import android.view.Surface.Rotation; import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; import android.view.SurfaceSession; -import android.view.View; import android.view.WindowInsets; import android.view.WindowManager; import android.view.WindowManager.DisplayImePolicy; @@ -1425,7 +1424,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mWaitingForConfig = true; if (mTransitionController.isShellTransitionsEnabled()) { requestChangeTransitionIfNeeded(changes, null /* displayChange */); - } else { + } else if (mLastHasContent) { mWmService.startFreezingDisplay(0 /* exitAnim */, 0 /* enterAnim */, this); } sendNewConfiguration(); @@ -1702,8 +1701,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp case SOFT_INPUT_STATE_HIDDEN: return false; } - return r.mLastImeShown && mInputMethodWindow != null && mInputMethodWindow.mHasSurface - && mInputMethodWindow.mViewVisibility == View.VISIBLE; + return r.mLastImeShown; } /** Returns {@code true} if the top activity is transformed with the new rotation of display. */ @@ -3222,6 +3220,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp */ void requestChangeTransitionIfNeeded(@ActivityInfo.Config int changes, @Nullable TransitionRequestInfo.DisplayChange displayChange) { + if (!mLastHasContent) return; final TransitionController controller = mTransitionController; if (controller.isCollecting()) { if (displayChange != null) { @@ -4198,10 +4197,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp final SurfaceControl newParent = computeImeParent(); if (newParent != null && newParent != mInputMethodSurfaceParent) { mInputMethodSurfaceParent = newParent; - getPendingTransaction().reparent(mImeWindowsContainer.mSurfaceControl, newParent); + getSyncTransaction().reparent(mImeWindowsContainer.mSurfaceControl, newParent); // When surface parent is removed, the relative layer will also be removed. We need to // do a force update to make sure there is a layer set for the new parent. - assignRelativeLayerForIme(getPendingTransaction(), true /* forceUpdate */); + assignRelativeLayerForIme(getSyncTransaction(), true /* forceUpdate */); scheduleAnimation(); } } @@ -4995,8 +4994,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // exists so it get's layered above the starting window. if (imeTarget != null && !(imeTarget.mActivityRecord != null && imeTarget.mActivityRecord.hasStartingWindow())) { + final WindowToken imeControlTargetToken = + mImeControlTarget != null && mImeControlTarget.getWindow() != null + ? mImeControlTarget.getWindow().mToken : null; final boolean canImeTargetSetRelativeLayer = imeTarget.getSurfaceControl() != null - && imeTarget == mImeControlTarget + && imeTarget.mToken == imeControlTargetToken && !imeTarget.inMultiWindowMode() && imeTarget.mToken.getActivity(app -> app.isAnimating(TRANSITION | PARENTS, ANIMATION_TYPE_ALL & ~ANIMATION_TYPE_RECENTS)) == null; @@ -5087,6 +5089,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return mLastHasContent; } + @VisibleForTesting + void setLastHasContent() { + mLastHasContent = true; + } + void registerPointerEventListener(@NonNull PointerEventListener listener) { mPointerEventDispatcher.registerInputEventListener(listener); } diff --git a/services/core/java/com/android/server/wm/FadeRotationAnimationController.java b/services/core/java/com/android/server/wm/FadeRotationAnimationController.java index c85e04dbfa15..2cefd9935870 100644 --- a/services/core/java/com/android/server/wm/FadeRotationAnimationController.java +++ b/services/core/java/com/android/server/wm/FadeRotationAnimationController.java @@ -256,7 +256,7 @@ public class FadeRotationAnimationController extends FadeAnimationController { false /* applyFixedTransformationHint */); for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) { final SurfaceControl leash = mTargetWindowTokens.valueAt(i); - if (leash != null) { + if (leash != null && leash.isValid()) { rotator.applyTransform(t, leash); } } @@ -265,7 +265,7 @@ public class FadeRotationAnimationController extends FadeAnimationController { // Hide the windows immediately because a screenshot layer should cover the screen. for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) { final SurfaceControl leash = mTargetWindowTokens.valueAt(i); - if (leash != null) { + if (leash != null && leash.isValid()) { t.setAlpha(leash, 0f); } } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 3f2e97505765..5a420caa176c 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -983,29 +983,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> mWmService.checkDrawnWindowsLocked(); } - final int N = mWmService.mPendingRemove.size(); - if (N > 0) { - if (mWmService.mPendingRemoveTmp.length < N) { - mWmService.mPendingRemoveTmp = new WindowState[N + 10]; - } - mWmService.mPendingRemove.toArray(mWmService.mPendingRemoveTmp); - mWmService.mPendingRemove.clear(); - ArrayList<DisplayContent> displayList = new ArrayList(); - for (i = 0; i < N; i++) { - final WindowState w = mWmService.mPendingRemoveTmp[i]; - w.removeImmediately(); - final DisplayContent displayContent = w.getDisplayContent(); - if (displayContent != null && !displayList.contains(displayContent)) { - displayList.add(displayContent); - } - } - - for (int j = displayList.size() - 1; j >= 0; --j) { - final DisplayContent dc = displayList.get(j); - dc.assignWindowLayers(true /*setLayoutNeeded*/); - } - } - forAllDisplays(dc -> { dc.getInputMonitor().updateInputWindowsLw(true /*force*/); dc.updateSystemGestureExclusion(); @@ -2052,24 +2029,29 @@ class RootWindowContainer extends WindowContainer<DisplayContent> try { final Task task = r.getTask(); - // If the activity in current PIP task needs to be moved back to the parent Task of next - // PIP activity, we can't use that parent Task as the next PIP Task. - // Because we need to start the Shell transition from the root Task, we delay to dismiss - // the current PIP task until root Task is ready. - boolean origPipWillBeMovedToTask = false; + // Create a transition now to collect the current pinned Task dismiss. Only do the + // create here as the Task (trigger) to enter PIP is not ready yet. + final TransitionController transitionController = task.mTransitionController; + Transition newTransition = null; + if (transitionController.isCollecting()) { + transitionController.setReady(task, false /* ready */); + } else if (transitionController.getTransitionPlayer() != null) { + newTransition = transitionController.createTransition(TRANSIT_PIP); + } + + // This will change the root pinned task's windowing mode to its original mode, ensuring + // we only have one root task that is in pinned mode. final Task rootPinnedTask = taskDisplayArea.getRootPinnedTask(); if (rootPinnedTask != null) { - final ActivityRecord topPipActivity = rootPinnedTask.getTopMostActivity(); - if (topPipActivity != null && topPipActivity.getLastParentBeforePip() == task) { - origPipWillBeMovedToTask = true; - } + transitionController.collect(rootPinnedTask); + rootPinnedTask.dismissPip(); } // Set a transition to ensure that we don't immediately try and update the visibility // of the activity entering PIP r.getDisplayContent().prepareAppTransition(TRANSIT_NONE); - final boolean singleActivity = task.getChildCount() == 1 && !origPipWillBeMovedToTask; + final boolean singleActivity = task.getChildCount() == 1; final Task rootTask; if (singleActivity) { rootTask = task; @@ -2123,7 +2105,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> final ActivityRecord oldTopActivity = task.getTopMostActivity(); if (oldTopActivity != null && oldTopActivity.isState(STOPPED) && task.getDisplayContent().mAppTransition.containsTransitRequest( - TRANSIT_TO_BACK) && !origPipWillBeMovedToTask) { + TRANSIT_TO_BACK)) { task.getDisplayContent().mClosingApps.add(oldTopActivity); oldTopActivity.mRequestForceTransition = true; } @@ -2137,14 +2119,13 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // display area, so reparent. rootTask.reparent(taskDisplayArea, true /* onTop */); } - rootTask.mTransitionController.requestTransitionIfNeeded(TRANSIT_PIP, rootTask); - // This will change the root pinned task's windowing mode to its original mode, ensuring - // we only have one root task that is in pinned mode. - if (rootPinnedTask != null) { - rootTask.mTransitionController.collect(rootPinnedTask); - rootPinnedTask.dismissPip(); + // The new PIP Task is ready, start the transition before updating the windowing mode. + if (newTransition != null) { + transitionController.requestStartTransition(newTransition, rootTask, + null /* remoteTransition */, null /* displayChange */); } + transitionController.collect(rootTask); // Defer the windowing mode change until after the transition to prevent the activity // from doing work and changing the activity visuals while animating diff --git a/services/core/java/com/android/server/wm/ShellRoot.java b/services/core/java/com/android/server/wm/ShellRoot.java index 6ed59e96c700..f9d7b53e4e78 100644 --- a/services/core/java/com/android/server/wm/ShellRoot.java +++ b/services/core/java/com/android/server/wm/ShellRoot.java @@ -25,15 +25,14 @@ import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMAT import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION; import android.annotation.NonNull; +import android.annotation.Nullable; import android.graphics.Point; -import android.graphics.Rect; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; import android.view.DisplayInfo; import android.view.IWindow; import android.view.SurfaceControl; -import android.view.WindowInfo; import android.view.WindowManager; import android.view.animation.Animation; @@ -136,47 +135,12 @@ public class ShellRoot { ANIMATION_TYPE_WINDOW_ANIMATION); } - WindowInfo getWindowInfo() { - if (mShellRootLayer != SHELL_ROOT_LAYER_DIVIDER - && mShellRootLayer != SHELL_ROOT_LAYER_PIP) { - return null; - } - if (mShellRootLayer == SHELL_ROOT_LAYER_DIVIDER - && !mDisplayContent.getDefaultTaskDisplayArea().isSplitScreenModeActivated()) { - return null; - } - if (mShellRootLayer == SHELL_ROOT_LAYER_PIP - && mDisplayContent.getDefaultTaskDisplayArea().getRootPinnedTask() == null) { - return null; - } - if (mAccessibilityWindow == null) { - return null; - } - WindowInfo windowInfo = WindowInfo.obtain(); - windowInfo.displayId = mToken.getDisplayArea().getDisplayContent().mDisplayId; - windowInfo.type = mToken.windowType; - windowInfo.layer = mToken.getWindowLayerFromType(); - windowInfo.token = mAccessibilityWindow.asBinder(); - windowInfo.focused = false; - windowInfo.hasFlagWatchOutsideTouch = false; - final Rect regionRect = new Rect(); - - - // DividerView - if (mShellRootLayer == SHELL_ROOT_LAYER_DIVIDER) { - windowInfo.inPictureInPicture = false; - mDisplayContent.getDockedDividerController().getTouchRegion(regionRect); - windowInfo.regionInScreen.set(regionRect); - windowInfo.title = "Splitscreen Divider"; - } - // PipMenuView - if (mShellRootLayer == SHELL_ROOT_LAYER_PIP) { - windowInfo.inPictureInPicture = true; - mDisplayContent.getDefaultTaskDisplayArea().getRootPinnedTask().getBounds(regionRect); - windowInfo.regionInScreen.set(regionRect); - windowInfo.title = "Picture-in-Picture menu"; + @Nullable + IBinder getAccessibilityWindowToken() { + if (mAccessibilityWindow != null) { + return mAccessibilityWindow.asBinder(); } - return windowInfo; + return null; } void setAccessibilityWindow(IWindow window) { @@ -197,9 +161,5 @@ public class ShellRoot { mAccessibilityWindow = null; } } - if (mDisplayContent.mWmService.mAccessibilityController.hasCallbacks()) { - mDisplayContent.mWmService.mAccessibilityController.onSomeWindowResizedOrMoved( - mDisplayContent.getDisplayId()); - } } } diff --git a/services/core/java/com/android/server/wm/StartingSurfaceController.java b/services/core/java/com/android/server/wm/StartingSurfaceController.java index 5fe40766fb66..eb73cd807204 100644 --- a/services/core/java/com/android/server/wm/StartingSurfaceController.java +++ b/services/core/java/com/android/server/wm/StartingSurfaceController.java @@ -34,6 +34,7 @@ import android.content.pm.ApplicationInfo; import android.util.Slog; import android.window.TaskSnapshot; +import java.util.ArrayList; import java.util.function.Supplier; /** @@ -45,6 +46,14 @@ public class StartingSurfaceController { private final WindowManagerService mService; private final SplashScreenExceptionList mSplashScreenExceptionsList; + // Cache status while deferring add starting window + boolean mInitProcessRunning; + boolean mInitNewTask; + boolean mInitTaskSwitch; + private final ArrayList<DeferringStartingWindowRecord> mDeferringAddStartActivities = + new ArrayList<>(); + private boolean mDeferringAddStartingWindow; + public StartingSurfaceController(WindowManagerService wm) { mService = wm; mSplashScreenExceptionsList = new SplashScreenExceptionList(wm.mContext.getMainExecutor()); @@ -70,7 +79,7 @@ public class StartingSurfaceController { return mSplashScreenExceptionsList.isException(packageName, targetSdk, infoProvider); } - int makeStartingWindowTypeParameter(boolean newTask, boolean taskSwitch, + static int makeStartingWindowTypeParameter(boolean newTask, boolean taskSwitch, boolean processRunning, boolean allowTaskSnapshot, boolean activityCreated, boolean useEmpty, boolean useLegacy, boolean activityDrawn) { int parameter = 0; @@ -142,6 +151,82 @@ public class StartingSurfaceController { } } + private static final class DeferringStartingWindowRecord { + final ActivityRecord mDeferring; + final ActivityRecord mPrev; + final ActivityRecord mSource; + + DeferringStartingWindowRecord(ActivityRecord deferring, ActivityRecord prev, + ActivityRecord source) { + mDeferring = deferring; + mPrev = prev; + mSource = source; + } + } + + /** + * Shows a starting window while starting a new activity. Do not use this method to create a + * starting window for an existing activity. + */ + void showStartingWindow(ActivityRecord target, ActivityRecord prev, + boolean newTask, boolean isTaskSwitch, ActivityRecord source) { + if (mDeferringAddStartingWindow) { + addDeferringRecord(target, prev, newTask, isTaskSwitch, source); + } else { + target.showStartingWindow(prev, newTask, isTaskSwitch, true /* startActivity */, + source); + } + } + + /** + * Queueing the starting activity status while deferring add starting window. + * @see Task#startActivityLocked + */ + private void addDeferringRecord(ActivityRecord deferring, ActivityRecord prev, + boolean newTask, boolean isTaskSwitch, ActivityRecord source) { + // Set newTask, taskSwitch, processRunning form first activity because those can change + // after first activity started. + if (mDeferringAddStartActivities.isEmpty()) { + mInitProcessRunning = deferring.isProcessRunning(); + mInitNewTask = newTask; + mInitTaskSwitch = isTaskSwitch; + } + mDeferringAddStartActivities.add(new DeferringStartingWindowRecord( + deferring, prev, source)); + } + + private void showStartingWindowFromDeferringActivities() { + // Attempt to add starting window from the top-most activity. + for (int i = mDeferringAddStartActivities.size() - 1; i >= 0; --i) { + final DeferringStartingWindowRecord next = mDeferringAddStartActivities.get(i); + next.mDeferring.showStartingWindow(next.mPrev, mInitNewTask, mInitTaskSwitch, + mInitProcessRunning, true /* startActivity */, next.mSource); + // If one succeeds, it is done. + if (next.mDeferring.mStartingData != null) { + break; + } + } + mDeferringAddStartActivities.clear(); + } + + /** + * Begin deferring add starting window in one pass. + * This is used to deferring add starting window while starting multiples activities because + * system only need to provide a starting window to the top-visible activity. + * Most call {@link #endDeferAddStartingWindow} when starting activities process finished. + * @see #endDeferAddStartingWindow() + */ + void beginDeferAddStartingWindow() { + mDeferringAddStartingWindow = true; + } + + /** + * End deferring add starting window. + */ + void endDeferAddStartingWindow() { + mDeferringAddStartingWindow = false; + showStartingWindowFromDeferringActivities(); + } final class StartingSurface { private final Task mTask; diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 7617726e1fcd..43038ce32649 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -5149,8 +5149,8 @@ class Task extends TaskFragment { final ActivityRecord prev = baseTask.getActivity( a -> a.mStartingData != null && a.showToCurrentUser()); - r.showStartingWindow(prev, newTask, isTaskSwitch, - true /* startActivity */, sourceRecord); + mWmService.mStartingSurfaceController.showStartingWindow(r, prev, newTask, + isTaskSwitch, sourceRecord); } } else { // If this is the first activity, don't do any fancy animations, diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java index fe405e5b3af8..fc154a8b3179 100644 --- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java +++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java @@ -107,7 +107,7 @@ class WallpaperWindowToken extends WindowToken { /** Returns {@code true} if visibility is changed. */ boolean updateWallpaperWindows(boolean visible) { boolean changed = false; - if (isVisible() != visible) { + if (mVisibleRequested != visible) { ProtoLog.d(WM_DEBUG_WALLPAPER, "Wallpaper token %s visible=%b", token, visible); setVisibility(visible); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 7be128b53f8c..b5e6f49146f1 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -222,7 +222,6 @@ import android.util.DisplayMetrics; import android.util.EventLog; import android.util.MergedConfiguration; import android.util.Slog; -import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.TimeUtils; import android.util.TypedValue; @@ -588,20 +587,6 @@ public class WindowManagerService extends IWindowManager.Stub final ArrayList<WindowState> mResizingWindows = new ArrayList<>(); /** - * Windows whose animations have ended and now must be removed. - */ - final ArrayList<WindowState> mPendingRemove = new ArrayList<>(); - - /** - * Used when processing mPendingRemove to avoid working on the original array. - */ - WindowState[] mPendingRemoveTmp = new WindowState[20]; - - // TODO: use WindowProcessController once go/wm-unified is done. - /** Mapping of process pids to configurations */ - final SparseArray<Configuration> mProcessConfigurations = new SparseArray<>(); - - /** * Windows whose surface should be destroyed. */ final ArrayList<WindowState> mDestroySurface = new ArrayList<>(); @@ -2038,7 +2023,6 @@ public class WindowManagerService extends IWindowManager.Stub dc.mWinRemovedSinceNullFocus.add(win); } mEmbeddedWindowController.onWindowRemoved(win); - mPendingRemove.remove(win); mResizingWindows.remove(win); updateNonSystemOverlayWindowsVisibilityIfNeeded(win, false /* surfaceShown */); mWindowsChanged = true; @@ -6342,23 +6326,6 @@ public class WindowManagerService extends IWindowManager.Stub } } } - if (mPendingRemove.size() > 0) { - pw.println(); - pw.println(" Remove pending for:"); - for (int i=mPendingRemove.size()-1; i>=0; i--) { - WindowState w = mPendingRemove.get(i); - if (windows == null || windows.contains(w)) { - pw.print(" Remove #"); pw.print(i); pw.print(' '); - pw.print(w); - if (dumpAll) { - pw.println(":"); - w.dump(pw, " ", true); - } else { - pw.println(); - } - } - } - } if (mForceRemoves != null && mForceRemoves.size() > 0) { pw.println(); pw.println(" Windows force removing:"); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 573ff2ff9963..5bbe2cd853f6 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -4809,9 +4809,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (isAnimating()) { return; } - if (mWmService.mAccessibilityController.hasCallbacks()) { - mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(getDisplayId()); - } if (!isSelfOrAncestorWindowAnimatingExit()) { return; @@ -4838,15 +4835,20 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (hasSurface) { mWmService.mDestroySurface.add(this); } - if (mRemoveOnExit) { - mWmService.mPendingRemove.add(this); - mRemoveOnExit = false; - } } mAnimatingExit = false; getDisplayContent().mWallpaperController.hideWallpapers(this); } + @Override + boolean handleCompleteDeferredRemoval() { + if (mRemoveOnExit) { + mRemoveOnExit = false; + removeImmediately(); + } + return super.handleCompleteDeferredRemoval(); + } + boolean clearAnimatingFlags() { boolean didSomething = false; // We don't want to clear it out for windows that get replaced, because the diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp index 4190a91710fc..94bc22a05d7a 100644 --- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp +++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp @@ -59,6 +59,9 @@ using android::base::unique_fd; namespace android { +static bool cancelRunningCompaction; +static bool compactionInProgress; + // Legacy method for compacting processes, any new code should // use compactProcess instead. static inline void compactProcessProcfs(int pid, const std::string& compactionType) { @@ -83,9 +86,18 @@ static int64_t compactMemory(const std::vector<Vma>& vmas, int pid, int madviseT // Skip compaction if failed to open pidfd with any error return -errno; } + compactionInProgress = true; + cancelRunningCompaction = false; int64_t totalBytesCompacted = 0; for (int iBase = 0; iBase < vmas.size(); iBase += UIO_MAXIOV) { + if (CC_UNLIKELY(cancelRunningCompaction)) { + // There could be a significant delay betweenwhen a compaction + // is requested and when it is handled during this time + // our OOM adjust could have improved. + cancelRunningCompaction = false; + break; + } int totalVmasToKernel = std::min(UIO_MAXIOV, (int)(vmas.size() - iBase)); for (int iVec = 0, iVma = iBase; iVec < totalVmasToKernel; ++iVec, ++iVma) { vmasToKernel[iVec].iov_base = (void*)vmas[iVma].start; @@ -95,11 +107,13 @@ static int64_t compactMemory(const std::vector<Vma>& vmas, int pid, int madviseT auto bytesCompacted = process_madvise(pidfd, vmasToKernel, totalVmasToKernel, madviseType, 0); if (CC_UNLIKELY(bytesCompacted == -1)) { + compactionInProgress = false; return -errno; } totalBytesCompacted += bytesCompacted; } + compactionInProgress = false; return totalBytesCompacted; } @@ -228,6 +242,12 @@ static void com_android_server_am_CachedAppOptimizer_compactSystem(JNIEnv *, job } } +static void com_android_server_am_CachedAppOptimizer_cancelCompaction(JNIEnv*, jobject) { + if (compactionInProgress) { + cancelRunningCompaction = true; + } +} + static void com_android_server_am_CachedAppOptimizer_compactProcess(JNIEnv*, jobject, jint pid, jint compactionFlags) { compactProcessOrFallback(pid, compactionFlags); @@ -279,6 +299,8 @@ static jstring com_android_server_am_CachedAppOptimizer_getFreezerCheckPath(JNIE static const JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ + {"cancelCompaction", "()V", + (void*)com_android_server_am_CachedAppOptimizer_cancelCompaction}, {"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem}, {"compactProcess", "(II)V", (void*)com_android_server_am_CachedAppOptimizer_compactProcess}, {"freezeBinder", "(IZ)I", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder}, diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp index 546b075ea0be..b484796af6e2 100644 --- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp +++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp @@ -61,8 +61,8 @@ static struct { static struct { jfieldID startAmplitude; jfieldID endAmplitude; - jfieldID startFrequency; - jfieldID endFrequency; + jfieldID startFrequencyHz; + jfieldID endFrequencyHz; jfieldID duration; } sRampClassInfo; @@ -157,8 +157,8 @@ static aidl::ActivePwle activePwleFromJavaPrimitive(JNIEnv* env, jobject ramp) { static_cast<float>(env->GetFloatField(ramp, sRampClassInfo.startAmplitude)); pwle.endAmplitude = static_cast<float>(env->GetFloatField(ramp, sRampClassInfo.endAmplitude)); pwle.startFrequency = - static_cast<float>(env->GetFloatField(ramp, sRampClassInfo.startFrequency)); - pwle.endFrequency = static_cast<float>(env->GetFloatField(ramp, sRampClassInfo.endFrequency)); + static_cast<float>(env->GetFloatField(ramp, sRampClassInfo.startFrequencyHz)); + pwle.endFrequency = static_cast<float>(env->GetFloatField(ramp, sRampClassInfo.endFrequencyHz)); pwle.duration = static_cast<int32_t>(env->GetIntField(ramp, sRampClassInfo.duration)); return pwle; } @@ -363,7 +363,7 @@ static void vibratorAlwaysOnDisable(JNIEnv* env, jclass /* clazz */, jlong ptr, } static jboolean vibratorGetInfo(JNIEnv* env, jclass /* clazz */, jlong ptr, - jfloat suggestedSafeRange, jobject vibratorInfoBuilder) { + jobject vibratorInfoBuilder) { VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr); if (wrapper == nullptr) { ALOGE("vibratorGetInfo failed because native wrapper was not initialized"); @@ -437,9 +437,9 @@ static jboolean vibratorGetInfo(JNIEnv* env, jclass /* clazz */, jlong ptr, env->SetFloatArrayRegion(maxAmplitudes, 0, amplitudes.size(), reinterpret_cast<jfloat*>(amplitudes.data())); } - jobject frequencyMapping = env->NewObject(sFrequencyMappingClass, sFrequencyMappingCtor, - minFrequency, resonantFrequency, frequencyResolution, - suggestedSafeRange, maxAmplitudes); + jobject frequencyMapping = + env->NewObject(sFrequencyMappingClass, sFrequencyMappingCtor, resonantFrequency, + minFrequency, frequencyResolution, maxAmplitudes); env->CallObjectMethod(vibratorInfoBuilder, sVibratorInfoBuilderClassInfo.setFrequencyMapping, frequencyMapping); @@ -463,7 +463,7 @@ static const JNINativeMethod method_table[] = { {"setExternalControl", "(JZ)V", (void*)vibratorSetExternalControl}, {"alwaysOnEnable", "(JJJJ)V", (void*)vibratorAlwaysOnEnable}, {"alwaysOnDisable", "(JJ)V", (void*)vibratorAlwaysOnDisable}, - {"getInfo", "(JFLandroid/os/VibratorInfo$Builder;)Z", (void*)vibratorGetInfo}, + {"getInfo", "(JLandroid/os/VibratorInfo$Builder;)Z", (void*)vibratorGetInfo}, }; int register_android_server_vibrator_VibratorController(JavaVM* jvm, JNIEnv* env) { @@ -481,13 +481,13 @@ int register_android_server_vibrator_VibratorController(JavaVM* jvm, JNIEnv* env jclass rampClass = FindClassOrDie(env, "android/os/vibrator/RampSegment"); sRampClassInfo.startAmplitude = GetFieldIDOrDie(env, rampClass, "mStartAmplitude", "F"); sRampClassInfo.endAmplitude = GetFieldIDOrDie(env, rampClass, "mEndAmplitude", "F"); - sRampClassInfo.startFrequency = GetFieldIDOrDie(env, rampClass, "mStartFrequency", "F"); - sRampClassInfo.endFrequency = GetFieldIDOrDie(env, rampClass, "mEndFrequency", "F"); + sRampClassInfo.startFrequencyHz = GetFieldIDOrDie(env, rampClass, "mStartFrequencyHz", "F"); + sRampClassInfo.endFrequencyHz = GetFieldIDOrDie(env, rampClass, "mEndFrequencyHz", "F"); sRampClassInfo.duration = GetFieldIDOrDie(env, rampClass, "mDuration", "I"); jclass frequencyMappingClass = FindClassOrDie(env, "android/os/VibratorInfo$FrequencyMapping"); sFrequencyMappingClass = static_cast<jclass>(env->NewGlobalRef(frequencyMappingClass)); - sFrequencyMappingCtor = GetMethodIDOrDie(env, sFrequencyMappingClass, "<init>", "(FFFF[F)V"); + sFrequencyMappingCtor = GetMethodIDOrDie(env, sFrequencyMappingClass, "<init>", "(FFF[F)V"); jclass vibratorInfoBuilderClass = FindClassOrDie(env, "android/os/VibratorInfo$Builder"); sVibratorInfoBuilderClassInfo.setCapabilities = diff --git a/services/incremental/TEST_MAPPING b/services/incremental/TEST_MAPPING index f2ad068854c4..9fe090a400c8 100644 --- a/services/incremental/TEST_MAPPING +++ b/services/incremental/TEST_MAPPING @@ -10,6 +10,12 @@ }, { "name": "CtsIncrementalInstallHostTestCases" + }, + { + "name": "libincfs-test" + }, + { + "name": "service.incremental_test" } ], "presubmit-large": [ diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 29797a549e49..5fcee9b4f69e 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -310,6 +310,8 @@ public final class SystemServer implements Dumpable { "com.android.clockwork.connectivity.WearConnectivityService"; private static final String WEAR_POWER_SERVICE_CLASS = "com.android.clockwork.power.WearPowerService"; + private static final String HEALTH_SERVICE_CLASS = + "com.google.android.clockwork.healthservices.HealthService"; private static final String WEAR_SIDEKICK_SERVICE_CLASS = "com.google.android.clockwork.sidekick.SidekickService"; private static final String WEAR_DISPLAYOFFLOAD_SERVICE_CLASS = @@ -920,12 +922,6 @@ public final class SystemServer implements Dumpable { startBootstrapServices(t); startCoreServices(t); startOtherServices(t); - // Apex services must be the last category of services to start. No other service must - // be starting after this point. This is to prevent unnessary stability issues when - // these apexes are updated outside of OTA; and to avoid breaking dependencies from - // system into apexes. - // TODO(satayev): lock mSystemServiceManager.startService to stop accepting new services - // after this step startApexServices(t); } catch (Throwable ex) { Slog.e("System", "******************************************"); @@ -1460,13 +1456,18 @@ public final class SystemServer implements Dumpable { ServiceManager.addService("scheduling_policy", new SchedulingPolicyService()); t.traceEnd(); - t.traceBegin("StartTelecomLoaderService"); - mSystemServiceManager.startService(TelecomLoaderService.class); - t.traceEnd(); + // TelecomLoader hooks into classes with defined HFP logic, + // so check for either telephony or microphone. + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_MICROPHONE) || + mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { + t.traceBegin("StartTelecomLoaderService"); + mSystemServiceManager.startService(TelecomLoaderService.class); + t.traceEnd(); + } t.traceBegin("StartTelephonyRegistry"); telephonyRegistry = new TelephonyRegistry( - context, new TelephonyRegistry.ConfigurationProvider()); + context, new TelephonyRegistry.ConfigurationProvider()); ServiceManager.addService("telephony.registry", telephonyRegistry); t.traceEnd(); @@ -1903,7 +1904,7 @@ public final class SystemServer implements Dumpable { t.traceBegin("StartNetworkStatsService"); try { - networkStats = NetworkStatsService.create(context, networkManagement); + networkStats = NetworkStatsService.create(context); ServiceManager.addService(Context.NETWORK_STATS_SERVICE, networkStats); } catch (Throwable e) { reportWtf("starting NetworkStats Service", e); @@ -2500,6 +2501,10 @@ public final class SystemServer implements Dumpable { mSystemServiceManager.startService(WEAR_POWER_SERVICE_CLASS); t.traceEnd(); + t.traceBegin("StartHealthService"); + mSystemServiceManager.startService(HEALTH_SERVICE_CLASS); + t.traceEnd(); + t.traceBegin("StartWearConnectivityService"); mSystemServiceManager.startService(WEAR_CONNECTIVITY_SERVICE_CLASS); t.traceEnd(); @@ -2572,10 +2577,12 @@ public final class SystemServer implements Dumpable { mActivityManagerService.enterSafeMode(); } - // MMS service broker - t.traceBegin("StartMmsService"); - mmsService = mSystemServiceManager.startService(MmsServiceBroker.class); - t.traceEnd(); + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { + // MMS service broker + t.traceBegin("StartMmsService"); + mmsService = mSystemServiceManager.startService(MmsServiceBroker.class); + t.traceEnd(); + } if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOFILL)) { t.traceBegin("StartAutoFillService"); @@ -2991,9 +2998,7 @@ public final class SystemServer implements Dumpable { t.traceEnd(); t.traceBegin("MakeTelephonyRegistryReady"); try { - if (telephonyRegistryF != null) { - telephonyRegistryF.systemRunning(); - } + if (telephonyRegistryF != null) telephonyRegistryF.systemRunning(); } catch (Throwable e) { reportWtf("Notifying TelephonyRegistry running", e); } @@ -3007,15 +3012,15 @@ public final class SystemServer implements Dumpable { reportWtf("Notifying MediaRouterService running", e); } t.traceEnd(); - t.traceBegin("MakeMmsServiceReady"); - try { - if (mmsServiceF != null) { - mmsServiceF.systemRunning(); + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { + t.traceBegin("MakeMmsServiceReady"); + try { + if (mmsServiceF != null) mmsServiceF.systemRunning(); + } catch (Throwable e) { + reportWtf("Notifying MmsService running", e); } - } catch (Throwable e) { - reportWtf("Notifying MmsService running", e); + t.traceEnd(); } - t.traceEnd(); t.traceBegin("IncidentDaemonReady"); try { @@ -3051,11 +3056,14 @@ public final class SystemServer implements Dumpable { /** * Starts system services defined in apexes. + * + * <p>Apex services must be the last category of services to start. No other service must be + * starting after this point. This is to prevent unnecessary stability issues when these apexes + * are updated outside of OTA; and to avoid breaking dependencies from system into apexes. */ private void startApexServices(@NonNull TimingsTraceAndSlog t) { t.traceBegin("startApexServices"); Map<String, String> services = ApexManager.getInstance().getApexSystemServices(); - // TODO(satayev): filter out already started services // TODO(satayev): introduce android:order for services coming the same apexes for (String name : new TreeSet<>(services.keySet())) { String jarPath = services.get(name); @@ -3067,6 +3075,10 @@ public final class SystemServer implements Dumpable { } t.traceEnd(); } + + // make sure no other services are started after this point + mSystemServiceManager.sealStartedServices(); + t.traceEnd(); // startApexServices } diff --git a/services/midi/OWNERS b/services/midi/OWNERS new file mode 100644 index 000000000000..f4d51f91b51b --- /dev/null +++ b/services/midi/OWNERS @@ -0,0 +1 @@ +philburk@google.com diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp index 8538603d4180..635f1360ff73 100644 --- a/services/tests/mockingservicestests/Android.bp +++ b/services/tests/mockingservicestests/Android.bp @@ -40,26 +40,26 @@ android_test { ], static_libs: [ - "frameworks-base-testutils", - "services.core", - "services.devicepolicy", - "services.net", - "services.usage", - "service-jobscheduler", - "service-permission.impl", - "service-blobstore", "androidx.test.core", "androidx.test.runner", "androidx.test.ext.truth", - "mockito-target-extended-minus-junit4", - "platform-test-annotations", - "truth-prebuilt", + "frameworks-base-testutils", "hamcrest-library", - "servicestests-utils-mockito-extended", + "kotlin-test", "mockingservicestests-utils-mockito", + "mockito-target-extended-minus-junit4", + "platform-test-annotations", + "service-blobstore", + "service-jobscheduler", + "service-permission.impl", + "services.core", + "services.devicepolicy", + "services.net", + "services.usage", "servicestests-core-utils", + "servicestests-utils-mockito-extended", "testables", - "kotlin-test", + "truth-prebuilt", // TODO: remove once Android migrates to JUnit 4.12, which provides assertThrows "testng", ], diff --git a/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java deleted file mode 100644 index c46884f51651..000000000000 --- a/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.communal; - -import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.spy; - -import android.Manifest; -import android.app.communal.ICommunalManager; -import android.content.ContextWrapper; -import android.os.RemoteException; -import android.platform.test.annotations.Presubmit; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.SmallTest; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.MockitoSession; -import org.mockito.junit.MockitoJUnitRunner; -import org.mockito.quality.Strictness; - -/** - * Test class for {@link CommunalManagerService}. - * - * Build/Install/Run: - * atest FrameworksMockingServicesTests:CommunalManagerServiceTest - */ -@RunWith(MockitoJUnitRunner.class) -@SmallTest -@Presubmit -public class CommunalManagerServiceTest { - private MockitoSession mMockingSession; - private CommunalManagerService mService; - - private ICommunalManager mBinder; - private ContextWrapper mContextSpy; - - @Before - public final void setUp() { - mMockingSession = mockitoSession() - .initMocks(this) - .strictness(Strictness.WARN) - .startMocking(); - - mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext())); - - doNothing().when(mContextSpy).enforceCallingPermission( - eq(Manifest.permission.WRITE_COMMUNAL_STATE), anyString()); - doNothing().when(mContextSpy).enforceCallingPermission( - eq(Manifest.permission.READ_COMMUNAL_STATE), anyString()); - - mService = new CommunalManagerService(mContextSpy); - mBinder = mService.getBinderServiceInstance(); - } - - @After - public void tearDown() { - if (mMockingSession != null) { - mMockingSession.finishMocking(); - } - } - - @Test - public void testIsCommunalMode_isTrue() throws RemoteException { - mBinder.setCommunalViewShowing(true); - assertThat(mBinder.isCommunalMode()).isTrue(); - } - - @Test - public void testIsCommunalMode_isFalse() throws RemoteException { - mBinder.setCommunalViewShowing(false); - assertThat(mBinder.isCommunalMode()).isFalse(); - } -} diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java index 0c3e472b46a8..bdeb2b4fd839 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java @@ -122,15 +122,14 @@ public class JobSchedulerServiceTest { .when(() -> LocalServices.getService(ActivityManagerInternal.class)); doReturn(mock(AppStandbyInternal.class)) .when(() -> LocalServices.getService(AppStandbyInternal.class)); + doReturn(mock(BatteryManagerInternal.class)) + .when(() -> LocalServices.getService(BatteryManagerInternal.class)); doReturn(mock(UsageStatsManagerInternal.class)) .when(() -> LocalServices.getService(UsageStatsManagerInternal.class)); when(mContext.getString(anyInt())).thenReturn("some_test_string"); // Called in BackgroundJobsController constructor. doReturn(mock(AppStateTrackerImpl.class)) .when(() -> LocalServices.getService(AppStateTracker.class)); - // Called in BatteryController constructor. - doReturn(mock(BatteryManagerInternal.class)) - .when(() -> LocalServices.getService(BatteryManagerInternal.class)); // Called in ConnectivityController constructor. when(mContext.getSystemService(ConnectivityManager.class)) .thenReturn(mock(ConnectivityManager.class)); diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java index a9853bf94eeb..f61d6ca750cd 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java @@ -48,18 +48,14 @@ import static org.mockito.Mockito.verify; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.job.JobInfo; -import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; -import android.content.Intent; import android.content.pm.PackageManagerInternal; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkPolicyManager; -import android.os.BatteryManager; -import android.os.BatteryManagerInternal; import android.os.Build; import android.os.Looper; import android.os.SystemClock; @@ -74,7 +70,6 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; -import org.mockito.ArgumentMatchers; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @@ -88,8 +83,6 @@ public class ConnectivityControllerTest { @Mock private Context mContext; @Mock - private BatteryManagerInternal mBatteryManagerInternal; - @Mock private ConnectivityManager mConnManager; @Mock private NetworkPolicyManager mNetPolicyManager; @@ -115,9 +108,6 @@ public class ConnectivityControllerTest { LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class); LocalServices.addService(NetworkPolicyManagerInternal.class, mNetPolicyManagerInternal); - LocalServices.removeServiceForTest(BatteryManagerInternal.class); - LocalServices.addService(BatteryManagerInternal.class, mBatteryManagerInternal); - when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper()); // Freeze the clocks at this moment in time @@ -158,18 +148,10 @@ public class ConnectivityControllerTest { .setMinimumNetworkChunkBytes(DataUnit.KIBIBYTES.toBytes(100)) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY); - final ArgumentCaptor<BroadcastReceiver> chargingCaptor = - ArgumentCaptor.forClass(BroadcastReceiver.class); - when(mBatteryManagerInternal.isPowered(eq(BatteryManager.BATTERY_PLUGGED_ANY))) - .thenReturn(false); + when(mService.isBatteryCharging()).thenReturn(false); final ConnectivityController controller = new ConnectivityController(mService); - verify(mContext).registerReceiver(chargingCaptor.capture(), - ArgumentMatchers.argThat(filter -> - filter.hasAction(BatteryManager.ACTION_CHARGING) - && filter.hasAction(BatteryManager.ACTION_DISCHARGING))); when(mService.getMaxJobExecutionTimeMs(any())).thenReturn(10 * 60_000L); - final BroadcastReceiver chargingReceiver = chargingCaptor.getValue(); - chargingReceiver.onReceive(mContext, new Intent(BatteryManager.ACTION_DISCHARGING)); + controller.onBatteryStateChangedLocked(); // Slow network is too slow assertFalse(controller.isSatisfied(createJobStatus(job), net, @@ -225,17 +207,15 @@ public class ConnectivityControllerTest { createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(130) .setLinkDownstreamBandwidthKbps(130).build(), mConstants)); // Slow network is too slow, but device is charging and network is unmetered. - when(mBatteryManagerInternal.isPowered(eq(BatteryManager.BATTERY_PLUGGED_ANY))) - .thenReturn(true); - chargingReceiver.onReceive(mContext, new Intent(BatteryManager.ACTION_CHARGING)); + when(mService.isBatteryCharging()).thenReturn(true); + controller.onBatteryStateChangedLocked(); assertTrue(controller.isSatisfied(createJobStatus(job), net, createCapabilitiesBuilder().addCapability(NET_CAPABILITY_NOT_METERED) .setLinkUpstreamBandwidthKbps(1).setLinkDownstreamBandwidthKbps(1).build(), mConstants)); - when(mBatteryManagerInternal.isPowered(eq(BatteryManager.BATTERY_PLUGGED_ANY))) - .thenReturn(false); - chargingReceiver.onReceive(mContext, new Intent(BatteryManager.ACTION_DISCHARGING)); + when(mService.isBatteryCharging()).thenReturn(false); + controller.onBatteryStateChangedLocked(); when(mService.getMaxJobExecutionTimeMs(any())).thenReturn(60_000L); // Slow network is too slow @@ -259,9 +239,8 @@ public class ConnectivityControllerTest { createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(130) .setLinkDownstreamBandwidthKbps(130).build(), mConstants)); // Slow network is too slow, but device is charging and network is unmetered. - when(mBatteryManagerInternal.isPowered(eq(BatteryManager.BATTERY_PLUGGED_ANY))) - .thenReturn(true); - chargingReceiver.onReceive(mContext, new Intent(BatteryManager.ACTION_CHARGING)); + when(mService.isBatteryCharging()).thenReturn(true); + controller.onBatteryStateChangedLocked(); assertTrue(controller.isSatisfied(createJobStatus(job), net, createCapabilitiesBuilder().addCapability(NET_CAPABILITY_NOT_METERED) .setLinkUpstreamBandwidthKbps(1).setLinkDownstreamBandwidthKbps(1).build(), diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java index 300f93feed28..cfae9a3d586a 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java @@ -63,16 +63,13 @@ import android.app.job.JobInfo; import android.app.usage.UsageEvents; import android.app.usage.UsageStatsManager; import android.app.usage.UsageStatsManagerInternal; -import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; -import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.ServiceInfo; -import android.os.BatteryManager; import android.os.BatteryManagerInternal; import android.os.Handler; import android.os.Looper; @@ -126,7 +123,6 @@ public class QuotaControllerTest { private static final String SOURCE_PACKAGE = "com.android.frameworks.mockingservicestests"; private static final int SOURCE_USER_ID = 0; - private BroadcastReceiver mChargingReceiver; private QuotaController mQuotaController; private QuotaController.QcConstants mQcConstants; private JobSchedulerService.Constants mConstants = new JobSchedulerService.Constants(); @@ -225,8 +221,6 @@ public class QuotaControllerTest { // Initialize real objects. // Capture the listeners. - ArgumentCaptor<BroadcastReceiver> receiverCaptor = - ArgumentCaptor.forClass(BroadcastReceiver.class); ArgumentCaptor<IUidObserver> uidObserverCaptor = ArgumentCaptor.forClass(IUidObserver.class); ArgumentCaptor<PowerAllowlistInternal.TempAllowlistChangeListener> taChangeCaptor = @@ -236,11 +230,6 @@ public class QuotaControllerTest { mQuotaController = new QuotaController(mJobSchedulerService, mock(BackgroundJobsController.class), mock(ConnectivityController.class)); - verify(mContext).registerReceiver(receiverCaptor.capture(), - ArgumentMatchers.argThat(filter -> - filter.hasAction(BatteryManager.ACTION_CHARGING) - && filter.hasAction(BatteryManager.ACTION_DISCHARGING))); - mChargingReceiver = receiverCaptor.getValue(); verify(mPowerAllowlistInternal) .registerTempAllowlistChangeListener(taChangeCaptor.capture()); mTempAllowlistListener = taChangeCaptor.getValue(); @@ -280,13 +269,17 @@ public class QuotaControllerTest { } private void setCharging() { - Intent intent = new Intent(BatteryManager.ACTION_CHARGING); - mChargingReceiver.onReceive(mContext, intent); + doReturn(true).when(mJobSchedulerService).isBatteryCharging(); + synchronized (mQuotaController.mLock) { + mQuotaController.onBatteryStateChangedLocked(); + } } private void setDischarging() { - Intent intent = new Intent(BatteryManager.ACTION_DISCHARGING); - mChargingReceiver.onReceive(mContext, intent); + doReturn(false).when(mJobSchedulerService).isBatteryCharging(); + synchronized (mQuotaController.mLock) { + mQuotaController.onBatteryStateChangedLocked(); + } } private void setProcessState(int procState) { diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java index f2415b4665d5..bdfdf7723c02 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java @@ -158,10 +158,10 @@ public class StagingManagerTest { mStagingManager.restoreSessions(Arrays.asList(session1, session2), true); - assertThat(session1.getErrorCode()).isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED); + assertThat(session1.getErrorCode()).isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED); assertThat(session1.getErrorMessage()).isEqualTo("Build fingerprint has changed"); - assertThat(session2.getErrorCode()).isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED); + assertThat(session2.getErrorCode()).isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED); assertThat(session2.getErrorMessage()).isEqualTo("Build fingerprint has changed"); } @@ -247,12 +247,12 @@ public class StagingManagerTest { verify(mStorageManager, never()).abortChanges(eq("abort-staged-install"), eq(false)); assertThat(apexSession.getErrorCode()) - .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED); + .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED); assertThat(apexSession.getErrorMessage()).isEqualTo("apexd did not know anything about a " + "staged session supposed to be activated"); assertThat(apkSession.getErrorCode()) - .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED); + .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED); assertThat(apkSession.getErrorMessage()).isEqualTo("Another apex session failed"); } @@ -303,22 +303,22 @@ public class StagingManagerTest { verify(mStorageManager, never()).abortChanges(eq("abort-staged-install"), eq(false)); assertThat(apexSession1.getErrorCode()) - .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED); + .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED); assertThat(apexSession1.getErrorMessage()).isEqualTo("APEX activation failed. " + "Error: Failed for test"); assertThat(apexSession2.getErrorCode()) - .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED); + .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED); assertThat(apexSession2.getErrorMessage()).isEqualTo("Staged session 101 at boot didn't " + "activate nor fail. Marking it as failed anyway."); assertThat(apexSession3.getErrorCode()) - .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED); + .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED); assertThat(apexSession3.getErrorMessage()).isEqualTo("apexd did not know anything about a " + "staged session supposed to be activated"); assertThat(apkSession.getErrorCode()) - .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED); + .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED); assertThat(apkSession.getErrorMessage()).isEqualTo("Another apex session failed"); } @@ -351,12 +351,12 @@ public class StagingManagerTest { verify(mStorageManager, never()).abortChanges(eq("abort-staged-install"), eq(false)); assertThat(apexSession.getErrorCode()) - .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED); + .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED); assertThat(apexSession.getErrorMessage()).isEqualTo("Staged session 1543 at boot didn't " + "activate nor fail. Marking it as failed anyway."); assertThat(apkSession.getErrorCode()) - .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED); + .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED); assertThat(apkSession.getErrorMessage()).isEqualTo("Another apex session failed"); } @@ -445,11 +445,11 @@ public class StagingManagerTest { verify(mStorageManager, never()).abortChanges(eq("abort-staged-install"), eq(false)); assertThat(apexSession.getErrorCode()) - .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED); + .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED); assertThat(apexSession.getErrorMessage()).isEqualTo("Impossible state"); assertThat(apkSession.getErrorCode()) - .isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED); + .isEqualTo(SessionInfo.SESSION_ACTIVATION_FAILED); assertThat(apkSession.getErrorMessage()).isEqualTo("Another apex session failed"); } @@ -754,7 +754,7 @@ public class StagingManagerTest { /* isReady */ false, /* isFailed */ false, /* isApplied */false, - /* stagedSessionErrorCode */ PackageInstaller.SessionInfo.STAGED_SESSION_NO_ERROR, + /* stagedSessionErrorCode */ PackageInstaller.SessionInfo.SESSION_NO_ERROR, /* stagedSessionErrorMessage */ "no error"); StagingManager.StagedSession stagedSession = spy(session.mStagedSession); diff --git a/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java b/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java new file mode 100644 index 000000000000..b65c3e939954 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.power; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.ActivityManagerInternal; +import android.attention.AttentionManagerInternal; +import android.content.Context; +import android.content.ContextWrapper; +import android.content.res.Resources; +import android.hardware.SensorManager; +import android.hardware.devicestate.DeviceStateManager; +import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback; +import android.hardware.display.AmbientDisplayConfiguration; +import android.hardware.display.DisplayManagerInternal; +import android.os.BatteryManagerInternal; +import android.os.Handler; +import android.os.Looper; +import android.os.PowerManager; +import android.os.PowerSaveState; +import android.os.test.TestLooper; +import android.provider.Settings; +import android.service.dreams.DreamManagerInternal; +import android.test.mock.MockContentResolver; +import android.view.Display; +import android.view.DisplayInfo; + +import androidx.test.InstrumentationRegistry; + +import com.android.internal.app.IBatteryStats; +import com.android.internal.util.test.FakeSettingsProvider; +import com.android.server.LocalServices; +import com.android.server.SystemService; +import com.android.server.lights.LightsManager; +import com.android.server.policy.WindowManagerPolicy; +import com.android.server.power.PowerManagerService.BatteryReceiver; +import com.android.server.power.PowerManagerService.Injector; +import com.android.server.power.PowerManagerService.NativeWrapper; +import com.android.server.power.PowerManagerService.UserSwitchedReceiver; +import com.android.server.power.batterysaver.BatterySaverController; +import com.android.server.power.batterysaver.BatterySaverPolicy; +import com.android.server.power.batterysaver.BatterySaverStateMachine; +import com.android.server.power.batterysaver.BatterySavingStats; +import com.android.server.testutils.OffsettableClock; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Tests for {@link com.android.server.power.PowerManagerService}. + * + * Build/Install/Run: + * atest FrameworksServicesTests:PowerManagerServiceMockingTest + */ +public class PowerManagerServiceMockingTest { + private static final String SYSTEM_PROPERTY_QUIESCENT = "ro.boot.quiescent"; + private static final String SYSTEM_PROPERTY_REBOOT_REASON = "sys.boot.reason"; + + private static final float BRIGHTNESS_FACTOR = 0.7f; + private static final boolean BATTERY_SAVER_ENABLED = true; + + @Mock private BatterySaverController mBatterySaverControllerMock; + @Mock private BatterySaverPolicy mBatterySaverPolicyMock; + @Mock private BatterySaverStateMachine mBatterySaverStateMachineMock; + @Mock private LightsManager mLightsManagerMock; + @Mock private DisplayManagerInternal mDisplayManagerInternalMock; + @Mock private BatteryManagerInternal mBatteryManagerInternalMock; + @Mock private ActivityManagerInternal mActivityManagerInternalMock; + @Mock private AttentionManagerInternal mAttentionManagerInternalMock; + @Mock private DreamManagerInternal mDreamManagerInternalMock; + @Mock private PowerManagerService.NativeWrapper mNativeWrapperMock; + @Mock private Notifier mNotifierMock; + @Mock private WirelessChargerDetector mWirelessChargerDetectorMock; + @Mock private AmbientDisplayConfiguration mAmbientDisplayConfigurationMock; + @Mock private SystemPropertiesWrapper mSystemPropertiesMock; + @Mock private DeviceStateManager mDeviceStateManagerMock; + + @Mock + private InattentiveSleepWarningController mInattentiveSleepWarningControllerMock; + + private PowerManagerService mService; + private PowerSaveState mPowerSaveState; + private ContextWrapper mContextSpy; + private BatteryReceiver mBatteryReceiver; + private UserSwitchedReceiver mUserSwitchedReceiver; + private Resources mResourcesSpy; + private OffsettableClock mClock; + private TestLooper mTestLooper; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + FakeSettingsProvider.clearSettingsProvider(); + + mPowerSaveState = new PowerSaveState.Builder() + .setBatterySaverEnabled(BATTERY_SAVER_ENABLED) + .setBrightnessFactor(BRIGHTNESS_FACTOR) + .build(); + when(mBatterySaverPolicyMock.getBatterySaverPolicy( + eq(PowerManager.ServiceType.SCREEN_BRIGHTNESS))) + .thenReturn(mPowerSaveState); + when(mBatteryManagerInternalMock.isPowered(anyInt())).thenReturn(false); + when(mInattentiveSleepWarningControllerMock.isShown()).thenReturn(false); + when(mDisplayManagerInternalMock.requestPowerState(anyInt(), any(), anyBoolean())) + .thenReturn(true); + when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), anyString())).thenReturn(""); + when(mAmbientDisplayConfigurationMock.ambientDisplayAvailable()).thenReturn(true); + + addLocalServiceMock(LightsManager.class, mLightsManagerMock); + addLocalServiceMock(DisplayManagerInternal.class, mDisplayManagerInternalMock); + addLocalServiceMock(BatteryManagerInternal.class, mBatteryManagerInternalMock); + addLocalServiceMock(ActivityManagerInternal.class, mActivityManagerInternalMock); + addLocalServiceMock(AttentionManagerInternal.class, mAttentionManagerInternalMock); + addLocalServiceMock(DreamManagerInternal.class, mDreamManagerInternalMock); + + mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext())); + mResourcesSpy = spy(mContextSpy.getResources()); + when(mContextSpy.getResources()).thenReturn(mResourcesSpy); + + MockContentResolver cr = new MockContentResolver(mContextSpy); + cr.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); + when(mContextSpy.getContentResolver()).thenReturn(cr); + + when(mContextSpy.getSystemService(DeviceStateManager.class)) + .thenReturn(mDeviceStateManagerMock); + + Settings.Global.putInt(mContextSpy.getContentResolver(), + Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0); + + mClock = new OffsettableClock.Stopped(); + mTestLooper = new TestLooper(mClock::now); + } + + private PowerManagerService createService() { + mService = new PowerManagerService(mContextSpy, new Injector() { + @Override + Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats, + SuspendBlocker suspendBlocker, WindowManagerPolicy policy, + FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector) { + return mNotifierMock; + } + + @Override + SuspendBlocker createSuspendBlocker(PowerManagerService service, String name) { + return super.createSuspendBlocker(service, name); + } + + @Override + BatterySaverPolicy createBatterySaverPolicy( + Object lock, Context context, BatterySavingStats batterySavingStats) { + return mBatterySaverPolicyMock; + } + + @Override + BatterySaverController createBatterySaverController( + Object lock, Context context, BatterySaverPolicy batterySaverPolicy, + BatterySavingStats batterySavingStats) { + return mBatterySaverControllerMock; + } + + @Override + BatterySaverStateMachine createBatterySaverStateMachine(Object lock, Context context, + BatterySaverController batterySaverController) { + return mBatterySaverStateMachineMock; + } + + @Override + NativeWrapper createNativeWrapper() { + return mNativeWrapperMock; + } + + @Override + WirelessChargerDetector createWirelessChargerDetector( + SensorManager sensorManager, SuspendBlocker suspendBlocker, Handler handler) { + return mWirelessChargerDetectorMock; + } + + @Override + AmbientDisplayConfiguration createAmbientDisplayConfiguration(Context context) { + return mAmbientDisplayConfigurationMock; + } + + @Override + InattentiveSleepWarningController createInattentiveSleepWarningController() { + return mInattentiveSleepWarningControllerMock; + } + + @Override + public SystemPropertiesWrapper createSystemPropertiesWrapper() { + return mSystemPropertiesMock; + } + + @Override + PowerManagerService.Clock createClock() { + return () -> mClock.now(); + } + + @Override + Handler createHandler(Looper looper, Handler.Callback callback) { + return new Handler(mTestLooper.getLooper(), callback); + } + + @Override + void invalidateIsInteractiveCaches() { + // Avoids an SELinux failure. + } + }); + return mService; + } + + @After + public void tearDown() throws Exception { + LocalServices.removeServiceForTest(LightsManager.class); + LocalServices.removeServiceForTest(DisplayManagerInternal.class); + LocalServices.removeServiceForTest(BatteryManagerInternal.class); + LocalServices.removeServiceForTest(ActivityManagerInternal.class); + LocalServices.removeServiceForTest(AttentionManagerInternal.class); + LocalServices.removeServiceForTest(DreamManagerInternal.class); + FakeSettingsProvider.clearSettingsProvider(); + } + + /** + * Creates a mock and registers it to {@link LocalServices}. + */ + private static <T> void addLocalServiceMock(Class<T> clazz, T mock) { + LocalServices.removeServiceForTest(clazz); + LocalServices.addService(clazz, mock); + } + + private void advanceTime(long timeMs) { + mClock.fastForward(timeMs); + mTestLooper.dispatchAll(); + } + + @Test + public void testUserActivityOnDeviceStateChange() { + createService(); + mService.systemReady(null); + mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); + + final DisplayInfo info = new DisplayInfo(); + info.displayGroupId = Display.DEFAULT_DISPLAY_GROUP; + when(mDisplayManagerInternalMock.getDisplayInfo(Display.DEFAULT_DISPLAY)).thenReturn(info); + + final ArgumentCaptor<DeviceStateCallback> deviceStateCallbackCaptor = + ArgumentCaptor.forClass(DeviceStateCallback.class); + verify(mDeviceStateManagerMock).registerCallback(any(), + deviceStateCallbackCaptor.capture()); + + // Advance the time 10001 and verify that the device thinks it has been idle + // for just less than that. + mService.onUserActivity(); + advanceTime(10001); + assertThat(mService.wasDeviceIdleForInternal(10000)).isTrue(); + + // Send a display state change event and advance the clock 10. + final DeviceStateCallback deviceStateCallback = deviceStateCallbackCaptor.getValue(); + deviceStateCallback.onStateChanged(1); + final long timeToAdvance = 10; + advanceTime(timeToAdvance); + + // Ensure that the device has been idle for only 10 (doesn't include the idle time + // before the display state event). + assertThat(mService.wasDeviceIdleForInternal(timeToAdvance - 1)).isTrue(); + assertThat(mService.wasDeviceIdleForInternal(timeToAdvance)).isFalse(); + + // Send the same state and ensure that does not trigger an update. + deviceStateCallback.onStateChanged(1); + advanceTime(timeToAdvance); + final long newTime = timeToAdvance * 2; + + assertThat(mService.wasDeviceIdleForInternal(newTime - 1)).isTrue(); + assertThat(mService.wasDeviceIdleForInternal(newTime)).isFalse(); + } +} diff --git a/services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java b/services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java index 6a3548178cba..234d70b98580 100644 --- a/services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java @@ -22,6 +22,7 @@ import static android.hardware.display.DisplayManagerInternal.DisplayPowerReques import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_OFF; import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_VR; import static android.provider.DeviceConfig.NAMESPACE_ATTENTION_MANAGER_SERVICE; +import static android.view.Display.DEFAULT_DISPLAY_GROUP; import static com.android.server.power.ScreenUndimDetector.DEFAULT_MAX_DURATION_BETWEEN_UNDIMS_MILLIS; import static com.android.server.power.ScreenUndimDetector.KEY_KEEP_SCREEN_ON_ENABLED; @@ -60,6 +61,7 @@ public class ScreenUndimDetectorTest { POLICY_DIM, POLICY_BRIGHT, POLICY_VR); + private static final int OTHER_DISPLAY_GROUP = DEFAULT_DISPLAY_GROUP + 1; @ClassRule public static final TestableContext sContext = new TestableContext( @@ -106,8 +108,8 @@ public class ScreenUndimDetectorTest { KEY_KEEP_SCREEN_ON_ENABLED, Boolean.FALSE.toString(), false /*makeDefault*/); setup(); - mScreenUndimDetector.recordScreenPolicy(POLICY_DIM); - mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_DIM); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT); assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse(); } @@ -116,8 +118,8 @@ public class ScreenUndimDetectorTest { public void recordScreenPolicy_samePolicy_noop() { for (int policy : ALL_POLICIES) { setup(); - mScreenUndimDetector.recordScreenPolicy(policy); - mScreenUndimDetector.recordScreenPolicy(policy); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, policy); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, policy); assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse(); } @@ -125,13 +127,24 @@ public class ScreenUndimDetectorTest { @Test public void recordScreenPolicy_dimToBright_extends() { - mScreenUndimDetector.recordScreenPolicy(POLICY_DIM); - mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_DIM); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT); assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isTrue(); } @Test + public void recordScreenPolicy_dimToBright_ignoresOtherDisplayGroup() { + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_DIM); + + mScreenUndimDetector.recordScreenPolicy(OTHER_DISPLAY_GROUP, POLICY_BRIGHT); + assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse(); + + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT); + assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isTrue(); + } + + @Test public void recordScreenPolicy_otherTransitions_doesNotExtend() { for (int from : ALL_POLICIES) { for (int to : ALL_POLICIES) { @@ -139,8 +152,8 @@ public class ScreenUndimDetectorTest { continue; } setup(); - mScreenUndimDetector.recordScreenPolicy(from); - mScreenUndimDetector.recordScreenPolicy(to); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, from); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, to); assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse(); assertThat(mScreenUndimDetector.mUndimCounter).isEqualTo(0); @@ -155,14 +168,35 @@ public class ScreenUndimDetectorTest { Integer.toString(2), false /*makeDefault*/); mScreenUndimDetector.readValuesFromDeviceConfig(); - mScreenUndimDetector.recordScreenPolicy(POLICY_DIM); - mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_DIM); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT); + + assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse(); + + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_DIM); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT); + + assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isTrue(); + } + + @Test + public void recordScreenPolicy_dimToBright_twoUndimsNeeded_otherDisplayDoesNotExtend() { + DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE, + KEY_UNDIMS_REQUIRED, + Integer.toString(2), false /*makeDefault*/); + mScreenUndimDetector.readValuesFromDeviceConfig(); + + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_DIM); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT); assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse(); - mScreenUndimDetector.recordScreenPolicy(POLICY_DIM); - mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_DIM); + mScreenUndimDetector.recordScreenPolicy(OTHER_DISPLAY_GROUP, POLICY_BRIGHT); + + assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse(); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT); assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isTrue(); } @@ -173,10 +207,10 @@ public class ScreenUndimDetectorTest { Integer.toString(2), false /*makeDefault*/); mScreenUndimDetector.readValuesFromDeviceConfig(); - mScreenUndimDetector.recordScreenPolicy(POLICY_DIM); - mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT); - mScreenUndimDetector.recordScreenPolicy(POLICY_DIM); - mScreenUndimDetector.recordScreenPolicy(POLICY_OFF); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_DIM); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_DIM); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_OFF); assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse(); assertThat(mScreenUndimDetector.mUndimCounter).isEqualTo(0); @@ -189,10 +223,27 @@ public class ScreenUndimDetectorTest { Integer.toString(2), false /*makeDefault*/); mScreenUndimDetector.readValuesFromDeviceConfig(); - mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT); - mScreenUndimDetector.recordScreenPolicy(POLICY_DIM); - mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT); - mScreenUndimDetector.recordScreenPolicy(POLICY_OFF); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_DIM); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_OFF); + + assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse(); + assertThat(mScreenUndimDetector.mUndimCounter).isEqualTo(0); + } + + @Test + public void recordScreenPolicy_undimToOff_otherDisplayDoesNotResetCounter() { + DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE, + KEY_UNDIMS_REQUIRED, + Integer.toString(2), false /*makeDefault*/); + mScreenUndimDetector.readValuesFromDeviceConfig(); + + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_DIM); + + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_OFF); assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse(); assertThat(mScreenUndimDetector.mUndimCounter).isEqualTo(0); @@ -206,15 +257,15 @@ public class ScreenUndimDetectorTest { mScreenUndimDetector.readValuesFromDeviceConfig(); // undim - mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT); - mScreenUndimDetector.recordScreenPolicy(POLICY_DIM); - mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_DIM); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT); // off - mScreenUndimDetector.recordScreenPolicy(POLICY_OFF); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_OFF); // second undim - mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT); - mScreenUndimDetector.recordScreenPolicy(POLICY_DIM); - mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_DIM); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT); assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse(); assertThat(mScreenUndimDetector.mUndimCounter).isEqualTo(1); @@ -227,12 +278,12 @@ public class ScreenUndimDetectorTest { Integer.toString(2), false /*makeDefault*/); mScreenUndimDetector.readValuesFromDeviceConfig(); - mScreenUndimDetector.recordScreenPolicy(POLICY_DIM); - mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_DIM); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT); mClock.advanceTime(DEFAULT_MAX_DURATION_BETWEEN_UNDIMS_MILLIS + 5); - mScreenUndimDetector.recordScreenPolicy(POLICY_DIM); - mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_DIM); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT); assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse(); assertThat(mScreenUndimDetector.mUndimCounter).isEqualTo(1); @@ -246,8 +297,8 @@ public class ScreenUndimDetectorTest { mScreenUndimDetector.mUndimCounterStartedMillis = 123; mScreenUndimDetector.mWakeLock.acquire(); - mScreenUndimDetector.recordScreenPolicy(POLICY_DIM); - mScreenUndimDetector.recordScreenPolicy(to); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_DIM); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, to); assertThat(mScreenUndimDetector.mUndimCounter).isEqualTo(0); assertThat(mScreenUndimDetector.mUndimCounterStartedMillis).isEqualTo(0); @@ -264,8 +315,8 @@ public class ScreenUndimDetectorTest { mScreenUndimDetector.mUndimCounterStartedMillis = 123; mScreenUndimDetector.mWakeLock.acquire(); - mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT); - mScreenUndimDetector.recordScreenPolicy(to); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, POLICY_BRIGHT); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, to); assertThat(mScreenUndimDetector.mUndimCounter).isEqualTo(0); assertThat(mScreenUndimDetector.mUndimCounterStartedMillis).isEqualTo(0); @@ -294,8 +345,8 @@ public class ScreenUndimDetectorTest { mScreenUndimDetector.mUndimCounterStartedMillis = SystemClock.currentThreadTimeMillis(); - mScreenUndimDetector.recordScreenPolicy(from); - mScreenUndimDetector.recordScreenPolicy(to); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, from); + mScreenUndimDetector.recordScreenPolicy(DEFAULT_DISPLAY_GROUP, to); assertThat(mScreenUndimDetector.mUndimCounter).isNotEqualTo(0); assertThat(mScreenUndimDetector.mUndimCounterStartedMillis).isNotEqualTo(0); diff --git a/services/tests/servicestests/src/com/android/server/SystemServiceManagerTest.java b/services/tests/servicestests/src/com/android/server/SystemServiceManagerTest.java new file mode 100644 index 000000000000..f92f5ead2c3b --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/SystemServiceManagerTest.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import static org.junit.Assert.assertThrows; + +import android.test.AndroidTestCase; + +import org.junit.Test; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + + +/** + * Tests for {@link SystemServiceManager}. + */ +public class SystemServiceManagerTest extends AndroidTestCase { + + private static final String TAG = "SystemServiceManagerTest"; + + private final SystemServiceManager mSystemServiceManager = + new SystemServiceManager(getContext()); + + @Test + public void testSealStartedServices() throws Exception { + // must be effectively final, since it's changed from inner class below + AtomicBoolean serviceStarted = new AtomicBoolean(false); + SystemService service1 = new SystemService(getContext()) { + @Override + public void onStart() { + serviceStarted.set(true); + } + }; + SystemService service2 = new SystemService(getContext()) { + @Override + public void onStart() { + throw new IllegalStateException("Second service must not be called"); + } + }; + + // started services have their #onStart methods called + mSystemServiceManager.startService(service1); + assertTrue(serviceStarted.get()); + + // however, after locking started services, it is not possible to start a new service + mSystemServiceManager.sealStartedServices(); + assertThrows(UnsupportedOperationException.class, + () -> mSystemServiceManager.startService(service2)); + } + + @Test + public void testDuplicateServices() throws Exception { + AtomicInteger counter = new AtomicInteger(0); + SystemService service = new SystemService(getContext()) { + @Override + public void onStart() { + counter.incrementAndGet(); + } + }; + + mSystemServiceManager.startService(service); + assertEquals(1, counter.get()); + + // manager does not start the same service twice + mSystemServiceManager.startService(service); + assertEquals(1, counter.get()); + } + +} diff --git a/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java b/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java index cffff66b64f1..02cf971a8076 100644 --- a/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java @@ -23,7 +23,14 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.debug.AdbManager; +import android.debug.IAdbManager; +import android.os.ServiceManager; import android.provider.Settings; import android.util.Log; @@ -105,6 +112,7 @@ public final class AdbDebuggingManagerTest { public void tearDown() throws Exception { mKeyStore.deleteKeyStore(); setAllowedConnectionTime(mOriginalAllowedConnectionTime); + dropShellPermissionIdentity(); } /** @@ -813,6 +821,108 @@ public final class AdbDebuggingManagerTest { return hasAtLeastOneLetter; } + CountDownLatch mAdbActionLatch = new CountDownLatch(1); + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + Log.i(TAG, "Received intent action=" + action); + if (AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION.equals(action)) { + assertEquals("Received broadcast without MANAGE_DEBUGGING permission.", + context.checkSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING), + PackageManager.PERMISSION_GRANTED); + Log.i(TAG, "action=" + action + " paired_device=" + intent.getSerializableExtra( + AdbManager.WIRELESS_DEVICES_EXTRA).toString()); + mAdbActionLatch.countDown(); + } else if (AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION.equals(action)) { + assertEquals("Received broadcast without MANAGE_DEBUGGING permission.", + context.checkSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING), + PackageManager.PERMISSION_GRANTED); + int status = intent.getIntExtra(AdbManager.WIRELESS_STATUS_EXTRA, + AdbManager.WIRELESS_STATUS_DISCONNECTED); + Log.i(TAG, "action=" + action + " status=" + status); + mAdbActionLatch.countDown(); + } else if (AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION.equals(action)) { + assertEquals("Received broadcast without MANAGE_DEBUGGING permission.", + context.checkSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING), + PackageManager.PERMISSION_GRANTED); + Integer res = intent.getIntExtra( + AdbManager.WIRELESS_STATUS_EXTRA, + AdbManager.WIRELESS_STATUS_FAIL); + Log.i(TAG, "action=" + action + " result=" + res); + + if (res.equals(AdbManager.WIRELESS_STATUS_PAIRING_CODE)) { + String pairingCode = intent.getStringExtra( + AdbManager.WIRELESS_PAIRING_CODE_EXTRA); + Log.i(TAG, "pairingCode=" + pairingCode); + } else if (res.equals(AdbManager.WIRELESS_STATUS_CONNECTED)) { + int port = intent.getIntExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, 0); + Log.i(TAG, "port=" + port); + } + mAdbActionLatch.countDown(); + } + } + }; + + private void adoptShellPermissionIdentity() { + InstrumentationRegistry.getInstrumentation().getUiAutomation() + .adoptShellPermissionIdentity(android.Manifest.permission.MANAGE_DEBUGGING); + } + + private void dropShellPermissionIdentity() { + InstrumentationRegistry.getInstrumentation().getUiAutomation() + .dropShellPermissionIdentity(); + } + + @Test + public void testBroadcastReceiverWithPermissions() throws Exception { + adoptShellPermissionIdentity(); + final IAdbManager mAdbManager = IAdbManager.Stub.asInterface( + ServiceManager.getService(Context.ADB_SERVICE)); + IntentFilter intentFilter = + new IntentFilter(AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION); + intentFilter.addAction(AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION); + intentFilter.addAction(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION); + assertEquals("Context does not have MANAGE_DEBUGGING permission.", + mContext.checkSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING), + PackageManager.PERMISSION_GRANTED); + try { + mContext.registerReceiver(mReceiver, intentFilter); + mAdbManager.enablePairingByPairingCode(); + if (!mAdbActionLatch.await(TIMEOUT, TIMEOUT_TIME_UNIT)) { + fail("Receiver did not receive adb intent action within the timeout duration"); + } + } finally { + mContext.unregisterReceiver(mReceiver); + } + } + + @Test + public void testBroadcastReceiverWithoutPermissions() throws Exception { + adoptShellPermissionIdentity(); + final IAdbManager mAdbManager = IAdbManager.Stub.asInterface( + ServiceManager.getService(Context.ADB_SERVICE)); + IntentFilter intentFilter = + new IntentFilter(AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION); + intentFilter.addAction(AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION); + intentFilter.addAction(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION); + mAdbManager.enablePairingByPairingCode(); + + dropShellPermissionIdentity(); + assertEquals("Context has MANAGE_DEBUGGING permission.", + mContext.checkSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING), + PackageManager.PERMISSION_DENIED); + try { + mContext.registerReceiver(mReceiver, intentFilter); + + if (mAdbActionLatch.await(TIMEOUT, TIMEOUT_TIME_UNIT)) { + fail("Broadcast receiver received adb action intent without debug permissions"); + } + } finally { + mContext.unregisterReceiver(mReceiver); + } + } + /** * Runs an adb test with the provided configuration. * diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java deleted file mode 100644 index d4bac2c0402d..000000000000 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java +++ /dev/null @@ -1,326 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.biometrics.sensors; - -import static android.testing.TestableLooper.RunWithLooper; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyBoolean; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.testng.Assert.assertThrows; - -import android.os.Handler; -import android.platform.test.annotations.Presubmit; -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; - -import androidx.test.filters.SmallTest; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@Presubmit -@RunWith(AndroidTestingRunner.class) -@RunWithLooper(setAsMainLooper = true) -@SmallTest -public class BiometricSchedulerOperationTest { - - public interface FakeHal {} - public abstract static class InterruptableMonitor<T> - extends HalClientMonitor<T> implements Interruptable { - public InterruptableMonitor() { - super(null, null, null, null, 0, null, 0, 0, 0, 0, 0); - } - } - - @Mock - private InterruptableMonitor<FakeHal> mClientMonitor; - @Mock - private BaseClientMonitor.Callback mClientCallback; - @Mock - private FakeHal mHal; - @Captor - ArgumentCaptor<BaseClientMonitor.Callback> mStartCallback; - - private Handler mHandler; - private BiometricSchedulerOperation mOperation; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mHandler = new Handler(TestableLooper.get(this).getLooper()); - mOperation = new BiometricSchedulerOperation(mClientMonitor, mClientCallback); - } - - @Test - public void testStartWithCookie() { - final int cookie = 200; - when(mClientMonitor.getCookie()).thenReturn(cookie); - when(mClientMonitor.getFreshDaemon()).thenReturn(mHal); - - assertThat(mOperation.isReadyToStart()).isEqualTo(cookie); - assertThat(mOperation.isStarted()).isFalse(); - assertThat(mOperation.isCanceling()).isFalse(); - assertThat(mOperation.isFinished()).isFalse(); - - final boolean started = mOperation.startWithCookie( - mock(BaseClientMonitor.Callback.class), cookie); - - assertThat(started).isTrue(); - verify(mClientMonitor).start(mStartCallback.capture()); - mStartCallback.getValue().onClientStarted(mClientMonitor); - assertThat(mOperation.isStarted()).isTrue(); - } - - @Test - public void testNoStartWithoutCookie() { - final int goodCookie = 20; - final int badCookie = 22; - when(mClientMonitor.getCookie()).thenReturn(goodCookie); - when(mClientMonitor.getFreshDaemon()).thenReturn(mHal); - - assertThat(mOperation.isReadyToStart()).isEqualTo(goodCookie); - final boolean started = mOperation.startWithCookie( - mock(BaseClientMonitor.Callback.class), badCookie); - - assertThat(started).isFalse(); - assertThat(mOperation.isStarted()).isFalse(); - assertThat(mOperation.isCanceling()).isFalse(); - assertThat(mOperation.isFinished()).isFalse(); - } - - @Test - public void startsWhenReadyAndHalAvailable() { - when(mClientMonitor.getCookie()).thenReturn(0); - when(mClientMonitor.getFreshDaemon()).thenReturn(mHal); - - final BaseClientMonitor.Callback cb = mock(BaseClientMonitor.Callback.class); - mOperation.start(cb); - verify(mClientMonitor).start(mStartCallback.capture()); - mStartCallback.getValue().onClientStarted(mClientMonitor); - - assertThat(mOperation.isStarted()).isTrue(); - assertThat(mOperation.isCanceling()).isFalse(); - assertThat(mOperation.isFinished()).isFalse(); - - verify(mClientCallback).onClientStarted(eq(mClientMonitor)); - verify(cb).onClientStarted(eq(mClientMonitor)); - verify(mClientCallback, never()).onClientFinished(any(), anyBoolean()); - verify(cb, never()).onClientFinished(any(), anyBoolean()); - - mStartCallback.getValue().onClientFinished(mClientMonitor, true); - - assertThat(mOperation.isFinished()).isTrue(); - assertThat(mOperation.isCanceling()).isFalse(); - verify(mClientMonitor).destroy(); - verify(cb).onClientFinished(eq(mClientMonitor), eq(true)); - } - - @Test - public void startFailsWhenReadyButHalNotAvailable() { - when(mClientMonitor.getCookie()).thenReturn(0); - when(mClientMonitor.getFreshDaemon()).thenReturn(null); - - final BaseClientMonitor.Callback cb = mock(BaseClientMonitor.Callback.class); - mOperation.start(cb); - verify(mClientMonitor, never()).start(any()); - - assertThat(mOperation.isStarted()).isFalse(); - assertThat(mOperation.isCanceling()).isFalse(); - assertThat(mOperation.isFinished()).isTrue(); - - verify(mClientCallback, never()).onClientStarted(eq(mClientMonitor)); - verify(cb, never()).onClientStarted(eq(mClientMonitor)); - verify(mClientCallback).onClientFinished(eq(mClientMonitor), eq(false)); - verify(cb).onClientFinished(eq(mClientMonitor), eq(false)); - } - - @Test - public void doesNotStartWithCookie() { - when(mClientMonitor.getCookie()).thenReturn(9); - assertThrows(IllegalStateException.class, - () -> mOperation.start(mock(BaseClientMonitor.Callback.class))); - } - - @Test - public void cannotRestart() { - when(mClientMonitor.getFreshDaemon()).thenReturn(mHal); - - mOperation.start(mock(BaseClientMonitor.Callback.class)); - - assertThrows(IllegalStateException.class, - () -> mOperation.start(mock(BaseClientMonitor.Callback.class))); - } - - @Test - public void abortsNotRunning() { - when(mClientMonitor.getFreshDaemon()).thenReturn(mHal); - - mOperation.abort(); - - assertThat(mOperation.isFinished()).isTrue(); - verify(mClientMonitor).unableToStart(); - verify(mClientMonitor).destroy(); - assertThrows(IllegalStateException.class, - () -> mOperation.start(mock(BaseClientMonitor.Callback.class))); - } - - @Test - public void cannotAbortRunning() { - when(mClientMonitor.getFreshDaemon()).thenReturn(mHal); - - mOperation.start(mock(BaseClientMonitor.Callback.class)); - - assertThrows(IllegalStateException.class, () -> mOperation.abort()); - } - - @Test - public void cancel() { - when(mClientMonitor.getFreshDaemon()).thenReturn(mHal); - - final BaseClientMonitor.Callback startCb = mock(BaseClientMonitor.Callback.class); - final BaseClientMonitor.Callback cancelCb = mock(BaseClientMonitor.Callback.class); - mOperation.start(startCb); - verify(mClientMonitor).start(mStartCallback.capture()); - mStartCallback.getValue().onClientStarted(mClientMonitor); - mOperation.cancel(mHandler, cancelCb); - - assertThat(mOperation.isCanceling()).isTrue(); - verify(mClientMonitor).cancel(); - verify(mClientMonitor, never()).cancelWithoutStarting(any()); - verify(mClientMonitor, never()).destroy(); - - mStartCallback.getValue().onClientFinished(mClientMonitor, true); - - assertThat(mOperation.isFinished()).isTrue(); - assertThat(mOperation.isCanceling()).isFalse(); - verify(mClientMonitor).destroy(); - - // should be unused since the operation was started - verify(cancelCb, never()).onClientStarted(any()); - verify(cancelCb, never()).onClientFinished(any(), anyBoolean()); - } - - @Test - public void cancelWithoutStarting() { - when(mClientMonitor.getFreshDaemon()).thenReturn(mHal); - - final BaseClientMonitor.Callback cancelCb = mock(BaseClientMonitor.Callback.class); - mOperation.cancel(mHandler, cancelCb); - - assertThat(mOperation.isCanceling()).isTrue(); - ArgumentCaptor<BaseClientMonitor.Callback> cbCaptor = - ArgumentCaptor.forClass(BaseClientMonitor.Callback.class); - verify(mClientMonitor).cancelWithoutStarting(cbCaptor.capture()); - - cbCaptor.getValue().onClientFinished(mClientMonitor, true); - verify(cancelCb).onClientFinished(eq(mClientMonitor), eq(true)); - verify(mClientMonitor, never()).start(any()); - verify(mClientMonitor, never()).cancel(); - verify(mClientMonitor).destroy(); - } - - @Test - public void markCanceling() { - when(mClientMonitor.getFreshDaemon()).thenReturn(mHal); - - mOperation.markCanceling(); - - assertThat(mOperation.isMarkedCanceling()).isTrue(); - assertThat(mOperation.isCanceling()).isFalse(); - assertThat(mOperation.isFinished()).isFalse(); - verify(mClientMonitor, never()).start(any()); - verify(mClientMonitor, never()).cancel(); - verify(mClientMonitor, never()).cancelWithoutStarting(any()); - verify(mClientMonitor, never()).unableToStart(); - verify(mClientMonitor, never()).destroy(); - } - - @Test - public void cancelPendingWithCookie() { - markCancellingAndStart(2); - } - - @Test - public void cancelPendingWithoutCookie() { - markCancellingAndStart(null); - } - - private void markCancellingAndStart(Integer withCookie) { - when(mClientMonitor.getFreshDaemon()).thenReturn(mHal); - if (withCookie != null) { - when(mClientMonitor.getCookie()).thenReturn(withCookie); - } - - mOperation.markCanceling(); - final BaseClientMonitor.Callback cb = mock(BaseClientMonitor.Callback.class); - if (withCookie != null) { - mOperation.startWithCookie(cb, withCookie); - } else { - mOperation.start(cb); - } - - assertThat(mOperation.isFinished()).isTrue(); - verify(cb).onClientFinished(eq(mClientMonitor), eq(true)); - verify(mClientMonitor, never()).start(any()); - verify(mClientMonitor, never()).cancel(); - verify(mClientMonitor, never()).cancelWithoutStarting(any()); - verify(mClientMonitor, never()).unableToStart(); - verify(mClientMonitor).destroy(); - } - - @Test - public void cancelWatchdogWhenStarted() { - cancelWatchdog(true); - } - - @Test - public void cancelWatchdogWithoutStarting() { - cancelWatchdog(false); - } - - private void cancelWatchdog(boolean start) { - when(mClientMonitor.getFreshDaemon()).thenReturn(mHal); - - mOperation.start(mock(BaseClientMonitor.Callback.class)); - if (start) { - verify(mClientMonitor).start(mStartCallback.capture()); - mStartCallback.getValue().onClientStarted(mClientMonitor); - } - mOperation.cancel(mHandler, mock(BaseClientMonitor.Callback.class)); - - assertThat(mOperation.isCanceling()).isTrue(); - - // omit call to onClientFinished and trigger watchdog - mOperation.mCancelWatchdog.run(); - - assertThat(mOperation.isFinished()).isTrue(); - assertThat(mOperation.isCanceling()).isFalse(); - verify(mClientMonitor).destroy(); - } -} diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java index ac0831983262..d192697827f6 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java @@ -16,14 +16,10 @@ package com.android.server.biometrics.sensors; -import static android.testing.TestableLooper.RunWithLooper; - import static junit.framework.Assert.assertTrue; -import static junit.framework.Assert.fail; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; @@ -38,13 +34,10 @@ import android.content.Context; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.IBiometricService; import android.os.Binder; -import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; -import android.testing.AndroidTestingRunner; import android.testing.TestableContext; -import android.testing.TestableLooper; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -53,18 +46,16 @@ import androidx.test.filters.SmallTest; import com.android.server.biometrics.nano.BiometricSchedulerProto; import com.android.server.biometrics.nano.BiometricsProto; +import com.android.server.biometrics.sensors.BiometricScheduler.Operation; import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @Presubmit @SmallTest -@RunWith(AndroidTestingRunner.class) -@RunWithLooper(setAsMainLooper = true) public class BiometricSchedulerTest { private static final String TAG = "BiometricSchedulerTest"; @@ -85,9 +76,8 @@ public class BiometricSchedulerTest { public void setUp() { MockitoAnnotations.initMocks(this); mToken = new Binder(); - mScheduler = new BiometricScheduler(TAG, new Handler(TestableLooper.get(this).getLooper()), - BiometricScheduler.SENSOR_TYPE_UNKNOWN, null /* gestureAvailabilityTracker */, - mBiometricService, LOG_NUM_RECENT_OPERATIONS, + mScheduler = new BiometricScheduler(TAG, BiometricScheduler.SENSOR_TYPE_UNKNOWN, + null /* gestureAvailabilityTracker */, mBiometricService, LOG_NUM_RECENT_OPERATIONS, CoexCoordinator.getInstance()); } @@ -96,9 +86,9 @@ public class BiometricSchedulerTest { final HalClientMonitor.LazyDaemon<Object> nonNullDaemon = () -> mock(Object.class); final HalClientMonitor<Object> client1 = - new TestHalClientMonitor(mContext, mToken, nonNullDaemon); + new TestClientMonitor(mContext, mToken, nonNullDaemon); final HalClientMonitor<Object> client2 = - new TestHalClientMonitor(mContext, mToken, nonNullDaemon); + new TestClientMonitor(mContext, mToken, nonNullDaemon); mScheduler.scheduleClientMonitor(client1); mScheduler.scheduleClientMonitor(client2); @@ -109,17 +99,20 @@ public class BiometricSchedulerTest { @Test public void testRemovesPendingOperations_whenNullHal_andNotBiometricPrompt() { // Even if second client has a non-null daemon, it needs to be canceled. - final TestHalClientMonitor client1 = new TestHalClientMonitor( - mContext, mToken, () -> null); - final TestHalClientMonitor client2 = new TestHalClientMonitor( - mContext, mToken, () -> mock(Object.class)); + Object daemon2 = mock(Object.class); + + final HalClientMonitor.LazyDaemon<Object> lazyDaemon1 = () -> null; + final HalClientMonitor.LazyDaemon<Object> lazyDaemon2 = () -> daemon2; + + final TestClientMonitor client1 = new TestClientMonitor(mContext, mToken, lazyDaemon1); + final TestClientMonitor client2 = new TestClientMonitor(mContext, mToken, lazyDaemon2); final BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class); final BaseClientMonitor.Callback callback2 = mock(BaseClientMonitor.Callback.class); // Pretend the scheduler is busy so the first operation doesn't start right away. We want // to pretend like there are two operations in the queue before kicking things off - mScheduler.mCurrentOperation = new BiometricSchedulerOperation( + mScheduler.mCurrentOperation = new BiometricScheduler.Operation( mock(BaseClientMonitor.class), mock(BaseClientMonitor.Callback.class)); mScheduler.scheduleClientMonitor(client1, callback1); @@ -129,11 +122,11 @@ public class BiometricSchedulerTest { mScheduler.scheduleClientMonitor(client2, callback2); waitForIdle(); - assertTrue(client1.mUnableToStart); + assertTrue(client1.wasUnableToStart()); verify(callback1).onClientFinished(eq(client1), eq(false) /* success */); verify(callback1, never()).onClientStarted(any()); - assertTrue(client2.mUnableToStart); + assertTrue(client2.wasUnableToStart()); verify(callback2).onClientFinished(eq(client2), eq(false) /* success */); verify(callback2, never()).onClientStarted(any()); @@ -145,19 +138,21 @@ public class BiometricSchedulerTest { // Second non-BiometricPrompt client has a valid daemon final Object daemon2 = mock(Object.class); + final HalClientMonitor.LazyDaemon<Object> lazyDaemon1 = () -> null; + final HalClientMonitor.LazyDaemon<Object> lazyDaemon2 = () -> daemon2; + final ClientMonitorCallbackConverter listener1 = mock(ClientMonitorCallbackConverter.class); final TestAuthenticationClient client1 = - new TestAuthenticationClient(mContext, () -> null, mToken, listener1); - final TestHalClientMonitor client2 = - new TestHalClientMonitor(mContext, mToken, () -> daemon2); + new TestAuthenticationClient(mContext, lazyDaemon1, mToken, listener1); + final TestClientMonitor client2 = new TestClientMonitor(mContext, mToken, lazyDaemon2); final BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class); final BaseClientMonitor.Callback callback2 = mock(BaseClientMonitor.Callback.class); // Pretend the scheduler is busy so the first operation doesn't start right away. We want // to pretend like there are two operations in the queue before kicking things off - mScheduler.mCurrentOperation = new BiometricSchedulerOperation( + mScheduler.mCurrentOperation = new BiometricScheduler.Operation( mock(BaseClientMonitor.class), mock(BaseClientMonitor.Callback.class)); mScheduler.scheduleClientMonitor(client1, callback1); @@ -177,8 +172,8 @@ public class BiometricSchedulerTest { verify(callback1, never()).onClientStarted(any()); // Client 2 was able to start - assertFalse(client2.mUnableToStart); - assertTrue(client2.mStarted); + assertFalse(client2.wasUnableToStart()); + assertTrue(client2.hasStarted()); verify(callback2).onClientStarted(eq(client2)); } @@ -192,18 +187,16 @@ public class BiometricSchedulerTest { // Schedule a BiometricPrompt authentication request mScheduler.scheduleClientMonitor(client1, callback1); - assertNotEquals(0, mScheduler.mCurrentOperation.isReadyToStart()); - assertEquals(client1, mScheduler.mCurrentOperation.getClientMonitor()); + assertEquals(Operation.STATE_WAITING_FOR_COOKIE, mScheduler.mCurrentOperation.mState); + assertEquals(client1, mScheduler.mCurrentOperation.mClientMonitor); assertEquals(0, mScheduler.mPendingOperations.size()); // Request it to be canceled. The operation can be canceled immediately, and the scheduler // should go back to idle, since in this case the framework has not even requested the HAL // to authenticate yet. mScheduler.cancelAuthenticationOrDetection(mToken, 1 /* requestId */); - waitForIdle(); assertTrue(client1.isAlreadyDone()); assertTrue(client1.mDestroyed); - assertFalse(client1.mStartedHal); assertNull(mScheduler.mCurrentOperation); } @@ -217,8 +210,8 @@ public class BiometricSchedulerTest { // assertEquals(0, bsp.recentOperations.length); // Pretend the scheduler is busy enrolling, and check the proto dump again. - final TestHalClientMonitor client = new TestHalClientMonitor(mContext, mToken, - () -> mock(Object.class), 0, BiometricsProto.CM_ENROLL); + final TestClientMonitor2 client = new TestClientMonitor2(mContext, mToken, + () -> mock(Object.class), BiometricsProto.CM_ENROLL); mScheduler.scheduleClientMonitor(client); waitForIdle(); bsp = getDump(true /* clearSchedulerBuffer */); @@ -237,8 +230,8 @@ public class BiometricSchedulerTest { @Test public void testProtoDump_fifo() throws Exception { // Add the first operation - final TestHalClientMonitor client = new TestHalClientMonitor(mContext, mToken, - () -> mock(Object.class), 0, BiometricsProto.CM_ENROLL); + final TestClientMonitor2 client = new TestClientMonitor2(mContext, mToken, + () -> mock(Object.class), BiometricsProto.CM_ENROLL); mScheduler.scheduleClientMonitor(client); waitForIdle(); BiometricSchedulerProto bsp = getDump(false /* clearSchedulerBuffer */); @@ -251,8 +244,8 @@ public class BiometricSchedulerTest { client.getCallback().onClientFinished(client, true); // Add another operation - final TestHalClientMonitor client2 = new TestHalClientMonitor(mContext, mToken, - () -> mock(Object.class), 0, BiometricsProto.CM_REMOVE); + final TestClientMonitor2 client2 = new TestClientMonitor2(mContext, mToken, + () -> mock(Object.class), BiometricsProto.CM_REMOVE); mScheduler.scheduleClientMonitor(client2); waitForIdle(); bsp = getDump(false /* clearSchedulerBuffer */); @@ -263,8 +256,8 @@ public class BiometricSchedulerTest { client2.getCallback().onClientFinished(client2, true); // And another operation - final TestHalClientMonitor client3 = new TestHalClientMonitor(mContext, mToken, - () -> mock(Object.class), 0, BiometricsProto.CM_AUTHENTICATE); + final TestClientMonitor2 client3 = new TestClientMonitor2(mContext, mToken, + () -> mock(Object.class), BiometricsProto.CM_AUTHENTICATE); mScheduler.scheduleClientMonitor(client3); waitForIdle(); bsp = getDump(false /* clearSchedulerBuffer */); @@ -297,7 +290,8 @@ public class BiometricSchedulerTest { @Test public void testCancelPendingAuth() throws RemoteException { final HalClientMonitor.LazyDaemon<Object> lazyDaemon = () -> mock(Object.class); - final TestHalClientMonitor client1 = new TestHalClientMonitor(mContext, mToken, lazyDaemon); + + final TestClientMonitor client1 = new TestClientMonitor(mContext, mToken, lazyDaemon); final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class); final TestAuthenticationClient client2 = new TestAuthenticationClient(mContext, lazyDaemon, mToken, callback); @@ -308,12 +302,14 @@ public class BiometricSchedulerTest { waitForIdle(); assertEquals(mScheduler.getCurrentClient(), client1); - assertFalse(mScheduler.mPendingOperations.getFirst().isStarted()); + assertEquals(Operation.STATE_WAITING_IN_QUEUE, + mScheduler.mPendingOperations.getFirst().mState); // Request cancel before the authentication client has started mScheduler.cancelAuthenticationOrDetection(mToken, 1 /* requestId */); waitForIdle(); - assertTrue(mScheduler.mPendingOperations.getFirst().isMarkedCanceling()); + assertEquals(Operation.STATE_WAITING_IN_QUEUE_CANCELING, + mScheduler.mPendingOperations.getFirst().mState); // Finish the blocking client. The authentication client should send ERROR_CANCELED client1.getCallback().onClientFinished(client1, true /* success */); @@ -330,109 +326,67 @@ public class BiometricSchedulerTest { @Test public void testCancels_whenAuthRequestIdNotSet() { - testCancelsAuthDetectWhenRequestId(null /* requestId */, 2, true /* started */); + testCancelsWhenRequestId(null /* requestId */, 2, true /* started */); } @Test public void testCancels_whenAuthRequestIdNotSet_notStarted() { - testCancelsAuthDetectWhenRequestId(null /* requestId */, 2, false /* started */); + testCancelsWhenRequestId(null /* requestId */, 2, false /* started */); } @Test public void testCancels_whenAuthRequestIdMatches() { - testCancelsAuthDetectWhenRequestId(200L, 200, true /* started */); + testCancelsWhenRequestId(200L, 200, true /* started */); } @Test public void testCancels_whenAuthRequestIdMatches_noStarted() { - testCancelsAuthDetectWhenRequestId(200L, 200, false /* started */); + testCancelsWhenRequestId(200L, 200, false /* started */); } @Test public void testDoesNotCancel_whenAuthRequestIdMismatched() { - testCancelsAuthDetectWhenRequestId(10L, 20, true /* started */); + testCancelsWhenRequestId(10L, 20, true /* started */); } @Test public void testDoesNotCancel_whenAuthRequestIdMismatched_notStarted() { - testCancelsAuthDetectWhenRequestId(10L, 20, false /* started */); - } - - private void testCancelsAuthDetectWhenRequestId(@Nullable Long requestId, long cancelRequestId, - boolean started) { - final HalClientMonitor.LazyDaemon<Object> lazyDaemon = () -> mock(Object.class); - final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class); - testCancelsWhenRequestId(requestId, cancelRequestId, started, - new TestAuthenticationClient(mContext, lazyDaemon, mToken, callback)); - } - - @Test - public void testCancels_whenEnrollRequestIdNotSet() { - testCancelsEnrollWhenRequestId(null /* requestId */, 2, false /* started */); - } - - @Test - public void testCancels_whenEnrollRequestIdMatches() { - testCancelsEnrollWhenRequestId(200L, 200, false /* started */); - } - - @Test - public void testDoesNotCancel_whenEnrollRequestIdMismatched() { - testCancelsEnrollWhenRequestId(10L, 20, false /* started */); + testCancelsWhenRequestId(10L, 20, false /* started */); } - private void testCancelsEnrollWhenRequestId(@Nullable Long requestId, long cancelRequestId, + private void testCancelsWhenRequestId(@Nullable Long requestId, long cancelRequestId, boolean started) { + final boolean matches = requestId == null || requestId == cancelRequestId; final HalClientMonitor.LazyDaemon<Object> lazyDaemon = () -> mock(Object.class); final ClientMonitorCallbackConverter callback = mock(ClientMonitorCallbackConverter.class); - testCancelsWhenRequestId(requestId, cancelRequestId, started, - new TestEnrollClient(mContext, lazyDaemon, mToken, callback)); - } - - private void testCancelsWhenRequestId(@Nullable Long requestId, long cancelRequestId, - boolean started, HalClientMonitor<?> client) { - final boolean matches = requestId == null || requestId == cancelRequestId; + final TestAuthenticationClient client = new TestAuthenticationClient( + mContext, lazyDaemon, mToken, callback); if (requestId != null) { client.setRequestId(requestId); } - final boolean isAuth = client instanceof TestAuthenticationClient; - final boolean isEnroll = client instanceof TestEnrollClient; - mScheduler.scheduleClientMonitor(client); if (started) { mScheduler.startPreparedClient(client.getCookie()); } waitForIdle(); - if (isAuth) { - mScheduler.cancelAuthenticationOrDetection(mToken, cancelRequestId); - } else if (isEnroll) { - mScheduler.cancelEnrollment(mToken, cancelRequestId); - } else { - fail("unexpected operation type"); - } + mScheduler.cancelAuthenticationOrDetection(mToken, cancelRequestId); waitForIdle(); - if (isAuth) { - // auth clients that were waiting for cookie when canceled should never invoke the hal - final TestAuthenticationClient authClient = (TestAuthenticationClient) client; - assertEquals(matches && started ? 1 : 0, authClient.mNumCancels); - assertEquals(started, authClient.mStartedHal); - } else if (isEnroll) { - final TestEnrollClient enrollClient = (TestEnrollClient) client; - assertEquals(matches ? 1 : 0, enrollClient.mNumCancels); - assertTrue(enrollClient.mStartedHal); - } + assertEquals(matches && started ? 1 : 0, client.mNumCancels); if (matches) { - if (started || isEnroll) { // prep'd auth clients and enroll clients - assertTrue(mScheduler.mCurrentOperation.isCanceling()); + if (started) { + assertEquals(Operation.STATE_STARTED_CANCELING, + mScheduler.mCurrentOperation.mState); } } else { - if (started || isEnroll) { // prep'd auth clients and enroll clients - assertTrue(mScheduler.mCurrentOperation.isStarted()); + if (started) { + assertEquals(Operation.STATE_STARTED, + mScheduler.mCurrentOperation.mState); } else { - assertNotEquals(0, mScheduler.mCurrentOperation.isReadyToStart()); + assertEquals(Operation.STATE_WAITING_FOR_COOKIE, + mScheduler.mCurrentOperation.mState); } } } @@ -457,14 +411,18 @@ public class BiometricSchedulerTest { mScheduler.cancelAuthenticationOrDetection(mToken, 9999); waitForIdle(); - assertTrue(mScheduler.mCurrentOperation.isStarted()); - assertFalse(mScheduler.mPendingOperations.getFirst().isStarted()); + assertEquals(Operation.STATE_STARTED, + mScheduler.mCurrentOperation.mState); + assertEquals(Operation.STATE_WAITING_IN_QUEUE, + mScheduler.mPendingOperations.getFirst().mState); mScheduler.cancelAuthenticationOrDetection(mToken, requestId2); waitForIdle(); - assertTrue(mScheduler.mCurrentOperation.isStarted()); - assertTrue(mScheduler.mPendingOperations.getFirst().isMarkedCanceling()); + assertEquals(Operation.STATE_STARTED, + mScheduler.mCurrentOperation.mState); + assertEquals(Operation.STATE_WAITING_IN_QUEUE_CANCELING, + mScheduler.mPendingOperations.getFirst().mState); } @Test @@ -501,12 +459,12 @@ public class BiometricSchedulerTest { @Test public void testClientDestroyed_afterFinish() { final HalClientMonitor.LazyDaemon<Object> nonNullDaemon = () -> mock(Object.class); - final TestHalClientMonitor client = - new TestHalClientMonitor(mContext, mToken, nonNullDaemon); + final TestClientMonitor client = + new TestClientMonitor(mContext, mToken, nonNullDaemon); mScheduler.scheduleClientMonitor(client); client.mCallback.onClientFinished(client, true /* success */); waitForIdle(); - assertTrue(client.mDestroyed); + assertTrue(client.wasDestroyed()); } private BiometricSchedulerProto getDump(boolean clearSchedulerBuffer) throws Exception { @@ -514,10 +472,8 @@ public class BiometricSchedulerTest { } private static class TestAuthenticationClient extends AuthenticationClient<Object> { - boolean mStartedHal = false; - boolean mStoppedHal = false; - boolean mDestroyed = false; int mNumCancels = 0; + boolean mDestroyed = false; public TestAuthenticationClient(@NonNull Context context, @NonNull LazyDaemon<Object> lazyDaemon, @NonNull IBinder token, @@ -532,16 +488,18 @@ public class BiometricSchedulerTest { @Override protected void stopHalOperation() { - mStoppedHal = true; + } @Override protected void startHalOperation() { - mStartedHal = true; + } @Override - protected void handleLifecycleAfterAuth(boolean authenticated) {} + protected void handleLifecycleAfterAuth(boolean authenticated) { + + } @Override public boolean wasUserDetected() { @@ -561,59 +519,36 @@ public class BiometricSchedulerTest { } } - private static class TestEnrollClient extends EnrollClient<Object> { - boolean mStartedHal = false; - boolean mStoppedHal = false; - int mNumCancels = 0; - - TestEnrollClient(@NonNull Context context, - @NonNull LazyDaemon<Object> lazyDaemon, @NonNull IBinder token, - @NonNull ClientMonitorCallbackConverter listener) { - super(context, lazyDaemon, token, listener, 0 /* userId */, new byte[69], - "test" /* owner */, mock(BiometricUtils.class), - 5 /* timeoutSec */, 0 /* statsModality */, TEST_SENSOR_ID, - true /* shouldVibrate */); - } - - @Override - protected void stopHalOperation() { - mStoppedHal = true; - } - - @Override - protected void startHalOperation() { - mStartedHal = true; - } + private static class TestClientMonitor2 extends TestClientMonitor { + private final int mProtoEnum; - @Override - protected boolean hasReachedEnrollmentLimit() { - return false; + public TestClientMonitor2(@NonNull Context context, @NonNull IBinder token, + @NonNull LazyDaemon<Object> lazyDaemon, int protoEnum) { + super(context, token, lazyDaemon); + mProtoEnum = protoEnum; } @Override - public void cancel() { - mNumCancels++; - super.cancel(); + public int getProtoEnum() { + return mProtoEnum; } } - private static class TestHalClientMonitor extends HalClientMonitor<Object> { - private final int mProtoEnum; + private static class TestClientMonitor extends HalClientMonitor<Object> { private boolean mUnableToStart; private boolean mStarted; private boolean mDestroyed; - TestHalClientMonitor(@NonNull Context context, @NonNull IBinder token, + public TestClientMonitor(@NonNull Context context, @NonNull IBinder token, @NonNull LazyDaemon<Object> lazyDaemon) { - this(context, token, lazyDaemon, 0 /* cookie */, BiometricsProto.CM_UPDATE_ACTIVE_USER); + this(context, token, lazyDaemon, 0 /* cookie */); } - TestHalClientMonitor(@NonNull Context context, @NonNull IBinder token, - @NonNull LazyDaemon<Object> lazyDaemon, int cookie, int protoEnum) { + public TestClientMonitor(@NonNull Context context, @NonNull IBinder token, + @NonNull LazyDaemon<Object> lazyDaemon, int cookie) { super(context, lazyDaemon, token /* token */, null /* listener */, 0 /* userId */, TAG, cookie, TEST_SENSOR_ID, 0 /* statsModality */, 0 /* statsAction */, 0 /* statsClient */); - mProtoEnum = protoEnum; } @Override @@ -624,7 +559,9 @@ public class BiometricSchedulerTest { @Override public int getProtoEnum() { - return mProtoEnum; + // Anything other than CM_NONE, which is used to represent "idle". Tests that need + // real proto enums should use TestClientMonitor2 + return BiometricsProto.CM_UPDATE_ACTIVE_USER; } @Override @@ -636,7 +573,7 @@ public class BiometricSchedulerTest { @Override protected void startHalOperation() { - mStarted = true; + } @Override @@ -644,9 +581,22 @@ public class BiometricSchedulerTest { super.destroy(); mDestroyed = true; } + + public boolean wasUnableToStart() { + return mUnableToStart; + } + + public boolean hasStarted() { + return mStarted; + } + + public boolean wasDestroyed() { + return mDestroyed; + } + } - private void waitForIdle() { - TestableLooper.get(this).processAllMessages(); + private static void waitForIdle() { + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); } } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/LockoutResetDispatcherTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/LockoutResetDispatcherTest.java new file mode 100644 index 000000000000..a53e22e6e58e --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/LockoutResetDispatcherTest.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; +import android.os.Bundle; +import android.os.IBinder; +import android.os.IRemoteCallback; +import android.platform.test.annotations.Presubmit; +import android.testing.TestableContext; + +import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +@Presubmit +@SmallTest +public class LockoutResetDispatcherTest { + + @Rule + public final MockitoRule mockito = MockitoJUnit.rule(); + + @Rule + public final TestableContext mContext = new TestableContext( + InstrumentationRegistry.getInstrumentation().getTargetContext(), null); + + @Mock + private IBinder mBinder; + @Mock + private IBiometricServiceLockoutResetCallback mCallback; + + private LockoutResetDispatcher mDispatcher; + + @Before + public void setup() { + when(mCallback.asBinder()).thenReturn(mBinder); + mDispatcher = new LockoutResetDispatcher(mContext); + } + + @Test + public void linksToDeath() throws Exception { + mDispatcher.addCallback(mCallback, "package"); + verify(mBinder).linkToDeath(eq(mDispatcher), anyInt()); + } + + @Test + public void notifyLockoutReset() throws Exception { + final int sensorId = 24; + + mDispatcher.addCallback(mCallback, "some.package"); + mDispatcher.notifyLockoutResetCallbacks(sensorId); + + final ArgumentCaptor<IRemoteCallback> captor = + ArgumentCaptor.forClass(IRemoteCallback.class); + verify(mCallback).onLockoutReset(eq(sensorId), captor.capture()); + captor.getValue().sendResult(new Bundle()); + } + + @Test + public void releaseWakeLockOnDeath() { + mDispatcher.addCallback(mCallback, "a.b.cee"); + mDispatcher.binderDied(mBinder); + + // would be better to check the wake lock + // but this project lacks the extended mockito support to do it + assertThat(mDispatcher.mClientCallbacks).isEmpty(); + } + + @Test + public void releaseCorrectWakeLockOnDeath() { + mDispatcher.addCallback(mCallback, "a.b"); + mDispatcher.binderDied(mock(IBinder.class)); + + assertThat(mDispatcher.mClientCallbacks).hasSize(1); + } +} diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java index 407f5fb04adf..7fccd49db04b 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java @@ -16,8 +16,6 @@ package com.android.server.biometrics.sensors; -import static android.testing.TestableLooper.RunWithLooper; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; @@ -30,53 +28,52 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.hardware.biometrics.IBiometricService; import android.os.Binder; -import android.os.Handler; import android.os.IBinder; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import org.junit.Before; import org.junit.Test; -import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @Presubmit -@RunWith(AndroidTestingRunner.class) -@RunWithLooper @SmallTest public class UserAwareBiometricSchedulerTest { - private static final String TAG = "UserAwareBiometricSchedulerTest"; + private static final String TAG = "BiometricSchedulerTest"; private static final int TEST_SENSOR_ID = 0; - private Handler mHandler; private UserAwareBiometricScheduler mScheduler; - private IBinder mToken = new Binder(); + private IBinder mToken; @Mock private Context mContext; @Mock private IBiometricService mBiometricService; - private TestUserStartedCallback mUserStartedCallback = new TestUserStartedCallback(); - private TestUserStoppedCallback mUserStoppedCallback = new TestUserStoppedCallback(); + private TestUserStartedCallback mUserStartedCallback; + private TestUserStoppedCallback mUserStoppedCallback; private int mCurrentUserId = UserHandle.USER_NULL; - private boolean mStartOperationsFinish = true; - private int mStartUserClientCount = 0; + private boolean mStartOperationsFinish; + private int mStartUserClientCount; @Before public void setUp() { MockitoAnnotations.initMocks(this); - mHandler = new Handler(TestableLooper.get(this).getLooper()); + + mToken = new Binder(); + mStartOperationsFinish = true; + mStartUserClientCount = 0; + mUserStartedCallback = new TestUserStartedCallback(); + mUserStoppedCallback = new TestUserStoppedCallback(); + mScheduler = new UserAwareBiometricScheduler(TAG, - mHandler, BiometricScheduler.SENSOR_TYPE_UNKNOWN, null /* gestureAvailabilityDispatcher */, mBiometricService, @@ -120,7 +117,7 @@ public class UserAwareBiometricSchedulerTest { mCurrentUserId = UserHandle.USER_NULL; mStartOperationsFinish = false; - final BaseClientMonitor[] nextClients = new BaseClientMonitor[]{ + final BaseClientMonitor[] nextClients = new BaseClientMonitor[] { mock(BaseClientMonitor.class), mock(BaseClientMonitor.class), mock(BaseClientMonitor.class) @@ -150,11 +147,11 @@ public class UserAwareBiometricSchedulerTest { waitForIdle(); final TestStartUserClient startUserClient = - (TestStartUserClient) mScheduler.mCurrentOperation.getClientMonitor(); + (TestStartUserClient) mScheduler.mCurrentOperation.mClientMonitor; mScheduler.reset(); assertNull(mScheduler.mCurrentOperation); - final BiometricSchedulerOperation fakeOperation = new BiometricSchedulerOperation( + final BiometricScheduler.Operation fakeOperation = new BiometricScheduler.Operation( mock(BaseClientMonitor.class), new BaseClientMonitor.Callback() {}); mScheduler.mCurrentOperation = fakeOperation; startUserClient.mCallback.onClientFinished(startUserClient, true); @@ -197,8 +194,8 @@ public class UserAwareBiometricSchedulerTest { verify(nextClient).start(any()); } - private void waitForIdle() { - TestableLooper.get(this).processAllMessages(); + private static void waitForIdle() { + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); } private class TestUserStoppedCallback implements StopUserClient.UserStoppedCallback { diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java index 0891eca9f61c..a13dff21439d 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java @@ -79,7 +79,6 @@ public class SensorTest { when(mContext.getSystemService(Context.BIOMETRIC_SERVICE)).thenReturn(mBiometricService); mScheduler = new UserAwareBiometricScheduler(TAG, - new Handler(mLooper.getLooper()), BiometricScheduler.SENSOR_TYPE_FACE, null /* gestureAvailabilityDispatcher */, () -> USER_ID, diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java index 21a7a8ae65b9..39c51d5f5e5e 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java @@ -32,9 +32,7 @@ import android.hardware.face.FaceSensorProperties; import android.hardware.face.FaceSensorPropertiesInternal; import android.hardware.face.IFaceServiceReceiver; import android.os.Binder; -import android.os.Handler; import android.os.IBinder; -import android.os.Looper; import android.os.UserManager; import android.platform.test.annotations.Presubmit; @@ -71,7 +69,6 @@ public class Face10Test { @Mock private BiometricScheduler mScheduler; - private final Handler mHandler = new Handler(Looper.getMainLooper()); private LockoutResetDispatcher mLockoutResetDispatcher; private com.android.server.biometrics.sensors.face.hidl.Face10 mFace10; private IBinder mBinder; @@ -100,7 +97,7 @@ public class Face10Test { resetLockoutRequiresChallenge); Face10.sSystemClock = Clock.fixed(Instant.ofEpochMilli(100), ZoneId.of("PST")); - mFace10 = new Face10(mContext, sensorProps, mLockoutResetDispatcher, mHandler, mScheduler); + mFace10 = new Face10(mContext, sensorProps, mLockoutResetDispatcher, mScheduler); mBinder = new Binder(); } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java index a012b8b06c7f..0d520ca9a4e4 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java @@ -79,7 +79,6 @@ public class SensorTest { when(mContext.getSystemService(Context.BIOMETRIC_SERVICE)).thenReturn(mBiometricService); mScheduler = new UserAwareBiometricScheduler(TAG, - new Handler(mLooper.getLooper()), BiometricScheduler.SENSOR_TYPE_FP_OTHER, null /* gestureAvailabilityDispatcher */, () -> USER_ID, diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java index 62a2b1be139d..59f2ca4f6106 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java @@ -187,7 +187,7 @@ public class PackageInstallerSessionTest { /* isFailed */ false, /* isApplied */false, /* stagedSessionErrorCode */ - PackageInstaller.SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, + PackageInstaller.SessionInfo.SESSION_VERIFICATION_FAILED, /* stagedSessionErrorMessage */ "some error"); } diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java index cf6165fdbd79..e4273dce7893 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java @@ -375,7 +375,7 @@ public final class UserManagerTest { switchUser(user1.id, null, /* ignoreHandle= */ true); assertThat(mUserManager.removeUserOrSetEphemeral(user1.id, /* evenWhenDisallowed= */ false)) - .isEqualTo(UserManager.REMOVE_RESULT_SET_EPHEMERAL); + .isEqualTo(UserManager.REMOVE_RESULT_DEFERRED); assertThat(hasUser(user1.id)).isTrue(); assertThat(getUser(user1.id).isEphemeral()).isTrue(); diff --git a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java index 26b34fdd4e04..304fe5a1c9c3 100644 --- a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java @@ -30,6 +30,7 @@ import android.hardware.power.stats.PowerEntity; import android.hardware.power.stats.State; import android.hardware.power.stats.StateResidency; import android.hardware.power.stats.StateResidencyResult; +import android.os.Looper; import androidx.test.InstrumentationRegistry; @@ -145,12 +146,12 @@ public class PowerStatsServiceTest { } @Override - PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath, - String meterFilename, String meterCacheFilename, + PowerStatsLogger createPowerStatsLogger(Context context, Looper looper, + File dataStoragePath, String meterFilename, String meterCacheFilename, String modelFilename, String modelCacheFilename, String residencyFilename, String residencyCacheFilename, IPowerStatsHALWrapper powerStatsHALWrapper) { - mPowerStatsLogger = new PowerStatsLogger(context, dataStoragePath, + mPowerStatsLogger = new PowerStatsLogger(context, looper, dataStoragePath, meterFilename, meterCacheFilename, modelFilename, modelCacheFilename, residencyFilename, residencyCacheFilename, diff --git a/services/tests/servicestests/src/com/android/server/vibrator/DeviceVibrationEffectAdapterTest.java b/services/tests/servicestests/src/com/android/server/vibrator/DeviceVibrationEffectAdapterTest.java index b934ecb80564..739b3b179de7 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/DeviceVibrationEffectAdapterTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/DeviceVibrationEffectAdapterTest.java @@ -54,11 +54,10 @@ public class DeviceVibrationEffectAdapterTest { /* 50Hz= */ 0.1f, 0.2f, 0.4f, 0.8f, /* 150Hz= */ 1f, 0.9f, /* 200Hz= */ 0.8f}; private static final VibratorInfo.FrequencyMapping EMPTY_FREQUENCY_MAPPING = - new VibratorInfo.FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, Float.NaN, null); + new VibratorInfo.FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, null); private static final VibratorInfo.FrequencyMapping TEST_FREQUENCY_MAPPING = - new VibratorInfo.FrequencyMapping(TEST_MIN_FREQUENCY, - TEST_RESONANT_FREQUENCY, TEST_FREQUENCY_RESOLUTION, - /* suggestedSafeRangeHz= */ 50, TEST_AMPLITUDE_MAP); + new VibratorInfo.FrequencyMapping(TEST_RESONANT_FREQUENCY, TEST_MIN_FREQUENCY, + TEST_FREQUENCY_RESOLUTION, TEST_AMPLITUDE_MAP); private DeviceVibrationEffectAdapter mAdapter; @@ -87,14 +86,14 @@ public class DeviceVibrationEffectAdapterTest { @Test public void testStepAndRampSegments_withoutPwleCapability_convertsRampsToSteps() { VibrationEffect.Composed effect = new VibrationEffect.Composed(Arrays.asList( - new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 10), - new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100), + new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 200, /* duration= */ 10), + new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 150, /* duration= */ 100), new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 0.2f, - /* startFrequency= */ -4, /* endFrequency= */ 2, /* duration= */ 10), + /* startFrequencyHz= */ 1, /* endFrequencyHz= */ 300, /* duration= */ 10), new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f, - /* startFrequency= */ 0, /* endFrequency= */ 0, /* duration= */ 100), + /* startFrequencyHz= */ 0, /* endFrequencyHz= */ 0, /* duration= */ 100), new RampSegment(/* startAmplitude= */ 0.65f, /* endAmplitude= */ 0.65f, - /* startFrequency= */ 0, /* endFrequency= */ 1, /* duration= */ 1000)), + /* startFrequencyHz= */ 0, /* endFrequencyHz= */ 1, /* duration= */ 1000)), /* repeatIndex= */ 3); VibrationEffect.Composed adaptedEffect = (VibrationEffect.Composed) mAdapter.apply(effect, @@ -110,23 +109,23 @@ public class DeviceVibrationEffectAdapterTest { @Test public void testStepAndRampSegments_withPwleCapability_convertsStepsToRamps() { VibrationEffect.Composed effect = new VibrationEffect.Composed(Arrays.asList( - new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 10), - new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100), + new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 175, /* duration= */ 10), + new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 150, /* duration= */ 60), new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1, - /* startFrequency= */ -4, /* endFrequency= */ 2, /* duration= */ 50), + /* startFrequencyHz= */ 50, /* endFrequencyHz= */ 200, /* duration= */ 50), new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f, - /* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20)), + /* startFrequencyHz= */ 1000, /* endFrequencyHz= */ 1, /* duration= */ 20)), /* repeatIndex= */ 2); VibrationEffect.Composed expected = new VibrationEffect.Composed(Arrays.asList( new RampSegment(/* startAmplitude= */ 0, /* endAmplitude*/ 0, - /* startFrequency= */ 175, /* endFrequency= */ 175, /* duration= */ 10), + /* startFrequencyHz= */ 175, /* endFrequencyHz= */ 175, /* duration= */ 10), new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.5f, - /* startFrequency= */ 150, /* endFrequency= */ 150, /* duration= */ 100), + /* startFrequencyHz= */ 150, /* endFrequencyHz= */ 150, /* duration= */ 60), new RampSegment(/* startAmplitude= */ 0.1f, /* endAmplitude= */ 0.8f, - /* startFrequency= */ 50, /* endFrequency= */ 200, /* duration= */ 50), + /* startFrequencyHz= */ 50, /* endFrequencyHz= */ 200, /* duration= */ 50), new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.1f, - /* startFrequency= */ 200, /* endFrequency= */ 50, /* duration= */ 20)), + /* startFrequencyHz= */ 200, /* endFrequencyHz= */ 50, /* duration= */ 20)), /* repeatIndex= */ 2); VibratorInfo info = createVibratorInfo(TEST_FREQUENCY_MAPPING, @@ -135,28 +134,28 @@ public class DeviceVibrationEffectAdapterTest { } @Test - public void testStepAndRampSegments_withEmptyFreqMapping_returnsSameAmplitudesAndZeroFreq() { + public void testStepAndRampSegments_withEmptyFreqMapping_returnsAmplitudesWithResonantFreq() { VibrationEffect.Composed effect = new VibrationEffect.Composed(Arrays.asList( - new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 10), - new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100), + new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 175, /* duration= */ 10), + new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 100), new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 1, - /* startFrequency= */ -1, /* endFrequency= */ 1, /* duration= */ 50), + /* startFrequencyHz= */ 50, /* endFrequencyHz= */ 200, /* duration= */ 50), new RampSegment(/* startAmplitude= */ 0.7f, /* endAmplitude= */ 0.5f, - /* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20)), + /* startFrequencyHz= */ 1000, /* endFrequencyHz= */ 1, /* duration= */ 20)), /* repeatIndex= */ 2); VibrationEffect.Composed expected = new VibrationEffect.Composed(Arrays.asList( new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 0, - /* startFrequency= */ Float.NaN, /* endFrequency= */ Float.NaN, + /* startFrequencyHz= */ Float.NaN, /* endFrequencyHz= */ Float.NaN, /* duration= */ 10), new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.5f, - /* startFrequency= */ Float.NaN, /* endFrequency= */ Float.NaN, + /* startFrequencyHz= */ Float.NaN, /* endFrequencyHz= */ Float.NaN, /* duration= */ 100), new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 1, - /* startFrequency= */ Float.NaN, /* endFrequency= */ Float.NaN, + /* startFrequencyHz= */ Float.NaN, /* endFrequencyHz= */ Float.NaN, /* duration= */ 50), new RampSegment(/* startAmplitude= */ 0.7f, /* endAmplitude= */ 0.5f, - /* startFrequency= */ Float.NaN, /* endFrequency= */ Float.NaN, + /* startFrequencyHz= */ Float.NaN, /* endFrequencyHz= */ Float.NaN, /* duration= */ 20)), /* repeatIndex= */ 2); @@ -168,25 +167,25 @@ public class DeviceVibrationEffectAdapterTest { @Test public void testStepAndRampSegments_withValidFreqMapping_returnsClippedValues() { VibrationEffect.Composed effect = new VibrationEffect.Composed(Arrays.asList( - new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 10), - new StepSegment(/* amplitude= */ 1, /* frequency= */ -1, /* duration= */ 100), + new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 10), + new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 125, /* duration= */ 100), new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1, - /* startFrequency= */ -4, /* endFrequency= */ 2, /* duration= */ 50), + /* startFrequencyHz= */ 50, /* endFrequencyHz= */ 200, /* duration= */ 50), new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f, - /* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20)), + /* startFrequencyHz= */ 1000, /* endFrequencyHz= */ 1, /* duration= */ 20)), /* repeatIndex= */ 2); VibrationEffect.Composed expected = new VibrationEffect.Composed(Arrays.asList( new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.5f, - /* startFrequency= */ 150, /* endFrequency= */ 150, + /* startFrequencyHz= */ 150, /* endFrequencyHz= */ 150, /* duration= */ 10), new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.8f, - /* startFrequency= */ 125, /* endFrequency= */ 125, + /* startFrequencyHz= */ 125, /* endFrequencyHz= */ 125, /* duration= */ 100), new RampSegment(/* startAmplitude= */ 0.1f, /* endAmplitude= */ 0.8f, - /* startFrequency= */ 50, /* endFrequency= */ 200, /* duration= */ 50), + /* startFrequencyHz= */ 50, /* endFrequencyHz= */ 200, /* duration= */ 50), new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.1f, - /* startFrequency= */ 200, /* endFrequency= */ 50, /* duration= */ 20)), + /* startFrequencyHz= */ 200, /* endFrequencyHz= */ 50, /* duration= */ 20)), /* repeatIndex= */ 2); VibratorInfo info = createVibratorInfo(TEST_FREQUENCY_MAPPING, diff --git a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibrator.java b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibrator.java index e2a348efa409..4556a4a47017 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibrator.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibrator.java @@ -17,6 +17,7 @@ package com.android.server.vibrator; import android.annotation.NonNull; +import android.content.Context; import android.os.VibrationAttributes; import android.os.VibrationEffect; import android.os.Vibrator; @@ -24,37 +25,8 @@ import android.os.Vibrator; /** Fake implementation of {@link Vibrator} for service tests. */ final class FakeVibrator extends Vibrator { - private int mDefaultHapticFeedbackIntensity = Vibrator.VIBRATION_INTENSITY_MEDIUM; - private int mDefaultNotificationIntensity = Vibrator.VIBRATION_INTENSITY_MEDIUM; - private int mDefaultRingIntensity = Vibrator.VIBRATION_INTENSITY_MEDIUM; - - @Override - public int getDefaultHapticFeedbackIntensity() { - return mDefaultHapticFeedbackIntensity; - } - - @Override - public int getDefaultNotificationVibrationIntensity() { - return mDefaultNotificationIntensity; - } - - @Override - public int getDefaultRingVibrationIntensity() { - return mDefaultRingIntensity; - } - - public void setDefaultHapticFeedbackIntensity( - @VibrationIntensity int defaultHapticFeedbackIntensity) { - mDefaultHapticFeedbackIntensity = defaultHapticFeedbackIntensity; - } - - public void setDefaultNotificationVibrationIntensity( - @VibrationIntensity int defaultNotificationIntensity) { - mDefaultNotificationIntensity = defaultNotificationIntensity; - } - - public void setDefaultRingVibrationIntensity(@VibrationIntensity int defaultRingIntensity) { - mDefaultRingIntensity = defaultRingIntensity; + FakeVibrator(Context context) { + super(context); } @Override diff --git a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java index 777e3f4e4a01..2ad0e93dd1fb 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java @@ -87,7 +87,7 @@ final class FakeVibratorControllerProvider { @Override public long on(long milliseconds, long vibrationId) { mEffectSegments.add(new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE, - /* frequency= */ 0, (int) milliseconds)); + /* frequencyHz= */ 0, (int) milliseconds)); applyLatency(); scheduleListener(milliseconds, vibrationId); return milliseconds; @@ -158,7 +158,7 @@ final class FakeVibratorControllerProvider { } @Override - public boolean getInfo(float suggestedFrequencyRange, VibratorInfo.Builder infoBuilder) { + public boolean getInfo(VibratorInfo.Builder infoBuilder) { infoBuilder.setCapabilities(mCapabilities); infoBuilder.setSupportedBraking(mSupportedBraking); infoBuilder.setPwleSizeMax(mPwleSizeMax); @@ -170,9 +170,8 @@ final class FakeVibratorControllerProvider { } infoBuilder.setCompositionSizeMax(mCompositionSizeMax); infoBuilder.setQFactor(mQFactor); - infoBuilder.setFrequencyMapping(new VibratorInfo.FrequencyMapping(mMinFrequency, - mResonantFrequency, mFrequencyResolution, suggestedFrequencyRange, - mMaxAmplitudes)); + infoBuilder.setFrequencyMapping(new VibratorInfo.FrequencyMapping( + mResonantFrequency, mMinFrequency, mFrequencyResolution, mMaxAmplitudes)); return mIsInfoLoadSuccessful; } diff --git a/services/tests/servicestests/src/com/android/server/vibrator/RampDownAdapterTest.java b/services/tests/servicestests/src/com/android/server/vibrator/RampDownAdapterTest.java index 4c3312c41550..a3edf2345a22 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/RampDownAdapterTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/RampDownAdapterTest.java @@ -70,9 +70,9 @@ public class RampDownAdapterTest { @Test public void testRampAndStepSegments_withNoOffSegment_keepsListUnchanged() { List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList( - new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100), + new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 100), new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f, - /* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20))); + /* startFrequencyHz= */ 10, /* endFrequencyHz= */ 50, /* duration= */ 20))); List<VibrationEffectSegment> originalSegments = new ArrayList<>(segments); assertEquals(-1, mAdapter.apply(segments, -1, TEST_VIBRATOR_INFO)); @@ -86,12 +86,12 @@ public class RampDownAdapterTest { mAdapter = new RampDownAdapter(/* rampDownDuration= */ 0, TEST_STEP_DURATION); List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList( - new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10), - new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 100), + new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 10), + new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 100), new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f, - /* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20), + /* startFrequencyHz= */ 10, /* endFrequencyHz= */ 50, /* duration= */ 20), new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 0, - /* startFrequency= */ 0, /* endFrequency= */ 0, /* duration= */ 50))); + /* startFrequencyHz= */ 0, /* endFrequencyHz= */ 0, /* duration= */ 50))); List<VibrationEffectSegment> originalSegments = new ArrayList<>(segments); assertEquals(-1, mAdapter.apply(segments, -1, TEST_VIBRATOR_INFO)); @@ -102,12 +102,12 @@ public class RampDownAdapterTest { @Test public void testStepSegments_withShortZeroSegment_replaceWithStepsDown() { List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList( - new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10), - new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 10))); + new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 10), + new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 10))); List<VibrationEffectSegment> expectedSegments = Arrays.asList( - new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10), - new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 5), - new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 5)); + new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 10), + new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 5), + new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 5)); assertEquals(-1, mAdapter.apply(segments, -1, TEST_VIBRATOR_INFO)); assertEquals(expectedSegments, segments); @@ -116,17 +116,17 @@ public class RampDownAdapterTest { @Test public void testStepSegments_withLongZeroSegment_replaceWithStepsDownWithRemainingOffSegment() { List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList( - new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10), + new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 10), new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 0, - /* startFrequency= */ 0, /* endFrequency= */ 0, /* duration= */ 50), - new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ 0, /* duration= */ 100))); + /* startFrequencyHz= */ 0, /* endFrequencyHz= */ 0, /* duration= */ 50), + new StepSegment(/* amplitude= */ 0.8f, /* frequencyHz= */ 0, /* duration= */ 100))); List<VibrationEffectSegment> expectedSegments = Arrays.asList( - new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10), - new StepSegment(/* amplitude= */ 0.75f, /* frequency= */ 0, /* duration= */ 5), - new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 5), - new StepSegment(/* amplitude= */ 0.25f, /* frequency= */ 0, /* duration= */ 5), - new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 35), - new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ 0, /* duration= */ 100)); + new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 10), + new StepSegment(/* amplitude= */ 0.75f, /* frequencyHz= */ 0, /* duration= */ 5), + new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 5), + new StepSegment(/* amplitude= */ 0.25f, /* frequencyHz= */ 0, /* duration= */ 5), + new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 35), + new StepSegment(/* amplitude= */ 0.8f, /* frequencyHz= */ 0, /* duration= */ 100)); assertEquals(-1, mAdapter.apply(segments, -1, TEST_VIBRATOR_INFO)); assertEquals(expectedSegments, segments); @@ -135,16 +135,16 @@ public class RampDownAdapterTest { @Test public void testStepSegments_withZeroSegmentBeforeRepeat_fixesRepeat() { List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList( - new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10), - new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 50), - new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ 0, /* duration= */ 100))); + new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 10), + new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 50), + new StepSegment(/* amplitude= */ 0.8f, /* frequencyHz= */ 0, /* duration= */ 100))); List<VibrationEffectSegment> expectedSegments = Arrays.asList( - new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10), - new StepSegment(/* amplitude= */ 0.75f, /* frequency= */ 0, /* duration= */ 5), - new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 5), - new StepSegment(/* amplitude= */ 0.25f, /* frequency= */ 0, /* duration= */ 5), - new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 35), - new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ 0, /* duration= */ 100)); + new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 10), + new StepSegment(/* amplitude= */ 0.75f, /* frequencyHz= */ 0, /* duration= */ 5), + new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 5), + new StepSegment(/* amplitude= */ 0.25f, /* frequencyHz= */ 0, /* duration= */ 5), + new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 35), + new StepSegment(/* amplitude= */ 0.8f, /* frequencyHz= */ 0, /* duration= */ 100)); // Repeat index fixed after intermediate steps added assertEquals(5, mAdapter.apply(segments, 2, TEST_VIBRATOR_INFO)); @@ -154,14 +154,14 @@ public class RampDownAdapterTest { @Test public void testStepSegments_withZeroSegmentAfterRepeat_preservesRepeat() { List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList( - new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10), - new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 10), - new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ 0, /* duration= */ 100))); + new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 10), + new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 10), + new StepSegment(/* amplitude= */ 0.8f, /* frequencyHz= */ 0, /* duration= */ 100))); List<VibrationEffectSegment> expectedSegments = Arrays.asList( - new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10), - new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 5), - new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 5), - new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ 0, /* duration= */ 100)); + new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 10), + new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 5), + new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 5), + new StepSegment(/* amplitude= */ 0.8f, /* frequencyHz= */ 0, /* duration= */ 100)); assertEquals(3, mAdapter.apply(segments, 2, TEST_VIBRATOR_INFO)); assertEquals(expectedSegments, segments); @@ -170,22 +170,22 @@ public class RampDownAdapterTest { @Test public void testStepSegments_withZeroSegmentAtRepeat_fixesRepeatAndAppendOriginalToListEnd() { List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList( - new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10), - new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 50), - new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 100))); + new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 10), + new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 50), + new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 100))); List<VibrationEffectSegment> expectedSegments = Arrays.asList( - new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 10), - new StepSegment(/* amplitude= */ 0.75f, /* frequency= */ 0, /* duration= */ 5), - new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 5), - new StepSegment(/* amplitude= */ 0.25f, /* frequency= */ 0, /* duration= */ 5), - new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 35), - new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 100), + new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 10), + new StepSegment(/* amplitude= */ 0.75f, /* frequencyHz= */ 0, /* duration= */ 5), + new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 5), + new StepSegment(/* amplitude= */ 0.25f, /* frequencyHz= */ 0, /* duration= */ 5), + new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 35), + new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 100), // Original zero segment appended to the end of new looping vibration, // then converted to ramp down as well. - new StepSegment(/* amplitude= */ 0.75f, /* frequency= */ 0, /* duration= */ 5), - new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 5), - new StepSegment(/* amplitude= */ 0.25f, /* frequency= */ 0, /* duration= */ 5), - new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 35)); + new StepSegment(/* amplitude= */ 0.75f, /* frequencyHz= */ 0, /* duration= */ 5), + new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 5), + new StepSegment(/* amplitude= */ 0.25f, /* frequencyHz= */ 0, /* duration= */ 5), + new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 35)); // Repeat index fixed after intermediate steps added assertEquals(5, mAdapter.apply(segments, 1, TEST_VIBRATOR_INFO)); @@ -195,8 +195,8 @@ public class RampDownAdapterTest { @Test public void testStepSegments_withRepeatToNonZeroSegment_keepsOriginalSteps() { List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList( - new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ 0, /* duration= */ 10), - new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100))); + new StepSegment(/* amplitude= */ 0.8f, /* frequencyHz= */ 0, /* duration= */ 10), + new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 100))); List<VibrationEffectSegment> originalSegments = new ArrayList<>(segments); assertEquals(0, mAdapter.apply(segments, 0, TEST_VIBRATOR_INFO)); @@ -208,14 +208,14 @@ public class RampDownAdapterTest { public void testStepSegments_withRepeatToShortZeroSegment_skipAndAppendRampDown() { List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList( new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 0, - /* startFrequency= */ 0, /* endFrequency= */ 0, /* duration= */ 10), - new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 30))); + /* startfrequencyHz= */ 0, /* endfrequencyHz= */ 0, /* duration= */ 10), + new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 30))); List<VibrationEffectSegment> expectedSegments = Arrays.asList( new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 0, - /* startFrequency= */ 0, /* endFrequency= */ 0, /* duration= */ 10), - new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 30), - new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 5), - new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 5)); + /* startfrequencyHz= */ 0, /* endfrequencyHz= */ 0, /* duration= */ 10), + new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 30), + new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 5), + new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 5)); // Shift repeat index to the right to use append instead of zero segment. assertEquals(1, mAdapter.apply(segments, 0, TEST_VIBRATOR_INFO)); @@ -226,17 +226,17 @@ public class RampDownAdapterTest { @Test public void testStepSegments_withRepeatToLongZeroSegment_splitAndAppendRampDown() { List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList( - new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 120), - new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 30))); + new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 120), + new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 30))); List<VibrationEffectSegment> expectedSegments = Arrays.asList( // Split long zero segment to skip part of it. - new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 20), - new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 100), - new StepSegment(/* amplitude= */ 1, /* frequency= */ 0, /* duration= */ 30), - new StepSegment(/* amplitude= */ 0.75f, /* frequency= */ 0, /* duration= */ 5), - new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 5), - new StepSegment(/* amplitude= */ 0.25f, /* frequency= */ 0, /* duration= */ 5), - new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 5)); + new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 20), + new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 100), + new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 30), + new StepSegment(/* amplitude= */ 0.75f, /* frequencyHz= */ 0, /* duration= */ 5), + new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 5), + new StepSegment(/* amplitude= */ 0.25f, /* frequencyHz= */ 0, /* duration= */ 5), + new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 5)); // Shift repeat index to the right to use append with part of the zero segment. assertEquals(1, mAdapter.apply(segments, 0, TEST_VIBRATOR_INFO)); @@ -248,18 +248,20 @@ public class RampDownAdapterTest { public void testRampSegments_withShortZeroSegment_replaceWithRampDown() { List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList( new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f, - /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10), + /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 100, /* duration= */ 10), new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 0, - /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 20), + /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 100, /* duration= */ 20), new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1, - /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 30))); + /* startFrequencyHz= */ 200, /* endFrequencyHz= */ 200, + /* duration= */ 30))); List<VibrationEffectSegment> expectedSegments = Arrays.asList( new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f, - /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10), + /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 100, /* duration= */ 10), new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0, - /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 20), + /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 100, /* duration= */ 20), new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1, - /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 30)); + /* startFrequencyHz= */ 200, /* endFrequencyHz= */ 200, + /* duration= */ 30)); assertEquals(2, mAdapter.apply(segments, 2, TEST_VIBRATOR_INFO)); @@ -269,20 +271,23 @@ public class RampDownAdapterTest { @Test public void testRampSegments_withLongZeroSegment_splitAndAddRampDown() { List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList( - new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f, - /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10), - new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 150), + new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.5f, + /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 100, /* duration= */ 10), + new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 150, /* duration= */ 150), new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1, - /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 30))); + /* startFrequencyHz= */ 200, /* endFrequencyHz= */ 200, + /* duration= */ 30))); List<VibrationEffectSegment> expectedSegments = Arrays.asList( new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f, - /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10), + /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 100, /* duration= */ 10), new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0, - /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 20), + /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 100, /* duration= */ 20), new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 0, - /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 130), + /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 100, + /* duration= */ 130), new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1, - /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 30)); + /* startFrequencyHz= */ 200, /* endFrequencyHz= */ 200, + /* duration= */ 30)); // Repeat index fixed after intermediate steps added assertEquals(3, mAdapter.apply(segments, 2, TEST_VIBRATOR_INFO)); @@ -294,9 +299,10 @@ public class RampDownAdapterTest { public void testRampSegments_withRepeatToNonZeroSegment_keepsOriginalSteps() { List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList( new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f, - /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10), + /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 100, /* duration= */ 10), new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1, - /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 30))); + /* startFrequencyHz= */ 200, /* endFrequencyHz= */ 200, + /* duration= */ 30))); List<VibrationEffectSegment> originalSegments = new ArrayList<>(segments); assertEquals(0, mAdapter.apply(segments, 0, TEST_VIBRATOR_INFO)); @@ -307,15 +313,15 @@ public class RampDownAdapterTest { @Test public void testRampSegments_withRepeatToShortZeroSegment_skipAndAppendRampDown() { List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList( - new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 20), + new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 200, /* duration= */ 20), new RampSegment(/* startAmplitude= */ 0, /* endAmplitude*/ 1, - /* startFrequency= */ 0, /* endFrequency= */ 1, /* duration= */ 20))); + /* startFrequencyHz= */ 40, /* endFrequencyHz= */ 80, /* duration= */ 20))); List<VibrationEffectSegment> expectedSegments = Arrays.asList( - new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 20), + new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 200, /* duration= */ 20), new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 1, - /* startFrequency= */ 0, /* endFrequency= */ 1, /* duration= */ 20), + /* startFrequencyHz= */ 40, /* endFrequencyHz= */ 80, /* duration= */ 20), new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 0, - /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 20)); + /* startFrequencyHz= */ 80, /* endFrequencyHz= */ 80, /* duration= */ 20)); // Shift repeat index to the right to use append instead of zero segment. assertEquals(1, mAdapter.apply(segments, 0, TEST_VIBRATOR_INFO)); @@ -327,19 +333,19 @@ public class RampDownAdapterTest { public void testRampSegments_withRepeatToLongZeroSegment_splitAndAppendRampDown() { List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList( new RampSegment(/* startAmplitude= */ 0, /* endAmplitude*/ 0, - /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 70), + /* startFrequencyHz= */ 1, /* endFrequencyHz= */ 1, /* duration= */ 70), new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 1, - /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 30))); + /* startFrequencyHz= */ 1, /* endFrequencyHz= */ 1, /* duration= */ 30))); List<VibrationEffectSegment> expectedSegments = Arrays.asList( // Split long zero segment to skip part of it. new RampSegment(/* startAmplitude= */ 0, /* endAmplitude*/ 0, - /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 20), + /* startFrequencyHz= */ 1, /* endFrequencyHz= */ 1, /* duration= */ 20), new RampSegment(/* startAmplitude= */ 0, /* endAmplitude*/ 0, - /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 50), + /* startFrequencyHz= */ 1, /* endFrequencyHz= */ 1, /* duration= */ 50), new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 1, - /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 30), + /* startFrequencyHz= */ 1, /* endFrequencyHz= */ 1, /* duration= */ 30), new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 0, - /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 20)); + /* startFrequencyHz= */ 1, /* endFrequencyHz= */ 1, /* duration= */ 20)); // Shift repeat index to the right to use append with part of the zero segment. assertEquals(1, mAdapter.apply(segments, 0, TEST_VIBRATOR_INFO)); diff --git a/services/tests/servicestests/src/com/android/server/vibrator/RampToStepAdapterTest.java b/services/tests/servicestests/src/com/android/server/vibrator/RampToStepAdapterTest.java index 95c3bd93e69b..22db91736756 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/RampToStepAdapterTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/RampToStepAdapterTest.java @@ -45,6 +45,12 @@ import java.util.stream.IntStream; @Presubmit public class RampToStepAdapterTest { private static final int TEST_STEP_DURATION = 5; + private static final float[] TEST_AMPLITUDE_MAP = new float[]{ + /* 50Hz= */ 0.1f, 0.2f, 0.4f, 0.8f, /* 150Hz= */ 1f, 0.9f, /* 200Hz= */ 0.8f}; + private static final VibratorInfo.FrequencyMapping TEST_FREQUENCY_MAPPING = + new VibratorInfo.FrequencyMapping( + /* resonantFrequencyHz= */ 150f, /* minFrequencyHz= */ 50f, + /* frequencyResolutionHz= */ 25f, TEST_AMPLITUDE_MAP); private RampToStepAdapter mAdapter; @@ -56,7 +62,7 @@ public class RampToStepAdapterTest { @Test public void testStepAndPrebakedAndPrimitiveSegments_keepsListUnchanged() { List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList( - new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 10), + new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 1, /* duration= */ 10), new PrebakedSegment( VibrationEffect.EFFECT_CLICK, false, VibrationEffect.EFFECT_STRENGTH_LIGHT), new PrimitiveSegment(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 10))); @@ -71,9 +77,9 @@ public class RampToStepAdapterTest { @Test public void testRampSegments_withPwleCapability_keepsListUnchanged() { List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList( - new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100), + new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 100), new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f, - /* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20))); + /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 1, /* duration= */ 20))); List<VibrationEffectSegment> originalSegments = new ArrayList<>(segments); VibratorInfo vibratorInfo = createVibratorInfo(IVibrator.CAP_COMPOSE_PWLE_EFFECTS); @@ -86,27 +92,28 @@ public class RampToStepAdapterTest { @Test public void testRampSegments_withoutPwleCapability_convertsRampsToSteps() { List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList( - new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 10), - new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100), + new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 1, /* duration= */ 10), + new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 10, /* duration= */ 100), new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 0.2f, - /* startFrequency= */ -4, /* endFrequency= */ 2, /* duration= */ 10), + /* startFrequencyHz= */ 10, /* endFrequencyHz= */ 0, /* duration= */ 10), new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f, - /* startFrequency= */ -3, /* endFrequency= */ 0, /* duration= */ 11), + /* startFrequencyHz= */ 30, /* endFrequencyHz= */ 60, /* duration= */ 11), new RampSegment(/* startAmplitude= */ 0.65f, /* endAmplitude= */ 0.65f, - /* startFrequency= */ 0, /* endFrequency= */ 1, /* duration= */ 200))); + /* startFrequencyHz= */ 0, /* endFrequencyHz= */ 1, /* duration= */ 200))); List<VibrationEffectSegment> expectedSegments = Arrays.asList( - new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 10), - new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100), + new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 1, /* duration= */ 10), + new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 10, /* duration= */ 100), // 10ms ramp becomes 2 steps - new StepSegment(/* amplitude= */ 1, /* frequency= */ -4, /* duration= */ 5), - new StepSegment(/* amplitude= */ 0.2f, /* frequency= */ 2, /* duration= */ 5), + new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 10, /* duration= */ 5), + new StepSegment(/* amplitude= */ 0.2f, /* frequencyHz= */ 150, /* duration= */ 5), // 11ms ramp becomes 3 steps - new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ -3, /* duration= */ 5), - new StepSegment(/* amplitude= */ 0.6f, /* frequency= */ -2, /* duration= */ 5), - new StepSegment(/* amplitude= */ 0.2f, /* frequency= */ 0, /* duration= */ 1), + new StepSegment(/* amplitude= */ 0.8f, /* frequencyHz= */ 30, /* duration= */ 5), + new StepSegment(/* amplitude= */ 0.6f, /* frequencyHz= */ 40, /* duration= */ 5), + new StepSegment(/* amplitude= */ 0.2f, /* frequencyHz= */ 60, /* duration= */ 1), // 200ms ramp with same amplitude becomes a single step - new StepSegment(/* amplitude= */ 0.65f, /* frequency= */ 0, /* duration= */ 200)); + new StepSegment(/* amplitude= */ 0.65f, /* frequencyHz= */ 150, + /* duration= */ 200)); // Repeat index fixed after intermediate steps added assertEquals(4, mAdapter.apply(segments, 3, createVibratorInfo())); @@ -117,6 +124,7 @@ public class RampToStepAdapterTest { private static VibratorInfo createVibratorInfo(int... capabilities) { return new VibratorInfo.Builder(0) .setCapabilities(IntStream.of(capabilities).reduce((a, b) -> a | b).orElse(0)) + .setFrequencyMapping(TEST_FREQUENCY_MAPPING) .build(); } } diff --git a/services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java b/services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java index 128cd2f9e0a1..18ff953446a2 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java @@ -44,6 +44,13 @@ import java.util.stream.IntStream; */ @Presubmit public class StepToRampAdapterTest { + private static final float[] TEST_AMPLITUDE_MAP = new float[]{ + /* 50Hz= */ 0.1f, 0.2f, 0.4f, 0.8f, /* 150Hz= */ 1f, 0.9f, /* 200Hz= */ 0.8f}; + private static final VibratorInfo.FrequencyMapping TEST_FREQUENCY_MAPPING = + new VibratorInfo.FrequencyMapping( + /* resonantFrequencyHz= */ 150f, /* minFrequencyHz= */ 50f, + /* frequencyResolutionHz= */ 25f, TEST_AMPLITUDE_MAP); + private StepToRampAdapter mAdapter; @Before @@ -55,7 +62,7 @@ public class StepToRampAdapterTest { public void testRampAndPrebakedAndPrimitiveSegments_returnsOriginalSegments() { List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList( new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 0.2f, - /* startFrequency= */ -4, /* endFrequency= */ 2, /* duration= */ 10), + /* startFrequencyHz= */ 40f, /* endFrequencyHz= */ 20f, /* duration= */ 10), new PrebakedSegment( VibrationEffect.EFFECT_CLICK, false, VibrationEffect.EFFECT_STRENGTH_LIGHT), new PrimitiveSegment(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 10))); @@ -71,27 +78,28 @@ public class StepToRampAdapterTest { public void testRampSegments_withPwleDurationLimit_splitsLongRamps() { List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList( new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f, - /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10), + /* startFrequencyHz= */ 10, /* endFrequencyHz= */ 10, /* duration= */ 10), new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 1, - /* startFrequency= */ 0, /* endFrequency= */ -1, /* duration= */ 25), + /* startFrequencyHz= */ 0, /* endFrequencyHz= */ 50, /* duration= */ 25), new RampSegment(/* startAmplitude= */ 1, /* endAmplitude*/ 1, - /* startFrequency= */ 0, /* endFrequency= */ 1, /* duration= */ 5))); + /* startFrequencyHz= */ 10, /* endFrequencyHz= */ 20, /* duration= */ 5))); List<VibrationEffectSegment> expectedSegments = Arrays.asList( new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f, - /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10), + /* startFrequencyHz= */ 10, /* endFrequencyHz= */ 10, /* duration= */ 10), new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 0.32f, - /* startFrequency= */ 0, /* endFrequency= */ -0.32f, /* duration= */ 8), + /* startFrequencyHz= */ 150, /* endFrequencyHz= */ 118f, /* duration= */ 8), new RampSegment(/* startAmplitude= */ 0.32f, /* endAmplitude= */ 0.64f, - /* startFrequency= */ -0.32f, /* endFrequency= */ -0.64f, + /* startFrequencyHz= */ 118f, /* endFrequencyHz= */ 86f, /* duration= */ 8), new RampSegment(/* startAmplitude= */ 0.64f, /* endAmplitude= */ 1, - /* startFrequency= */ -0.64f, /* endFrequency= */ -1, /* duration= */ 9), + /* startFrequencyHz= */ 86f, /* endFrequencyHz= */ 50f, /* duration= */ 9), new RampSegment(/* startAmplitude= */ 1, /* endAmplitude*/ 1, - /* startFrequency= */ 0, /* endFrequency= */ 1, /* duration= */ 5)); + /* startFrequencyHz= */ 10, /* endFrequencyHz= */ 20, /* duration= */ 5)); VibratorInfo vibratorInfo = new VibratorInfo.Builder(0) .setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS) .setPwlePrimitiveDurationMax(10) + .setFrequencyMapping(TEST_FREQUENCY_MAPPING) .build(); // Update repeat index to skip the ramp splits. @@ -102,9 +110,9 @@ public class StepToRampAdapterTest { @Test public void testStepAndRampSegments_withoutPwleCapability_keepsListUnchanged() { List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList( - new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 10), + new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 1, /* duration= */ 10), new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f, - /* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20))); + /* startFrequencyHz= */ 10, /* endFrequencyHz= */ 50, /* duration= */ 20))); List<VibrationEffectSegment> originalSegments = new ArrayList<>(segments); assertEquals(-1, mAdapter.apply(segments, -1, createVibratorInfo())); @@ -116,13 +124,13 @@ public class StepToRampAdapterTest { @Test public void testStepAndRampSegments_withPwleCapabilityAndNoFrequency_keepsOriginalSteps() { List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList( - new StepSegment(/* amplitude= */ 0, /* frequency= */ 0, /* duration= */ 10), - new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100), + new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 10), + new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 100), new PrimitiveSegment(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 10), new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1, - /* startFrequency= */ -4, /* endFrequency= */ 2, /* duration= */ 50), + /* startFrequencyHz= */ 40, /* endFrequencyHz= */ 200, /* duration= */ 50), new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f, - /* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20))); + /* startFrequencyHz= */ 10, /* endFrequencyHz= */ 1, /* duration= */ 20))); List<VibrationEffectSegment> originalSegments = new ArrayList<>(segments); VibratorInfo vibratorInfo = createVibratorInfo(IVibrator.CAP_COMPOSE_PWLE_EFFECTS); @@ -135,25 +143,25 @@ public class StepToRampAdapterTest { @Test public void testStepAndRampSegments_withPwleCapabilityAndStepNextToRamp_convertsStepsToRamps() { List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList( - new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 10), - new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100), + new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 200, /* duration= */ 10), + new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 150, /* duration= */ 60), new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1, - /* startFrequency= */ -4, /* endFrequency= */ 2, /* duration= */ 50), + /* startFrequencyHz= */ 1, /* endFrequencyHz= */ 300, /* duration= */ 50), new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f, - /* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20), - new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ -1, /* duration= */ 60))); + /* startFrequencyHz= */ 1000, /* endFrequencyHz= */ 1, /* duration= */ 20), + new StepSegment(/* amplitude= */ 0.8f, /* frequencyHz= */ 10, /* duration= */ 60))); List<VibrationEffectSegment> expectedSegments = Arrays.asList( new RampSegment(/* startAmplitude= */ 0, /* endAmplitude*/ 0, - /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 10), + /* startFrequencyHz= */ 200, /* endFrequencyHz= */ 200, /* duration= */ 10), new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.5f, - /* startFrequency= */ 0, /* endFrequency= */ 0, /* duration= */ 100), + /* startFrequencyHz= */ 150, /* endFrequencyHz= */ 150, /* duration= */ 60), new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 1, - /* startFrequency= */ -4, /* endFrequency= */ 2, /* duration= */ 50), + /* startFrequencyHz= */ 1, /* endFrequencyHz= */ 300, /* duration= */ 50), new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f, - /* startFrequency= */ 10, /* endFrequency= */ -5, /* duration= */ 20), + /* startFrequencyHz= */ 1000, /* endFrequencyHz= */ 1, /* duration= */ 20), new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.8f, - /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 60)); + /* startFrequencyHz= */ 10, /* endFrequencyHz= */ 10, /* duration= */ 60)); VibratorInfo vibratorInfo = createVibratorInfo(IVibrator.CAP_COMPOSE_PWLE_EFFECTS); assertEquals(-1, mAdapter.apply(segments, -1, vibratorInfo)); @@ -165,13 +173,13 @@ public class StepToRampAdapterTest { @Test public void testStepSegments_withPwleCapabilityAndFrequency_convertsStepsToRamps() { List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList( - new StepSegment(/* amplitude= */ 0, /* frequency= */ -1, /* duration= */ 10), - new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 1, /* duration= */ 100))); + new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 100, /* duration= */ 10), + new StepSegment(/* amplitude= */ 0.5f, /* frequencyHz= */ 0, /* duration= */ 6))); List<VibrationEffectSegment> expectedSegments = Arrays.asList( new RampSegment(/* startAmplitude= */ 0, /* endAmplitude*/ 0, - /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10), + /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 100, /* duration= */ 10), new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.5f, - /* startFrequency= */ 1, /* endFrequency= */ 1, /* duration= */ 100)); + /* startFrequencyHz= */ 150, /* endFrequencyHz= */ 150, /* duration= */ 6)); VibratorInfo vibratorInfo = createVibratorInfo(IVibrator.CAP_COMPOSE_PWLE_EFFECTS); assertEquals(-1, mAdapter.apply(segments, -1, vibratorInfo)); @@ -183,6 +191,7 @@ public class StepToRampAdapterTest { private static VibratorInfo createVibratorInfo(int... capabilities) { return new VibratorInfo.Builder(0) .setCapabilities(IntStream.of(capabilities).reduce((a, b) -> a | b).orElse(0)) + .setFrequencyMapping(TEST_FREQUENCY_MAPPING) .build(); } } diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java index 59c0b0e96fcd..6369dbc6b171 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java @@ -16,6 +16,14 @@ package com.android.server.vibrator; +import static android.os.VibrationAttributes.USAGE_NOTIFICATION; +import static android.os.VibrationAttributes.USAGE_RINGTONE; +import static android.os.VibrationAttributes.USAGE_TOUCH; +import static android.os.Vibrator.VIBRATION_INTENSITY_HIGH; +import static android.os.Vibrator.VIBRATION_INTENSITY_LOW; +import static android.os.Vibrator.VIBRATION_INTENSITY_MEDIUM; +import static android.os.Vibrator.VIBRATION_INTENSITY_OFF; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertTrue; @@ -24,7 +32,6 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import android.content.ContentResolver; -import android.content.Context; import android.content.ContextWrapper; import android.os.Handler; import android.os.IExternalVibratorService; @@ -37,6 +44,7 @@ import android.os.test.TestLooper; import android.os.vibrator.PrebakedSegment; import android.os.vibrator.PrimitiveSegment; import android.os.vibrator.StepSegment; +import android.os.vibrator.VibrationConfig; import android.os.vibrator.VibrationEffectSegment; import android.platform.test.annotations.Presubmit; import android.provider.Settings; @@ -68,29 +76,31 @@ public class VibrationScalerTest { @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule(); @Mock private PowerManagerInternal mPowerManagerInternalMock; + @Mock private VibrationConfig mVibrationConfigMock; private TestLooper mTestLooper; private ContextWrapper mContextSpy; - private FakeVibrator mFakeVibrator; private VibrationSettings mVibrationSettings; private VibrationScaler mVibrationScaler; @Before public void setUp() throws Exception { mTestLooper = new TestLooper(); - mFakeVibrator = new FakeVibrator(); mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext())); ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy); when(mContextSpy.getContentResolver()).thenReturn(contentResolver); - when(mContextSpy.getSystemService(eq(Context.VIBRATOR_SERVICE))).thenReturn(mFakeVibrator); LocalServices.removeServiceForTest(PowerManagerInternal.class); LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternalMock); + Settings.System.putInt(contentResolver, Settings.System.HAPTIC_FEEDBACK_ENABLED, 1); + Settings.System.putInt(contentResolver, Settings.System.VIBRATE_WHEN_RINGING, 1); + mVibrationSettings = new VibrationSettings( - mContextSpy, new Handler(mTestLooper.getLooper())); + mContextSpy, new Handler(mTestLooper.getLooper()), mVibrationConfigMock); mVibrationScaler = new VibrationScaler(mContextSpy, mVibrationSettings); + mVibrationSettings.onSystemReady(); } @@ -101,91 +111,80 @@ public class VibrationScalerTest { @Test public void testGetExternalVibrationScale() { - mFakeVibrator.setDefaultHapticFeedbackIntensity(Vibrator.VIBRATION_INTENSITY_LOW); - setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, - Vibrator.VIBRATION_INTENSITY_HIGH); + setDefaultIntensity(USAGE_TOUCH, Vibrator.VIBRATION_INTENSITY_LOW); + setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH); assertEquals(IExternalVibratorService.SCALE_VERY_HIGH, - mVibrationScaler.getExternalVibrationScale(VibrationAttributes.USAGE_TOUCH)); + mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH)); - setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, - Vibrator.VIBRATION_INTENSITY_MEDIUM); + setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_MEDIUM); assertEquals(IExternalVibratorService.SCALE_HIGH, - mVibrationScaler.getExternalVibrationScale(VibrationAttributes.USAGE_TOUCH)); + mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH)); - setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, - Vibrator.VIBRATION_INTENSITY_LOW); + setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_LOW); assertEquals(IExternalVibratorService.SCALE_NONE, - mVibrationScaler.getExternalVibrationScale(VibrationAttributes.USAGE_TOUCH)); + mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH)); - mFakeVibrator.setDefaultHapticFeedbackIntensity(Vibrator.VIBRATION_INTENSITY_MEDIUM); + setDefaultIntensity(USAGE_TOUCH, VIBRATION_INTENSITY_MEDIUM); assertEquals(IExternalVibratorService.SCALE_LOW, - mVibrationScaler.getExternalVibrationScale(VibrationAttributes.USAGE_TOUCH)); + mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH)); - mFakeVibrator.setDefaultHapticFeedbackIntensity(Vibrator.VIBRATION_INTENSITY_HIGH); + setDefaultIntensity(USAGE_TOUCH, VIBRATION_INTENSITY_HIGH); assertEquals(IExternalVibratorService.SCALE_VERY_LOW, - mVibrationScaler.getExternalVibrationScale(VibrationAttributes.USAGE_TOUCH)); + mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH)); - setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, - Vibrator.VIBRATION_INTENSITY_OFF); + setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF); // Unexpected vibration intensity will be treated as SCALE_NONE. assertEquals(IExternalVibratorService.SCALE_NONE, - mVibrationScaler.getExternalVibrationScale(VibrationAttributes.USAGE_TOUCH)); + mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH)); } @Test public void scale_withPrebakedSegment_setsEffectStrengthBasedOnSettings() { - setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, - Vibrator.VIBRATION_INTENSITY_HIGH); + setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_HIGH); PrebakedSegment effect = new PrebakedSegment(VibrationEffect.EFFECT_CLICK, /* shouldFallback= */ false, VibrationEffect.EFFECT_STRENGTH_MEDIUM); - PrebakedSegment scaled = mVibrationScaler.scale( - effect, VibrationAttributes.USAGE_NOTIFICATION); + PrebakedSegment scaled = mVibrationScaler.scale(effect, USAGE_NOTIFICATION); assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_STRONG); setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, - Vibrator.VIBRATION_INTENSITY_MEDIUM); - scaled = mVibrationScaler.scale(effect, VibrationAttributes.USAGE_NOTIFICATION); + VIBRATION_INTENSITY_MEDIUM); + scaled = mVibrationScaler.scale(effect, USAGE_NOTIFICATION); assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_MEDIUM); - setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, - Vibrator.VIBRATION_INTENSITY_LOW); - scaled = mVibrationScaler.scale(effect, VibrationAttributes.USAGE_NOTIFICATION); + setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW); + scaled = mVibrationScaler.scale(effect, USAGE_NOTIFICATION); assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_LIGHT); - setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, - Vibrator.VIBRATION_INTENSITY_OFF); - scaled = mVibrationScaler.scale(effect, VibrationAttributes.USAGE_NOTIFICATION); + setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF); + scaled = mVibrationScaler.scale(effect, USAGE_NOTIFICATION); // Unexpected intensity setting will be mapped to STRONG. assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_STRONG); } @Test public void scale_withPrebakedEffect_setsEffectStrengthBasedOnSettings() { - setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, - Vibrator.VIBRATION_INTENSITY_HIGH); + setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_HIGH); VibrationEffect effect = VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK); PrebakedSegment scaled = getFirstSegment(mVibrationScaler.scale( - effect, VibrationAttributes.USAGE_NOTIFICATION)); + effect, USAGE_NOTIFICATION)); assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_STRONG); setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, - Vibrator.VIBRATION_INTENSITY_MEDIUM); + VIBRATION_INTENSITY_MEDIUM); scaled = getFirstSegment(mVibrationScaler.scale( - effect, VibrationAttributes.USAGE_NOTIFICATION)); + effect, USAGE_NOTIFICATION)); assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_MEDIUM); - setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, - Vibrator.VIBRATION_INTENSITY_LOW); + setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW); scaled = getFirstSegment(mVibrationScaler.scale( - effect, VibrationAttributes.USAGE_NOTIFICATION)); + effect, USAGE_NOTIFICATION)); assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_LIGHT); - setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, - Vibrator.VIBRATION_INTENSITY_OFF); + setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF); scaled = getFirstSegment(mVibrationScaler.scale( - effect, VibrationAttributes.USAGE_NOTIFICATION)); + effect, USAGE_NOTIFICATION)); // Unexpected intensity setting will be mapped to STRONG. assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_STRONG); } @@ -193,81 +192,77 @@ public class VibrationScalerTest { @Test public void scale_withOneShotAndWaveform_resolvesAmplitude() { // No scale, default amplitude still resolved - mFakeVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_LOW); - setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, - Vibrator.VIBRATION_INTENSITY_LOW); + setDefaultIntensity(USAGE_RINGTONE, VIBRATION_INTENSITY_LOW); + setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW); StepSegment resolved = getFirstSegment(mVibrationScaler.scale( VibrationEffect.createOneShot(10, VibrationEffect.DEFAULT_AMPLITUDE), - VibrationAttributes.USAGE_RINGTONE)); + USAGE_RINGTONE)); assertTrue(resolved.getAmplitude() > 0); resolved = getFirstSegment(mVibrationScaler.scale( VibrationEffect.createWaveform(new long[]{10}, new int[]{VibrationEffect.DEFAULT_AMPLITUDE}, -1), - VibrationAttributes.USAGE_RINGTONE)); + USAGE_RINGTONE)); assertTrue(resolved.getAmplitude() > 0); } @Test public void scale_withOneShotAndWaveform_scalesAmplitude() { - mFakeVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_LOW); - setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, - Vibrator.VIBRATION_INTENSITY_HIGH); - mFakeVibrator.setDefaultNotificationVibrationIntensity(Vibrator.VIBRATION_INTENSITY_HIGH); - setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, - Vibrator.VIBRATION_INTENSITY_LOW); - mFakeVibrator.setDefaultHapticFeedbackIntensity(Vibrator.VIBRATION_INTENSITY_MEDIUM); - setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, - Vibrator.VIBRATION_INTENSITY_MEDIUM); + setDefaultIntensity(USAGE_RINGTONE, VIBRATION_INTENSITY_LOW); + setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_HIGH); + setDefaultIntensity(USAGE_NOTIFICATION, VIBRATION_INTENSITY_HIGH); + setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW); + setDefaultIntensity(USAGE_TOUCH, VIBRATION_INTENSITY_MEDIUM); + setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_MEDIUM); StepSegment scaled = getFirstSegment(mVibrationScaler.scale( - VibrationEffect.createOneShot(128, 128), VibrationAttributes.USAGE_RINGTONE)); + VibrationEffect.createOneShot(128, 128), USAGE_RINGTONE)); // Ringtone scales up. assertTrue(scaled.getAmplitude() > 0.5); scaled = getFirstSegment(mVibrationScaler.scale( VibrationEffect.createWaveform(new long[]{128}, new int[]{128}, -1), - VibrationAttributes.USAGE_NOTIFICATION)); + USAGE_NOTIFICATION)); // Notification scales down. assertTrue(scaled.getAmplitude() < 0.5); scaled = getFirstSegment(mVibrationScaler.scale(VibrationEffect.createOneShot(128, 128), - VibrationAttributes.USAGE_TOUCH)); + USAGE_TOUCH)); // Haptic feedback does not scale. assertEquals(128f / 255, scaled.getAmplitude(), 1e-5); } @Test public void scale_withComposed_scalesPrimitives() { - mFakeVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_LOW); - setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, - Vibrator.VIBRATION_INTENSITY_HIGH); - mFakeVibrator.setDefaultNotificationVibrationIntensity(Vibrator.VIBRATION_INTENSITY_HIGH); - setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, - Vibrator.VIBRATION_INTENSITY_LOW); - mFakeVibrator.setDefaultHapticFeedbackIntensity(Vibrator.VIBRATION_INTENSITY_MEDIUM); - setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, - Vibrator.VIBRATION_INTENSITY_MEDIUM); + setDefaultIntensity(USAGE_RINGTONE, VIBRATION_INTENSITY_LOW); + setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_HIGH); + setDefaultIntensity(USAGE_NOTIFICATION, VIBRATION_INTENSITY_HIGH); + setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW); + setDefaultIntensity(USAGE_TOUCH, VIBRATION_INTENSITY_MEDIUM); + setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_MEDIUM); VibrationEffect composed = VibrationEffect.startComposition() .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f).compose(); - PrimitiveSegment scaled = getFirstSegment(mVibrationScaler.scale(composed, - VibrationAttributes.USAGE_RINGTONE)); + PrimitiveSegment scaled = getFirstSegment(mVibrationScaler.scale(composed, USAGE_RINGTONE)); // Ringtone scales up. assertTrue(scaled.getScale() > 0.5f); - scaled = getFirstSegment(mVibrationScaler.scale(composed, - VibrationAttributes.USAGE_NOTIFICATION)); + scaled = getFirstSegment(mVibrationScaler.scale(composed, USAGE_NOTIFICATION)); // Notification scales down. assertTrue(scaled.getScale() < 0.5f); - scaled = getFirstSegment(mVibrationScaler.scale(composed, VibrationAttributes.USAGE_TOUCH)); + scaled = getFirstSegment(mVibrationScaler.scale(composed, USAGE_TOUCH)); // Haptic feedback does not scale. assertEquals(0.5, scaled.getScale(), 1e-5); } + private void setDefaultIntensity(@VibrationAttributes.Usage int usage, + @Vibrator.VibrationIntensity int intensity) { + when(mVibrationConfigMock.getDefaultVibrationIntensity(eq(usage))).thenReturn(intensity); + } + private <T extends VibrationEffectSegment> T getFirstSegment(VibrationEffect.Composed effect) { return (T) effect.getSegments().get(0); } diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java index ab9fbb581416..ff59d0f22c3c 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java @@ -16,9 +16,11 @@ package com.android.server.vibrator; +import static android.os.VibrationAttributes.USAGE_ACCESSIBILITY; import static android.os.VibrationAttributes.USAGE_ALARM; import static android.os.VibrationAttributes.USAGE_COMMUNICATION_REQUEST; import static android.os.VibrationAttributes.USAGE_HARDWARE_FEEDBACK; +import static android.os.VibrationAttributes.USAGE_MEDIA; import static android.os.VibrationAttributes.USAGE_NOTIFICATION; import static android.os.VibrationAttributes.USAGE_PHYSICAL_EMULATION; import static android.os.VibrationAttributes.USAGE_RINGTONE; @@ -35,6 +37,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.spy; @@ -45,7 +48,6 @@ import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.content.ContentResolver; -import android.content.Context; import android.content.ContextWrapper; import android.content.Intent; import android.media.AudioManager; @@ -55,7 +57,9 @@ import android.os.PowerSaveState; import android.os.UserHandle; import android.os.VibrationAttributes; import android.os.VibrationEffect; +import android.os.Vibrator; import android.os.test.TestLooper; +import android.os.vibrator.VibrationConfig; import android.platform.test.annotations.Presubmit; import android.provider.Settings; @@ -87,6 +91,19 @@ public class VibrationSettingsTest { private static final PowerSaveState LOW_POWER_STATE = new PowerSaveState.Builder() .setBatterySaverEnabled(true).build(); + private static final int[] ALL_USAGES = new int[] { + USAGE_UNKNOWN, + USAGE_ACCESSIBILITY, + USAGE_ALARM, + USAGE_COMMUNICATION_REQUEST, + USAGE_HARDWARE_FEEDBACK, + USAGE_MEDIA, + USAGE_NOTIFICATION, + USAGE_PHYSICAL_EMULATION, + USAGE_RINGTONE, + USAGE_TOUCH, + }; + @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule(); @Rule @@ -96,23 +113,23 @@ public class VibrationSettingsTest { private VibrationSettings.OnVibratorSettingsChanged mListenerMock; @Mock private PowerManagerInternal mPowerManagerInternalMock; + @Mock + private VibrationConfig mVibrationConfigMock; private TestLooper mTestLooper; private ContextWrapper mContextSpy; private AudioManager mAudioManager; - private FakeVibrator mFakeVibrator; private VibrationSettings mVibrationSettings; private PowerManagerInternal.LowPowerModeListener mRegisteredPowerModeListener; @Before public void setUp() throws Exception { mTestLooper = new TestLooper(); - mFakeVibrator = new FakeVibrator(); mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext())); ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy); when(mContextSpy.getContentResolver()).thenReturn(contentResolver); - when(mContextSpy.getSystemService(eq(Context.VIBRATOR_SERVICE))).thenReturn(mFakeVibrator); + doAnswer(invocation -> { mRegisteredPowerModeListener = invocation.getArgument(0); return null; @@ -121,16 +138,18 @@ public class VibrationSettingsTest { LocalServices.removeServiceForTest(PowerManagerInternal.class); LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternalMock); + setDefaultIntensity(VIBRATION_INTENSITY_MEDIUM); mAudioManager = mContextSpy.getSystemService(AudioManager.class); mVibrationSettings = new VibrationSettings(mContextSpy, - new Handler(mTestLooper.getLooper())); - mVibrationSettings.onSystemReady(); + new Handler(mTestLooper.getLooper()), mVibrationConfigMock); // Simulate System defaults. + setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 1); setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0); setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1); setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0); setRingerMode(AudioManager.RINGER_MODE_NORMAL); + mVibrationSettings.onSystemReady(); } @After @@ -145,12 +164,15 @@ public class VibrationSettingsTest { setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1); setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0); setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0); + setUserSetting(Settings.System.ALARM_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF); setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF); + setUserSetting(Settings.System.MEDIA_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF); setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF); + setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 0); setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF); setUserSetting(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF); - verify(mListenerMock, times(7)).onChange(); + verify(mListenerMock, times(10)).onChange(); } @Test @@ -192,9 +214,7 @@ public class VibrationSettingsTest { UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0); for (int usage : expectedAllowedVibrations) { - assertNull("Error for usage " + VibrationAttributes.usageToString(usage), - mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(usage))); + assertVibrationNotIgnoredForUsage(usage); } } @@ -209,10 +229,7 @@ public class VibrationSettingsTest { UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0); for (int usage : expectedIgnoredVibrations) { - assertEquals("Error for usage " + VibrationAttributes.usageToString(usage), - Vibration.Status.IGNORED_BACKGROUND, - mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(usage))); + assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_BACKGROUND); } } @@ -221,10 +238,9 @@ public class VibrationSettingsTest { mVibrationSettings.mUidObserver.onUidStateChanged( UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0); - assertNull(mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(USAGE_TOUCH))); - assertNull(mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(USAGE_ALARM))); + for (int usage : ALL_USAGES) { + assertVibrationNotIgnoredForUsage(usage); + } } @Test @@ -238,9 +254,7 @@ public class VibrationSettingsTest { mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE); for (int usage : expectedAllowedVibrations) { - assertNull("Error for usage " + VibrationAttributes.usageToString(usage), - mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(usage))); + assertVibrationNotIgnoredForUsage(usage); } } @@ -257,10 +271,7 @@ public class VibrationSettingsTest { mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE); for (int usage : expectedIgnoredVibrations) { - assertEquals("Error for usage " + VibrationAttributes.usageToString(usage), - Vibration.Status.IGNORED_FOR_POWER, - mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(usage))); + assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_POWER); } } @@ -268,130 +279,130 @@ public class VibrationSettingsTest { public void shouldIgnoreVibration_notInBatterySaverMode_allowsAnyUsage() { mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE); - assertNull(mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(USAGE_TOUCH))); - assertNull(mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(USAGE_COMMUNICATION_REQUEST))); + for (int usage : ALL_USAGES) { + assertVibrationNotIgnoredForUsage(usage); + } } @Test public void shouldIgnoreVibration_withRingerModeSilent_ignoresRingtoneAndTouch() { // Vibrating settings on are overruled by ringer mode. + setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 1); setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1); - setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0); + setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1); setRingerMode(AudioManager.RINGER_MODE_SILENT); - assertEquals(Vibration.Status.IGNORED_FOR_RINGER_MODE, - mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(USAGE_RINGTONE))); - assertEquals(Vibration.Status.IGNORED_FOR_RINGER_MODE, - mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(USAGE_TOUCH))); - assertNull(mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(USAGE_COMMUNICATION_REQUEST))); - assertNull(mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(USAGE_HARDWARE_FEEDBACK))); + for (int usage : ALL_USAGES) { + if (usage == USAGE_RINGTONE || usage == USAGE_TOUCH) { + assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_RINGER_MODE); + } else { + assertVibrationNotIgnoredForUsage(usage); + } + } } @Test public void shouldIgnoreVibration_withRingerModeVibrate_allowsAllVibrations() { - // Vibrating settings off are overruled by ringer mode. - setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0); - setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0); setRingerMode(AudioManager.RINGER_MODE_VIBRATE); - assertNull(mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(USAGE_TOUCH))); - assertNull(mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(USAGE_PHYSICAL_EMULATION))); - assertNull(mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(USAGE_RINGTONE))); + for (int usage : ALL_USAGES) { + assertVibrationNotIgnoredForUsage(usage); + } + } + + @Test + public void shouldIgnoreVibration_withRingerModeNormal_allowsAllVibrations() { + setRingerMode(AudioManager.RINGER_MODE_NORMAL); + + for (int usage : ALL_USAGES) { + assertVibrationNotIgnoredForUsage(usage); + } } @Test - public void shouldIgnoreVibration_withRingerModeNormalAndRingSettingsOff_ignoresRingtoneOnly() { - // Vibrating settings off are respected for normal ringer mode. + public void shouldIgnoreVibration_withRingSettingsOff_disableRingtoneVibrations() { setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0); setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0); - setRingerMode(AudioManager.RINGER_MODE_NORMAL); - assertEquals(Vibration.Status.IGNORED_FOR_RINGER_MODE, - mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(USAGE_RINGTONE))); - assertNull(mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(USAGE_TOUCH))); - assertNull(mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(USAGE_NOTIFICATION))); + for (int usage : ALL_USAGES) { + if (usage == USAGE_RINGTONE) { + assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS); + } else { + assertVibrationNotIgnoredForUsage(usage); + } + } } @Test - public void shouldIgnoreVibration_withRingerModeNormalAndRingSettingsOn_allowsAllVibrations() { + public void shouldIgnoreVibration_withRingSettingsOn_allowsAllVibrations() { setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1); setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0); - setRingerMode(AudioManager.RINGER_MODE_NORMAL); - assertNull(mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(USAGE_TOUCH))); - assertNull(mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(USAGE_RINGTONE))); - assertNull(mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(USAGE_ALARM))); + for (int usage : ALL_USAGES) { + assertVibrationNotIgnoredForUsage(usage); + } } @Test - public void shouldIgnoreVibration_withRingerModeNormalAndRampingRingerOn_allowsAllVibrations() { + public void shouldIgnoreVibration_withRampingRingerOn_allowsAllVibrations() { setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0); setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1); - setRingerMode(AudioManager.RINGER_MODE_NORMAL); - assertNull(mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(USAGE_TOUCH))); - assertNull(mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(USAGE_RINGTONE))); - assertNull(mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(USAGE_COMMUNICATION_REQUEST))); + for (int usage : ALL_USAGES) { + assertVibrationNotIgnoredForUsage(usage); + } + } + + @Test + public void shouldIgnoreVibration_withHapticFeedbackDisabled_ignoresTouchVibration() { + setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 0); + + for (int usage : ALL_USAGES) { + if (usage == USAGE_TOUCH) { + assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS); + } else { + assertVibrationNotIgnoredForUsage(usage); + } + } } @Test public void shouldIgnoreVibration_withHapticFeedbackSettingsOff_ignoresTouchVibration() { setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF); - assertEquals(Vibration.Status.IGNORED_FOR_SETTINGS, - mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(USAGE_TOUCH))); - assertNull(mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(USAGE_RINGTONE))); - assertNull(mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(USAGE_HARDWARE_FEEDBACK))); + for (int usage : ALL_USAGES) { + if (usage == USAGE_TOUCH) { + assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS); + } else { + assertVibrationNotIgnoredForUsage(usage); + } + } } @Test public void shouldIgnoreVibration_withHardwareFeedbackSettingsOff_ignoresHardwareVibrations() { setUserSetting(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF); - assertEquals(Vibration.Status.IGNORED_FOR_SETTINGS, - mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(USAGE_HARDWARE_FEEDBACK))); - assertEquals(Vibration.Status.IGNORED_FOR_SETTINGS, - mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(USAGE_PHYSICAL_EMULATION))); - assertNull(mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(USAGE_TOUCH))); - assertNull(mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(USAGE_NOTIFICATION))); + for (int usage : ALL_USAGES) { + if (usage == USAGE_HARDWARE_FEEDBACK || usage == USAGE_PHYSICAL_EMULATION) { + assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS); + } else { + assertVibrationNotIgnoredForUsage(usage); + } + } } @Test public void shouldIgnoreVibration_withNotificationSettingsOff_ignoresNotificationVibrations() { setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF); - assertEquals(Vibration.Status.IGNORED_FOR_SETTINGS, - mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(USAGE_NOTIFICATION))); - assertNull(mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(USAGE_ALARM))); - assertNull(mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(USAGE_RINGTONE))); + for (int usage : ALL_USAGES) { + if (usage == USAGE_NOTIFICATION) { + assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS); + } else { + assertVibrationNotIgnoredForUsage(usage); + } + } } @Test @@ -402,15 +413,13 @@ public class VibrationSettingsTest { setRingerMode(AudioManager.RINGER_MODE_VIBRATE); setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF); - assertEquals(Vibration.Status.IGNORED_FOR_SETTINGS, - mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(USAGE_RINGTONE))); - assertNull(mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(USAGE_NOTIFICATION))); - assertNull(mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(USAGE_ALARM))); - assertNull(mVibrationSettings.shouldIgnoreVibration(UID, - VibrationAttributes.createForUsage(USAGE_TOUCH))); + for (int usage : ALL_USAGES) { + if (usage == USAGE_RINGTONE) { + assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS); + } else { + assertVibrationNotIgnoredForUsage(usage); + } + } } @Test @@ -423,90 +432,40 @@ public class VibrationSettingsTest { } @Test - public void getDefaultIntensity_beforeSystemReady_returnsMediumToAllExceptAlarm() { - mFakeVibrator.setDefaultHapticFeedbackIntensity(VIBRATION_INTENSITY_HIGH); - mFakeVibrator.setDefaultNotificationVibrationIntensity(VIBRATION_INTENSITY_HIGH); - mFakeVibrator.setDefaultRingVibrationIntensity(VIBRATION_INTENSITY_HIGH); - - setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF); - setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF); + public void getDefaultIntensity_returnsIntensityFromVibratorConfig() { + setDefaultIntensity(VIBRATION_INTENSITY_HIGH); + setUserSetting(Settings.System.ALARM_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF); setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF); setUserSetting(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF); - - VibrationSettings vibrationSettings = new VibrationSettings(mContextSpy, - new Handler(mTestLooper.getLooper())); - - assertEquals(VIBRATION_INTENSITY_HIGH, - vibrationSettings.getDefaultIntensity(USAGE_ALARM)); - assertEquals(VIBRATION_INTENSITY_MEDIUM, - vibrationSettings.getDefaultIntensity(USAGE_TOUCH)); - assertEquals(VIBRATION_INTENSITY_MEDIUM, - vibrationSettings.getDefaultIntensity(USAGE_HARDWARE_FEEDBACK)); - assertEquals(VIBRATION_INTENSITY_MEDIUM, - vibrationSettings.getDefaultIntensity(USAGE_PHYSICAL_EMULATION)); - assertEquals(VIBRATION_INTENSITY_MEDIUM, - vibrationSettings.getDefaultIntensity(USAGE_NOTIFICATION)); - assertEquals(VIBRATION_INTENSITY_MEDIUM, - vibrationSettings.getDefaultIntensity(USAGE_UNKNOWN)); - assertEquals(VIBRATION_INTENSITY_MEDIUM, - vibrationSettings.getDefaultIntensity(USAGE_RINGTONE)); - } - - @Test - public void getDefaultIntensity_returnsIntensityFromVibratorService() { - mFakeVibrator.setDefaultHapticFeedbackIntensity(VIBRATION_INTENSITY_HIGH); - mFakeVibrator.setDefaultNotificationVibrationIntensity(VIBRATION_INTENSITY_MEDIUM); - mFakeVibrator.setDefaultRingVibrationIntensity(VIBRATION_INTENSITY_LOW); - setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF); + setUserSetting(Settings.System.MEDIA_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF); setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF); - setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF); - assertEquals(VIBRATION_INTENSITY_HIGH, - mVibrationSettings.getDefaultIntensity(USAGE_ALARM)); - assertEquals(VIBRATION_INTENSITY_HIGH, - mVibrationSettings.getDefaultIntensity(USAGE_TOUCH)); - assertEquals(VIBRATION_INTENSITY_HIGH, - mVibrationSettings.getDefaultIntensity(USAGE_HARDWARE_FEEDBACK)); - assertEquals(VIBRATION_INTENSITY_HIGH, - mVibrationSettings.getDefaultIntensity(USAGE_PHYSICAL_EMULATION)); - assertEquals(VIBRATION_INTENSITY_MEDIUM, - mVibrationSettings.getDefaultIntensity(USAGE_NOTIFICATION)); - assertEquals(VIBRATION_INTENSITY_MEDIUM, - mVibrationSettings.getDefaultIntensity(USAGE_UNKNOWN)); - assertEquals(VIBRATION_INTENSITY_LOW, - mVibrationSettings.getDefaultIntensity(USAGE_RINGTONE)); + for (int usage : ALL_USAGES) { + assertEquals(VIBRATION_INTENSITY_HIGH, mVibrationSettings.getDefaultIntensity(usage)); + } } @Test public void getCurrentIntensity_returnsIntensityFromSettings() { - mFakeVibrator.setDefaultHapticFeedbackIntensity(VIBRATION_INTENSITY_OFF); - mFakeVibrator.setDefaultNotificationVibrationIntensity(VIBRATION_INTENSITY_OFF); - mFakeVibrator.setDefaultRingVibrationIntensity(VIBRATION_INTENSITY_OFF); - - setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH); + setDefaultIntensity(VIBRATION_INTENSITY_OFF); + setUserSetting(Settings.System.ALARM_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW); + setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_LOW); setUserSetting(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_LOW); - setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, - VIBRATION_INTENSITY_MEDIUM); + setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW); + setUserSetting(Settings.System.MEDIA_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW); setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW); - assertEquals(VIBRATION_INTENSITY_HIGH, mVibrationSettings.getCurrentIntensity(USAGE_ALARM)); - assertEquals(VIBRATION_INTENSITY_HIGH, mVibrationSettings.getCurrentIntensity(USAGE_TOUCH)); - assertEquals(VIBRATION_INTENSITY_LOW, - mVibrationSettings.getCurrentIntensity(USAGE_HARDWARE_FEEDBACK)); - assertEquals(VIBRATION_INTENSITY_LOW, - mVibrationSettings.getCurrentIntensity(USAGE_PHYSICAL_EMULATION)); - assertEquals(VIBRATION_INTENSITY_MEDIUM, - mVibrationSettings.getCurrentIntensity(USAGE_NOTIFICATION)); - assertEquals(VIBRATION_INTENSITY_MEDIUM, - mVibrationSettings.getCurrentIntensity(USAGE_UNKNOWN)); - assertEquals(VIBRATION_INTENSITY_LOW, - mVibrationSettings.getCurrentIntensity(USAGE_RINGTONE)); + for (int usage : ALL_USAGES) { + assertEquals(errorMessageForUsage(usage), + VIBRATION_INTENSITY_LOW, + mVibrationSettings.getCurrentIntensity(usage)); + } } @Test public void getCurrentIntensity_updateTriggeredAfterUserSwitched() { - mFakeVibrator.setDefaultRingVibrationIntensity(VIBRATION_INTENSITY_OFF); + setDefaultIntensity(USAGE_RINGTONE, VIBRATION_INTENSITY_OFF); setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_HIGH); assertEquals(VIBRATION_INTENSITY_HIGH, mVibrationSettings.getCurrentIntensity(USAGE_RINGTONE)); @@ -524,8 +483,9 @@ public class VibrationSettingsTest { @Test public void getCurrentIntensity_noHardwareFeedbackValueUsesHapticFeedbackValue() { - mFakeVibrator.setDefaultHapticFeedbackIntensity(VIBRATION_INTENSITY_MEDIUM); + setDefaultIntensity(USAGE_HARDWARE_FEEDBACK, VIBRATION_INTENSITY_MEDIUM); setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF); + mVibrationSettings.updateSettings(); assertEquals(VIBRATION_INTENSITY_OFF, mVibrationSettings.getCurrentIntensity(USAGE_TOUCH)); // If haptic feedback is off, fallback to default value. assertEquals(VIBRATION_INTENSITY_MEDIUM, @@ -533,15 +493,11 @@ public class VibrationSettingsTest { assertEquals(VIBRATION_INTENSITY_MEDIUM, mVibrationSettings.getCurrentIntensity(USAGE_PHYSICAL_EMULATION)); - // Switching user is not working with FakeSettingsProvider. - // Testing the broadcast flow manually. - Settings.System.putIntForUser(mContextSpy.getContentResolver(), - Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH, - UserHandle.USER_CURRENT); - mVibrationSettings.mUserReceiver.onReceive(mContextSpy, - new Intent(Intent.ACTION_USER_SWITCHED)); + setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH); + mVibrationSettings.updateSettings(); assertEquals(VIBRATION_INTENSITY_HIGH, mVibrationSettings.getCurrentIntensity(USAGE_TOUCH)); + // If haptic feedback is on, fallback to that value. assertEquals(VIBRATION_INTENSITY_HIGH, mVibrationSettings.getCurrentIntensity(USAGE_HARDWARE_FEEDBACK)); assertEquals(VIBRATION_INTENSITY_HIGH, @@ -557,6 +513,33 @@ public class VibrationSettingsTest { assertNotNull(mVibrationSettings.getFallbackEffect(VibrationEffect.EFFECT_DOUBLE_CLICK)); } + private void assertVibrationIgnoredForUsage(@VibrationAttributes.Usage int usage, + Vibration.Status expectedStatus) { + assertEquals(errorMessageForUsage(usage), + expectedStatus, + mVibrationSettings.shouldIgnoreVibration(UID, + VibrationAttributes.createForUsage(usage))); + } + + private void assertVibrationNotIgnoredForUsage(@VibrationAttributes.Usage int usage) { + assertNull(errorMessageForUsage(usage), + mVibrationSettings.shouldIgnoreVibration(UID, + VibrationAttributes.createForUsage(usage))); + } + + private String errorMessageForUsage(int usage) { + return "Error for usage " + VibrationAttributes.usageToString(usage); + } + + private void setDefaultIntensity(@Vibrator.VibrationIntensity int intensity) { + when(mVibrationConfigMock.getDefaultVibrationIntensity(anyInt())).thenReturn(intensity); + } + + private void setDefaultIntensity(@VibrationAttributes.Usage int usage, + @Vibrator.VibrationIntensity int intensity) { + when(mVibrationConfigMock.getDefaultVibrationIntensity(eq(usage))).thenReturn(intensity); + } + private void setUserSetting(String settingName, int value) { Settings.System.putIntForUser( mContextSpy.getContentResolver(), settingName, value, UserHandle.USER_CURRENT); diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java index 4cc4d55f228d..5dd44ffc664f 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java @@ -44,11 +44,13 @@ import android.os.Process; import android.os.SystemClock; import android.os.VibrationAttributes; import android.os.VibrationEffect; +import android.os.Vibrator; import android.os.test.TestLooper; import android.os.vibrator.PrebakedSegment; import android.os.vibrator.PrimitiveSegment; import android.os.vibrator.RampSegment; import android.os.vibrator.StepSegment; +import android.os.vibrator.VibrationConfig; import android.os.vibrator.VibrationEffectSegment; import android.platform.test.annotations.LargeTest; import android.platform.test.annotations.Presubmit; @@ -101,6 +103,8 @@ public class VibrationThreadTest { private IBinder mVibrationToken; @Mock private IBatteryStats mIBatteryStatsMock; + @Mock + private VibrationConfig mVibrationConfigMock; private final Map<Integer, FakeVibratorControllerProvider> mVibratorProviders = new HashMap<>(); private VibrationSettings mVibrationSettings; @@ -113,9 +117,13 @@ public class VibrationThreadTest { public void setUp() throws Exception { mTestLooper = new TestLooper(); + when(mVibrationConfigMock.getDefaultVibrationIntensity(anyInt())) + .thenReturn(Vibrator.VIBRATION_INTENSITY_MEDIUM); + when(mVibrationConfigMock.getRampStepDurationMs()).thenReturn(TEST_RAMP_STEP_DURATION); + Context context = InstrumentationRegistry.getContext(); mVibrationSettings = new VibrationSettings(context, new Handler(mTestLooper.getLooper()), - /* rampDownDuration= */ 0, TEST_RAMP_STEP_DURATION); + mVibrationConfigMock); mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings); mWakeLock = context.getSystemService( PowerManager.class).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*"); @@ -553,8 +561,8 @@ public class VibrationThreadTest { VibrationEffect effect = VibrationEffect.startWaveform() .addStep(1, 10) .addRamp(0, 20) - .addStep(0.8f, 1, 30) - .addRamp(0.6f, -1, 40) + .addStep(0.8f, 100, 30) + .addRamp(0.6f, 200, 40) .build(); VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); waitForCompletion(thread); @@ -565,12 +573,13 @@ public class VibrationThreadTest { verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); assertFalse(thread.getVibrators().get(VIBRATOR_ID).isVibrating()); assertEquals(Arrays.asList( - expectedRamp(/* amplitude= */ 1, /* frequency= */ 150, /* duration= */ 10), - expectedRamp(/* StartAmplitude= */ 1, /* endAmplitude= */ 0, - /* startFrequency= */ 150, /* endFrequency= */ 150, /* duration= */ 20), - expectedRamp(/* amplitude= */ 0.6f, /* frequency= */ 200, /* duration= */ 30), - expectedRamp(/* StartAmplitude= */ 0.6f, /* endAmplitude= */ 0.5f, - /* startFrequency= */ 200, /* endFrequency= */ 100, /* duration= */ 40)), + expectedRamp(/* amplitude= */ 1, /* frequencyHz= */ 150, /* duration= */ 10), + expectedRamp(/* startAmplitude= */ 1, /* endAmplitude= */ 0, + /* startFrequencyHz= */ 150, /* endFrequencyHz= */ 150, /* duration= */ 20), + expectedRamp(/* amplitude= */ 0.5f, /* frequencyHz= */ 100, /* duration= */ 30), + expectedRamp(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.6f, + /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 200, + /* duration= */ 40)), fakeVibrator.getEffectSegments()); assertEquals(Arrays.asList(Braking.CLAB), fakeVibrator.getBraking()); } @@ -589,8 +598,8 @@ public class VibrationThreadTest { VibrationEffect effect = VibrationEffect.startWaveform() .addStep(1, 10) .addRamp(0, 20) - .addStep(0.8f, 1, 30) - .addRamp(0.6f, -1, 40) + .addStep(0.8f, 10, 30) + .addRamp(0.6f, 100, 40) .build(); VibrationThread thread = startThreadAndDispatcher(vibrationId, effect); waitForCompletion(thread); @@ -1110,9 +1119,7 @@ public class VibrationThreadTest { @Test public void vibrate_waveformWithRampDown_addsRampDownAfterVibrationCompleted() { - int rampDownDuration = 15; - mVibrationSettings = new VibrationSettings(InstrumentationRegistry.getContext(), - new Handler(mTestLooper.getLooper()), rampDownDuration, TEST_RAMP_STEP_DURATION); + when(mVibrationConfigMock.getRampDownDurationMs()).thenReturn(15); mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings); mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); @@ -1138,9 +1145,7 @@ public class VibrationThreadTest { @Test public void vibrate_waveformWithRampDown_triggersCallbackWhenOriginalVibrationEnds() { - int rampDownDuration = 10_000; - mVibrationSettings = new VibrationSettings(InstrumentationRegistry.getContext(), - new Handler(mTestLooper.getLooper()), rampDownDuration, TEST_RAMP_STEP_DURATION); + when(mVibrationConfigMock.getRampDownDurationMs()).thenReturn(10_000); mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings); mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); @@ -1173,9 +1178,7 @@ public class VibrationThreadTest { @Test public void vibrate_waveformCancelledWithRampDown_addsRampDownAfterVibrationCancelled() throws Exception { - int rampDownDuration = 15; - mVibrationSettings = new VibrationSettings(InstrumentationRegistry.getContext(), - new Handler(mTestLooper.getLooper()), rampDownDuration, TEST_RAMP_STEP_DURATION); + when(mVibrationConfigMock.getRampDownDurationMs()).thenReturn(15); mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings); mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); @@ -1201,9 +1204,7 @@ public class VibrationThreadTest { @Test public void vibrate_predefinedWithRampDown_doesNotAddRampDown() { - int rampDownDuration = 15; - mVibrationSettings = new VibrationSettings(InstrumentationRegistry.getContext(), - new Handler(mTestLooper.getLooper()), rampDownDuration, TEST_RAMP_STEP_DURATION); + when(mVibrationConfigMock.getRampDownDurationMs()).thenReturn(15); mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings); mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); mVibratorProviders.get(VIBRATOR_ID).setSupportedEffects(VibrationEffect.EFFECT_CLICK); @@ -1223,9 +1224,7 @@ public class VibrationThreadTest { @Test public void vibrate_composedWithRampDown_doesNotAddRampDown() { - int rampDownDuration = 15; - mVibrationSettings = new VibrationSettings(InstrumentationRegistry.getContext(), - new Handler(mTestLooper.getLooper()), rampDownDuration, TEST_RAMP_STEP_DURATION); + when(mVibrationConfigMock.getRampDownDurationMs()).thenReturn(15); mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings); mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL, IVibrator.CAP_COMPOSE_EFFECTS); @@ -1250,9 +1249,7 @@ public class VibrationThreadTest { @Test public void vibrate_pwleWithRampDown_doesNotAddRampDown() { - int rampDownDuration = 15; - mVibrationSettings = new VibrationSettings(InstrumentationRegistry.getContext(), - new Handler(mTestLooper.getLooper()), rampDownDuration, TEST_RAMP_STEP_DURATION); + when(mVibrationConfigMock.getRampDownDurationMs()).thenReturn(15); mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings); FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID); fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL, @@ -1345,7 +1342,8 @@ public class VibrationThreadTest { } private VibrationEffectSegment expectedOneShot(long millis) { - return new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE, /* frequency= */ 0, (int) millis); + return new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE, + /* frequencyHz= */ 0, (int) millis); } private VibrationEffectSegment expectedPrebaked(int effectId) { @@ -1356,13 +1354,13 @@ public class VibrationThreadTest { return new PrimitiveSegment(primitiveId, scale, delay); } - private VibrationEffectSegment expectedRamp(float amplitude, float frequency, int duration) { - return expectedRamp(amplitude, amplitude, frequency, frequency, duration); + private VibrationEffectSegment expectedRamp(float amplitude, float frequencyHz, int duration) { + return expectedRamp(amplitude, amplitude, frequencyHz, frequencyHz, duration); } private VibrationEffectSegment expectedRamp(float startAmplitude, float endAmplitude, - float startFrequency, float endFrequency, int duration) { - return new RampSegment(startAmplitude, endAmplitude, startFrequency, endFrequency, + float startFrequencyHz, float endFrequencyHz, int duration) { + return new RampSegment(startAmplitude, endAmplitude, startFrequencyHz, endFrequencyHz, duration); } diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java index 9fb8b38a706e..cb4982be40c3 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java @@ -21,7 +21,6 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; @@ -236,7 +235,7 @@ public class VibratorControllerTest { RampSegment[] primitives = new RampSegment[]{ new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 1, - /* startFrequency= */ -1, /* endFrequency= */ 1, /* duration= */ 10) + /* startFrequencyHz= */ 100, /* endFrequencyHz= */ 200, /* duration= */ 10) }; assertEquals(15L, controller.on(primitives, 12)); assertTrue(controller.isVibrating()); @@ -312,10 +311,10 @@ public class VibratorControllerTest { private void mockVibratorCapabilities(int capabilities) { VibratorInfo.FrequencyMapping frequencyMapping = new VibratorInfo.FrequencyMapping( - Float.NaN, Float.NaN, Float.NaN, Float.NaN, null); - when(mNativeWrapperMock.getInfo(anyFloat(), any(VibratorInfo.Builder.class))) + Float.NaN, Float.NaN, Float.NaN, null); + when(mNativeWrapperMock.getInfo(any(VibratorInfo.Builder.class))) .then(invocation -> { - ((VibratorInfo.Builder) invocation.getArgument(1)) + ((VibratorInfo.Builder) invocation.getArgument(0)) .setCapabilities(capabilities) .setFrequencyMapping(frequencyMapping); return true; diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java index c0f75966bcf2..b0bdaf084b1a 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java @@ -19,6 +19,7 @@ package com.android.server.vibrator; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -155,12 +156,13 @@ public class VibratorManagerServiceTest { @Before public void setUp() throws Exception { mTestLooper = new TestLooper(); - mVibrator = new FakeVibrator(); mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext())); InputManager inputManager = InputManager.resetInstance(mIInputManagerMock); ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy); when(mContextSpy.getContentResolver()).thenReturn(contentResolver); + + mVibrator = new FakeVibrator(mContextSpy); when(mContextSpy.getSystemService(eq(Context.VIBRATOR_SERVICE))).thenReturn(mVibrator); when(mContextSpy.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager); when(mContextSpy.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManagerMock); @@ -175,8 +177,13 @@ public class VibratorManagerServiceTest { }).when(mPowerManagerInternalMock).registerLowPowerModeObserver(any()); setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1); + setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 1); + setUserSetting(Settings.System.ALARM_VIBRATION_INTENSITY, + Vibrator.VIBRATION_INTENSITY_MEDIUM); setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_MEDIUM); + setUserSetting(Settings.System.MEDIA_VIBRATION_INTENSITY, + Vibrator.VIBRATION_INTENSITY_MEDIUM); setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_MEDIUM); setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, @@ -437,7 +444,7 @@ public class VibratorManagerServiceTest { UID, PACKAGE_NAME, 1, effect, ALARM_ATTRS)); PrebakedSegment expected = new PrebakedSegment( - VibrationEffect.EFFECT_CLICK, false, VibrationEffect.EFFECT_STRENGTH_STRONG); + VibrationEffect.EFFECT_CLICK, false, VibrationEffect.EFFECT_STRENGTH_MEDIUM); // Only vibrators 1 and 3 have always-on capabilities. assertEquals(mVibratorProviders.get(1).getAlwaysOnEffect(1), expected); @@ -461,10 +468,10 @@ public class VibratorManagerServiceTest { UID, PACKAGE_NAME, 1, effect, ALARM_ATTRS)); PrebakedSegment expectedClick = new PrebakedSegment( - VibrationEffect.EFFECT_CLICK, false, VibrationEffect.EFFECT_STRENGTH_STRONG); + VibrationEffect.EFFECT_CLICK, false, VibrationEffect.EFFECT_STRENGTH_MEDIUM); PrebakedSegment expectedTick = new PrebakedSegment( - VibrationEffect.EFFECT_TICK, false, VibrationEffect.EFFECT_STRENGTH_STRONG); + VibrationEffect.EFFECT_TICK, false, VibrationEffect.EFFECT_STRENGTH_MEDIUM); // Enables click on vibrator 1 and tick on vibrator 2 only. assertEquals(mVibratorProviders.get(1).getAlwaysOnEffect(1), expectedClick); @@ -539,7 +546,6 @@ public class VibratorManagerServiceTest { public void vibrate_withRingtone_usesRingtoneSettings() throws Exception { mockVibrators(1); FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1); - mVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_MEDIUM); fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_HEAVY_CLICK, VibrationEffect.EFFECT_DOUBLE_CLICK); @@ -932,55 +938,67 @@ public class VibratorManagerServiceTest { @Test public void vibrate_withIntensitySettings_appliesSettingsToScaleVibrations() throws Exception { - mVibrator.setDefaultNotificationVibrationIntensity(Vibrator.VIBRATION_INTENSITY_LOW); + int defaultNotificationIntensity = + mVibrator.getDefaultVibrationIntensity(VibrationAttributes.USAGE_NOTIFICATION); setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, - Vibrator.VIBRATION_INTENSITY_HIGH); + defaultNotificationIntensity < Vibrator.VIBRATION_INTENSITY_HIGH + ? defaultNotificationIntensity + 1 + : defaultNotificationIntensity); + + int defaultTouchIntensity = + mVibrator.getDefaultVibrationIntensity(VibrationAttributes.USAGE_TOUCH); setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, - Vibrator.VIBRATION_INTENSITY_LOW); - setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, - Vibrator.VIBRATION_INTENSITY_OFF); + defaultTouchIntensity > Vibrator.VIBRATION_INTENSITY_LOW + ? defaultTouchIntensity - 1 + : defaultTouchIntensity); + + setUserSetting(Settings.System.ALARM_VIBRATION_INTENSITY, + mVibrator.getDefaultVibrationIntensity(VibrationAttributes.USAGE_ALARM)); + setUserSetting(Settings.System.MEDIA_VIBRATION_INTENSITY, + Vibrator.VIBRATION_INTENSITY_HIGH); + setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_OFF); mockVibrators(1); FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1); fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL, IVibrator.CAP_COMPOSE_EFFECTS); - fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK); VibratorManagerService service = createSystemReadyService(); vibrate(service, CombinedVibration.startSequential() - .addNext(1, VibrationEffect.createOneShot(20, 100)) + .addNext(1, VibrationEffect.createOneShot(100, 125)) .combine(), NOTIFICATION_ATTRS); assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 1, service, TEST_TIMEOUT_MILLIS)); vibrate(service, VibrationEffect.startComposition() - .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f) .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f) .compose(), HAPTIC_FEEDBACK_ATTRS); - assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 3, + assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 2, service, TEST_TIMEOUT_MILLIS)); - vibrate(service, CombinedVibration.startParallel() - .addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK)) - .combine(), ALARM_ATTRS); - assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 4, + vibrate(service, VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f) + .compose(), ALARM_ATTRS); + assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 3, service, TEST_TIMEOUT_MILLIS)); - vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), RINGTONE_ATTRS); + vibrate(service, VibrationEffect.createOneShot(100, 125), RINGTONE_ATTRS); + assertFalse(waitUntil(s -> fakeVibrator.getEffectSegments().size() > 3, + service, TEST_TIMEOUT_MILLIS)); - assertEquals(4, fakeVibrator.getEffectSegments().size()); + assertEquals(3, fakeVibrator.getEffectSegments().size()); - // Notification vibrations will be scaled with SCALE_VERY_HIGH. - assertTrue(0.6 < fakeVibrator.getAmplitudes().get(0)); + // Notification vibrations will be scaled with SCALE_HIGH or none if default is high. + assertEquals(defaultNotificationIntensity < Vibrator.VIBRATION_INTENSITY_HIGH, + 0.6 < fakeVibrator.getAmplitudes().get(0)); - // Haptic feedback vibrations will be scaled with SCALE_LOW. - assertTrue(0.5 < ((PrimitiveSegment) fakeVibrator.getEffectSegments().get(1)).getScale()); - assertTrue(0.5 > ((PrimitiveSegment) fakeVibrator.getEffectSegments().get(2)).getScale()); + // Haptic feedback vibrations will be scaled with SCALE_LOW or none if default is low. + assertEquals(defaultTouchIntensity > Vibrator.VIBRATION_INTENSITY_LOW, + 0.5 > ((PrimitiveSegment) fakeVibrator.getEffectSegments().get(1)).getScale()); - // Alarm vibration is always VIBRATION_INTENSITY_HIGH. - PrebakedSegment expected = new PrebakedSegment( - VibrationEffect.EFFECT_CLICK, false, VibrationEffect.EFFECT_STRENGTH_STRONG); - assertEquals(expected, fakeVibrator.getEffectSegments().get(3)); + // Alarm vibration will be scaled with SCALE_NONE. + assertEquals(1f, + ((PrimitiveSegment) fakeVibrator.getEffectSegments().get(2)).getScale(), 1e-5); // Ring vibrations have intensity OFF and are not played. } @@ -1100,7 +1118,7 @@ public class VibratorManagerServiceTest { int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration); mExternalVibratorService.onExternalVibrationStop(externalVibration); - assertEquals(IExternalVibratorService.SCALE_NONE, scale); + assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale); assertEquals(Arrays.asList(false, true, false), mVibratorProviders.get(1).getExternalControlStates()); } @@ -1127,8 +1145,8 @@ public class VibratorManagerServiceTest { ringtoneAudioAttrs, secondController); int secondScale = mExternalVibratorService.onExternalVibrationStart(secondVibration); - assertEquals(IExternalVibratorService.SCALE_NONE, firstScale); - assertEquals(IExternalVibratorService.SCALE_NONE, secondScale); + assertNotEquals(IExternalVibratorService.SCALE_MUTE, firstScale); + assertNotEquals(IExternalVibratorService.SCALE_MUTE, secondScale); verify(firstController).mute(); verify(secondController, never()).mute(); // Set external control called only once. @@ -1151,7 +1169,7 @@ public class VibratorManagerServiceTest { ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME, AUDIO_ATTRS, mock(IExternalVibrationController.class)); int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration); - assertEquals(IExternalVibratorService.SCALE_NONE, scale); + assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale); // Vibration is cancelled. assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS)); @@ -1163,7 +1181,6 @@ public class VibratorManagerServiceTest { public void onExternalVibration_withRingtone_usesRingerModeSettings() { mockVibrators(1); mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL); - mVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_MEDIUM); AudioAttributes audioAttrs = new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE) .build(); @@ -1181,13 +1198,13 @@ public class VibratorManagerServiceTest { setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1); createSystemReadyService(); scale = mExternalVibratorService.onExternalVibrationStart(externalVibration); - assertEquals(IExternalVibratorService.SCALE_NONE, scale); + assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale); setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1); setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0); createSystemReadyService(); scale = mExternalVibratorService.onExternalVibrationStart(externalVibration); - assertEquals(IExternalVibratorService.SCALE_NONE, scale); + assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale); } private VibrationEffectSegment expectedPrebaked(int effectId) { @@ -1235,10 +1252,6 @@ public class VibratorManagerServiceTest { mContextSpy.getContentResolver(), settingName, value, UserHandle.USER_CURRENT); } - private void setGlobalSetting(String settingName, int value) { - Settings.Global.putInt(mContextSpy.getContentResolver(), settingName, value); - } - private void vibrate(VibratorManagerService service, VibrationEffect effect, VibrationAttributes attrs) { vibrate(service, CombinedVibration.createParallel(effect), attrs); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java index 182848b4f628..bd7186e74354 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java @@ -108,7 +108,7 @@ public class NotificationHistoryDatabaseTest extends UiServiceTestCase { @Test public void testDeletionReceiver() { - verify(mContext, times(1)).registerReceiver(any(), any()); + verify(mContext, times(1)).registerReceiver(any(), any(), anyInt()); } @Test 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 5fcb8029af31..d83190353a87 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -182,7 +182,6 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; -import android.util.Slog; import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; import android.util.Xml; @@ -205,6 +204,7 @@ import com.android.server.lights.LogicalLight; import com.android.server.notification.NotificationManagerService.NotificationAssistants; import com.android.server.notification.NotificationManagerService.NotificationListeners; import com.android.server.pm.PackageManagerService; +import com.android.server.policy.PermissionPolicyInternal; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.uri.UriGrantsManagerInternal; import com.android.server.utils.quota.MultiRateLimiter; @@ -235,6 +235,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.concurrent.CountDownLatch; import java.util.function.Consumer; @@ -243,6 +244,8 @@ import java.util.function.Consumer; @RunWithLooper public class NotificationManagerServiceTest extends UiServiceTestCase { private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId"; + private static final String PKG_NO_CHANNELS = "com.example.no.channels"; + private static final int TEST_TASK_ID = 1; private static final int UID_HEADLESS = 1000000; private final int mUid = Binder.getCallingUid(); @@ -257,6 +260,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Mock private PackageManagerInternal mPackageManagerInternal; @Mock + private PermissionPolicyInternal mPermissionPolicyInternal; + @Mock private WindowManagerInternal mWindowManagerInternal; @Mock private PermissionHelper mPermissionHelper; @@ -385,6 +390,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { LocalServices.addService(ActivityManagerInternal.class, mAmi); LocalServices.removeServiceForTest(PackageManagerInternal.class); LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal); + LocalServices.removeServiceForTest(PermissionPolicyInternal.class); + LocalServices.addService(PermissionPolicyInternal.class, mPermissionPolicyInternal); mContext.addMockSystemService(Context.ALARM_SERVICE, mAlarmManager); doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any()); @@ -415,8 +422,18 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mUgmInternal.newUriPermissionOwner(anyString())).thenReturn(mPermOwner); when(mPackageManager.getPackagesForUid(mUid)).thenReturn(new String[]{PKG}); when(mPackageManagerClient.getPackagesForUid(anyInt())).thenReturn(new String[]{PKG}); + when(mPermissionPolicyInternal.canShowPermissionPromptForTask( + any(ActivityManager.RecentTaskInfo.class))).thenReturn(false); mContext.addMockSystemService(AppOpsManager.class, mock(AppOpsManager.class)); + ActivityManager.AppTask task = mock(ActivityManager.AppTask.class); + List<ActivityManager.AppTask> taskList = new ArrayList<>(); + ActivityManager.RecentTaskInfo taskInfo = new ActivityManager.RecentTaskInfo(); + taskInfo.taskId = TEST_TASK_ID; + when(task.getTaskInfo()).thenReturn(taskInfo); + taskList.add(task); + when(mAtm.getAppTasks(anyString(), anyInt())).thenReturn(taskList); + // write to a test file; the system file isn't readable from tests mFile = new File(mContext.getCacheDir(), "test.xml"); mFile.createNewFile(); @@ -941,6 +958,51 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testCreateNotificationChannels_FirstChannelWithFgndTaskStartsPermDialog() + throws Exception { + when(mPermissionPolicyInternal.canShowPermissionPromptForTask(any( + ActivityManager.RecentTaskInfo.class))).thenReturn(true); + final NotificationChannel channel = + new NotificationChannel("id", "name", IMPORTANCE_DEFAULT); + mBinderService.createNotificationChannels(PKG_NO_CHANNELS, + new ParceledListSlice(Arrays.asList(channel))); + verify(mWorkerHandler).post(eq(new NotificationManagerService + .ShowNotificationPermissionPromptRunnable(PKG_NO_CHANNELS, + UserHandle.getUserId(mUid), TEST_TASK_ID, mPermissionPolicyInternal))); + } + + @Test + public void testCreateNotificationChannels_SecondChannelWithFgndTaskDoesntStartPermDialog() + throws Exception { + when(mPermissionPolicyInternal.canShowPermissionPromptForTask(any( + ActivityManager.RecentTaskInfo.class))).thenReturn(true); + assertTrue(mBinderService.getNumNotificationChannelsForPackage(PKG, mUid, true) > 0); + + final NotificationChannel channel = + new NotificationChannel("id", "name", IMPORTANCE_DEFAULT); + mBinderService.createNotificationChannels(PKG, + new ParceledListSlice(Arrays.asList(channel))); + verify(mWorkerHandler, never()).post(any( + NotificationManagerService.ShowNotificationPermissionPromptRunnable.class)); + } + + @Test + public void testCreateNotificationChannels_FirstChannelWithBgndTaskDoesntStartPermDialog() + throws Exception { + reset(mPermissionPolicyInternal); + when(mPermissionPolicyInternal.canShowPermissionPromptForTask(any( + ActivityManager.RecentTaskInfo.class))).thenReturn(false); + + final NotificationChannel channel = + new NotificationChannel("id", "name", IMPORTANCE_DEFAULT); + mBinderService.createNotificationChannels(PKG, + new ParceledListSlice(Arrays.asList(channel))); + + verify(mWorkerHandler, never()).post(any( + NotificationManagerService.ShowNotificationPermissionPromptRunnable.class)); + } + + @Test public void testCreateNotificationChannels_TwoChannels() throws Exception { final NotificationChannel channel1 = new NotificationChannel("id1", "name", IMPORTANCE_DEFAULT); @@ -2892,7 +2954,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { .thenReturn(singletonList(mock(AssociationInfo.class))); NotificationChannelGroup ncg = new NotificationChannelGroup("a", "b/c"); mService.setPreferencesHelper(mPreferencesHelper); - when(mPreferencesHelper.getNotificationChannelGroup(eq(ncg.getId()), eq(PKG), anyInt())) + when(mPreferencesHelper.getNotificationChannelGroupWithChannels( + eq(PKG), anyInt(), eq(ncg.getId()), anyBoolean())) .thenReturn(ncg); reset(mListeners); mBinderService.deleteNotificationChannelGroup(PKG, ncg.getId()); @@ -2902,6 +2965,54 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testDeleteChannelGroupChecksForFgses() throws Exception { + when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid))) + .thenReturn(singletonList(mock(AssociationInfo.class))); + CountDownLatch latch = new CountDownLatch(2); + mService.createNotificationChannelGroup( + PKG, mUid, new NotificationChannelGroup("group", "group"), true, false); + new Thread(() -> { + NotificationChannel notificationChannel = new NotificationChannel("id", "id", + NotificationManager.IMPORTANCE_HIGH); + notificationChannel.setGroup("group"); + ParceledListSlice<NotificationChannel> pls = + new ParceledListSlice(ImmutableList.of(notificationChannel)); + try { + mBinderService.createNotificationChannelsForPackage(PKG, mUid, pls); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + latch.countDown(); + }).start(); + new Thread(() -> { + try { + synchronized (this) { + wait(5000); + } + mService.createNotificationChannelGroup(PKG, mUid, + new NotificationChannelGroup("new", "new group"), true, false); + NotificationChannel notificationChannel = + new NotificationChannel("id", "id", NotificationManager.IMPORTANCE_HIGH); + notificationChannel.setGroup("new"); + ParceledListSlice<NotificationChannel> pls = + new ParceledListSlice(ImmutableList.of(notificationChannel)); + try { + mBinderService.createNotificationChannelsForPackage(PKG, mUid, pls); + mBinderService.deleteNotificationChannelGroup(PKG, "group"); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } catch (Exception e) { + e.printStackTrace(); + } + latch.countDown(); + }).start(); + + latch.await(); + verify(mAmi).hasForegroundServiceNotification(anyString(), anyInt(), anyString()); + } + + @Test public void testUpdateNotificationChannelFromPrivilegedListener_success() throws Exception { mService.setPreferencesHelper(mPreferencesHelper); when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid))) @@ -7062,6 +7173,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testAreNotificationsEnabledForPackage_viaInternalService() throws Exception { + assertEquals(mInternalService.areNotificationsEnabledForPackage( + mContext.getPackageName(), mUid), + mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(), mUid)); + verify(mPermissionHelper, never()).hasPermission(anyInt()); + } + + @Test public void testAreBubblesAllowedForPackage_crossUser() throws Exception { try { mBinderService.getBubblePreferenceForPackage(mContext.getPackageName(), diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java index 0c8fe35484f7..1362628bde5e 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java @@ -536,6 +536,12 @@ public class NotificationPermissionMigrationTest extends UiServiceTestCase { } @Test + public void testAreNotificationsEnabledForPackage_viaInternalService() { + mInternalService.areNotificationsEnabledForPackage(mContext.getPackageName(), mUid); + verify(mPermissionHelper).hasPermission(mUid); + } + + @Test public void testGetPackageImportance() throws Exception { when(mPermissionHelper.hasPermission(mUid)).thenReturn(true); assertThat(mBinderService.getPackageImportance(mContext.getPackageName())) diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java index bd3ba0404c03..fa294dd61ea3 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java @@ -16,6 +16,7 @@ package com.android.server.notification; import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED; +import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED; import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET; import static android.content.pm.PackageManager.GET_PERMISSIONS; @@ -224,7 +225,30 @@ public class PermissionHelperTest extends UiServiceTestCase { verify(mPermManager).grantRuntimePermission( "pkg", Manifest.permission.POST_NOTIFICATIONS, 10); verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS, - FLAG_PERMISSION_USER_SET, FLAG_PERMISSION_USER_SET, true, 10); + FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_REVIEW_REQUIRED, + FLAG_PERMISSION_USER_SET, true, 10); + } + + @Test + public void testSetNotificationPermission_grantReviewRequired() throws Exception { + mPermissionHelper.setNotificationPermission("pkg", 10, true, false, true); + + verify(mPermManager).grantRuntimePermission( + "pkg", Manifest.permission.POST_NOTIFICATIONS, 10); + verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS, + FLAG_PERMISSION_REVIEW_REQUIRED, FLAG_PERMISSION_REVIEW_REQUIRED, true, 10); + } + + @Test + public void testSetNotificationPermission_pkgPerm_grantReviewRequired() throws Exception { + PermissionHelper.PackagePermission pkgPerm = new PermissionHelper.PackagePermission( + "pkg", 10, true, false); + mPermissionHelper.setNotificationPermission(pkgPerm); + + verify(mPermManager).grantRuntimePermission( + "pkg", Manifest.permission.POST_NOTIFICATIONS, 10); + verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS, + FLAG_PERMISSION_REVIEW_REQUIRED, FLAG_PERMISSION_REVIEW_REQUIRED, true, 10); } @Test @@ -234,7 +258,8 @@ public class PermissionHelperTest extends UiServiceTestCase { verify(mPermManager).revokeRuntimePermission( eq("pkg"), eq(Manifest.permission.POST_NOTIFICATIONS), eq(10), anyString()); verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS, - FLAG_PERMISSION_USER_SET, FLAG_PERMISSION_USER_SET, true, 10); + FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_REVIEW_REQUIRED, + FLAG_PERMISSION_USER_SET, true, 10); } @Test diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index d49cf670f471..76bd4ebfb228 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -3174,6 +3174,19 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test + public void testIsGroupBlocked_appCannotCreateAsBlocked() throws Exception { + NotificationChannelGroup group = new NotificationChannelGroup("id", "name"); + group.setBlocked(true); + mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group, true); + assertFalse(mHelper.isGroupBlocked(PKG_N_MR1, UID_N_MR1, group.getId())); + + NotificationChannelGroup group3 = group.clone(); + group3.setBlocked(false); + mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group3, true); + assertFalse(mHelper.isGroupBlocked(PKG_N_MR1, UID_N_MR1, group.getId())); + } + + @Test public void testIsGroup_appCannotResetBlock() throws Exception { NotificationChannelGroup group = new NotificationChannelGroup("id", "name"); mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group, true); @@ -4706,7 +4719,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { public void testGetConversations_noDisabledGroups() { NotificationChannelGroup group = new NotificationChannelGroup("a", "a"); group.setBlocked(true); - mHelper.createNotificationChannelGroup(PKG_O, UID_O, group, true); + mHelper.createNotificationChannelGroup(PKG_O, UID_O, group, false); NotificationChannel parent = new NotificationChannel("parent", "p", 1); mHelper.createNotificationChannel(PKG_O, UID_O, parent, true, false); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java index 525888df7c78..cb9eb52bfc00 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java @@ -30,7 +30,6 @@ import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER import static android.window.DisplayAreaOrganizer.FEATURE_FULLSCREEN_MAGNIFICATION; import static android.window.DisplayAreaOrganizer.FEATURE_IME_PLACEHOLDER; import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED; -import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED_BACKGROUND_PANEL; import static android.window.DisplayAreaOrganizer.FEATURE_ROOT; import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_LAST; @@ -197,25 +196,6 @@ public class DisplayAreaPolicyBuilderTest { } @Test - public void testBuilder_defaultPolicy_hasOneHandedBackgroundFeature() { - final DisplayAreaPolicy.Provider defaultProvider = DisplayAreaPolicy.Provider.fromResources( - resourcesWithProvider("")); - final DisplayAreaPolicyBuilder.Result defaultPolicy = - (DisplayAreaPolicyBuilder.Result) defaultProvider.instantiate(mWms, mDisplayContent, - mRoot, mImeContainer); - if (mDisplayContent.isDefaultDisplay) { - final List<Feature> features = defaultPolicy.getFeatures(); - boolean hasOneHandedBackgroundFeature = false; - for (Feature feature : features) { - hasOneHandedBackgroundFeature |= - feature.getId() == FEATURE_ONE_HANDED_BACKGROUND_PANEL; - } - - assertThat(hasOneHandedBackgroundFeature).isTrue(); - } - } - - @Test public void testBuilder_defaultPolicy_hasWindowedMagnificationFeature() { final DisplayAreaPolicy.Provider defaultProvider = DisplayAreaPolicy.Provider.fromResources( resourcesWithProvider("")); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index 2ef59f6ac5c9..2f78b588f305 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -1718,6 +1718,7 @@ public class DisplayContentTests extends WindowTestsBase { @Test public void testShellTransitRotation() { DisplayContent dc = createNewDisplay(); + dc.setLastHasContent(); final TestTransitionPlayer testPlayer = registerTestTransitionPlayer(); final DisplayRotation dr = dc.getDisplayRotation(); diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java index ed3888c7aedd..141588a87585 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java @@ -488,6 +488,7 @@ public class TransitionTests extends WindowTestsBase { final TestTransitionPlayer player = registerTestTransitionPlayer(); mDisplayContent.getDisplayRotation().setRotation(mDisplayContent.getRotation() + 1); + mDisplayContent.setLastHasContent(); mDisplayContent.requestChangeTransitionIfNeeded(1 /* any changes */, null /* displayChange */); final FadeRotationAnimationController fadeController = @@ -536,6 +537,7 @@ public class TransitionTests extends WindowTestsBase { null /* remoteTransition */, null /* displayChange */); mDisplayContent.getDisplayRotation().setRotation(mDisplayContent.getRotation() + 1); final int anyChanges = 1; + mDisplayContent.setLastHasContent(); mDisplayContent.requestChangeTransitionIfNeeded(anyChanges, null /* displayChange */); transition.setKnownConfigChanges(mDisplayContent, anyChanges); final FadeRotationAnimationController fadeController = @@ -550,9 +552,11 @@ public class TransitionTests extends WindowTestsBase { assertTrue(app.getTask().inTransition()); final SurfaceControl.Transaction startTransaction = mock(SurfaceControl.Transaction.class); + final SurfaceControl leash = statusBar.mToken.getAnimationLeash(); + doReturn(true).when(leash).isValid(); player.onTransactionReady(startTransaction); // The leash should be unrotated. - verify(startTransaction).setMatrix(eq(statusBar.mToken.getAnimationLeash()), any(), any()); + verify(startTransaction).setMatrix(eq(leash), any(), any()); // The redrawn window will be faded in when the transition finishes. And because this test // only use one non-activity window, the fade rotation controller should also be cleared. diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java index 049966c7310d..4dffe7e8345b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java @@ -428,6 +428,25 @@ public class ZOrderingTests extends WindowTestsBase { assertWindowHigher(mImeWindow, imeAppTarget); } + @Test + public void testAssignWindowLayers_ForImeOnPopupImeLayeringTarget() { + final WindowState imeAppTarget = createWindow(null, TYPE_APPLICATION, + mAppWindow.mActivityRecord, "imeAppTarget"); + mDisplayContent.setImeInputTarget(imeAppTarget); + mDisplayContent.setImeLayeringTarget(imeAppTarget); + mDisplayContent.setImeControlTarget(imeAppTarget); + + // Set a popup IME layering target and keeps the original IME control target behinds it. + final WindowState popupImeTargetWin = createWindow(imeAppTarget, + TYPE_APPLICATION_SUB_PANEL, mAppWindow.mActivityRecord, "popupImeTargetWin"); + mDisplayContent.setImeLayeringTarget(popupImeTargetWin); + mDisplayContent.updateImeParent(); + + // Ime should on top of the popup IME layering target window. + mDisplayContent.assignChildLayers(mTransaction); + assertWindowHigher(mImeWindow, popupImeTargetWin); + } + @Test public void testAssignWindowLayers_ForNegativelyZOrderedSubtype() { diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java index d94fafc6a5bf..ce9530c196ef 100644 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -701,8 +701,15 @@ public final class Call { */ public static final int PROPERTY_CROSS_SIM = 0x00004000; + /** + * Connection is a tethered external call. + * Indicates that the {@link Connection} is fixed on this device but the audio streams are + * re-routed to another device. + */ + public static final int PROPERTY_TETHERED_CALL = 0x00008000; + //****************************************************************************************** - // Next PROPERTY value: 0x00004000 + // Next PROPERTY value: 0x00010000 //****************************************************************************************** private final @CallState int mState; @@ -899,6 +906,9 @@ public final class Call { if (hasProperty(properties, PROPERTY_CROSS_SIM)) { builder.append(" PROPERTY_CROSS_SIM"); } + if (hasProperty(properties, PROPERTY_TETHERED_CALL)) { + builder.append(" PROPERTY_TETHERED_CALL"); + } builder.append("]"); return builder.toString(); } diff --git a/telecomm/java/android/telecom/CallAudioState.java b/telecomm/java/android/telecom/CallAudioState.java index fccdf76372dd..55957bd85eaa 100644 --- a/telecomm/java/android/telecom/CallAudioState.java +++ b/telecomm/java/android/telecom/CallAudioState.java @@ -27,7 +27,6 @@ import android.os.Parcelable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -42,7 +41,8 @@ import java.util.stream.Collectors; public final class CallAudioState implements Parcelable { /** @hide */ @Retention(RetentionPolicy.SOURCE) - @IntDef(value={ROUTE_EARPIECE, ROUTE_BLUETOOTH, ROUTE_WIRED_HEADSET, ROUTE_SPEAKER}, + @IntDef(value = {ROUTE_EARPIECE, ROUTE_BLUETOOTH, ROUTE_WIRED_HEADSET, ROUTE_SPEAKER, + ROUTE_EXTERNAL}, flag=true) public @interface CallAudioRoute {} @@ -58,6 +58,9 @@ public final class CallAudioState implements Parcelable { /** Direct the audio stream through the device's speakerphone. */ public static final int ROUTE_SPEAKER = 0x00000008; + /** Direct the audio stream through another device. */ + public static final int ROUTE_EXTERNAL = 0x00000010; + /** * Direct the audio stream through the device's earpiece or wired headset if one is * connected. @@ -70,7 +73,7 @@ public final class CallAudioState implements Parcelable { * @hide **/ public static final int ROUTE_ALL = ROUTE_EARPIECE | ROUTE_BLUETOOTH | ROUTE_WIRED_HEADSET | - ROUTE_SPEAKER; + ROUTE_SPEAKER | ROUTE_EXTERNAL; private final boolean isMuted; private final int route; @@ -189,7 +192,11 @@ public final class CallAudioState implements Parcelable { */ @CallAudioRoute public int getSupportedRouteMask() { - return supportedRouteMask; + if (route == ROUTE_EXTERNAL) { + return ROUTE_EXTERNAL; + } else { + return supportedRouteMask; + } } /** @@ -233,6 +240,10 @@ public final class CallAudioState implements Parcelable { listAppend(buffer, "SPEAKER"); } + if ((route & ROUTE_EXTERNAL) == ROUTE_EXTERNAL) { + listAppend(buffer, "EXTERNAL"); + } + return buffer.toString(); } @@ -248,10 +259,10 @@ public final class CallAudioState implements Parcelable { int route = source.readInt(); int supportedRouteMask = source.readInt(); BluetoothDevice activeBluetoothDevice = source.readParcelable( - ClassLoader.getSystemClassLoader(), android.bluetooth.BluetoothDevice.class); + ClassLoader.getSystemClassLoader()); List<BluetoothDevice> supportedBluetoothDevices = new ArrayList<>(); source.readParcelableList(supportedBluetoothDevices, - ClassLoader.getSystemClassLoader(), android.bluetooth.BluetoothDevice.class); + ClassLoader.getSystemClassLoader()); return new CallAudioState(isMuted, route, supportedRouteMask, activeBluetoothDevice, supportedBluetoothDevices); } diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index 21a180459978..d63cdc004a3d 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -561,6 +561,15 @@ public abstract class Connection extends Conferenceable { */ public static final int PROPERTY_CROSS_SIM = 1 << 13; + /** + * Connection is a tethered external call. + * <p> + * Indicates that the {@link Connection} is fixed on this device but the audio streams are + * re-routed to another device. + * <p> + */ + public static final int PROPERTY_TETHERED_CALL = 1 << 14; + //********************************************************************************************** // Next PROPERTY value: 1<<14 //********************************************************************************************** @@ -3537,9 +3546,9 @@ public abstract class Connection extends Conferenceable { mIsBlocked = in.readByte() != 0; mIsInContacts = in.readByte() != 0; CallScreeningService.ParcelableCallResponse response - = in.readParcelable(CallScreeningService.class.getClassLoader(), android.telecom.CallScreeningService.ParcelableCallResponse.class); + = in.readParcelable(CallScreeningService.class.getClassLoader()); mCallResponse = response == null ? null : response.toCallResponse(); - mCallScreeningComponent = in.readParcelable(ComponentName.class.getClassLoader(), android.content.ComponentName.class); + mCallScreeningComponent = in.readParcelable(ComponentName.class.getClassLoader()); } @NonNull diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java index 1172e1392ef8..be5fae488d5e 100644 --- a/telecomm/java/android/telecom/ConnectionRequest.java +++ b/telecomm/java/android/telecom/ConnectionRequest.java @@ -272,17 +272,17 @@ public final class ConnectionRequest implements Parcelable { } private ConnectionRequest(Parcel in) { - mAccountHandle = in.readParcelable(getClass().getClassLoader(), android.telecom.PhoneAccountHandle.class); - mAddress = in.readParcelable(getClass().getClassLoader(), android.net.Uri.class); - mExtras = in.readParcelable(getClass().getClassLoader(), android.os.Bundle.class); + mAccountHandle = in.readParcelable(getClass().getClassLoader()); + mAddress = in.readParcelable(getClass().getClassLoader()); + mExtras = in.readParcelable(getClass().getClassLoader()); mVideoState = in.readInt(); mTelecomCallId = in.readString(); mShouldShowIncomingCallUi = in.readInt() == 1; - mRttPipeFromInCall = in.readParcelable(getClass().getClassLoader(), android.os.ParcelFileDescriptor.class); - mRttPipeToInCall = in.readParcelable(getClass().getClassLoader(), android.os.ParcelFileDescriptor.class); + mRttPipeFromInCall = in.readParcelable(getClass().getClassLoader()); + mRttPipeToInCall = in.readParcelable(getClass().getClassLoader()); mParticipants = new ArrayList<Uri>(); - in.readList(mParticipants, getClass().getClassLoader(), android.net.Uri.class); + in.readList(mParticipants, getClass().getClassLoader()); mIsAdhocConference = in.readInt() == 1; } diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java index 0f034ad6a45e..ed7b79f62753 100644 --- a/telecomm/java/android/telecom/DisconnectCause.java +++ b/telecomm/java/android/telecom/DisconnectCause.java @@ -287,7 +287,7 @@ public final class DisconnectCause implements Parcelable { int tone = source.readInt(); int telephonyDisconnectCause = source.readInt(); int telephonyPreciseDisconnectCause = source.readInt(); - ImsReasonInfo imsReasonInfo = source.readParcelable(null, android.telephony.ims.ImsReasonInfo.class); + ImsReasonInfo imsReasonInfo = source.readParcelable(null); return new DisconnectCause(code, label, description, reason, tone, telephonyDisconnectCause, telephonyPreciseDisconnectCause, imsReasonInfo); } diff --git a/telecomm/java/android/telecom/ParcelableCall.java b/telecomm/java/android/telecom/ParcelableCall.java index f412a1825e2a..320308c9e926 100644 --- a/telecomm/java/android/telecom/ParcelableCall.java +++ b/telecomm/java/android/telecom/ParcelableCall.java @@ -623,9 +623,9 @@ public final class ParcelableCall implements Parcelable { ClassLoader classLoader = ParcelableCall.class.getClassLoader(); String id = source.readString(); int state = source.readInt(); - DisconnectCause disconnectCause = source.readParcelable(classLoader, android.telecom.DisconnectCause.class); + DisconnectCause disconnectCause = source.readParcelable(classLoader); List<String> cannedSmsResponses = new ArrayList<>(); - source.readList(cannedSmsResponses, classLoader, java.lang.String.class); + source.readList(cannedSmsResponses, classLoader); int capabilities = source.readInt(); int properties = source.readInt(); long connectTimeMillis = source.readLong(); @@ -633,23 +633,23 @@ public final class ParcelableCall implements Parcelable { int handlePresentation = source.readInt(); String callerDisplayName = source.readString(); int callerDisplayNamePresentation = source.readInt(); - GatewayInfo gatewayInfo = source.readParcelable(classLoader, android.telecom.GatewayInfo.class); - PhoneAccountHandle accountHandle = source.readParcelable(classLoader, android.telecom.PhoneAccountHandle.class); + GatewayInfo gatewayInfo = source.readParcelable(classLoader); + PhoneAccountHandle accountHandle = source.readParcelable(classLoader); boolean isVideoCallProviderChanged = source.readByte() == 1; IVideoProvider videoCallProvider = IVideoProvider.Stub.asInterface(source.readStrongBinder()); String parentCallId = source.readString(); List<String> childCallIds = new ArrayList<>(); - source.readList(childCallIds, classLoader, java.lang.String.class); - StatusHints statusHints = source.readParcelable(classLoader, android.telecom.StatusHints.class); + source.readList(childCallIds, classLoader); + StatusHints statusHints = source.readParcelable(classLoader); int videoState = source.readInt(); List<String> conferenceableCallIds = new ArrayList<>(); - source.readList(conferenceableCallIds, classLoader, java.lang.String.class); + source.readList(conferenceableCallIds, classLoader); Bundle intentExtras = source.readBundle(classLoader); Bundle extras = source.readBundle(classLoader); int supportedAudioRoutes = source.readInt(); boolean isRttCallChanged = source.readByte() == 1; - ParcelableRttCall rttCall = source.readParcelable(classLoader, android.telecom.ParcelableRttCall.class); + ParcelableRttCall rttCall = source.readParcelable(classLoader); long creationTimeMillis = source.readLong(); int callDirection = source.readInt(); int callerNumberVerificationStatus = source.readInt(); diff --git a/telecomm/java/android/telecom/ParcelableConference.java b/telecomm/java/android/telecom/ParcelableConference.java index e57c833e930e..1f8aafbca476 100644 --- a/telecomm/java/android/telecom/ParcelableConference.java +++ b/telecomm/java/android/telecom/ParcelableConference.java @@ -292,24 +292,24 @@ public final class ParcelableConference implements Parcelable { @Override public ParcelableConference createFromParcel(Parcel source) { ClassLoader classLoader = ParcelableConference.class.getClassLoader(); - PhoneAccountHandle phoneAccount = source.readParcelable(classLoader, android.telecom.PhoneAccountHandle.class); + PhoneAccountHandle phoneAccount = source.readParcelable(classLoader); int state = source.readInt(); int capabilities = source.readInt(); List<String> connectionIds = new ArrayList<>(2); - source.readList(connectionIds, classLoader, java.lang.String.class); + source.readList(connectionIds, classLoader); long connectTimeMillis = source.readLong(); IVideoProvider videoCallProvider = IVideoProvider.Stub.asInterface(source.readStrongBinder()); int videoState = source.readInt(); - StatusHints statusHints = source.readParcelable(classLoader, android.telecom.StatusHints.class); + StatusHints statusHints = source.readParcelable(classLoader); Bundle extras = source.readBundle(classLoader); int properties = source.readInt(); long connectElapsedTimeMillis = source.readLong(); - Uri address = source.readParcelable(classLoader, android.net.Uri.class); + Uri address = source.readParcelable(classLoader); int addressPresentation = source.readInt(); String callerDisplayName = source.readString(); int callerDisplayNamePresentation = source.readInt(); - DisconnectCause disconnectCause = source.readParcelable(classLoader, android.telecom.DisconnectCause.class); + DisconnectCause disconnectCause = source.readParcelable(classLoader); boolean isRingbackRequested = source.readInt() == 1; int callDirection = source.readInt(); diff --git a/telecomm/java/android/telecom/ParcelableConnection.java b/telecomm/java/android/telecom/ParcelableConnection.java index 7b8333870eaf..2b9ce9b46ad7 100644 --- a/telecomm/java/android/telecom/ParcelableConnection.java +++ b/telecomm/java/android/telecom/ParcelableConnection.java @@ -261,10 +261,10 @@ public final class ParcelableConnection implements Parcelable { public ParcelableConnection createFromParcel(Parcel source) { ClassLoader classLoader = ParcelableConnection.class.getClassLoader(); - PhoneAccountHandle phoneAccount = source.readParcelable(classLoader, android.telecom.PhoneAccountHandle.class); + PhoneAccountHandle phoneAccount = source.readParcelable(classLoader); int state = source.readInt(); int capabilities = source.readInt(); - Uri address = source.readParcelable(classLoader, android.net.Uri.class); + Uri address = source.readParcelable(classLoader); int addressPresentation = source.readInt(); String callerDisplayName = source.readString(); int callerDisplayNamePresentation = source.readInt(); @@ -274,8 +274,8 @@ public final class ParcelableConnection implements Parcelable { boolean ringbackRequested = source.readByte() == 1; boolean audioModeIsVoip = source.readByte() == 1; long connectTimeMillis = source.readLong(); - StatusHints statusHints = source.readParcelable(classLoader, android.telecom.StatusHints.class); - DisconnectCause disconnectCause = source.readParcelable(classLoader, android.telecom.DisconnectCause.class); + StatusHints statusHints = source.readParcelable(classLoader); + DisconnectCause disconnectCause = source.readParcelable(classLoader); List<String> conferenceableConnectionIds = new ArrayList<>(); source.readStringList(conferenceableConnectionIds); Bundle extras = Bundle.setDefusable(source.readBundle(classLoader), true); diff --git a/telecomm/java/android/telecom/ParcelableRttCall.java b/telecomm/java/android/telecom/ParcelableRttCall.java index b88473a8a63b..fbcf486151f9 100644 --- a/telecomm/java/android/telecom/ParcelableRttCall.java +++ b/telecomm/java/android/telecom/ParcelableRttCall.java @@ -46,8 +46,8 @@ public class ParcelableRttCall implements Parcelable { protected ParcelableRttCall(Parcel in) { mRttMode = in.readInt(); - mTransmitStream = in.readParcelable(ParcelFileDescriptor.class.getClassLoader(), android.os.ParcelFileDescriptor.class); - mReceiveStream = in.readParcelable(ParcelFileDescriptor.class.getClassLoader(), android.os.ParcelFileDescriptor.class); + mTransmitStream = in.readParcelable(ParcelFileDescriptor.class.getClassLoader()); + mReceiveStream = in.readParcelable(ParcelFileDescriptor.class.getClassLoader()); } public static final @android.annotation.NonNull Creator<ParcelableRttCall> CREATOR = new Creator<ParcelableRttCall>() { diff --git a/telecomm/java/android/telecom/PhoneAccountSuggestion.java b/telecomm/java/android/telecom/PhoneAccountSuggestion.java index d9f89d544f40..2589d9504f6d 100644 --- a/telecomm/java/android/telecom/PhoneAccountSuggestion.java +++ b/telecomm/java/android/telecom/PhoneAccountSuggestion.java @@ -84,7 +84,7 @@ public final class PhoneAccountSuggestion implements Parcelable { } private PhoneAccountSuggestion(Parcel in) { - mHandle = in.readParcelable(PhoneAccountHandle.class.getClassLoader(), android.telecom.PhoneAccountHandle.class); + mHandle = in.readParcelable(PhoneAccountHandle.class.getClassLoader()); mReason = in.readInt(); mShouldAutoSelect = in.readByte() != 0; } diff --git a/telecomm/java/android/telecom/StatusHints.java b/telecomm/java/android/telecom/StatusHints.java index 2faecc2e3468..762c93a49022 100644 --- a/telecomm/java/android/telecom/StatusHints.java +++ b/telecomm/java/android/telecom/StatusHints.java @@ -132,8 +132,8 @@ public final class StatusHints implements Parcelable { private StatusHints(Parcel in) { mLabel = in.readCharSequence(); - mIcon = in.readParcelable(getClass().getClassLoader(), android.graphics.drawable.Icon.class); - mExtras = in.readParcelable(getClass().getClassLoader(), android.os.Bundle.class); + mIcon = in.readParcelable(getClass().getClassLoader()); + mExtras = in.readParcelable(getClass().getClassLoader()); } @Override diff --git a/telephony/java/android/telephony/AvailableNetworkInfo.java b/telephony/java/android/telephony/AvailableNetworkInfo.java index 6d673fbc7305..2b355ae216e3 100644 --- a/telephony/java/android/telephony/AvailableNetworkInfo.java +++ b/telephony/java/android/telephony/AvailableNetworkInfo.java @@ -185,9 +185,9 @@ public final class AvailableNetworkInfo implements Parcelable { mMccMncs = new ArrayList<>(); in.readStringList(mMccMncs); mBands = new ArrayList<>(); - in.readList(mBands, Integer.class.getClassLoader(), java.lang.Integer.class); + in.readList(mBands, Integer.class.getClassLoader()); mRadioAccessSpecifiers = new ArrayList<>(); - in.readList(mRadioAccessSpecifiers, RadioAccessSpecifier.class.getClassLoader(), android.telephony.RadioAccessSpecifier.class); + in.readList(mRadioAccessSpecifiers, RadioAccessSpecifier.class.getClassLoader()); } public AvailableNetworkInfo(int subId, int priority, @NonNull List<String> mccMncs, diff --git a/telephony/java/android/telephony/BarringInfo.java b/telephony/java/android/telephony/BarringInfo.java index 29152f19d17d..0aa4b5805cd6 100644 --- a/telephony/java/android/telephony/BarringInfo.java +++ b/telephony/java/android/telephony/BarringInfo.java @@ -294,8 +294,8 @@ public final class BarringInfo implements Parcelable { /** @hide */ public BarringInfo(Parcel p) { - mCellIdentity = p.readParcelable(CellIdentity.class.getClassLoader(), android.telephony.CellIdentity.class); - mBarringServiceInfos = p.readSparseArray(BarringServiceInfo.class.getClassLoader(), android.telephony.BarringInfo.BarringServiceInfo.class); + mCellIdentity = p.readParcelable(CellIdentity.class.getClassLoader()); + mBarringServiceInfos = p.readSparseArray(BarringServiceInfo.class.getClassLoader()); } @Override diff --git a/telephony/java/android/telephony/CallAttributes.java b/telephony/java/android/telephony/CallAttributes.java index b7bef39aa275..0c258f4b6435 100644 --- a/telephony/java/android/telephony/CallAttributes.java +++ b/telephony/java/android/telephony/CallAttributes.java @@ -53,9 +53,9 @@ public final class CallAttributes implements Parcelable { } private CallAttributes(Parcel in) { - this.mPreciseCallState = in.readParcelable(PreciseCallState.class.getClassLoader(), android.telephony.PreciseCallState.class); + this.mPreciseCallState = in.readParcelable(PreciseCallState.class.getClassLoader()); this.mNetworkType = in.readInt(); - this.mCallQuality = in.readParcelable(CallQuality.class.getClassLoader(), android.telephony.CallQuality.class); + this.mCallQuality = in.readParcelable(CallQuality.class.getClassLoader()); } // getters diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index d604dc1d2c92..808df50466a3 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -37,6 +37,8 @@ import android.os.PersistableBundle; import android.os.RemoteException; import android.service.carrier.CarrierService; import android.telecom.TelecomManager; +import android.telephony.AccessNetworkConstants.AccessNetworkType; +import android.telephony.data.ApnSetting; import android.telephony.data.DataCallResponse; import android.telephony.gba.TlsParams; import android.telephony.gba.UaSecurityProtocolIdentifier; @@ -4532,7 +4534,7 @@ public class CarrierConfigManager { "carrier_auto_cancel_cs_notification"; /** - * Passing this value as {@link KEY_SUBSCRIPTION_GROUP_UUID_STRING} will remove the + * Passing this value as {@link #KEY_SUBSCRIPTION_GROUP_UUID_STRING} will remove the * subscription from a group instead of adding it to a group. * * TODO: Expose in a future release. @@ -4790,6 +4792,396 @@ public class CarrierConfigManager { public static final String KEY_RCS_REQUEST_RETRY_INTERVAL_MILLIS_LONG = KEY_PREFIX + "rcs_request_retry_interval_millis_long"; + /** SIP timer T1 as per 3GPP TS 24.229 Table 7.7.1 */ + public static final String KEY_SIP_TIMER_T1_MILLIS_INT = + KEY_PREFIX + "sip_timer_t1_millis_int"; + + /** SIP timer T2 as per 3GPP TS 24.229 Table 7.7.1 */ + public static final String KEY_SIP_TIMER_T2_MILLIS_INT = + KEY_PREFIX + "sip_timer_t2_millis_int"; + + /** SIP timer T4 as per 3GPP TS 24.229 Table 7.7.1 */ + public static final String KEY_SIP_TIMER_T4_MILLIS_INT = + KEY_PREFIX + "sip_timer_t4_millis_int"; + + /** SIP timer B as per 3GPP TS 24.229 Table 7.7.1 */ + public static final String KEY_SIP_TIMER_B_MILLIS_INT = + KEY_PREFIX + "sip_timer_b_millis_int"; + + /** SIP timer C as per 3GPP TS 24.229 Table 7.7.1 */ + public static final String KEY_SIP_TIMER_C_MILLIS_INT = + KEY_PREFIX + "sip_timer_c_millis_int"; + + /** SIP timer D as per 3GPP TS 24.229 Table 7.7.1 */ + public static final String KEY_SIP_TIMER_D_MILLIS_INT = + KEY_PREFIX + "sip_timer_d_millis_int"; + + /** SIP timer F as per 3GPP TS 24.229 Table 7.7.1 */ + public static final String KEY_SIP_TIMER_F_MILLIS_INT = + KEY_PREFIX + "sip_timer_f_millis_int"; + + /** SIP timer H as per 3GPP TS 24.229 Table 7.7.1 */ + public static final String KEY_SIP_TIMER_H_MILLIS_INT = + KEY_PREFIX + "sip_timer_h_millis_int"; + + /** SIP timer J as per 3GPP TS 24.229 Table 7.7.1 */ + public static final String KEY_SIP_TIMER_J_MILLIS_INT = + KEY_PREFIX + "sip_timer_j_millis_int"; + + /** Specifies the SIP Server default port. */ + public static final String KEY_SIP_SERVER_PORT_NUMBER_INT = + KEY_PREFIX + "sip_server_port_number_int"; + + /** + * Specify the “phone-context” parameter as defined in + * section 7.2A.10 in 3GPP TS 24.229. + */ + public static final String KEY_PHONE_CONTEXT_DOMAIN_NAME_STRING = + KEY_PREFIX + "phone_context_domain_name_string"; + + /** @hide */ + @IntDef({REQUEST_URI_FORMAT_TEL, REQUEST_URI_FORMAT_SIP}) + + public @interface RequestUriFormatType {} + + /** + * Request URI is of type TEL URI. + */ + public static final int REQUEST_URI_FORMAT_TEL = 0; + + /** + * Request URI is of type SIP URI. + */ + public static final int REQUEST_URI_FORMAT_SIP = 1; + + /** + * Specify whether the request URI is SIP URI + * {@link #REQUEST_URI_FORMAT_SIP} or + * TEL URI {@link #REQUEST_URI_FORMAT_TEL}. + */ + public static final String KEY_REQUEST_URI_TYPE_INT = + KEY_PREFIX + "request_uri_type_int"; + + /** + * Flag indicating whether Globally Routable User agent (GRUU) + * in supported HEADER is included or not. + * + * <p> Reference: RFC 5627. + */ + public static final String KEY_GRUU_ENABLED_BOOL = + KEY_PREFIX + "gruu_enabled_bool"; + + /** + * Flag indicating whether to keep/release IMS PDN in case of + * moving to non VOPS area. + * + * <p>if {@code True}, keep IMS PDN in case of moving to non VOPS area. + * if {@code false}, otherwise. + */ + public static final String KEY_KEEP_PDN_UP_IN_NO_VOPS_BOOL = + KEY_PREFIX + "keep_pdn_up_in_no_vops_bool"; + + /** @hide */ + @IntDef({ + PREFERRED_TRANSPORT_UDP, + PREFERRED_TRANSPORT_TCP, + PREFERRED_TRANSPORT_DYNAMIC_UDP_TCP, + PREFERRED_TRANSPORT_TLS + }) + + public @interface PreferredTransportType {} + + /** Preferred Transport is always UDP. */ + public static final int PREFERRED_TRANSPORT_UDP = 0; + + /** Preferred Transport is always TCP. */ + public static final int PREFERRED_TRANSPORT_TCP = 1; + + /** + * Preferred Transport is both UDP and TCP and selected based + * on MTU size specified in {@link #KEY_IPV4_SIP_MTU_SIZE_CELLULAR_INT} + * and {@link #KEY_IPV6_SIP_MTU_SIZE_CELLULAR_INT}. + * + * <p>Default transport is UDP. If message size is larger + * than MTU, then TCP shall be used. + */ + public static final int PREFERRED_TRANSPORT_DYNAMIC_UDP_TCP = 2; + + /** Preferred Transport is TLS. */ + public static final int PREFERRED_TRANSPORT_TLS = 3; + + /** + * Specify the preferred transport protocol for SIP messages. + * + * <p>Possible values are, + * {@link #PREFERRED_TRANSPORT_UDP}, + * {@link #PREFERRED_TRANSPORT_TCP}, + * {@link #PREFERRED_TRANSPORT_DYNAMIC_UDP_TCP} + */ + public static final String KEY_SIP_PREFERRED_TRANSPORT_INT = + KEY_PREFIX + "sip_preferred_transport_int"; + + /** + * Specify the maximum IPV4 MTU size of SIP message on Cellular. + * + * <p>If {@link #KEY_SIP_PREFERRED_TRANSPORT_INT} is + * {@link #PREFERRED_TRANSPORT_DYNAMIC_UDP_TCP} and SIP message MTU size + * is more than this value, then SIP transport will be TCP, else the + * SIP transport is UDP. + */ + public static final String KEY_IPV4_SIP_MTU_SIZE_CELLULAR_INT = + KEY_PREFIX + "ipv4_sip_mtu_size_cellular_int"; + + /** + * Specify the maximum IPV6 MTU size of SIP message on Cellular. + * + * <p>If {@link #KEY_SIP_PREFERRED_TRANSPORT_INT} is + * {@link #PREFERRED_TRANSPORT_DYNAMIC_UDP_TCP} and SIP message MTU size + * is more than this value, then SIP transport will be TCP, else the + * SIP transport is UDP. + */ + public static final String KEY_IPV6_SIP_MTU_SIZE_CELLULAR_INT = + KEY_PREFIX + "ipv6_sip_mtu_size_cellular_int"; + + /** + * This config determines whether IMS PDN needs to be enabled + * when VOPS support is not available in both home and roaming scenarios. + * + * <p>This is applicable before IMS PDN is up, to decide whether + * IMS PDN needs to be enabled based on VOPS support in home/roaming. + * + * <p>Possible values are, + * {@link #NETWORK_TYPE_HOME}, + * {@link #NETWORK_TYPE_ROAMING} + * An empty array indicates IMS PDN depends on VOPS on both home + * and roaming scenarios. + */ + public static final String KEY_IMS_PDN_ENABLED_IN_NO_VOPS_SUPPORT_INT_ARRAY = + KEY_PREFIX + "ims_pdn_enabled_in_no_vops_support_int_array"; + + /** + * Flag indicating whether IPSec enabled for SIP messages. + * + * <p> Reference: 3GPP TS 33.203 and RFC 3329. + */ + public static final String KEY_SIP_OVER_IPSEC_ENABLED_BOOL = + KEY_PREFIX + "sip_over_ipsec_enabled_bool"; + + /** @hide */ + @IntDef({IPSEC_AUTHENTICATION_ALGORITHM_HMAC_MD5, IPSEC_AUTHENTICATION_ALGORITHM_HMAC_SHA1}) + + public @interface IpsecAuthenticationAlgorithmType {} + + /** IPSec Authentication algorithm is HMAC-MD5. see Annex H of TS 33.203 */ + public static final int IPSEC_AUTHENTICATION_ALGORITHM_HMAC_MD5 = 0; + + /** IPSec Authentication algorithm is HMAC-SHA1. see Annex H of TS 33.203 */ + public static final int IPSEC_AUTHENTICATION_ALGORITHM_HMAC_SHA1 = 1; + + /** + * List of supported IPSEC Authentication algorithms. + * + * <p>Possible values are, + * {@link #IPSEC_AUTHENTICATION_ALGORITHM_HMAC_MD5}, + * {@link #IPSEC_AUTHENTICATION_ALGORITHM_HMAC_SHA1} + */ + public static final String KEY_IPSEC_AUTHENTICATION_ALGORITHMS_INT_ARRAY = + KEY_PREFIX + "ipsec_authentication_algorithms_int_array"; + + /** @hide */ + @IntDef({ + IPSEC_ENCRYPTION_ALGORITHM_NULL, + IPSEC_ENCRYPTION_ALGORITHM_DES_EDE3_CBC, + IPSEC_ENCRYPTION_ALGORITHM_AES_CBC + }) + + public @interface IpsecEncryptionAlgorithmType {} + + /** IPSec Encryption algorithm is NULL. see Annex H of TS 33.203 */ + public static final int IPSEC_ENCRYPTION_ALGORITHM_NULL = 0; + + /** IPSec Encryption algorithm is DES_EDE3_CBC. see Annex H of TS 33.203 */ + public static final int IPSEC_ENCRYPTION_ALGORITHM_DES_EDE3_CBC = 1; + + /** IPSec Encryption algorithm is AES_CBC. see Annex H of TS 33.203 */ + public static final int IPSEC_ENCRYPTION_ALGORITHM_AES_CBC = 2; + + /** + * List of supported IPSEC encryption algorithms. + * + * <p>Possible values are, + * {@link #IPSEC_ENCRYPTION_ALGORITHM_NULL}, + * {@link #IPSEC_ENCRYPTION_ALGORITHM_DES_EDE3_CBC}, + * {@link #IPSEC_ENCRYPTION_ALGORITHM_AES_CBC} + */ + public static final String KEY_IPSEC_ENCRYPTION_ALGORITHMS_INT_ARRAY = + KEY_PREFIX + "ipsec_encryption_algorithms_int_array"; + + /** + * Expiry timer for IMS Registration in seconds. + * <p>Reference: RFC 3261 Section 20.19. + */ + public static final String KEY_REGISTRATION_EXPIRY_TIMER_SEC_INT = + KEY_PREFIX + "registration_expiry_timer_sec_int"; + + /** Registration Retry Base-time as per RFC 5626 Section 4.5. */ + public static final String KEY_REGISTRATION_RETRY_BASE_TIMER_MILLIS_INT = + KEY_PREFIX + "registration_retry_base_timer_millis_int"; + + /** Registration Retry max-time as per RFC 5626 Section 4.5. */ + public static final String KEY_REGISTRATION_RETRY_MAX_TIMER_MILLIS_INT = + KEY_PREFIX + "registration_retry_max_timer_millis_int"; + + /** + * Flag indicating whether subscription to registration event package + * is supported or not. + */ + public static final String KEY_REGISTRATION_EVENT_PACKAGE_SUPPORTED_BOOL = + KEY_PREFIX + "registration_event_package_supported_bool"; + + /** + * Expiry timer for SUBSCRIBE in seconds. + * <p>Reference: RFC 3261 Section 20.19. + */ + public static final String KEY_REGISTRATION_SUBSCRIBE_EXPIRY_TIMER_SEC_INT = + KEY_PREFIX + "registration_subscribe_expiry_timer_sec_int"; + + /** @hide */ + @IntDef({ + GEOLOCATION_PIDF_FOR_NON_EMERGENCY_ON_WIFI, + GEOLOCATION_PIDF_FOR_EMERGENCY_ON_WIFI, + GEOLOCATION_PIDF_FOR_NON_EMERGENCY_ON_CELLULAR, + GEOLOCATION_PIDF_FOR_EMERGENCY_ON_CELLULAR + }) + + public @interface GeolocationPidfAllowedType {} + + /** + * Indicates geolocation PIDF XML needs to be included for + * normal/non-emergency call scenario on WiFi + * + * <p>Geolocation for normal/non-emergency call should only include + * country code. + */ + public static final int GEOLOCATION_PIDF_FOR_NON_EMERGENCY_ON_WIFI = 1; + + /** + * Indicates geolocation PIDF XML needs to be included for emergency + * call scenario on WiFi + */ + public static final int GEOLOCATION_PIDF_FOR_EMERGENCY_ON_WIFI = 2; + + /** + * Indicates geolocation PIDF XML needs to be included for normal/non-emergency + * call scenario on Cellular + * + * <p>Geolocation for normal/non-emergency call should only include + * country code. + */ + public static final int GEOLOCATION_PIDF_FOR_NON_EMERGENCY_ON_CELLULAR = 3; + + /** + * Indicates geolocation PIDF XML needs to be included for emergency + * call scenario on Cellular + */ + public static final int GEOLOCATION_PIDF_FOR_EMERGENCY_ON_CELLULAR = 4; + + /** + * List of cases where geolocation PIDF XML needs to be included in the + * SIP REGISTER over WiFi and Cellular. + * + * <p>Possible values are, + * {@link #GEOLOCATION_PIDF_FOR_NON_EMERGENCY_ON_WIFI}, + * {@link #GEOLOCATION_PIDF_FOR_EMERGENCY_ON_WIFI}, + * {@link #GEOLOCATION_PIDF_FOR_NON_EMERGENCY_ON_CELLULAR}, + * {@link #GEOLOCATION_PIDF_FOR_EMERGENCY_ON_CELLULAR} + * + * <p>An empty array indicates geolocation PIDF XML should not be included in + * the SIP REGISTER over WiFi and Cellular. + */ + public static final String KEY_GEOLOCATION_PIDF_IN_SIP_REGISTER_SUPPORT_INT_ARRAY = + KEY_PREFIX + "geolocation_pidf_in_sip_register_support_int_array"; + + /** + * List of cases where geolocation PIDF XML needs to be included in the + * SIP INVITE over WiFi and Cellular. + * + * <p>Possible values are, + * {@link #GEOLOCATION_PIDF_FOR_NON_EMERGENCY_ON_WIFI}, + * {@link #GEOLOCATION_PIDF_FOR_EMERGENCY_ON_WIFI}, + * {@link #GEOLOCATION_PIDF_FOR_NON_EMERGENCY_ON_CELLULAR}, + * {@link #GEOLOCATION_PIDF_FOR_EMERGENCY_ON_CELLULAR} + * + * <p>An empty array indicates geolocation PIDF XML should not be included + * in the SIP INVITE over WiFi and Cellular. + */ + public static final String KEY_GEOLOCATION_PIDF_IN_SIP_INVITE_SUPPORT_INT_ARRAY = + KEY_PREFIX + "geolocation_pidf_in_sip_invite_support_int_array"; + + /** + * Specifies the IMS User Agent in template format. + * + * <p>Example: #MANUFACTURER#_#MODEL#_Android#AV#_#BUILD#". + * IMS Stack should internally substitute the tokens with the + * values from the respective android properties. + * + * <p>List of allowed tokens and the corresponding android properties are, + * <UL> + * <LI>MANUFACTURER : ro.product.manufacturer</LI> + * <LI>MODEL : ro.product.model</LI> + * <LI>AV : ro.build.version.release"</LI> + * <LI>BUILD : ro.build.id</LI> + * </UL> + * <p> Vendor IMS Stack should strip any whitespace characters present + * in the android properties values before replacing the token. + * + * <p> An empty string is invalid as per IR92 section 2.6. This key is + * considered invalid if the format is violated. If the key is invalid or + * not configured, IMS stack should use internal default values. + */ + public static final String KEY_IMS_USER_AGENT_STRING = + KEY_PREFIX + "ims_user_agent_string"; + + /** @hide */ + @IntDef({ + NETWORK_TYPE_HOME, + NETWORK_TYPE_ROAMING + }) + + public @interface NetworkType {} + + /** Indicates HOME Network. */ + public static final int NETWORK_TYPE_HOME = 0; + + /** Indicates Roaming Network. */ + public static final int NETWORK_TYPE_ROAMING = 1; + + /** @hide */ + @IntDef({ + RTCP_INACTIVITY_ON_HOLD, + RTCP_INACTIVITY_ON_CONNECTED, + RTP_INACTIVITY_ON_CONNECTED, + E911_RTCP_INACTIVITY_ON_CONNECTED, + E911_RTP_INACTIVITY_ON_CONNECTED + }) + + public @interface MediaInactivityReason {} + + /** RTCP inactivity occurred when call is on HOLD. */ + public static final int RTCP_INACTIVITY_ON_HOLD = 0; + + /** RTCP inactivity occurred when call is connected. */ + public static final int RTCP_INACTIVITY_ON_CONNECTED = 1; + + /** RTP inactivity occurred when call is connected. */ + public static final int RTP_INACTIVITY_ON_CONNECTED = 2; + + /** E911 RTCP inactivity occurred when call is connected. */ + public static final int E911_RTCP_INACTIVITY_ON_CONNECTED = 3; + + /** E911 RTP inactivity occurred when call is connected. */ + public static final int E911_RTP_INACTIVITY_ON_CONNECTED = 4; + private Ims() {} private static PersistableBundle getDefaults() { @@ -4826,6 +5218,2159 @@ public class CarrierConfigManager { "+g.gsma.rcs.botversion=\"#=1,#=2\"", "+g.gsma.rcs.cpimext"}); + defaults.putBoolean(KEY_GRUU_ENABLED_BOOL, true); + defaults.putBoolean(KEY_SIP_OVER_IPSEC_ENABLED_BOOL, true); + defaults.putBoolean(KEY_KEEP_PDN_UP_IN_NO_VOPS_BOOL, false); + defaults.putBoolean(KEY_REGISTRATION_EVENT_PACKAGE_SUPPORTED_BOOL, true); + + defaults.putInt(KEY_SIP_TIMER_T1_MILLIS_INT, 2000); + defaults.putInt(KEY_SIP_TIMER_T2_MILLIS_INT, 16000); + defaults.putInt(KEY_SIP_TIMER_T4_MILLIS_INT, 17000); + defaults.putInt(KEY_SIP_TIMER_B_MILLIS_INT, 128000); + defaults.putInt(KEY_SIP_TIMER_C_MILLIS_INT, 210000); + defaults.putInt(KEY_SIP_TIMER_D_MILLIS_INT, 130000); + defaults.putInt(KEY_SIP_TIMER_F_MILLIS_INT, 128000); + defaults.putInt(KEY_SIP_TIMER_H_MILLIS_INT, 128000); + defaults.putInt(KEY_SIP_TIMER_J_MILLIS_INT, 128000); + defaults.putInt(KEY_SIP_SERVER_PORT_NUMBER_INT, 5060); + defaults.putInt(KEY_REQUEST_URI_TYPE_INT, REQUEST_URI_FORMAT_SIP); + defaults.putInt(KEY_SIP_PREFERRED_TRANSPORT_INT, PREFERRED_TRANSPORT_DYNAMIC_UDP_TCP); + defaults.putInt(KEY_IPV4_SIP_MTU_SIZE_CELLULAR_INT, 1500); + defaults.putInt(KEY_IPV6_SIP_MTU_SIZE_CELLULAR_INT, 1500); + defaults.putInt(KEY_REGISTRATION_EXPIRY_TIMER_SEC_INT, 600000); + defaults.putInt(KEY_REGISTRATION_RETRY_BASE_TIMER_MILLIS_INT, 30000); + defaults.putInt(KEY_REGISTRATION_RETRY_MAX_TIMER_MILLIS_INT, 1800000); + defaults.putInt(KEY_REGISTRATION_SUBSCRIBE_EXPIRY_TIMER_SEC_INT, 600000); + + defaults.putIntArray( + KEY_IPSEC_AUTHENTICATION_ALGORITHMS_INT_ARRAY, + new int[] { + IPSEC_AUTHENTICATION_ALGORITHM_HMAC_MD5, + IPSEC_AUTHENTICATION_ALGORITHM_HMAC_SHA1 + }); + defaults.putIntArray( + KEY_IPSEC_ENCRYPTION_ALGORITHMS_INT_ARRAY, + new int[] { + IPSEC_ENCRYPTION_ALGORITHM_NULL, + IPSEC_ENCRYPTION_ALGORITHM_DES_EDE3_CBC, + IPSEC_ENCRYPTION_ALGORITHM_AES_CBC + }); + defaults.putIntArray( + KEY_IMS_PDN_ENABLED_IN_NO_VOPS_SUPPORT_INT_ARRAY, + new int[] { + }); + defaults.putIntArray( + KEY_GEOLOCATION_PIDF_IN_SIP_REGISTER_SUPPORT_INT_ARRAY, + new int[] { + GEOLOCATION_PIDF_FOR_EMERGENCY_ON_WIFI + }); + defaults.putIntArray( + KEY_GEOLOCATION_PIDF_IN_SIP_INVITE_SUPPORT_INT_ARRAY, + new int[] { + GEOLOCATION_PIDF_FOR_EMERGENCY_ON_WIFI + }); + + defaults.putString(KEY_PHONE_CONTEXT_DOMAIN_NAME_STRING, ""); + defaults.putString(KEY_IMS_USER_AGENT_STRING, + "#MANUFACTURER#_#MODEL#_Android#AV#_#BUILD#"); + + return defaults; + } + } + + /** + * IMS Voice configs. This groups the configs required for IMS Voice - VoNR/VoLTE + * + * <p>Reference: IR.92 + */ + public static final class ImsVoice { + private ImsVoice() {} + + /** Prefix of all imsvoice.KEY_* constants. */ + public static final String KEY_PREFIX = "imsvoice."; + + /** + * Flag specifying whether VoLTE should be available when on + * roaming network. + * + * <p>If {@code false}: hard disabled. + * If {@code true}: then depends on availability, etc. + */ + public static final String KEY_CARRIER_VOLTE_ROAMING_AVAILABLE_BOOL = + KEY_PREFIX + "carrier_volte_roaming_available_bool"; + + /** + * Flag specifying whether to send vertical caller id service codes + * (*67 and *82) in the dialed string in the SIP:INVITE. + * + * <p>If {@code true}, vertical caller id service codes *67 and *82 + * will be sent in the dialed string in the SIP:INVITE. + * If {@code false}, *67 and *82 will be removed. + */ + public static final String KEY_INCLUDE_CALLER_ID_SERVICE_CODES_IN_SIP_INVITE_BOOL = + KEY_PREFIX + "include_caller_id_service_codes_in_sip_invite_bool"; + + /** + * Flag indicating whether Multi-end point setting is enabled or not. + */ + public static final String KEY_MULTIENDPOINT_SUPPORTED_BOOL = + KEY_PREFIX + "multiendpoint_supported_bool"; + + /** + * Flag indicating whether Supported header field with the option tag + * 'timer' is enabled or not. + * + * <p>If {@code true}, session timer support is available.{@code false} otherwise. + * + * Reference: RFC 4028 Section 3 + */ + public static final String KEY_SESSION_TIMER_SUPPORTED_BOOL = + KEY_PREFIX + "session_timer_supported_bool"; + + /** + * Session-expires header field expressed in seconds as per + * RFC 4028 Section 3. + * + * <p>This establishes the upper bound for the session refresh interval. + */ + public static final String KEY_SESSION_EXPIRES_TIMER_SEC_INT = + KEY_PREFIX + "session_expires_timer_sec_int"; + + /** + * Indicates the minimum value for the session interval in seconds. + * Represented as min-SE header field as per RFC 4028 Section 3. + * + * <p>This establishes the lower bound for the session refresh interval. + */ + public static final String KEY_MINIMUM_SESSION_EXPIRES_TIMER_SEC_INT = + KEY_PREFIX + "minimum_session_expires_timer_sec_int"; + + /** @hide */ + @IntDef({ + SESSION_REFRESHER_TYPE_UNKNOWN, + SESSION_REFRESHER_TYPE_UAC, + SESSION_REFRESHER_TYPE_UAS + }) + + public @interface SessionRefresherType {} + + /** + * Session Refresher entity is unknown. This means UE does not include the + * "refresher" parameter in the Session-Expires header field of + * the SIP INVITE request. + */ + public static final int SESSION_REFRESHER_TYPE_UNKNOWN = 0; + + /** + * Session Refresher entity is User Agent Client (UAC). + * + * <p>Type of "refresher" parameter in the Session-Expires header field + * of the SIP INVITE request is UAC. + */ + public static final int SESSION_REFRESHER_TYPE_UAC = 1; + + /** + * Session Refresher entity is User Agent Server (UAS). + * + * <p>Type of "refresher" parameter in the Session-Expires header field + * of the SIP INVITE request is UAS. + */ + public static final int SESSION_REFRESHER_TYPE_UAS = 2; + + /** + * Session Refresher entity as per RFC 4028 and IR.92 Section 2.2.8. + * + * <p>This determines, + * a) whether to include the "refresher" parameter + * b) Type of refresher" parameter + * in the Session-Expires header field of the SIP INVITE request. + * + * <p>Possible values are, + * {@link #SESSION_REFRESHER_TYPE_UNKNOWN}, + * {@link #SESSION_REFRESHER_TYPE_UAC}, + * {@link #SESSION_REFRESHER_TYPE_UAS} + */ + public static final String KEY_SESSION_REFRESHER_TYPE_INT = + KEY_PREFIX + "session_refresher_type_int"; + + /** + * Flag indicating whether PRACK must be enabled for all 18x messages. + * + * <p>If {@code false}, only 18x responses with SDP are sent reliably. + * If {@code true}, SIP 18x responses (other than SIP 183 response) + * are sent reliably. + */ + public static final String KEY_PRACK_SUPPORTED_FOR_18X_BOOL = + KEY_PREFIX + "prack_supported_for_18x_bool"; + + /** @hide */ + @IntDef({ + CONFERENCE_SUBSCRIBE_TYPE_IN_DIALOG, + CONFERENCE_SUBSCRIBE_TYPE_OUT_OF_DIALOG + }) + + public @interface ConferenceSubscribeType {} + + /** + * The SIP SUBSCRIBE to conference state events is sent in the + * SIP INVITE dialog between the UE and the conference server. + * + * <p>Reference: IR.92 Section 2.3.3. + */ + public static final int CONFERENCE_SUBSCRIBE_TYPE_IN_DIALOG = 0; + + /** + * The SIP SUBSCRIBE to conference state events is sent out of + * the SIP INVITE dialog between the UE and the conference server. + * + * <p>Reference: IR.92 Section 2.3.3. + */ + public static final int CONFERENCE_SUBSCRIBE_TYPE_OUT_OF_DIALOG = 1; + + /** + * This is used to specify whether the SIP SUBSCRIBE to conference state events, + * is sent in or out of the SIP INVITE dialog between the UE and the + * conference server. + * + * <p>Reference: IR.92 Section 2.3.3. + * + * <p>Possible values are, + * {@link #CONFERENCE_SUBSCRIBE_TYPE_IN_DIALOG}, + * {@link #CONFERENCE_SUBSCRIBE_TYPE_OUT_OF_DIALOG} + * + * An empty array indicates SUBSCRIBE to conference event package + * is not required. + */ + public static final String KEY_CONFERENCE_SUBSCRIBE_TYPE_INT = + KEY_PREFIX + "conference_subscribe_type_int"; + + /** + * Flag specifying whether QoS preconditions are supported during call setup. + * + * <p>If {@code true}: QoS Preconditions are supported during call setup and + * 'precondition' tag is included in the SIP INVITE header and precondition + * parameters are sent in SDP as required. + * <p>If {@code false}: QoS Preconditions are not supported during call setup. + * + * <p>Reference: 3GPP TS 24.229 + */ + public static final String KEY_VOICE_QOS_PRECONDITION_SUPPORTED_BOOL = + KEY_PREFIX + "voice_qos_precondition_supported_bool"; + + /** + * Flag specifying whether voice is allowed on default bearer. + * + * <p>If {@code true}: voice packets can be sent on default bearer. {@code false} otherwise. + */ + public static final String KEY_VOICE_ON_DEFAULT_BEARER_SUPPORTED_BOOL = + KEY_PREFIX + "voice_on_default_bearer_supported_bool"; + + /** + * Specifies the dedicated bearer wait time during call establishment. + * + * <p>If dedicated bearer is not established within this time and if + * {@link #KEY_VOICE_ON_DEFAULT_BEARER_SUPPORTED_BOOL} is false, then call setup would fail. + * <p>If dedicated bearer is not established within this time and if + * {@link #KEY_VOICE_ON_DEFAULT_BEARER_SUPPORTED_BOOL} is true, then the media is allowed + * on default bearer. + */ + public static final String KEY_DEDICATED_BEARER_WAIT_TIMER_MILLIS_INT = + KEY_PREFIX + "dedicated_bearer_wait_timer_millis_int"; + + /** @hide */ + @IntDef({ + BASIC_SRVCC_SUPPORT, + ALERTING_SRVCC_SUPPORT, + PREALERTING_SRVCC_SUPPORT, + MIDCALL_SRVCC_SUPPORT + }) + + public @interface SrvccType {} + + /** + * Indicates support for basic SRVCC, typically 1 active call + * as detailed in IR.92 Section A.3. + */ + public static final int BASIC_SRVCC_SUPPORT = 0; + + /** + * SRVCC access transfer for calls in alerting phase as per 3GPP 24.237 + * and IR.64 Section 4.4. + * Media feature tag used: g.3gpp.srvcc-alerting. + */ + public static final int ALERTING_SRVCC_SUPPORT = 1; + + /** + * SRVCC access transfer for calls in pre-alerting phase as per 3GPP 24.237. + * Media feature tag used: g.3gpp.ps2cs-srvcc-orig-pre-alerting. + */ + public static final int PREALERTING_SRVCC_SUPPORT = 2; + + /** + * SRVCC access transfer for calls in mid-call phase as per 3GPP 24.237. + * and IR.64 Section 4.4. + * <p>This means UE supports the MSC server assisted mid-call feature. + * Media feature tag used: g.3gpp.mid-call. + */ + public static final int MIDCALL_SRVCC_SUPPORT = 3; + + /** + * List of different SRVCC types supported as defined in 3GPP 24.237. + * + * <p> Possible values are, + * {@link #BASIC_SRVCC_SUPPORT}, + * {@link #ALERTING_SRVCC_SUPPORT}, + * {@link #PREALERTING_SRVCC_SUPPORT}, + * {@link #MIDCALL_SRVCC_SUPPORT} + * + * <p> Reference: IR.64, 3GPP 24.237, 3GPP 23.216 + */ + public static final String KEY_SRVCC_TYPE_INT_ARRAY = + KEY_PREFIX + "srvcc_type_int_array"; + + /** + * Specifies the ringing timer for Mobile terminated calls. + * + * <p>Ringing timer starts when the device sends SIP 180 Ringing in + * response to a received SIP INVITE. If Ringing timer expires, + * the device sends SIP 486 response. + */ + public static final String KEY_RINGING_TIMER_MILLIS_INT = + KEY_PREFIX + "ringing_timer_millis_int"; + + /** + * Specifies the ringback timer for Mobile originated calls. + * + * <p>Ringback timer starts when the device receives SIP 180 Ringing + * in response to its SIP INVITE. If Ringback timer expires, + * the device sends SIP CANCEL. + */ + public static final String KEY_RINGBACK_TIMER_MILLIS_INT = + KEY_PREFIX + "ringback_timer_millis_int"; + + /** + * Specifies the timeout value for RTP inactivity for audio media. + * <p>On timer expiry, call will end. + * See {@link #KEY_AUDIO_INACTIVITY_CALL_END_REASONS_INT_ARRAY} for more + * details. + * <p> Value of 0 means this timer is not enabled. + */ + public static final String KEY_AUDIO_RTP_INACTIVITY_TIMER_MILLIS_INT = + KEY_PREFIX + "audio_rtp_inactivity_timer_millis_int"; + + /** + * Specifies the timeout value for RTCP inactivity for audio media. + * <p>On timer expiry, call will end. + * See {@link #KEY_AUDIO_INACTIVITY_CALL_END_REASONS_INT_ARRAY} for more + * details. + * <p> Value of 0 means this timer is not enabled. + */ + public static final String KEY_AUDIO_RTCP_INACTIVITY_TIMER_MILLIS_INT = + KEY_PREFIX + "audio_rtcp_inactivity_timer_millis_int"; + + /** + * Used to specify the conference factory URI. + * + * <p>If this is empty, then conference URI is generated from MCC/MNC as + * specified in clause 13.10 of 3GPP 23.003. + */ + public static final String KEY_CONFERENCE_FACTORY_URI_STRING = + KEY_PREFIX + "conference_factory_uri_string"; + + /** @hide */ + @IntDef({ + SESSION_REFRESH_METHOD_INVITE, + SESSION_REFRESH_METHOD_UPDATE_PREFERRED + }) + + public @interface SessionRefreshMethod {} + + /** + * SIP INVITE is used for Session Refresh + */ + public static final int SESSION_REFRESH_METHOD_INVITE = 0; + + /** + * Both SIP INVITE and UPDATE are used for session refresh. + * + * <p>SIP UPDATE will be used if UPDATE is in 'Allow' header. + * If UPDATE is not in 'Allow' header, then INVITE will be used. + */ + public static final int SESSION_REFRESH_METHOD_UPDATE_PREFERRED = 1; + + /** + * This is used to specify the method used for session refresh. + * + * <p>Possible values are, + * {@link #SESSION_REFRESH_METHOD_INVITE}, + * {@link #SESSION_REFRESH_METHOD_UPDATE_PREFERRED} + */ + public static final String KEY_SESSION_REFRESH_METHOD_INT = + KEY_PREFIX + "session_refresh_method_int"; + + /** + * Flag specifying whether the 'From' header field is used for determination of + * the originating party identity in Originating Identification Presentation(OIP) + * service. + * + * <p>If {@code true}: Indicates that the 'From' header field is used for + * determination of the originating party identity in OIP. + * {@code false} otherwise. + */ + public static final String KEY_OIP_SOURCE_FROM_HEADER_BOOL = + KEY_PREFIX + "oip_source_from_header_bool"; + + /** + * Specifies the timer value for INVITE to the first 1xx response + * (including 100 trying). If no response is received at timer expiry, + * call is redialed over CS. + * + * <p> Reference: 24.173 Table L.1 + */ + public static final String KEY_MO_CALL_REQUEST_TIMEOUT_MILLIS_INT = + KEY_PREFIX + "mo_call_request_timeout_millis_int"; + + /** + * List of various reasons of media inactivity for which + * voice/emergency call will end. + * + * <p>Possible values are, + * {@link Ims#RTCP_INACTIVITY_ON_HOLD}, + * {@link Ims#RTCP_INACTIVITY_ON_CONNECTED}, + * {@link Ims#RTP_INACTIVITY_ON_CONNECTED} + * {@link Ims#E911_RTCP_INACTIVITY_ON_CONNECTED}, + * {@link Ims#E911_RTP_INACTIVITY_ON_CONNECTED} + */ + public static final String KEY_AUDIO_INACTIVITY_CALL_END_REASONS_INT_ARRAY = + KEY_PREFIX + "audio_inactivity_call_end_reasons_int_array"; + + /** + * Specifies the AS (Application Specific) SDP modifier for audio media. + * + * <p>This value is expressed in kilobits per second. + * Reference: RFC 3556 Section 2. + */ + public static final String KEY_AUDIO_AS_BANDWIDTH_KBPS_INT = + KEY_PREFIX + "audio_as_bandwidth_kbps_int"; + + /** + * Specifies the RS SDP modifier for audio media. This indicates the RTCP + * bandwidth allocated to active data senders for audio media. + * + * <p>This value is expressed in bits per second. + * Reference: RFC 3556 Section 2. + */ + public static final String KEY_AUDIO_RS_BANDWIDTH_BPS_INT = + KEY_PREFIX + "audio_rs_bandwidth_bps_int"; + + /** + * Specifies the RR SDP modifier for audio media. This indicates the RTCP + * bandwidth allocated to receivers for audio media. + * + * <p>This value is expressed in bits per second. + * Reference: RFC 3556 Section 2. + */ + public static final String KEY_AUDIO_RR_BANDWIDTH_BPS_INT = + KEY_PREFIX + "audio_rr_bandwidth_bps_int"; + + /** + * Specifies the Audio Codec capability. This contains a list of payload types + * representing different audio codec instances. + * + * <p> The priority of the codecs is EVS, AMRWB, AMRNB, DTMF WB, DTMF NB + * from highest to lowest. In each individual codec, the priority is determined + * by the order of the payload types from highest to lowest. + * + * <p>Possible keys in this bundle are, + * <UL> + * <LI>{@link #KEY_EVS_PAYLOAD_TYPE_INT_ARRAY}</LI> + * <LI>{@link #KEY_AMRWB_PAYLOAD_TYPE_INT_ARRAY}</LI> + * <LI>{@link #KEY_AMRNB_PAYLOAD_TYPE_INT_ARRAY}</LI> + * <LI>{@link #KEY_DTMFWB_PAYLOAD_TYPE_INT_ARRAY}</LI> + * <LI>{@link #KEY_DTMFNB_PAYLOAD_TYPE_INT_ARRAY}</LI> + * </UL> + * <p>To specify payload descriptions for each of the audio payload types, see + * <UL> + * <LI>{@link #KEY_EVS_PAYLOAD_DESCRIPTION_BUNDLE}</LI> + * <LI>{@link #KEY_AMRNB_PAYLOAD_DESCRIPTION_BUNDLE}</LI> + * <LI>{@link #KEY_AMRWB_PAYLOAD_DESCRIPTION_BUNDLE}</LI> + * </UL> + */ + public static final String KEY_AUDIO_CODEC_CAPABILITY_PAYLOAD_TYPES_BUNDLE = + KEY_PREFIX + "audio_codec_capability_payload_types_bundle"; + + /** + * A list of integers representing the different payload types + * in EVS codec in priority order from highest to lowest. + * <p>Payload type is an integer in dynamic payload type range 96-127 + * as per RFC RFC 3551 Section 6. + */ + public static final String KEY_EVS_PAYLOAD_TYPE_INT_ARRAY = + KEY_PREFIX + "evs_payload_type_int_array"; + + /** + * A list of integers representing the different payload types + * in AMR-WB codec in priority order from highest to lowest. + * <p>Payload type is an integer in dynamic payload type range 96-127 + * as per RFC RFC 3551 Section 6. + */ + public static final String KEY_AMRWB_PAYLOAD_TYPE_INT_ARRAY = + KEY_PREFIX + "amrwb_payload_type_int_array"; + + /** + * A list of integers representing the different payload types + * in AMR-NB codec in priority order from highest to lowest. + * <p>Payload type is an integer in dynamic payload type range 96-127 + * as per RFC RFC 3551 Section 6. + */ + public static final String KEY_AMRNB_PAYLOAD_TYPE_INT_ARRAY = + KEY_PREFIX + "amrnb_payload_type_int_array"; + + /** + * A list of integers representing the different payload types + * in DTMF WB codec in priority order from highest to lowest. + * <p>Payload type is an integer in dynamic payload type range 96-127 + * as per RFC RFC 3551 Section 6. + */ + public static final String KEY_DTMFWB_PAYLOAD_TYPE_INT_ARRAY = + KEY_PREFIX + "dtmfwb_payload_type_int_array"; + + /** + * A list of integers representing the different payload types + * in DTMF NB codec in priority order from highest to lowest. + * <p>Payload type is an integer in dynamic payload type range 96-127 + * as per RFC RFC 3551 Section 6. + */ + public static final String KEY_DTMFNB_PAYLOAD_TYPE_INT_ARRAY = + KEY_PREFIX + "dtmfnb_payload_type_int_array"; + + /** @hide */ + @IntDef({ + BANDWIDTH_EFFICIENT, + OCTET_ALIGNED + }) + + public @interface AmrPayloadFormat {} + + /** AMR NB/WB Payload format is bandwidth-efficient. */ + public static final int BANDWIDTH_EFFICIENT = 0; + + /** AMR NB/WB Payload format is octet-aligned. */ + public static final int OCTET_ALIGNED = 1; + + /** + * Specifies the payload format of the AMR-NB/AMR-WB codec. + * + * <p>Possible values are, + * {@link #BANDWIDTH_EFFICIENT}, + * {@link #OCTET_ALIGNED} + + * <p>If value is not specified, payload format is + * {@link #BANDWIDTH_EFFICIENT}. + * + * <p>Reference: RFC 4867 Section 8.1. + */ + public static final String KEY_AMR_CODEC_ATTRIBUTE_PAYLOAD_FORMAT_INT = + KEY_PREFIX + "amr_codec_attribute_payload_format_int"; + + /** + * Restricts the active mode set to a subset of all modes in the codec. + * + * <p>This attribute is optional. If value is set, then session mode + * set is restricted to the modes specified in this list. If this value + * is not specified, then all available modes in the codec are allowed. + * This attribute is applicable for AMR-WB, AMR-NB, + * and EVS codec (operating in AMR-WB IO Mode). + * + * <p>Possible values are subset of, + * [0,1,2,3,4,5,6,7,8] - AMRWB with the modes representing nine speech codec modes + * with bit rates of 6.6, 8.85, 12.65, 14.25, 15.85, 18.25, 19.85, 23.05, 23.85 kbps. + * [0,1,2,3,4,5,6,7] - AMRNB with the modes representing eight speech codec modes + * with bit rates of 4.75, 5.15, 5.90, 6.70, 7.40, 7.95, 10.2, 12.2 kbps. + * + * <p>If value is not specified, then it means device supports all + * modes in the codec but not included in SDP. + * + * <p>Reference: RFC 4867 Section 8.1, 3GPP 26.445 A.3.1 + */ + public static final String KEY_AMR_CODEC_ATTRIBUTE_MODESET_INT_ARRAY = + KEY_PREFIX + "amr_codec_attribute_modeset_int_array"; + + /** + * Specifies the codec attributes of different payload types in + * the AMR NarrowBand (AMR-NB) codec. + * + * <p> The keys in this bundle are payload types specified + * in {@link #KEY_AMRNB_PAYLOAD_TYPE_INT_ARRAY}. + * + * <p>Codec attributes allowed as part of AMR-NB codec bundle are, + * <UL> + * <LI>{@link #KEY_AMR_CODEC_ATTRIBUTE_PAYLOAD_FORMAT_INT}</LI> + * <LI>{@link #KEY_AMR_CODEC_ATTRIBUTE_MODESET_INT_ARRAY}</LI> + * </UL> + * + * <p> If this bundle is not configured and AMRNB payload type is added + * in {@link #KEY_AMRNB_PAYLOAD_TYPE_INT_ARRAY}, then default + * values as in the individual codec attribute to be used + * for that payload type. + */ + public static final String KEY_AMRNB_PAYLOAD_DESCRIPTION_BUNDLE = + KEY_PREFIX + "amrnb_payload_description_bundle"; + + /** + * Specifies the codec attributes of different payload types in + * the AMR WideBand (AMR-WB) codec. + * + * <p> The keys in this bundle are payload types specified + * in {@link #KEY_AMRWB_PAYLOAD_TYPE_INT_ARRAY}. + * + * <p>Codec attributes allowed as part of AMR-NB codec bundle are, + * <UL> + * <LI>{@link #KEY_AMR_CODEC_ATTRIBUTE_PAYLOAD_FORMAT_INT}</LI> + * <LI>{@link #KEY_AMR_CODEC_ATTRIBUTE_MODESET_INT_ARRAY}</LI> + * </UL> + * + * <p> If this bundle is not configured and AMRWB payload type is added + * in {@link #KEY_AMRWB_PAYLOAD_TYPE_INT_ARRAY}, then default + * values as in the individual codec attribute to be used + * for that payload type. + */ + public static final String KEY_AMRWB_PAYLOAD_DESCRIPTION_BUNDLE = + KEY_PREFIX + "amrwb_payload_description_bundle"; + + /** @hide */ + @IntDef({ + EVS_OPERATIONAL_MODE_PRIMARY, + EVS_OPERATIONAL_MODE_AMRWB_IO + }) + + public @interface EvsOperationalMode {} + + /** Indicates the EVS primary mode. 3GPP 26.445 Section 3.1 */ + public static final int EVS_OPERATIONAL_MODE_PRIMARY = 0; + + /** Indicates the EVS AMR-WB IO mode. 3GPP 26.445 Section 3.1 */ + public static final int EVS_OPERATIONAL_MODE_AMRWB_IO = 1; + + /** + * Specifies if the EVS mode used is EVS primary mode + * or EVS AMR-WB IO mode. + * + * <p>Possible values are, + * {@link #EVS_OPERATIONAL_MODE_PRIMARY}, + * {@link #EVS_OPERATIONAL_MODE_AMRWB_IO} + * + * <p>If this is not present, then {@link #EVS_OPERATIONAL_MODE_PRIMARY} is used. + * <p>Reference: 3GPP 26.445 Section 3.1. + */ + public static final String KEY_EVS_CODEC_ATTRIBUTE_MODE_SWITCH_INT = + KEY_PREFIX + "evs_codec_attribute_mode_switch_int"; + + /** @hide */ + @IntDef({ + EVS_ENCODED_BW_TYPE_NB, + EVS_ENCODED_BW_TYPE_WB, + EVS_ENCODED_BW_TYPE_SWB, + EVS_ENCODED_BW_TYPE_FB, + EVS_ENCODED_BW_TYPE_NB_WB, + EVS_ENCODED_BW_TYPE_NB_WB_SWB, + EVS_ENCODED_BW_TYPE_NB_WB_SWB_FB, + EVS_ENCODED_BW_TYPE_WB_SWB, + EVS_ENCODED_BW_TYPE_WB_SWB_FB + }) + + public @interface EvsEncodedBwType {} + + /** + * EVS encoded Bandwidth is Narrow Band (NB). + * Reference: 3GPP 26.441 Table 1. + */ + public static final int EVS_ENCODED_BW_TYPE_NB = 0; + + /** + * EVS encoded Bandwidth is Wide Band (WB). + * Reference: 3GPP 26.441 Table 1. + */ + public static final int EVS_ENCODED_BW_TYPE_WB = 1; + + /** + * EVS encoded Bandwidth is Super WideBand (SWB). + * Reference: 3GPP 26.441 Table 1. + */ + public static final int EVS_ENCODED_BW_TYPE_SWB = 2; + + /** + * EVS encoded Bandwidth is Full Band (FB). + * Reference: 3GPP 26.441 Table 1. + */ + public static final int EVS_ENCODED_BW_TYPE_FB = 3; + + /** + * EVS encoded Bandwidth is in the range NB,WB. + * Reference: 3GPP 26.441 Table 1. + */ + public static final int EVS_ENCODED_BW_TYPE_NB_WB = 4; + + /** + * EVS encoded Bandwidth is in the range NB,WB,SWB. + * Reference: 3GPP 26.441 Table 1. + */ + public static final int EVS_ENCODED_BW_TYPE_NB_WB_SWB = 5; + + /** + * EVS encoded Bandwidth is in the range NB,WB,SWB,FB. + * Reference: 3GPP 26.441 Table 1. + */ + public static final int EVS_ENCODED_BW_TYPE_NB_WB_SWB_FB = 6; + + /** + * EVS encoded Bandwidth is in the range WB,SWB. + * Reference: 3GPP 26.441 Table 1. + */ + public static final int EVS_ENCODED_BW_TYPE_WB_SWB = 7; + + /** + * EVS encoded Bandwidth is in the range WB,SWB,FB. + * Reference: 3GPP 26.441 Table 1. + */ + public static final int EVS_ENCODED_BW_TYPE_WB_SWB_FB = 8; + + /** + * Specifies the EVS codec encoding bandwidth options. + * + * Possible values are, + * {@link #EVS_ENCODED_BW_TYPE_NB}, + * {@link #EVS_ENCODED_BW_TYPE_WB}, + * {@link #EVS_ENCODED_BW_TYPE_SWB}, + * {@link #EVS_ENCODED_BW_TYPE_FB}, + * {@link #EVS_ENCODED_BW_TYPE_NB_WB}, + * {@link #EVS_ENCODED_BW_TYPE_NB_WB_SWB}, + * {@link #EVS_ENCODED_BW_TYPE_NB_WB_SWB_FB}, + * {@link #EVS_ENCODED_BW_TYPE_WB_SWB}, + * {@link #EVS_ENCODED_BW_TYPE_WB_SWB_FB} + * + * If this key is not specified, then the behavior is same as + * value {@link #EVS_ENCODED_BW_TYPE_NB_WB_SWB} + * + * <p>Reference: 3GPP 26.441 Table 1. + */ + public static final String KEY_EVS_CODEC_ATTRIBUTE_BANDWIDTH_INT = + KEY_PREFIX + "evs_codec_attribute_bandwidth_int"; + + /** @hide */ + @IntDef({ + EVS_PRIMARY_MODE_BITRATE_5_9_KBPS, + EVS_PRIMARY_MODE_BITRATE_7_2_KBPS, + EVS_PRIMARY_MODE_BITRATE_8_0_KBPS, + EVS_PRIMARY_MODE_BITRATE_9_6_KBPS, + EVS_PRIMARY_MODE_BITRATE_13_2_KBPS, + EVS_PRIMARY_MODE_BITRATE_16_4_KBPS, + EVS_PRIMARY_MODE_BITRATE_24_4_KBPS, + EVS_PRIMARY_MODE_BITRATE_32_0_KBPS, + EVS_PRIMARY_MODE_BITRATE_48_0_KBPS, + EVS_PRIMARY_MODE_BITRATE_64_0_KBPS, + EVS_PRIMARY_MODE_BITRATE_96_0_KBPS, + EVS_PRIMARY_MODE_BITRATE_128_0_KBPS + }) + + public @interface EvsPrimaryModeBitRate {} + + /** EVS primary mode with bitrate 5.9 kbps */ + public static final int EVS_PRIMARY_MODE_BITRATE_5_9_KBPS = 0; + + /** EVS primary mode with bitrate 7.2 kbps */ + public static final int EVS_PRIMARY_MODE_BITRATE_7_2_KBPS = 1; + + /** EVS primary mode with bitrate 8.0 kbps */ + public static final int EVS_PRIMARY_MODE_BITRATE_8_0_KBPS = 2; + + /** EVS primary mode with bitrate 9.6 kbps */ + public static final int EVS_PRIMARY_MODE_BITRATE_9_6_KBPS = 3; + + /** EVS primary mode with bitrate 13.2 kbps */ + public static final int EVS_PRIMARY_MODE_BITRATE_13_2_KBPS = 4; + + /** EVS primary mode with bitrate 16.4 kbps */ + public static final int EVS_PRIMARY_MODE_BITRATE_16_4_KBPS = 5; + + /** EVS primary mode with bitrate 24.4 kbps */ + public static final int EVS_PRIMARY_MODE_BITRATE_24_4_KBPS = 6; + + /** EVS primary mode with bitrate 32.0 kbps */ + public static final int EVS_PRIMARY_MODE_BITRATE_32_0_KBPS = 7; + + /** EVS primary mode with bitrate 48.0 kbps */ + public static final int EVS_PRIMARY_MODE_BITRATE_48_0_KBPS = 8; + + /** EVS primary mode with bitrate 64.0 kbps */ + public static final int EVS_PRIMARY_MODE_BITRATE_64_0_KBPS = 9; + + /** EVS primary mode with bitrate 96.0 kbps */ + public static final int EVS_PRIMARY_MODE_BITRATE_96_0_KBPS = 10; + + /** EVS primary mode with bitrate 128.0 kbps */ + public static final int EVS_PRIMARY_MODE_BITRATE_128_0_KBPS = 11; + + /** + * Specifies the range of source codec bit-rate for EVS Primary mode + * in the session. This is expressed in kilobits per second and + * applicable for both the send and the receive directions. + * + * <p>The range is specified as integer aray of size 2, + * represented as [low, high], where low <= high + * + * <p>Possible values for low and high are, + * {@link #EVS_PRIMARY_MODE_BITRATE_5_9_KBPS}, + * {@link #EVS_PRIMARY_MODE_BITRATE_7_2_KBPS}, + * {@link #EVS_PRIMARY_MODE_BITRATE_8_0_KBPS}, + * {@link #EVS_PRIMARY_MODE_BITRATE_9_6_KBPS}, + * {@link #EVS_PRIMARY_MODE_BITRATE_13_2_KBPS}, + * {@link #EVS_PRIMARY_MODE_BITRATE_16_4_KBPS}, + * {@link #EVS_PRIMARY_MODE_BITRATE_24_4_KBPS}, + * {@link #EVS_PRIMARY_MODE_BITRATE_32_0_KBPS}, + * {@link #EVS_PRIMARY_MODE_BITRATE_48_0_KBPS}, + * {@link #EVS_PRIMARY_MODE_BITRATE_64_0_KBPS}, + * {@link #EVS_PRIMARY_MODE_BITRATE_96_0_KBPS}, + * {@link #EVS_PRIMARY_MODE_BITRATE_128_0_KBPS} + * + * If this key is not specified, then the behavior is same as + * value {@link #EVS_PRIMARY_MODE_BITRATE_24_4_KBPS} + * + * <p>Reference: 3GPP 26.445 Section A.3.1 + */ + public static final String KEY_EVS_CODEC_ATTRIBUTE_BITRATE_INT_ARRAY = + KEY_PREFIX + "evs_codec_attribute_bitrate_int_array"; + + /** + * Specifies the Channel aware mode (ch-aw-recv) for the receive direction. + * This is applicable for EVS codec. + * + * <p>Permissible values are -1, 0, 2, 3, 5, and 7. + * If this key is not specified, then the behavior is same as value 0 + * (channel aware mode disabled). + * <p> If this key is configured, then device is expected to send + * this parameter in the SDP offer. + * + * <p>Reference: 3GPP TS 26.445 section 4.4.5, 3GPP 26.445 Section A.3.1 + */ + public static final String KEY_EVS_CODEC_ATTRIBUTE_CH_AW_RECV_INT = + KEY_PREFIX + "evs_codec_attribute_ch_aw_recv_int"; + + /** + * Specifies whether to limit the session to header-full format. + * This applies to both directions in the session. This attribute + * is applicable for EVS codec. + * + * <p>Permissible values are 0, 1 + * If hf-only is 1, only Header-Full format is used and hf-only is + * included in the SDP. + * <p>If hf-only is 0, both Compact and Header-Full formats are used + * and hf-only is included in the SDP. + * <p>If this key is not present, then both Compact + * and Header-Full formats are used and hf-only is not included in + * the SDP. + * <p> If this key is configured, then device is expected to send + * this parameter in the SDP offer if operator required it. + * + * <p>Reference: 3GPP 26.445 Section A.3.1. + */ + public static final String KEY_EVS_CODEC_ATTRIBUTE_HF_ONLY_INT = + KEY_PREFIX + "evs_codec_attribute_hf_only_int"; + + /** + * Specifies whether DTX (Discontinuous transmission) is enabled + * or not. This applies to both directions in the session. + * This attribute is applicable for EVS codec and can be used + * in both EVS Primary mode and EVS AMR-WB IO mode. + * + * <p>If {@code true}: Indicates DTX is enabled. + * If {@code false}: Indicates DTX is disabled. + * + * <p>If this is not present, then default value of {@code true} + * will apply. + * <p>Reference: 3GPP TS 26.445 Section A.3.1. + */ + public static final String KEY_EVS_CODEC_ATTRIBUTE_DTX_BOOL = + KEY_PREFIX + "evs_codec_attribute_dtx_bool"; + + /** + * This is used if further restriction is required on DTX in the + * receive direction. This attribute is applicable for EVS codec + * and can be used in both EVS Primary mode and EVS AMR-WB IO mode. + * + * <p> If this value is true or not present, then DTX setting is + * dependent on {@link #KEY_EVS_CODEC_ATTRIBUTE_DTX_BOOL}. + * + * <p> If this is not present, then default value of {@code true} + * will apply. + * + * <p>Reference: 3GPP TS 26.445 Section A.3.1. + */ + public static final String KEY_EVS_CODEC_ATTRIBUTE_DTX_RECV_BOOL = + KEY_PREFIX + "evs_codec_attribute_dtx_recv_bool"; + + /** + * Specifies the number of audio channels. + * If this is not present, then default value of 1 will apply. + * + * <p>Reference: RFC 3551 + */ + public static final String KEY_EVS_CODEC_ATTRIBUTE_CHANNELS_INT = + KEY_PREFIX + "evs_codec_attribute_channels_int"; + + /** + * Indicates whether the Codec Mode Request (CMR) is supported + * for the session. + * This attribute is applicable for EVS codec in Primary Mode only. + * + * <p>Possible values are -1, 0, 1. If this key is not present, + * then behavior as per value 0 is applicable. + * + * <p>Reference: 3GPP 26.445 Section A.3.1, 3GPP 26.114 Table 6.2a + */ + public static final String KEY_EVS_CODEC_ATTRIBUTE_CMR_INT = + KEY_PREFIX + "codec_attribute_cmr_int"; + + /** + * Specifies the number of frame-blocks. This indicates the frame-block period + * at which codec mode changes are allowed for the sender. This attribute is + * applicable for EVS codec in AMR-WB IO mode and AMR-WB. + * + * <p>Possible values are 1, 2. + * If the key is not present, behavior as per value 1 is applicable and this + * parameter is not included in SDP. + * + * <p>Reference: RFC 4867 Section 8.1. + */ + public static final String KEY_CODEC_ATTRIBUTE_MODE_CHANGE_PERIOD_INT = + KEY_PREFIX + "codec_attribute_mode_change_period_int"; + + /** + * Specifies if the client is capable to transmit with a restricted mode + * change period. This attribute is applicable for EVS codec in + * AMR-WB IO mode and AMR-WB. + * + * <p>Possible values are 1, 2. If this key is not present, + * then behavior as per value 1 is applicable and this + * parameter is not included in SDP. + * + * <p>Reference: RFC 4867 Section 8.1. + */ + public static final String KEY_CODEC_ATTRIBUTE_MODE_CHANGE_CAPABILITY_INT = + KEY_PREFIX + "codec_attribute_mode_change_capability_int"; + + /** + * Specifies the allowed mode changes for the sender in the active mode set. + * This attribute is applicable for EVS codec in AMR-WB IO mode + * and AMR-WB. + * + * <p>Possible values are 0, 1. If value is 1, then the sender should only + * perform mode changes to the neighboring modes in the active codec mode set. + * If value is 0, then mode changes between any two modes + * in the active codec mode set is allowed. + * If the key is not present, behavior as per value 0 is applicable and this + * parameter is not included in SDP. + * + * <p>Reference: RFC 4867 Section 8.1. + */ + public static final String KEY_CODEC_ATTRIBUTE_MODE_CHANGE_NEIGHBOR_INT = + KEY_PREFIX + "codec_attribute_mode_change_neighbor_int"; + + /** + * Specifies the codec attributes of different payload types in + * the EVS codec. + * + * <p> The keys in this bundle are payload types specified + * in {@link #KEY_EVS_PAYLOAD_TYPE_INT_ARRAY}. + * + * <p>Codec attributes allowed as part of EVS codec are, + * <UL> + * <LI>{@link #KEY_EVS_CODEC_ATTRIBUTE_BANDWIDTH_INT}</LI> + * <LI>{@link #KEY_EVS_CODEC_ATTRIBUTE_BITRATE_INT_ARRAY}</LI> + * <LI>{@link #KEY_EVS_CODEC_ATTRIBUTE_CH_AW_RECV_INT}</LI> + * <LI>{@link #KEY_EVS_CODEC_ATTRIBUTE_HF_ONLY_INT}</LI> + * <LI>{@link #KEY_EVS_CODEC_ATTRIBUTE_DTX_BOOL}</LI> + * <LI>{@link #KEY_EVS_CODEC_ATTRIBUTE_DTX_RECV_BOOL}</LI> + * <LI>{@link #KEY_EVS_CODEC_ATTRIBUTE_MODE_SWITCH_INT}</LI> + * <LI>{@link #KEY_EVS_CODEC_ATTRIBUTE_CMR_INT}</LI> + * <LI>{@link #KEY_EVS_CODEC_ATTRIBUTE_CHANNELS_INT}</LI> + * <LI>{@link #KEY_CODEC_ATTRIBUTE_MODE_CHANGE_PERIOD_INT}</LI> + * <LI>{@link #KEY_CODEC_ATTRIBUTE_MODE_CHANGE_CAPABILITY_INT}</LI> + * <LI>{@link #KEY_CODEC_ATTRIBUTE_MODE_CHANGE_NEIGHBOR_INT}</LI> + * </UL> + */ + public static final String KEY_EVS_PAYLOAD_DESCRIPTION_BUNDLE = + KEY_PREFIX + "evs_payload_description_bundle"; + + private static PersistableBundle getDefaults() { + PersistableBundle defaults = new PersistableBundle(); + defaults.putBoolean(KEY_CARRIER_VOLTE_ROAMING_AVAILABLE_BOOL, true); + defaults.putBoolean(KEY_INCLUDE_CALLER_ID_SERVICE_CODES_IN_SIP_INVITE_BOOL, false); + defaults.putBoolean(KEY_MULTIENDPOINT_SUPPORTED_BOOL, false); + defaults.putBoolean(KEY_SESSION_TIMER_SUPPORTED_BOOL, true); + defaults.putBoolean(KEY_OIP_SOURCE_FROM_HEADER_BOOL, false); + defaults.putBoolean(KEY_PRACK_SUPPORTED_FOR_18X_BOOL, true); + defaults.putBoolean(KEY_VOICE_QOS_PRECONDITION_SUPPORTED_BOOL, true); + defaults.putBoolean(KEY_VOICE_ON_DEFAULT_BEARER_SUPPORTED_BOOL, false); + + defaults.putInt(KEY_SESSION_REFRESHER_TYPE_INT, SESSION_REFRESHER_TYPE_UNKNOWN); + defaults.putInt(KEY_SESSION_REFRESH_METHOD_INT, + SESSION_REFRESH_METHOD_UPDATE_PREFERRED); + defaults.putInt(KEY_CONFERENCE_SUBSCRIBE_TYPE_INT, + CONFERENCE_SUBSCRIBE_TYPE_OUT_OF_DIALOG); + defaults.putInt(KEY_AUDIO_RTP_INACTIVITY_TIMER_MILLIS_INT, 20000); + defaults.putInt(KEY_AUDIO_RTCP_INACTIVITY_TIMER_MILLIS_INT, 20000); + defaults.putInt(KEY_DEDICATED_BEARER_WAIT_TIMER_MILLIS_INT, 8000); + defaults.putInt(KEY_RINGING_TIMER_MILLIS_INT, 90000); + defaults.putInt(KEY_RINGBACK_TIMER_MILLIS_INT, 90000); + defaults.putInt(KEY_MO_CALL_REQUEST_TIMEOUT_MILLIS_INT, 5000); + defaults.putInt(KEY_SESSION_EXPIRES_TIMER_SEC_INT, 1800); + defaults.putInt(KEY_MINIMUM_SESSION_EXPIRES_TIMER_SEC_INT, 90); + defaults.putInt(KEY_AUDIO_AS_BANDWIDTH_KBPS_INT, 41); + defaults.putInt(KEY_AUDIO_RS_BANDWIDTH_BPS_INT, 600); + defaults.putInt(KEY_AUDIO_RR_BANDWIDTH_BPS_INT, 2000); + + defaults.putIntArray( + KEY_AUDIO_INACTIVITY_CALL_END_REASONS_INT_ARRAY, + new int[] { + Ims.RTCP_INACTIVITY_ON_CONNECTED, + Ims.RTP_INACTIVITY_ON_CONNECTED + }); + + defaults.putIntArray( + KEY_SRVCC_TYPE_INT_ARRAY, + new int[] { + BASIC_SRVCC_SUPPORT, + ALERTING_SRVCC_SUPPORT, + PREALERTING_SRVCC_SUPPORT, + MIDCALL_SRVCC_SUPPORT + }); + + defaults.putString(KEY_CONFERENCE_FACTORY_URI_STRING, ""); + + PersistableBundle audio_codec_capability_payload_types = new PersistableBundle(); + + audio_codec_capability_payload_types.putIntArray( + KEY_AMRWB_PAYLOAD_TYPE_INT_ARRAY, + new int[] { 97, 98 }); + + audio_codec_capability_payload_types.putIntArray( + KEY_AMRNB_PAYLOAD_TYPE_INT_ARRAY, + new int[] { 99, 100 }); + + audio_codec_capability_payload_types.putIntArray( + KEY_DTMFWB_PAYLOAD_TYPE_INT_ARRAY, + new int[] { 101 }); + + audio_codec_capability_payload_types.putIntArray( + KEY_DTMFNB_PAYLOAD_TYPE_INT_ARRAY, + new int[] { 102 }); + + defaults.putPersistableBundle( + KEY_AUDIO_CODEC_CAPABILITY_PAYLOAD_TYPES_BUNDLE, + audio_codec_capability_payload_types); + + /* Setting defaults for AMRWB */ + PersistableBundle all_amrwb_payload_bundles = new PersistableBundle(); + PersistableBundle amrwb_bundle_instance1 = new PersistableBundle(); + + all_amrwb_payload_bundles.putPersistableBundle( + "97", /* Same value of payload type as in KEY_AMRWB_PAYLOAD_TYPE_INT_ARRAY */ + amrwb_bundle_instance1); + + PersistableBundle amrwb_bundle_instance2 = new PersistableBundle(); + + amrwb_bundle_instance2.putInt(KEY_AMR_CODEC_ATTRIBUTE_PAYLOAD_FORMAT_INT, + OCTET_ALIGNED); + + all_amrwb_payload_bundles.putPersistableBundle( + "98", /* Same value of payload type as in KEY_AMRWB_PAYLOAD_TYPE_INT_ARRAY */ + amrwb_bundle_instance2); + + defaults.putPersistableBundle( + KEY_AMRWB_PAYLOAD_DESCRIPTION_BUNDLE, + all_amrwb_payload_bundles); + + /* Setting defaults for AMRNB */ + PersistableBundle all_amrnb_payload_bundles = new PersistableBundle(); + PersistableBundle amrnb_bundle_instance1 = new PersistableBundle(); + + all_amrnb_payload_bundles.putPersistableBundle( + "99", /* Same value of payload type as in KEY_AMRNB_PAYLOAD_TYPE_INT_ARRAY */ + amrnb_bundle_instance1); + + PersistableBundle amrnb_bundle_instance2 = new PersistableBundle(); + + amrnb_bundle_instance2.putInt(KEY_AMR_CODEC_ATTRIBUTE_PAYLOAD_FORMAT_INT, + OCTET_ALIGNED); + + all_amrnb_payload_bundles.putPersistableBundle( + "100", /* Same value of payload type as in KEY_AMRNB_PAYLOAD_TYPE_INT_ARRAY */ + amrnb_bundle_instance2); + + defaults.putPersistableBundle( + KEY_AMRNB_PAYLOAD_DESCRIPTION_BUNDLE, + all_amrnb_payload_bundles); + + return defaults; + } + } + + /** + * IMS SMS configs. This groups the configs specific for SMS over IMS + */ + public static final class ImsSms { + private ImsSms() {} + + /** Prefix of all imssms.KEY_* constants. */ + public static final String KEY_PREFIX = "imssms."; + + /** + * Flag specifying if SMS over IMS support is available or not. + * + * <p>If {@code true}: SMS over IMS support available. + * {@code false}: otherwise. + */ + public static final String KEY_SMS_OVER_IMS_SUPPORTED_BOOL = + KEY_PREFIX + "sms_over_ims_supported_bool"; + + /** + * Flag specifying whether to allow SMS CSFB in case of + * SMS over PS failure. + * + * <p>If {@code true}: allow SMS CSFB in case of SMS over PS failure. + * {@code false} otherwise. + */ + public static final String KEY_SMS_CSFB_RETRY_ON_FAILURE_BOOL = + KEY_PREFIX + "sms_csfb_retry_on_failure_bool"; + + /** @hide */ + @IntDef({ + SMS_FORMAT_3GPP, + SMS_FORMAT_3GPP2 + }) + + public @interface SmsFormat {} + + /** SMS format is 3GPP. */ + public static final int SMS_FORMAT_3GPP = 0; + + /** SMS format is 3GPP2. */ + public static final int SMS_FORMAT_3GPP2 = 1; + + /** + * Specifies the SMS over IMS format. + * + * <p>Possible values are, + * {@link #SMS_FORMAT_3GPP}, + * {@link #SMS_FORMAT_3GPP2} + */ + public static final String KEY_SMS_OVER_IMS_FORMAT_INT = + KEY_PREFIX + "sms_over_ims_format_int"; + + /** + * List of different RAT technologies on which SMS over IMS + * is supported. + * + * <p>Possible values are, + * {@link AccessNetworkConstants.AccessNetworkType#NGRAN} + * {@link AccessNetworkConstants.AccessNetworkType#EUTRAN} + * {@link AccessNetworkConstants.AccessNetworkType#IWLAN} + * {@link AccessNetworkConstants.AccessNetworkType#UTRAN} + * {@link AccessNetworkConstants.AccessNetworkType#GERAN} + */ + public static final String KEY_SMS_OVER_IMS_SUPPORTED_RATS_INT_ARRAY = + KEY_PREFIX + "sms_over_ims_supported_rats_int_array"; + + private static PersistableBundle getDefaults() { + PersistableBundle defaults = new PersistableBundle(); + defaults.putBoolean(KEY_SMS_OVER_IMS_SUPPORTED_BOOL, true); + defaults.putBoolean(KEY_SMS_CSFB_RETRY_ON_FAILURE_BOOL, true); + + defaults.putInt(KEY_SMS_OVER_IMS_FORMAT_INT, SMS_FORMAT_3GPP); + + defaults.putIntArray( + KEY_SMS_OVER_IMS_SUPPORTED_RATS_INT_ARRAY, + new int[] { + AccessNetworkType.EUTRAN, + AccessNetworkType.IWLAN + }); + + return defaults; + } + } + + /** + * IMS RTT configs. This groups the configs specific for text media, + * RTT (Real Time Text). + */ + public static final class ImsRtt { + private ImsRtt() {} + + /** Prefix of all imsrtt.KEY_* constants. */ + public static final String KEY_PREFIX = "imsrtt."; + + /** + * Flag specifying whether text media is allowed on default bearer. + * + * <p>If {@code true}: text media can be sent on default bearer. + * {@code false} otherwise. + */ + public static final String KEY_TEXT_ON_DEFAULT_BEARER_SUPPORTED_BOOL = + KEY_PREFIX + "text_on_default_bearer_supported_bool"; + + /** + * Flag specifying whether QoS preconditions are supported for text. + * + * <p>If {@code true}: QoS Preconditions are supported. + * {@code false} otherwise. + * <p>Reference: 3GPP TS 24.229 + */ + public static final String KEY_TEXT_QOS_PRECONDITION_SUPPORTED_BOOL = + KEY_PREFIX + "text_qos_precondition_supported_bool"; + + /** + * Specifies the AS (Application Specific) SDP modifier for text media. + * + * <p>Expressed in kilobits per second as per RFC 3556 Section 2. + */ + public static final String KEY_TEXT_AS_BANDWIDTH_KBPS_INT = + KEY_PREFIX + "text_as_bandwidth_kbps_int"; + + /** + * Specifies the RS (RTCP bandwidth-Sender) SDP modifier for text media. + * + * <p>This indicates the RTCP bandwidth allocated to active data senders + * for text media. + * + * <p>Expressed in bits per second as per RFC 3556 Section 2. + */ + public static final String KEY_TEXT_RS_BANDWIDTH_BPS_INT = + KEY_PREFIX + "text_rs_bandwidth_bps_int"; + + /** + * Specifies the RR (RTCP bandwidth-Receiver) SDP modifier for + * text media. + * + * <p>This indicates the RTCP bandwidth allocated to receivers + * for text media. + * + * <p>Expressed in bits per second as per RFC 3556 Section 2. + */ + public static final String KEY_TEXT_RR_BANDWIDTH_BPS_INT = + KEY_PREFIX + "text_rr_bandwidth_bps_int"; + + /** + * List of various reasons for RTT call to end due to + * media inactivity. + * + * <p>Possible values are, + * <UL> + * <LI>{@link Ims#RTCP_INACTIVITY_ON_HOLD}</LI> + * <LI>{@link Ims#RTCP_INACTIVITY_ON_CONNECTED}</LI> + * <LI>{@link Ims#RTP_INACTIVITY_ON_CONNECTED}</LI> + * <LI>{@link Ims#E911_RTCP_INACTIVITY_ON_CONNECTED}</LI> + * <LI>{@link Ims#E911_RTP_INACTIVITY_ON_CONNECTED}</LI> + * </UL> + */ + public static final String KEY_TEXT_INACTIVITY_CALL_END_REASONS_INT_ARRAY = + KEY_PREFIX + "text_inactivity_call_end_reasons_int_array"; + + /** + * Specifies the Text Codec capability. + * + * <p>Possible keys in this bundle are, + * <UL> + * <LI>{@link #KEY_T140_PAYLOAD_TYPE_INT}</LI> + * <LI>{@link #KEY_RED_PAYLOAD_TYPE_INT}</LI> + * </UL> + */ + public static final String KEY_TEXT_CODEC_CAPABILITY_PAYLOAD_TYPES_BUNDLE = + KEY_PREFIX + "text_codec_capability_payload_types_bundle"; + + /** Integer representing payload type for T140 codec. + * <p>Payload type is an integer in dynamic payload type range 96-127 + * as per RFC RFC 3551 Section 6. + */ + public static final String KEY_T140_PAYLOAD_TYPE_INT = + KEY_PREFIX + "t140_payload_type_int"; + + /** Integer representing payload type for RED/redundancy codec. + * <p>Payload type is an integer in dynamic payload type range 96-127 + * as per RFC RFC 3551 Section 6. + */ + public static final String KEY_RED_PAYLOAD_TYPE_INT = + KEY_PREFIX + "red_payload_type_int"; + + private static PersistableBundle getDefaults() { + PersistableBundle defaults = new PersistableBundle(); + defaults.putBoolean(KEY_TEXT_ON_DEFAULT_BEARER_SUPPORTED_BOOL, false); + defaults.putBoolean(KEY_TEXT_QOS_PRECONDITION_SUPPORTED_BOOL, true); + + defaults.putInt(KEY_TEXT_AS_BANDWIDTH_KBPS_INT, 4); + defaults.putInt(KEY_TEXT_RS_BANDWIDTH_BPS_INT, 100); + defaults.putInt(KEY_TEXT_RR_BANDWIDTH_BPS_INT, 300); + + defaults.putIntArray( + KEY_TEXT_INACTIVITY_CALL_END_REASONS_INT_ARRAY, + new int[] { + Ims.RTCP_INACTIVITY_ON_CONNECTED, + Ims.RTP_INACTIVITY_ON_CONNECTED + }); + + PersistableBundle text_codec_capability_payload_types = new PersistableBundle(); + + text_codec_capability_payload_types.putInt( + KEY_RED_PAYLOAD_TYPE_INT, + 112); + + text_codec_capability_payload_types.putInt( + KEY_T140_PAYLOAD_TYPE_INT, + 111); + + defaults.putPersistableBundle( + KEY_TEXT_CODEC_CAPABILITY_PAYLOAD_TYPES_BUNDLE, + text_codec_capability_payload_types); + + return defaults; + } + } + + /** + * Emergency Call/E911. This groups the configs specific for emergency call + * over IMS. + * + * <p> Reference: 3GPP 24.229, 3GPP 23.167 Annex H, 3GPP 24.301. + */ + public static final class ImsEmergency { + private ImsEmergency() {} + + /** Prefix of all imsemergency.KEY_* constants. */ + public static final String KEY_PREFIX = "imsemergency."; + + /** + * Flag specifying whether UE would retry E911 call on + * IMS PDN if emergency PDN setup failed. + * + * <p>If {@code true}: Allow UE to retry emergency call on + * IMS PDN if emergency PDN setup failed.{@code false} otherwise. + */ + public static final String KEY_RETRY_EMERGENCY_ON_IMS_PDN_BOOL = + KEY_PREFIX + "retry_emergency_on_ims_pdn_bool"; + + /** + * Flag specifying whether UE should enter Emergency CallBack Mode(ECBM) + * after E911 call is ended. + * + * <p>If {@code true}: Enter ECBM mode after E911 call is ended. + * {@code false} otherwise. + */ + public static final String KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL = + KEY_PREFIX + "emergency_callback_mode_supported_bool"; + + /** + * Flag specifying whether QoS preconditions are supported for emergency + * call setup. + * + * <p>If {@code true}: QoS Preconditions are supported. + * {@code false} otherwise. + * + * <p>Reference: 3GPP TS 24.229 + */ + public static final String KEY_EMERGENCY_QOS_PRECONDITION_SUPPORTED_BOOL = + KEY_PREFIX + "emergency_qos_precondition_supported_bool"; + + /** + * List of different RAT technologies on which emergency call using IMS + * is supported. + * + * <p>Possible values are, + * {@link AccessNetworkConstants.AccessNetworkType#NGRAN} + * {@link AccessNetworkConstants.AccessNetworkType#EUTRAN} + * {@link AccessNetworkConstants.AccessNetworkType#IWLAN} + */ + public static final String KEY_EMERGENCY_OVER_IMS_SUPPORTED_RATS_INT_ARRAY = + KEY_PREFIX + "emergency_over_ims_supported_rats_int_array"; + + /** + * Specifies the maximum time from deciding that an emergency service is to + * be established until completion of the emergency registration procedure. + * Upon timer expiry, the UE considers the emergency REGISTER request or + * the emergency call attempt as failed. + */ + public static final String KEY_EMERGENCY_REGISTRATION_TIMER_MILLIS_INT = + KEY_PREFIX + "emergency_registration_timer_millis_int"; + + private static PersistableBundle getDefaults() { + PersistableBundle defaults = new PersistableBundle(); + defaults.putBoolean(KEY_RETRY_EMERGENCY_ON_IMS_PDN_BOOL, false); + defaults.putBoolean(KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL, false); + defaults.putBoolean(KEY_EMERGENCY_QOS_PRECONDITION_SUPPORTED_BOOL, true); + + defaults.putIntArray( + KEY_EMERGENCY_OVER_IMS_SUPPORTED_RATS_INT_ARRAY, + new int[] { + AccessNetworkType.EUTRAN, + AccessNetworkType.IWLAN + }); + + defaults.putInt(KEY_EMERGENCY_REGISTRATION_TIMER_MILLIS_INT, 20000); + + return defaults; + } + } + + /** + * IMS Video Telephony configs. This groups the configs that are specific for video call. + */ + public static final class ImsVt { + private ImsVt() {} + + /** Prefix of all imsvt.KEY_* constants. */ + public static final String KEY_PREFIX = "imsvt."; + + /** + * Flag specifying whether video media is allowed on default bearer. + * + * <p>If {@code true}: video media can be sent on default bearer. + * {@code false} otherwise. + */ + public static final String KEY_VIDEO_ON_DEFAULT_BEARER_SUPPORTED_BOOL = + KEY_PREFIX + "video_on_default_bearer_supported_bool"; + + /** + * Specifies the timeout value for no video RTP packets received. + * <p>On timer expiry, VT call can downgrade to voice call or end + * or continue depending on the operator requirement. + */ + public static final String KEY_VIDEO_RTP_INACTIVITY_TIMER_MILLIS_INT = + KEY_PREFIX + "video_rtp_inactivity_timer_millis_int"; + + /** + * Specifies the timeout value for no video RTCP packets received. + * <p>On timer expiry, VT call can downgrade to voice call or end + * or continue depending on the operator requirement. + */ + public static final String KEY_VIDEO_RTCP_INACTIVITY_TIMER_MILLIS_INT = + KEY_PREFIX + "video_rtcp_inactivity_timer_millis_int"; + + /** + * Specifies the AS (Application Specific) SDP modifier for video media. + * + * <p>Expressed in kilobits per second as per RFC 3556 Section 2. + */ + public static final String KEY_VIDEO_AS_BANDWIDTH_KBPS_INT = + KEY_PREFIX + "video_as_bandwidth_kbps_int"; + + /** + * Specifies the RS (RTCP bandwidth-Sender) SDP modifier for video media. + * + * <p>This indicates the RTCP bandwidth allocated to active data senders + * for video media. + * + * <p>Expressed in bits per second as per RFC 3556 Section 2. + */ + public static final String KEY_VIDEO_RS_BANDWIDTH_BPS_INT = + KEY_PREFIX + "video_rs_bandwidth_bps_int"; + + /** + * Specifies the RR (RTCP bandwidth-Receiver) SDP modifier + * for video media. + * + * <p>This indicates the RTCP bandwidth allocated to receivers + * for video media. + * + * <p>Expressed in bits per second as per RFC 3556 Section 2. + */ + public static final String KEY_VIDEO_RR_BANDWIDTH_BPS_INT = + KEY_PREFIX + "video_rr_bandwidth_bps_int"; + + /** + * Specifies the differentiated services code point (DSCP) value + * for Video RTP. + * + * <p>Reference: RFC 4594 Section 1.4.4 + */ + public static final String KEY_VIDEO_RTP_DSCP_INT = + KEY_PREFIX + "video_rtp_dscp_int"; + + /** + * Flag specifying whether QoS preconditions are supported for Video. + * + * <p>If {@code true}: QoS Preconditions are supported. + * {@code false} otherwise. + * <p>Reference: 3GPP TS 24.229 + */ + public static final String KEY_VIDEO_QOS_PRECONDITION_SUPPORTED_BOOL = + KEY_PREFIX + "video_qos_precondition_supported_bool"; + + /** + * Specifies the Video Codec capability. This contains a list of + * payload types representing different Video codec instances. + + * <p>Possible key(s) in this bundle are, + * <UL> + * <LI>{@link #KEY_H264_PAYLOAD_TYPE_INT_ARRAY}</LI> + * </UL> + * <p>To specify payload descriptions for each of the payload types, see + * <UL> + * <LI>{@link #KEY_H264_PAYLOAD_DESCRIPTION_BUNDLE}</LI> + * </UL> + */ + public static final String KEY_VIDEO_CODEC_CAPABILITY_PAYLOAD_TYPES_BUNDLE = + KEY_PREFIX + "video_codec_capability_payload_types_bundle"; + + /** + * A list of integers representing the different payload types + * in H264 video codec in priority order from highest to lowest. + * <p>Payload type is an integer in dynamic payload type range 96-127 + * as per RFC RFC 3551 Section 6. + */ + public static final String KEY_H264_PAYLOAD_TYPE_INT_ARRAY = + KEY_PREFIX + "h264_payload_type_int_array"; + + /** + * Specifies the codec attributes of different payload types + * representing H264 video codec instances. + * + * <p> The allowed payload types of the video codecs are specified in, + * {@link #KEY_H264_PAYLOAD_TYPE_INT_ARRAY}. + * + * <p>Codec attributes allowed as part of H264 codec bundle are, + * <UL> + * <LI>{@link #KEY_H264_VIDEO_CODEC_ATTRIBUTE_PROFILE_LEVEL_ID_STRING}</LI> + * <LI>{@link #KEY_VIDEO_CODEC_ATTRIBUTE_PACKETIZATION_MODE_INT}</LI> + * <LI>{@link #KEY_VIDEO_CODEC_ATTRIBUTE_FRAME_RATE_INT}</LI> + * <LI>{@link #KEY_VIDEO_CODEC_ATTRIBUTE_RESOLUTION_INT_ARRAY}</LI> + * </UL> + * + * <p>If this bundle is not configured and + * {@link #KEY_H264_PAYLOAD_TYPE_INT_ARRAY} is not empty, + * then default values as in the individual codec attributes to + * be used for that payload type. + * <p>If the codec attributes in a particular codec instance bundle + * is not valid together, then that codec instance should not be used. + */ + public static final String KEY_H264_PAYLOAD_DESCRIPTION_BUNDLE = + KEY_PREFIX + "h264_payload_description_bundle"; + + /** + * Specifies the packetization mode of the video codec. + * + * <p>Permissible values are 0 (Single NAL unit mode), + * 1(Non-interleaved mode). + * + * <p>If this key is not specified or invalid, then the following + * default value to be used. + * <UL> + * <LI>For H264: 1(Non-interleaved mode)</LI> + * <UL> + * + * <p>Reference: RFC 6184 Section 5.4 + */ + public static final String KEY_VIDEO_CODEC_ATTRIBUTE_PACKETIZATION_MODE_INT = + KEY_PREFIX + "video_codec_attribute_packetization_mode_int"; + + /** + * Specifies the maximum frame rate the offerer wishes to receive. + * This gives the maximum video frame rate in frames/sec. + * + * <p>If this key is not specified or invalid, then the following + * default value to be used. + * <UL> + * <LI>For H264: 15 </LI> + * <UL> + * <p>Reference: RFC 4566 Section 6, 3GPP 26.114 Section 6.2.3.2 + */ + public static final String KEY_VIDEO_CODEC_ATTRIBUTE_FRAME_RATE_INT = + KEY_PREFIX + "video_codec_attribute_frame_rate_int"; + + /** + * Specifies the maximum resolution allowed for the video codec + * instance. + * + * <p>This is specified as an array of two integers, with + * index 0 : Width, + * index 1 : Height + * + * <p>If this key is not specified or invalid as per the video codec, + * then the following default value to be used. + * <UL> + * <LI>For H264: 240 (WIDTH) x 320 (HEIGHT) </LI> + * <UL> + * <p>Reference: RFC 4566 Section 6, 3GPP 26.114 Section 6.2.3.2 + * + */ + public static final String KEY_VIDEO_CODEC_ATTRIBUTE_RESOLUTION_INT_ARRAY = + KEY_PREFIX + "video_codec_attribute_resolution_int_array"; + + /** + * Specifies the profile level id of the H264 video codec. + * This value is represented as "profile-level-id" in the SDP offer + * as per RFC 6184 Section 8.1. + * + * <p>If this key is not specified or invalid as per the video codec, + * then default value of 42C00C to be used. + * + * <p>Reference: RFC 6184 Section 8.1, ITU-T Recommendation H.264 + */ + public static final String KEY_H264_VIDEO_CODEC_ATTRIBUTE_PROFILE_LEVEL_ID_STRING = + KEY_PREFIX + "h264_video_codec_attribute_profile_level_id_string"; + + private static PersistableBundle getDefaults() { + PersistableBundle defaults = new PersistableBundle(); + defaults.putBoolean(KEY_VIDEO_ON_DEFAULT_BEARER_SUPPORTED_BOOL, false); + defaults.putBoolean(KEY_VIDEO_QOS_PRECONDITION_SUPPORTED_BOOL, true); + + defaults.putInt(KEY_VIDEO_RTP_INACTIVITY_TIMER_MILLIS_INT, 0); + defaults.putInt(KEY_VIDEO_RTCP_INACTIVITY_TIMER_MILLIS_INT, 0); + + defaults.putInt(KEY_VIDEO_AS_BANDWIDTH_KBPS_INT, 960); + defaults.putInt(KEY_VIDEO_RS_BANDWIDTH_BPS_INT, 8000); + defaults.putInt(KEY_VIDEO_RR_BANDWIDTH_BPS_INT, 6000); + defaults.putInt(KEY_VIDEO_RTP_DSCP_INT, 40); + + PersistableBundle video_codec_capability_payload_types = new PersistableBundle(); + + video_codec_capability_payload_types.putIntArray( + KEY_H264_PAYLOAD_TYPE_INT_ARRAY, + new int[] { 99, 100 }); + + defaults.putPersistableBundle( + KEY_VIDEO_CODEC_CAPABILITY_PAYLOAD_TYPES_BUNDLE, + video_codec_capability_payload_types); + + PersistableBundle all_h264_payload_bundles = new PersistableBundle(); + + /* Setting default codec attributes for individual H264 profiles*/ + + /* For H264 profile-level-id: 42C00C, frame rate:15, Resolution: 240x320 */ + PersistableBundle h264_bundle_instance1 = new PersistableBundle(); + all_h264_payload_bundles.putPersistableBundle( + "99", /* Same value of payload type as in KEY_H264_PAYLOAD_TYPE_INT_ARRAY */ + h264_bundle_instance1); + + /* For H264 profile-level-id: 42C00C, packetisation mode:0, frame rate:15, + * Resolution: 240x320 */ + PersistableBundle h264_bundle_instance2 = new PersistableBundle(); + h264_bundle_instance2.putInt( + KEY_VIDEO_CODEC_ATTRIBUTE_PACKETIZATION_MODE_INT, + 0); + + all_h264_payload_bundles.putPersistableBundle( + "100", /* Same value of payload type as in KEY_H264_PAYLOAD_TYPE_INT_ARRAY */ + h264_bundle_instance2); + + defaults.putPersistableBundle( + KEY_H264_PAYLOAD_DESCRIPTION_BUNDLE, + all_h264_payload_bundles); + + return defaults; + } + } + + /** + * WiFi Calling. This groups the configs specific for Voice over WiFi/WFC call. + */ + public static final class ImsWfc { + private ImsWfc() {} + + /** Prefix of all imswfc.KEY_* constants. */ + public static final String KEY_PREFIX = "imswfc."; + + /** + * List of MDNs for which Geo-location PIDF XML with country info + * needs to included for normal calls involving short code. + */ + public static final String KEY_PIDF_SHORT_CODE_STRING_ARRAY = + KEY_PREFIX + "pidf_short_code_string_array"; + + /** + * Flag specifying whether emergency call over VoWiFi is requested over + * emergency PDN or IMS PDN. + * + * <p>If {@code false}: E911 call uses IMS PDN for E911 call over VoWiFi. + * If {@code true}: E911 call uses Emergency PDN for E911 call over VoWiFi. + */ + public static final String KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL = + KEY_PREFIX + "emergency_call_over_emergency_pdn_bool"; + + + private static PersistableBundle getDefaults() { + PersistableBundle defaults = new PersistableBundle(); + + defaults.putBoolean(KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL, false); + defaults.putStringArray(KEY_PIDF_SHORT_CODE_STRING_ARRAY, new String[] {}); + + return defaults; + } + } + + /** + * IMS supplementary services configs. This groups the configs required for + * supplementary services (SS) like XCAP over UT, + * Unstructured Supplementary Service Data(USSD). + */ + public static final class ImsSs { + private ImsSs() {} + + /** Prefix of all imsss.KEY_* constants. */ + public static final String KEY_PREFIX = "imsss."; + + /** + * Flag that controls whether XCAP over UT status need to be + * dependent on IMS registration. + * + * <p>If {@code true}: XCAP over UT status need to be + * dependent on IMS registration. + * {@code false} otherwise. + */ + public static final String KEY_UT_REQUIRES_IMS_REGISTRATION_BOOL = + KEY_PREFIX + "ut_requires_ims_registration_bool"; + + /** + * Flag that controls whether Circuit Switched Fallback (CSFB) + * option is available when XCAP over UT fails. + * + * <p>If {@code false}: XCAP over UT only with no CSFB option. + * If XCAP over UT fails, return error. + * if {@code true}, Use CSFB if XCAP over UT fails. + */ + public static final String KEY_USE_CSFB_ON_XCAP_OVER_UT_FAILURE_BOOL = + KEY_PREFIX + "use_csfb_on_xcap_over_ut_failure_bool"; + + /** + * Flag that controls whether XCAP over UT is enabled or not + * when PS data is turned off. + * + * <p>If {@code true}: XCAP over UT is enabled when PS data is off. + * {@code false}: Otherwise. + * + * Reference: IR.92 Section 5.5.1 + */ + public static final String KEY_UT_SUPPORTED_WHEN_PS_DATA_OFF_BOOL = + KEY_PREFIX + "ut_supported_when_ps_data_off_bool"; + + /** + * Flag that controls whether network initiated USSD over IMS is + * supported by the UE. + * + * <p>If {@code true}: Support Available.{@code false}: Otherwise. + * Reference: 3GPP 24.390. + */ + public static final String KEY_NETWORK_INITIATED_USSD_OVER_IMS_SUPPORTED_BOOL = + KEY_PREFIX + "network_initiated_ussd_over_ims_supported_bool"; + + /** + * Specifies the 'XCAP over UT' IP Type when device is + * on Home Network. + * + * <p>Possible values are, + * {@link ApnSetting#PROTOCOL_IPV4V6}, + * {@link ApnSetting#PROTOCOL_IP}, + * {@link ApnSetting#PROTOCOL_IPV6} + * + * If key is invalid or not configured, the default value + * {@link ApnSetting#PROTOCOL_IPV4V6} will apply. + */ + public static final String KEY_UT_IPTYPE_HOME_INT = + KEY_PREFIX + "ut_iptype_home_int"; + + /** + * Specifies the 'XCAP over UT' IP Type when device is roaming. + * + * <p>Possible values are, + * {@link ApnSetting#PROTOCOL_IPV4V6}, + * {@link ApnSetting#PROTOCOL_IP}, + * {@link ApnSetting#PROTOCOL_IPV6} + + * If key is invalid or not configured, the default value + * {@link ApnSetting#PROTOCOL_IPV4V6} will apply. + */ + public static final String KEY_UT_IPTYPE_ROAMING_INT = + KEY_PREFIX + "ut_iptype_roaming_int"; + + /** + * Specifies the XCAP Application Server fully qualified domain name (FQDN). + * <p> Reference: 24.623 Section 5.2.3. + */ + public static final String KEY_UT_AS_SERVER_FQDN_STRING = + KEY_PREFIX + "ut_as_server_fqdn_string"; + + /** + * Specifies the XCAP Application Server Remote port. + * As XCAP is a usage of HTTP, the default value is same as HTTP, i.e. 80. + */ + public static final String KEY_UT_AS_SERVER_PORT_INT = + KEY_PREFIX + "ut_as_server_port_int"; + + /** + * Specifies the preferred transport to be used for XCAP over UT. + * + * <p>Possible values are, + * {@link Ims#PREFERRED_TRANSPORT_TCP}, + * {@link Ims#PREFERRED_TRANSPORT_TLS} + * + * <p>If key is invalid or not configured, the default value + * {@link Ims#PREFERRED_TRANSPORT_TCP} will apply. + */ + public static final String KEY_UT_TRANSPORT_TYPE_INT = + KEY_PREFIX + "ut_transport_type_int"; + + /** @hide */ + @IntDef({ + SUPPLEMENTARY_SERVICE_CW, + SUPPLEMENTARY_SERVICE_CF_ALL, + SUPPLEMENTARY_SERVICE_CF_CFU, + SUPPLEMENTARY_SERVICE_CF_ALL_CONDITONAL_FORWARDING, + SUPPLEMENTARY_SERVICE_CF_CFB, + SUPPLEMENTARY_SERVICE_CF_CFNRY, + SUPPLEMENTARY_SERVICE_CF_CFNRC, + SUPPLEMENTARY_SERVICE_CF_CFNL, + SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIP, + SUPPLEMENTARY_SERVICE_IDENTIFICATION_TIP, + SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIR, + SUPPLEMENTARY_SERVICE_IDENTIFICATION_TIR, + SUPPLEMENTARY_SERVICE_CB_ALL, + SUPPLEMENTARY_SERVICE_CB_OBS, + SUPPLEMENTARY_SERVICE_CB_BAOC, + SUPPLEMENTARY_SERVICE_CB_BOIC, + SUPPLEMENTARY_SERVICE_CB_BOIC_EXHC, + SUPPLEMENTARY_SERVICE_CB_IBS, + SUPPLEMENTARY_SERVICE_CB_BAIC, + SUPPLEMENTARY_SERVICE_CB_BIC_ROAM, + SUPPLEMENTARY_SERVICE_CB_ACR, + SUPPLEMENTARY_SERVICE_CB_BIL + }) + + public @interface SsType {} + + /** Communication Waiting (CW) support as per 3GPP 24.615. */ + public static final int SUPPLEMENTARY_SERVICE_CW = 0; + + /** + * Call Diversion - All call forwarding support as per 3GPP 24.604. + * + * <p>This value is associated with MMI support service code 002 + * as indicated in TS 22.030 Table B.1 + */ + public static final int SUPPLEMENTARY_SERVICE_CF_ALL = 1; + + /** + * Call Diversion - All Unconditional call forwarding support (CFU) as + * per 3GPP 24.604. + * + * <p>This value is associated with MMI support service code 21 + * as indicated in TS 22.030 Table B.1 + */ + public static final int SUPPLEMENTARY_SERVICE_CF_CFU = 2; + + /** + * Call Diversion - All conditional call forwarding support as + * per 3GPP 24.604. + * + * <p>This value is associated with MMI support service code 004 + * as indicated in TS 22.030 Table B.1 + */ + public static final int SUPPLEMENTARY_SERVICE_CF_ALL_CONDITONAL_FORWARDING = 3; + + /** + * Call Diversion - Call forwarding on mobile subscriber busy (CFB) + * support as per 3GPP 24.604. + * + * <p>This value is associated with MMI support service code 67 + * as indicated in TS 22.030 Table B.1 + */ + public static final int SUPPLEMENTARY_SERVICE_CF_CFB = 4; + + /** + * Call Diversion - Call forwarding on no reply (CFNRY) + * support as per 3GPP 24.604. + * + * <p>This value is associated with MMI support service code 61 + * as indicated in TS 22.030 Table B.1 + */ + public static final int SUPPLEMENTARY_SERVICE_CF_CFNRY = 5; + + /** + * Call Diversion - Call forwarding on mobile subscriber not reachable + * (CFNRC) support as per 3GPP 24.604. + * + * <p>This value is associated with MMI support service code 62 + * as indicated in TS 22.030 Table B.1 + */ + public static final int SUPPLEMENTARY_SERVICE_CF_CFNRC = 6; + + /** + * Communication Forwarding on Not Logged-in (CFNL). + * support as per 3GPP 24.604 Section 4.2.1.7 + * + */ + public static final int SUPPLEMENTARY_SERVICE_CF_CFNL = 7; + + /** + * Originating Identification Presentation (OIP) support + * as per 3GPP 24.607. + * + */ + public static final int SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIP = 8; + + /** + * Terminating Identification Presentation (TIP) support + * as per 3GPP 24.608. + */ + public static final int SUPPLEMENTARY_SERVICE_IDENTIFICATION_TIP = 9; + + /** + * Originating Identification Restriction (OIR) support + * as per 3GPP 24.607. + */ + public static final int SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIR = 10; + + /** + * Terminating Identification Restriction (TIR) support + * as per 3GPP 24.608. + */ + public static final int SUPPLEMENTARY_SERVICE_IDENTIFICATION_TIR = 11; + + /** + * Call Barring - All barring services, + * This value is associated with MMI support service code 330 + * as indicated TS 22.030 Table B.1 + */ + public static final int SUPPLEMENTARY_SERVICE_CB_ALL = 12; + + /** + * Call Barring - Outgoing barring services, + * This value is associated with MMI support service code 333 + * as indicated TS 22.030 Table B.1 + */ + public static final int SUPPLEMENTARY_SERVICE_CB_OBS = 13; + + /** + * Call Barring - Barring of all outgoing calls (BAOC) + * support as per 3GPP TS 24.611. + * + * <p>This value is associated with MMI support service code 33 + * as indicated TS 22.030 Table B.1 + */ + public static final int SUPPLEMENTARY_SERVICE_CB_BAOC = 14; + + /** + * Call Barring - Barring of outgoing international calls + * (BOIC) support as per 3GPP TS 24.611. + * + * <p>This value is associated with MMI support service code 331 + * as indicated TS 22.030 Table B.1 + */ + public static final int SUPPLEMENTARY_SERVICE_CB_BOIC = 15; + + /** + * Call Barring - Barring of outgoing international calls + * except those directed to the home PLMN country (BOIC-EXHC) support + * as per 3GPP TS 24.611. + * + * <p>This value is associated with MMI support service code 332 + * as indicated TS 22.030 Table B.1 + */ + public static final int SUPPLEMENTARY_SERVICE_CB_BOIC_EXHC = 16; + + /** + * Call Barring - Incoming barring services, + * This value is associated with MMI support service code 353 + * as indicated TS 22.030 Table B.1 + */ + public static final int SUPPLEMENTARY_SERVICE_CB_IBS = 17; + + /** + * Call Barring - Barring of all incoming calls (BAIC) + * support as per 3GPP TS 24.611. + * + * <p>This value is associated with MMI support service code 35 + * as indicated TS 22.030 Table B.1 + */ + public static final int SUPPLEMENTARY_SERVICE_CB_BAIC = 18; + + /** + * Call Barring - Barring of incoming calls when roaming outside + * the home PLMN country (BIC-ROAM) support as per 3GPP TS 24.611. + * + * <p>This value is associated with MMI support service code 351 + * as indicated TS 22.030 Table B.1 + */ + public static final int SUPPLEMENTARY_SERVICE_CB_BIC_ROAM = 19; + + /** + * Call Barring - Anonymous Call Rejection/Barring of all anonymous + * incoming number support as per 3GPP TS 24.611. + */ + public static final int SUPPLEMENTARY_SERVICE_CB_ACR = 20; + + /** + * Call Barring - Barring list of incoming numbers support. + */ + public static final int SUPPLEMENTARY_SERVICE_CB_BIL = 21; + + /** + * List of UT services that are Server based. + * + * <p>Possible values are, + * <UL> + * <LI>{@link #SUPPLEMENTARY_SERVICE_CW}</LI> + * <LI>{@link #SUPPLEMENTARY_SERVICE_CF_ALL}</LI> + * <LI>{@link #SUPPLEMENTARY_SERVICE_CF_CFU}</LI> + * <LI>{@link #SUPPLEMENTARY_SERVICE_CF_ALL_CONDITONAL_FORWARDING}</LI> + * <LI>{@link #SUPPLEMENTARY_SERVICE_CF_CFB}</LI> + * <LI>{@link #SUPPLEMENTARY_SERVICE_CF_CFNRY}</LI> + * <LI>{@link #SUPPLEMENTARY_SERVICE_CF_CFNRC}</LI> + * <LI>{@link #SUPPLEMENTARY_SERVICE_CF_CFNL}</LI> + * <LI>{@link #SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIP}</LI> + * <LI>{@link #SUPPLEMENTARY_SERVICE_IDENTIFICATION_TIP}</LI> + * <LI>{@link #SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIR}</LI> + * <LI>{@link #SUPPLEMENTARY_SERVICE_IDENTIFICATION_TIR}</LI> + * <LI>{@link #SUPPLEMENTARY_SERVICE_CB_ALL}</LI> + * <LI>{@link #SUPPLEMENTARY_SERVICE_CB_OBS}</LI> + * <LI>{@link #SUPPLEMENTARY_SERVICE_CB_IBS}</LI> + * <LI>{@link #SUPPLEMENTARY_SERVICE_CB_BAOC}</LI> + * <LI>{@link #SUPPLEMENTARY_SERVICE_CB_BOIC}</LI> + * <LI>{@link #SUPPLEMENTARY_SERVICE_CB_BOIC_EXHC}</LI> + * <LI>{@link #SUPPLEMENTARY_SERVICE_CB_BAIC}</LI> + * <LI>{@link #SUPPLEMENTARY_SERVICE_CB_BIC_ROAM}</LI> + * <LI>{@link #SUPPLEMENTARY_SERVICE_CB_ACR}</LI> + * <LI>{@link #SUPPLEMENTARY_SERVICE_CB_BIL}</LI> + * </UL> + */ + public static final String KEY_UT_SERVER_BASED_SERVICES_INT_ARRAY = + KEY_PREFIX + "ut_server_based_services_int_array"; + + /** + * List of UT services that are terminal based. + * + * By default, all services are server based and defined in + * {@link #KEY_UT_SERVER_BASED_SERVICES_INT_ARRAY}. + * Adding here will override that service setting to terminal based. + * + * <p>Possible values are, + * <UL> + * <LI>{@link #SUPPLEMENTARY_SERVICE_CW}</LI> + * <LI>{@link #SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIR}</LI> + * </UL> + */ + public static final String KEY_UT_TERMINAL_BASED_SERVICES_INT_ARRAY = + KEY_PREFIX + "ut_terminal_based_services_int_array"; + + /** + * List of different RAT technologies on which XCAP over UT + * is supported. + * + * <p>Possible values are, + * {@link AccessNetworkConstants.AccessNetworkType#NGRAN} + * {@link AccessNetworkConstants.AccessNetworkType#EUTRAN} + * {@link AccessNetworkConstants.AccessNetworkType#IWLAN} + * {@link AccessNetworkConstants.AccessNetworkType#UTRAN} + * {@link AccessNetworkConstants.AccessNetworkType#GERAN} + */ + public static final String KEY_XCAP_OVER_UT_SUPPORTED_RATS_INT_ARRAY = + KEY_PREFIX + "xcap_over_ut_supported_rats_int_array"; + + private static PersistableBundle getDefaults() { + PersistableBundle defaults = new PersistableBundle(); + defaults.putBoolean(KEY_UT_REQUIRES_IMS_REGISTRATION_BOOL, true); + defaults.putBoolean(KEY_USE_CSFB_ON_XCAP_OVER_UT_FAILURE_BOOL, true); + defaults.putBoolean(KEY_UT_SUPPORTED_WHEN_PS_DATA_OFF_BOOL, true); + defaults.putBoolean(KEY_NETWORK_INITIATED_USSD_OVER_IMS_SUPPORTED_BOOL, true); + + defaults.putInt(KEY_UT_IPTYPE_HOME_INT, ApnSetting.PROTOCOL_IPV4V6); + defaults.putInt(KEY_UT_IPTYPE_ROAMING_INT, ApnSetting.PROTOCOL_IPV4V6); + defaults.putInt(KEY_UT_AS_SERVER_PORT_INT, 80); + defaults.putInt(KEY_UT_TRANSPORT_TYPE_INT, Ims.PREFERRED_TRANSPORT_TCP); + + defaults.putIntArray( + KEY_UT_SERVER_BASED_SERVICES_INT_ARRAY, + new int[] { + SUPPLEMENTARY_SERVICE_CW, + SUPPLEMENTARY_SERVICE_CF_ALL, + SUPPLEMENTARY_SERVICE_CF_CFU, + SUPPLEMENTARY_SERVICE_CF_CFNRC, + SUPPLEMENTARY_SERVICE_CF_ALL_CONDITONAL_FORWARDING, + SUPPLEMENTARY_SERVICE_CF_CFB, + SUPPLEMENTARY_SERVICE_CF_CFNRY, + SUPPLEMENTARY_SERVICE_CF_CFNL, + SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIP, + SUPPLEMENTARY_SERVICE_IDENTIFICATION_TIP, + SUPPLEMENTARY_SERVICE_IDENTIFICATION_OIR, + SUPPLEMENTARY_SERVICE_IDENTIFICATION_TIR, + SUPPLEMENTARY_SERVICE_CB_ALL, + SUPPLEMENTARY_SERVICE_CB_OBS, + SUPPLEMENTARY_SERVICE_CB_IBS, + SUPPLEMENTARY_SERVICE_CB_BAOC, + SUPPLEMENTARY_SERVICE_CB_BOIC, + SUPPLEMENTARY_SERVICE_CB_BOIC_EXHC, + SUPPLEMENTARY_SERVICE_CB_BAIC, + SUPPLEMENTARY_SERVICE_CB_BIC_ROAM, + SUPPLEMENTARY_SERVICE_CB_ACR, + SUPPLEMENTARY_SERVICE_CB_BIL + }); + defaults.putIntArray( + KEY_UT_TERMINAL_BASED_SERVICES_INT_ARRAY, + new int[] {}); + + defaults.putIntArray( + KEY_XCAP_OVER_UT_SUPPORTED_RATS_INT_ARRAY, + new int[] { + AccessNetworkType.EUTRAN, + AccessNetworkType.IWLAN + }); + defaults.putString(KEY_UT_AS_SERVER_FQDN_STRING, ""); + + return defaults; + } + } + + /** + * This groups the BSF (BootStrapping Function) related configs. + * Reference: 3GPP TS 24.109. + */ + public static final class Bsf { + private Bsf() {} + + /** Prefix of all bsf.KEY_* constants. */ + public static final String KEY_PREFIX = "bsf."; + + /** Specifies the fully qualified domain name (FQDN) of BSF Server + * as per 3GPP 24.109. + */ + public static final String KEY_BSF_SERVER_FQDN_STRING = + KEY_PREFIX + "bsf_server_fqdn_string"; + + /** + * Specifies the port number of the BSF server as per 3GPP 24.109. + * This is usually default port number of HTTP, i.e. 80. + */ + public static final String KEY_BSF_SERVER_PORT_INT = + KEY_PREFIX + "bsf_server_port_int"; + + /** + * Specifies the transport type used in communication with + * BSF server. + * + * <p>Possible values are, + * {@link Ims#PREFERRED_TRANSPORT_TCP}, + * {@link Ims#PREFERRED_TRANSPORT_TLS} + * + * <p>If key is invalid or not configured, the default value + * {@link Ims#PREFERRED_TRANSPORT_TCP} will apply. + */ + public static final String KEY_BSF_TRANSPORT_TYPE_INT = + KEY_PREFIX + "bsf_transport type_int"; + + private static PersistableBundle getDefaults() { + PersistableBundle defaults = new PersistableBundle(); + + defaults.putInt(KEY_BSF_SERVER_PORT_INT, 80); + defaults.putInt(KEY_BSF_TRANSPORT_TYPE_INT, Ims.PREFERRED_TRANSPORT_TCP); + defaults.putString(KEY_BSF_SERVER_FQDN_STRING, ""); + return defaults; } } @@ -6162,6 +8707,14 @@ public class CarrierConfigManager { }); sDefaults.putBoolean(KEY_SUPPORT_WPS_OVER_IMS_BOOL, true); sDefaults.putAll(Ims.getDefaults()); + sDefaults.putAll(ImsVoice.getDefaults()); + sDefaults.putAll(ImsSms.getDefaults()); + sDefaults.putAll(ImsRtt.getDefaults()); + sDefaults.putAll(ImsEmergency.getDefaults()); + sDefaults.putAll(ImsVt.getDefaults()); + sDefaults.putAll(ImsWfc.getDefaults()); + sDefaults.putAll(ImsSs.getDefaults()); + sDefaults.putAll(Bsf.getDefaults()); sDefaults.putAll(Iwlan.getDefaults()); sDefaults.putStringArray(KEY_CARRIER_CERTIFICATE_STRING_ARRAY, new String[0]); sDefaults.putBoolean(KEY_FORMAT_INCOMING_NUMBER_TO_NATIONAL_FOR_JP_BOOL, false); diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java index b4b8aee31b54..4db00cf258e5 100644 --- a/telephony/java/android/telephony/CellIdentityLte.java +++ b/telephony/java/android/telephony/CellIdentityLte.java @@ -379,7 +379,7 @@ public final class CellIdentityLte extends CellIdentity { mBands = in.createIntArray(); mBandwidth = in.readInt(); mAdditionalPlmns = (ArraySet<String>) in.readArraySet(null); - mCsgInfo = in.readParcelable(null, android.telephony.ClosedSubscriberGroupInfo.class); + mCsgInfo = in.readParcelable(null); updateGlobalCellId(); if (DBG) log(toString()); diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java index 90e6295abda8..13d93737f751 100644 --- a/telephony/java/android/telephony/CellIdentityTdscdma.java +++ b/telephony/java/android/telephony/CellIdentityTdscdma.java @@ -297,7 +297,7 @@ public final class CellIdentityTdscdma extends CellIdentity { mCpid = in.readInt(); mUarfcn = in.readInt(); mAdditionalPlmns = (ArraySet<String>) in.readArraySet(null); - mCsgInfo = in.readParcelable(null, android.telephony.ClosedSubscriberGroupInfo.class); + mCsgInfo = in.readParcelable(null); updateGlobalCellId(); if (DBG) log(toString()); diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java index 72282cdb344b..9b463da14f16 100644 --- a/telephony/java/android/telephony/CellIdentityWcdma.java +++ b/telephony/java/android/telephony/CellIdentityWcdma.java @@ -313,7 +313,7 @@ public final class CellIdentityWcdma extends CellIdentity { mPsc = in.readInt(); mUarfcn = in.readInt(); mAdditionalPlmns = (ArraySet<String>) in.readArraySet(null); - mCsgInfo = in.readParcelable(null, android.telephony.ClosedSubscriberGroupInfo.class); + mCsgInfo = in.readParcelable(null); updateGlobalCellId(); if (DBG) log(toString()); diff --git a/telephony/java/android/telephony/CellSignalStrengthNr.java b/telephony/java/android/telephony/CellSignalStrengthNr.java index f5ba3abf53a5..cd22abddd3a7 100644 --- a/telephony/java/android/telephony/CellSignalStrengthNr.java +++ b/telephony/java/android/telephony/CellSignalStrengthNr.java @@ -326,7 +326,7 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa mCsiRsrq = in.readInt(); mCsiSinr = in.readInt(); mCsiCqiTableIndex = in.readInt(); - mCsiCqiReport = in.readArrayList(Integer.class.getClassLoader(), java.lang.Integer.class); + mCsiCqiReport = in.readArrayList(Integer.class.getClassLoader()); mSsRsrp = in.readInt(); mSsRsrq = in.readInt(); mSsSinr = in.readInt(); diff --git a/telephony/java/android/telephony/DataSpecificRegistrationInfo.java b/telephony/java/android/telephony/DataSpecificRegistrationInfo.java index 837124fe89de..957f683292f7 100644 --- a/telephony/java/android/telephony/DataSpecificRegistrationInfo.java +++ b/telephony/java/android/telephony/DataSpecificRegistrationInfo.java @@ -105,7 +105,7 @@ public final class DataSpecificRegistrationInfo implements Parcelable { isDcNrRestricted = source.readBoolean(); isNrAvailable = source.readBoolean(); isEnDcAvailable = source.readBoolean(); - mVopsSupportInfo = source.readParcelable(VopsSupportInfo.class.getClassLoader(), android.telephony.VopsSupportInfo.class); + mVopsSupportInfo = source.readParcelable(VopsSupportInfo.class.getClassLoader()); } @Override diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java index c18443e81aff..6a807665a103 100644 --- a/telephony/java/android/telephony/NetworkRegistrationInfo.java +++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java @@ -311,12 +311,12 @@ public final class NetworkRegistrationInfo implements Parcelable { mRejectCause = source.readInt(); mEmergencyOnly = source.readBoolean(); mAvailableServices = new ArrayList<>(); - source.readList(mAvailableServices, Integer.class.getClassLoader(), java.lang.Integer.class); - mCellIdentity = source.readParcelable(CellIdentity.class.getClassLoader(), android.telephony.CellIdentity.class); + source.readList(mAvailableServices, Integer.class.getClassLoader()); + mCellIdentity = source.readParcelable(CellIdentity.class.getClassLoader()); mVoiceSpecificInfo = source.readParcelable( - VoiceSpecificRegistrationInfo.class.getClassLoader(), android.telephony.VoiceSpecificRegistrationInfo.class); + VoiceSpecificRegistrationInfo.class.getClassLoader()); mDataSpecificInfo = source.readParcelable( - DataSpecificRegistrationInfo.class.getClassLoader(), android.telephony.DataSpecificRegistrationInfo.class); + DataSpecificRegistrationInfo.class.getClassLoader()); mNrState = source.readInt(); mRplmn = source.readString(); mIsUsingCarrierAggregation = source.readBoolean(); diff --git a/telephony/java/android/telephony/NetworkScanRequest.java b/telephony/java/android/telephony/NetworkScanRequest.java index c8b8ffb9846b..326f4171de3b 100644 --- a/telephony/java/android/telephony/NetworkScanRequest.java +++ b/telephony/java/android/telephony/NetworkScanRequest.java @@ -221,7 +221,8 @@ public final class NetworkScanRequest implements Parcelable { private NetworkScanRequest(Parcel in) { mScanType = in.readInt(); - Parcelable[] tempSpecifiers = in.readParcelableArray(Object.class.getClassLoader()); + Parcelable[] tempSpecifiers = in.readParcelableArray(Object.class.getClassLoader(), + RadioAccessSpecifier.class); if (tempSpecifiers != null) { mSpecifiers = new RadioAccessSpecifier[tempSpecifiers.length]; for (int i = 0; i < tempSpecifiers.length; i++) { diff --git a/telephony/java/android/telephony/PhoneCapability.java b/telephony/java/android/telephony/PhoneCapability.java index 63e3468ac19b..a3aaf61a6fec 100644 --- a/telephony/java/android/telephony/PhoneCapability.java +++ b/telephony/java/android/telephony/PhoneCapability.java @@ -150,7 +150,7 @@ public final class PhoneCapability implements Parcelable { mMaxActiveDataSubscriptions = in.readInt(); mNetworkValidationBeforeSwitchSupported = in.readBoolean(); mLogicalModemList = new ArrayList<>(); - in.readList(mLogicalModemList, ModemInfo.class.getClassLoader(), android.telephony.ModemInfo.class); + in.readList(mLogicalModemList, ModemInfo.class.getClassLoader()); mDeviceNrCapabilities = in.createIntArray(); } diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java index 2670b03ca8ac..ce2f3f924554 100644 --- a/telephony/java/android/telephony/PreciseDataConnectionState.java +++ b/telephony/java/android/telephony/PreciseDataConnectionState.java @@ -125,9 +125,9 @@ public final class PreciseDataConnectionState implements Parcelable { mId = in.readInt(); mState = in.readInt(); mNetworkType = in.readInt(); - mLinkProperties = in.readParcelable(LinkProperties.class.getClassLoader(), android.net.LinkProperties.class); + mLinkProperties = in.readParcelable(LinkProperties.class.getClassLoader()); mFailCause = in.readInt(); - mApnSetting = in.readParcelable(ApnSetting.class.getClassLoader(), android.telephony.data.ApnSetting.class); + mApnSetting = in.readParcelable(ApnSetting.class.getClassLoader()); } /** diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index 70da9b95410a..5affb62ae5cd 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -479,7 +479,7 @@ public class ServiceState implements Parcelable { mIsEmergencyOnly = in.readInt() != 0; mArfcnRsrpBoost = in.readInt(); synchronized (mNetworkRegistrationInfos) { - in.readList(mNetworkRegistrationInfos, NetworkRegistrationInfo.class.getClassLoader(), android.telephony.NetworkRegistrationInfo.class); + in.readList(mNetworkRegistrationInfos, NetworkRegistrationInfo.class.getClassLoader()); } mChannelNumber = in.readInt(); mCellBandwidths = in.createIntArray(); diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java index f74ef0fe764a..b7bc46736e18 100644 --- a/telephony/java/android/telephony/SignalStrength.java +++ b/telephony/java/android/telephony/SignalStrength.java @@ -275,12 +275,12 @@ public class SignalStrength implements Parcelable { public SignalStrength(Parcel in) { if (DBG) log("Size of signalstrength parcel:" + in.dataSize()); - mCdma = in.readParcelable(CellSignalStrengthCdma.class.getClassLoader(), android.telephony.CellSignalStrengthCdma.class); - mGsm = in.readParcelable(CellSignalStrengthGsm.class.getClassLoader(), android.telephony.CellSignalStrengthGsm.class); - mWcdma = in.readParcelable(CellSignalStrengthWcdma.class.getClassLoader(), android.telephony.CellSignalStrengthWcdma.class); - mTdscdma = in.readParcelable(CellSignalStrengthTdscdma.class.getClassLoader(), android.telephony.CellSignalStrengthTdscdma.class); - mLte = in.readParcelable(CellSignalStrengthLte.class.getClassLoader(), android.telephony.CellSignalStrengthLte.class); - mNr = in.readParcelable(CellSignalStrengthLte.class.getClassLoader(), android.telephony.CellSignalStrengthNr.class); + mCdma = in.readParcelable(CellSignalStrengthCdma.class.getClassLoader()); + mGsm = in.readParcelable(CellSignalStrengthGsm.class.getClassLoader()); + mWcdma = in.readParcelable(CellSignalStrengthWcdma.class.getClassLoader()); + mTdscdma = in.readParcelable(CellSignalStrengthTdscdma.class.getClassLoader()); + mLte = in.readParcelable(CellSignalStrengthLte.class.getClassLoader()); + mNr = in.readParcelable(CellSignalStrengthLte.class.getClassLoader()); mTimestampMillis = in.readLong(); } diff --git a/telephony/java/android/telephony/ThermalMitigationRequest.java b/telephony/java/android/telephony/ThermalMitigationRequest.java index a0676ea63711..91ad9c3e1f51 100644 --- a/telephony/java/android/telephony/ThermalMitigationRequest.java +++ b/telephony/java/android/telephony/ThermalMitigationRequest.java @@ -100,7 +100,7 @@ public final class ThermalMitigationRequest implements Parcelable { private ThermalMitigationRequest(Parcel in) { mThermalMitigationAction = in.readInt(); - mDataThrottlingRequest = in.readParcelable(DataThrottlingRequest.class.getClassLoader(), android.telephony.DataThrottlingRequest.class); + mDataThrottlingRequest = in.readParcelable(DataThrottlingRequest.class.getClassLoader()); } /** diff --git a/telephony/java/android/telephony/UiccCardInfo.java b/telephony/java/android/telephony/UiccCardInfo.java index 74f9c876cc40..464389b84945 100644 --- a/telephony/java/android/telephony/UiccCardInfo.java +++ b/telephony/java/android/telephony/UiccCardInfo.java @@ -156,11 +156,9 @@ public final class UiccCardInfo implements Parcelable { @Nullable @Deprecated public String getIccId() { - // Temporarily bypassing exception - // TODO: add exception once refactoring completed. - //if (mIccIdAccessRestricted) { - // throw new UnsupportedOperationException("getIccId from UiccPortInfo"); - //} + if (mIccIdAccessRestricted) { + throw new UnsupportedOperationException("getIccId from UiccPortInfo"); + } //always return ICCID from first port. return getPorts().stream().findFirst().get().getIccId(); } diff --git a/telephony/java/android/telephony/UiccSlotInfo.java b/telephony/java/android/telephony/UiccSlotInfo.java index a8668e7d77d4..2b1c8c863eb6 100644 --- a/telephony/java/android/telephony/UiccSlotInfo.java +++ b/telephony/java/android/telephony/UiccSlotInfo.java @@ -159,11 +159,9 @@ public class UiccSlotInfo implements Parcelable { */ @Deprecated public boolean getIsActive() { - // Temporarily bypassing exception - // TODO: add exception once refactoring completed. - //if (mLogicalSlotAccessRestricted) { - // throw new UnsupportedOperationException("get port status from UiccPortInfo"); - //} + if (mLogicalSlotAccessRestricted) { + throw new UnsupportedOperationException("get port status from UiccPortInfo"); + } //always return status from first port. return getPorts().stream().findFirst().get().isActive(); } @@ -198,11 +196,9 @@ public class UiccSlotInfo implements Parcelable { */ @Deprecated public int getLogicalSlotIdx() { - // Temporarily bypassing exception - // TODO: add exception once refactoring completed. - //if (mLogicalSlotAccessRestricted) { - // throw new UnsupportedOperationException("get logical slot index from UiccPortInfo"); - //} + if (mLogicalSlotAccessRestricted) { + throw new UnsupportedOperationException("get logical slot index from UiccPortInfo"); + } //always return logical slot index from first port. //portList always have at least one element. return getPorts().stream().findFirst().get().getLogicalSlotIndex(); diff --git a/telephony/java/android/telephony/VisualVoicemailSms.java b/telephony/java/android/telephony/VisualVoicemailSms.java index bec715e3b81a..085f8823b840 100644 --- a/telephony/java/android/telephony/VisualVoicemailSms.java +++ b/telephony/java/android/telephony/VisualVoicemailSms.java @@ -121,7 +121,7 @@ public final class VisualVoicemailSms implements Parcelable { @Override public VisualVoicemailSms createFromParcel(Parcel in) { return new Builder() - .setPhoneAccountHandle((PhoneAccountHandle) in.readParcelable(null, android.telecom.PhoneAccountHandle.class)) + .setPhoneAccountHandle((PhoneAccountHandle) in.readParcelable(null)) .setPrefix(in.readString()) .setFields(in.readBundle()) .setMessageBody(in.readString()) diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java index 4ff59b568657..977fe33988d6 100644 --- a/telephony/java/android/telephony/data/ApnSetting.java +++ b/telephony/java/android/telephony/data/ApnSetting.java @@ -1629,7 +1629,7 @@ public class ApnSetting implements Parcelable { .setApnName(in.readString()) .setProxyAddress(in.readString()) .setProxyPort(in.readInt()) - .setMmsc(in.readParcelable(Uri.class.getClassLoader(), android.net.Uri.class)) + .setMmsc(in.readParcelable(Uri.class.getClassLoader())) .setMmsProxyAddress(in.readString()) .setMmsProxyPort(in.readInt()) .setUser(in.readString()) diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java index ae0d4e7e3b4e..ef02589abaf8 100644 --- a/telephony/java/android/telephony/data/DataCallResponse.java +++ b/telephony/java/android/telephony/data/DataCallResponse.java @@ -241,24 +241,24 @@ public final class DataCallResponse implements Parcelable { mProtocolType = source.readInt(); mInterfaceName = source.readString(); mAddresses = new ArrayList<>(); - source.readList(mAddresses, LinkAddress.class.getClassLoader(), android.net.LinkAddress.class); + source.readList(mAddresses, LinkAddress.class.getClassLoader()); mDnsAddresses = new ArrayList<>(); - source.readList(mDnsAddresses, InetAddress.class.getClassLoader(), java.net.InetAddress.class); + source.readList(mDnsAddresses, InetAddress.class.getClassLoader()); mGatewayAddresses = new ArrayList<>(); - source.readList(mGatewayAddresses, InetAddress.class.getClassLoader(), java.net.InetAddress.class); + source.readList(mGatewayAddresses, InetAddress.class.getClassLoader()); mPcscfAddresses = new ArrayList<>(); - source.readList(mPcscfAddresses, InetAddress.class.getClassLoader(), java.net.InetAddress.class); + source.readList(mPcscfAddresses, InetAddress.class.getClassLoader()); mMtu = source.readInt(); mMtuV4 = source.readInt(); mMtuV6 = source.readInt(); mHandoverFailureMode = source.readInt(); mPduSessionId = source.readInt(); - mDefaultQos = source.readParcelable(Qos.class.getClassLoader(), android.telephony.data.Qos.class); + mDefaultQos = source.readParcelable(Qos.class.getClassLoader()); mQosBearerSessions = new ArrayList<>(); - source.readList(mQosBearerSessions, QosBearerSession.class.getClassLoader(), android.telephony.data.QosBearerSession.class); - mSliceInfo = source.readParcelable(NetworkSliceInfo.class.getClassLoader(), android.telephony.data.NetworkSliceInfo.class); + source.readList(mQosBearerSessions, QosBearerSession.class.getClassLoader()); + mSliceInfo = source.readParcelable(NetworkSliceInfo.class.getClassLoader()); mTrafficDescriptors = new ArrayList<>(); - source.readList(mTrafficDescriptors, TrafficDescriptor.class.getClassLoader(), android.telephony.data.TrafficDescriptor.class); + source.readList(mTrafficDescriptors, TrafficDescriptor.class.getClassLoader()); } /** diff --git a/telephony/java/android/telephony/data/DataProfile.java b/telephony/java/android/telephony/data/DataProfile.java index a166a5d6404c..ec04c1ae9522 100644 --- a/telephony/java/android/telephony/data/DataProfile.java +++ b/telephony/java/android/telephony/data/DataProfile.java @@ -107,8 +107,8 @@ public final class DataProfile implements Parcelable { private DataProfile(Parcel source) { mType = source.readInt(); - mApnSetting = source.readParcelable(ApnSetting.class.getClassLoader(), android.telephony.data.ApnSetting.class); - mTrafficDescriptor = source.readParcelable(TrafficDescriptor.class.getClassLoader(), android.telephony.data.TrafficDescriptor.class); + mApnSetting = source.readParcelable(ApnSetting.class.getClassLoader()); + mTrafficDescriptor = source.readParcelable(TrafficDescriptor.class.getClassLoader()); mPreferred = source.readBoolean(); mSetupTimestamp = source.readLong(); } diff --git a/telephony/java/android/telephony/data/Qos.java b/telephony/java/android/telephony/data/Qos.java index 9c2a3bb1e15c..8c437c83e196 100644 --- a/telephony/java/android/telephony/data/Qos.java +++ b/telephony/java/android/telephony/data/Qos.java @@ -136,8 +136,8 @@ public abstract class Qos { protected Qos(@NonNull Parcel source) { type = source.readInt(); - downlink = source.readParcelable(QosBandwidth.class.getClassLoader(), android.telephony.data.Qos.QosBandwidth.class); - uplink = source.readParcelable(QosBandwidth.class.getClassLoader(), android.telephony.data.Qos.QosBandwidth.class); + downlink = source.readParcelable(QosBandwidth.class.getClassLoader()); + uplink = source.readParcelable(QosBandwidth.class.getClassLoader()); } /** diff --git a/telephony/java/android/telephony/data/QosBearerFilter.java b/telephony/java/android/telephony/data/QosBearerFilter.java index 5a7189f44552..d6f0cb02f0aa 100644 --- a/telephony/java/android/telephony/data/QosBearerFilter.java +++ b/telephony/java/android/telephony/data/QosBearerFilter.java @@ -18,6 +18,7 @@ package android.telephony.data; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.net.LinkAddress; import android.os.Parcel; import android.os.Parcelable; @@ -35,11 +36,10 @@ import java.util.Objects; * @hide */ public final class QosBearerFilter implements Parcelable { - - private List<LinkAddress> localAddresses; - private List<LinkAddress> remoteAddresses; - private PortRange localPort; - private PortRange remotePort; + private @NonNull List<LinkAddress> localAddresses; + private @NonNull List<LinkAddress> remoteAddresses; + private @Nullable PortRange localPort; + private @Nullable PortRange remotePort; /** @hide */ @Retention(RetentionPolicy.SOURCE) @@ -56,13 +56,12 @@ public final class QosBearerFilter implements Parcelable { public static final int QOS_PROTOCOL_AH = android.hardware.radio.V1_6.QosProtocol.AH; public static final int QOS_MIN_PORT = android.hardware.radio.V1_6.QosPortRange.MIN; /** - * Hardcoded inplace of android.hardware.radio.V1_6.QosPortRange.MAX as it + * Hardcoded in place of android.hardware.radio.V1_6.QosPortRange.MAX as it * returns -1 due to uint16_t to int conversion in java. (TODO: Fix the HAL) */ public static final int QOS_MAX_PORT = 65535; // android.hardware.radio.V1_6.QosPortRange.MIN; - @QosProtocol - private int protocol; + private @QosProtocol int protocol; private int typeOfServiceMask; @@ -85,8 +84,7 @@ public final class QosBearerFilter implements Parcelable { public static final int QOS_FILTER_DIRECTION_BIDIRECTIONAL = android.hardware.radio.V1_6.QosFilterDirection.BIDIRECTIONAL; - @QosBearerFilterDirection - private int filterDirection; + private @QosBearerFilterDirection int filterDirection; /** * Specified the order in which the filter needs to be matched. @@ -94,9 +92,10 @@ public final class QosBearerFilter implements Parcelable { */ private int precedence; - public QosBearerFilter(List<LinkAddress> localAddresses, List<LinkAddress> remoteAddresses, - PortRange localPort, PortRange remotePort, int protocol, int tos, - long flowLabel, long spi, int direction, int precedence) { + public QosBearerFilter(@NonNull List<LinkAddress> localAddresses, + @NonNull List<LinkAddress> remoteAddresses, @Nullable PortRange localPort, + @Nullable PortRange remotePort, @QosProtocol int protocol, int tos, long flowLabel, + long spi, @QosBearerFilterDirection int direction, int precedence) { this.localAddresses = new ArrayList<>(); this.localAddresses.addAll(localAddresses); this.remoteAddresses = new ArrayList<>(); @@ -111,19 +110,19 @@ public final class QosBearerFilter implements Parcelable { this.precedence = precedence; } - public List<LinkAddress> getLocalAddresses() { + public @NonNull List<LinkAddress> getLocalAddresses() { return localAddresses; } - public List<LinkAddress> getRemoteAddresses() { + public @NonNull List<LinkAddress> getRemoteAddresses() { return remoteAddresses; } - public PortRange getLocalPortRange() { + public @Nullable PortRange getLocalPortRange() { return localPort; } - public PortRange getRemotePortRange() { + public @Nullable PortRange getRemotePortRange() { return remotePort; } @@ -245,8 +244,8 @@ public final class QosBearerFilter implements Parcelable { && localAddresses.containsAll(other.localAddresses) && remoteAddresses.size() == other.remoteAddresses.size() && remoteAddresses.containsAll(other.remoteAddresses) - && localPort.equals(other.localPort) - && remotePort.equals(other.remotePort) + && Objects.equals(localPort, other.localPort) + && Objects.equals(remotePort, other.remotePort) && protocol == other.protocol && typeOfServiceMask == other.typeOfServiceMask && flowLabel == other.flowLabel @@ -257,11 +256,11 @@ public final class QosBearerFilter implements Parcelable { private QosBearerFilter(Parcel source) { localAddresses = new ArrayList<>(); - source.readList(localAddresses, LinkAddress.class.getClassLoader(), android.net.LinkAddress.class); + source.readList(localAddresses, LinkAddress.class.getClassLoader()); remoteAddresses = new ArrayList<>(); - source.readList(remoteAddresses, LinkAddress.class.getClassLoader(), android.net.LinkAddress.class); - localPort = source.readParcelable(PortRange.class.getClassLoader(), android.telephony.data.QosBearerFilter.PortRange.class); - remotePort = source.readParcelable(PortRange.class.getClassLoader(), android.telephony.data.QosBearerFilter.PortRange.class); + source.readList(remoteAddresses, LinkAddress.class.getClassLoader()); + localPort = source.readParcelable(PortRange.class.getClassLoader()); + remotePort = source.readParcelable(PortRange.class.getClassLoader()); protocol = source.readInt(); typeOfServiceMask = source.readInt(); flowLabel = source.readLong(); diff --git a/telephony/java/android/telephony/data/QosBearerSession.java b/telephony/java/android/telephony/data/QosBearerSession.java index dd080856d450..ffeb08a17584 100644 --- a/telephony/java/android/telephony/data/QosBearerSession.java +++ b/telephony/java/android/telephony/data/QosBearerSession.java @@ -46,9 +46,9 @@ public final class QosBearerSession implements Parcelable{ private QosBearerSession(Parcel source) { qosBearerSessionId = source.readInt(); - qos = source.readParcelable(Qos.class.getClassLoader(), android.telephony.data.Qos.class); + qos = source.readParcelable(Qos.class.getClassLoader()); qosBearerFilterList = new ArrayList<>(); - source.readList(qosBearerFilterList, QosBearerFilter.class.getClassLoader(), android.telephony.data.QosBearerFilter.class); + source.readList(qosBearerFilterList, QosBearerFilter.class.getClassLoader()); } public int getQosBearerSessionId() { diff --git a/telephony/java/android/telephony/gba/GbaAuthRequest.java b/telephony/java/android/telephony/gba/GbaAuthRequest.java index 2c6021a18ea2..5366e9af3147 100644 --- a/telephony/java/android/telephony/gba/GbaAuthRequest.java +++ b/telephony/java/android/telephony/gba/GbaAuthRequest.java @@ -120,7 +120,7 @@ public final class GbaAuthRequest implements Parcelable { int token = in.readInt(); int subId = in.readInt(); int appType = in.readInt(); - Uri nafUrl = in.readParcelable(GbaAuthRequest.class.getClassLoader(), android.net.Uri.class); + Uri nafUrl = in.readParcelable(GbaAuthRequest.class.getClassLoader()); int len = in.readInt(); byte[] protocol = new byte[len]; in.readByteArray(protocol); diff --git a/telephony/java/android/telephony/ims/DelegateRequest.java b/telephony/java/android/telephony/ims/DelegateRequest.java index c5c92009ee32..c322d924182a 100644 --- a/telephony/java/android/telephony/ims/DelegateRequest.java +++ b/telephony/java/android/telephony/ims/DelegateRequest.java @@ -63,7 +63,7 @@ public final class DelegateRequest implements Parcelable { */ private DelegateRequest(Parcel in) { mFeatureTags = new ArrayList<>(); - in.readList(mFeatureTags, null /*classLoader*/, java.lang.String.class); + in.readList(mFeatureTags, null /*classLoader*/); } @Override diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java index 7e2d80eb871c..8a665dc92421 100644 --- a/telephony/java/android/telephony/ims/ImsCallProfile.java +++ b/telephony/java/android/telephony/ims/ImsCallProfile.java @@ -843,7 +843,7 @@ public final class ImsCallProfile implements Parcelable { mServiceType = in.readInt(); mCallType = in.readInt(); mCallExtras = in.readBundle(); - mMediaProfile = in.readParcelable(ImsStreamMediaProfile.class.getClassLoader(), android.telephony.ims.ImsStreamMediaProfile.class); + mMediaProfile = in.readParcelable(ImsStreamMediaProfile.class.getClassLoader()); mEmergencyServiceCategories = in.readInt(); mEmergencyUrns = in.createStringArrayList(); mEmergencyCallRouting = in.readInt(); @@ -851,7 +851,8 @@ public final class ImsCallProfile implements Parcelable { mHasKnownUserIntentEmergency = in.readBoolean(); mRestrictCause = in.readInt(); mCallerNumberVerificationStatus = in.readInt(); - Object[] accepted = in.readArray(RtpHeaderExtensionType.class.getClassLoader()); + Object[] accepted = in.readArray(RtpHeaderExtensionType.class.getClassLoader(), + RtpHeaderExtensionType.class); mAcceptedRtpHeaderExtensionTypes = Arrays.stream(accepted) .map(o -> (RtpHeaderExtensionType) o).collect(Collectors.toSet()); } diff --git a/telephony/java/android/telephony/ims/ImsConferenceState.java b/telephony/java/android/telephony/ims/ImsConferenceState.java index d4d8c44196d5..1fa5f52968e5 100644 --- a/telephony/java/android/telephony/ims/ImsConferenceState.java +++ b/telephony/java/android/telephony/ims/ImsConferenceState.java @@ -133,7 +133,7 @@ public final class ImsConferenceState implements Parcelable { for (int i = 0; i < size; ++i) { String user = in.readString(); - Bundle state = in.readParcelable(null, android.os.Bundle.class); + Bundle state = in.readParcelable(null); mParticipants.put(user, state); } } diff --git a/telephony/java/android/telephony/ims/ImsExternalCallState.java b/telephony/java/android/telephony/ims/ImsExternalCallState.java index d45110772ce4..c663e393fe06 100644 --- a/telephony/java/android/telephony/ims/ImsExternalCallState.java +++ b/telephony/java/android/telephony/ims/ImsExternalCallState.java @@ -141,8 +141,8 @@ public final class ImsExternalCallState implements Parcelable { public ImsExternalCallState(Parcel in) { mCallId = in.readInt(); ClassLoader classLoader = ImsExternalCallState.class.getClassLoader(); - mAddress = in.readParcelable(classLoader, android.net.Uri.class); - mLocalAddress = in.readParcelable(classLoader, android.net.Uri.class); + mAddress = in.readParcelable(classLoader); + mLocalAddress = in.readParcelable(classLoader); mIsPullable = (in.readInt() != 0); mCallState = in.readInt(); mCallType = in.readInt(); diff --git a/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java index b77d3063e2cc..ccb3231526dd 100644 --- a/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java +++ b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java @@ -153,7 +153,7 @@ public final class ImsRegistrationAttributes implements Parcelable { mTransportType = source.readInt(); mImsAttributeFlags = source.readInt(); mFeatureTags = new ArrayList<>(); - source.readList(mFeatureTags, null /*classloader*/, java.lang.String.class); + source.readList(mFeatureTags, null /*classloader*/); } /** diff --git a/telephony/java/android/telephony/ims/ImsSsData.java b/telephony/java/android/telephony/ims/ImsSsData.java index 9f4b77e22dd7..868dea6a3121 100644 --- a/telephony/java/android/telephony/ims/ImsSsData.java +++ b/telephony/java/android/telephony/ims/ImsSsData.java @@ -365,8 +365,8 @@ public final class ImsSsData implements Parcelable { serviceClass = in.readInt(); result = in.readInt(); mSsInfo = in.createIntArray(); - mCfInfo = in.readParcelableList(new ArrayList<>(), this.getClass().getClassLoader(), android.telephony.ims.ImsCallForwardInfo.class); - mImsSsInfo = in.readParcelableList(new ArrayList<>(), this.getClass().getClassLoader(), android.telephony.ims.ImsSsInfo.class); + mCfInfo = in.readParcelableList(new ArrayList<>(), this.getClass().getClassLoader()); + mImsSsInfo = in.readParcelableList(new ArrayList<>(), this.getClass().getClassLoader()); } public static final @android.annotation.NonNull Creator<ImsSsData> CREATOR = new Creator<ImsSsData>() { diff --git a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java index 6a6c3063483e..9c28c36521f5 100644 --- a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java +++ b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java @@ -439,13 +439,13 @@ public final class RcsContactPresenceTuple implements Parcelable { } private RcsContactPresenceTuple(Parcel in) { - mContactUri = in.readParcelable(Uri.class.getClassLoader(), android.net.Uri.class); + mContactUri = in.readParcelable(Uri.class.getClassLoader()); mTimestamp = convertStringFormatTimeToInstant(in.readString()); mStatus = in.readString(); mServiceId = in.readString(); mServiceVersion = in.readString(); mServiceDescription = in.readString(); - mServiceCapabilities = in.readParcelable(ServiceCapabilities.class.getClassLoader(), android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities.class); + mServiceCapabilities = in.readParcelable(ServiceCapabilities.class.getClassLoader()); } @Override diff --git a/telephony/java/android/telephony/ims/RcsContactTerminatedReason.java b/telephony/java/android/telephony/ims/RcsContactTerminatedReason.java index ea022de3bc01..ee02564267c0 100644 --- a/telephony/java/android/telephony/ims/RcsContactTerminatedReason.java +++ b/telephony/java/android/telephony/ims/RcsContactTerminatedReason.java @@ -37,7 +37,7 @@ public final class RcsContactTerminatedReason implements Parcelable { } private RcsContactTerminatedReason(Parcel in) { - mContactUri = in.readParcelable(Uri.class.getClassLoader(), android.net.Uri.class); + mContactUri = in.readParcelable(Uri.class.getClassLoader()); mReason = in.readString(); } diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java index 0f1b3695270b..91121187a19a 100644 --- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java +++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java @@ -244,14 +244,14 @@ public final class RcsContactUceCapability implements Parcelable { } private RcsContactUceCapability(Parcel in) { - mContactUri = in.readParcelable(Uri.class.getClassLoader(), android.net.Uri.class); + mContactUri = in.readParcelable(Uri.class.getClassLoader()); mCapabilityMechanism = in.readInt(); mSourceType = in.readInt(); mRequestResult = in.readInt(); List<String> featureTagList = new ArrayList<>(); in.readStringList(featureTagList); mFeatureTags.addAll(featureTagList); - in.readParcelableList(mPresenceTuples, RcsContactPresenceTuple.class.getClassLoader(), android.telephony.ims.RcsContactPresenceTuple.class); + in.readParcelableList(mPresenceTuples, RcsContactPresenceTuple.class.getClassLoader()); } @Override diff --git a/telephony/java/android/telephony/ims/RtpHeaderExtensionType.java b/telephony/java/android/telephony/ims/RtpHeaderExtensionType.java index b9ffd247f658..af4e23476331 100644 --- a/telephony/java/android/telephony/ims/RtpHeaderExtensionType.java +++ b/telephony/java/android/telephony/ims/RtpHeaderExtensionType.java @@ -63,7 +63,7 @@ public final class RtpHeaderExtensionType implements Parcelable { private RtpHeaderExtensionType(Parcel in) { mLocalIdentifier = in.readInt(); - mUri = in.readParcelable(Uri.class.getClassLoader(), android.net.Uri.class); + mUri = in.readParcelable(Uri.class.getClassLoader()); } public static final @NonNull Creator<RtpHeaderExtensionType> CREATOR = diff --git a/telephony/java/android/telephony/ims/SipDelegateConfiguration.java b/telephony/java/android/telephony/ims/SipDelegateConfiguration.java index db0ae033713e..1bf5cad49c53 100644 --- a/telephony/java/android/telephony/ims/SipDelegateConfiguration.java +++ b/telephony/java/android/telephony/ims/SipDelegateConfiguration.java @@ -573,7 +573,7 @@ public final class SipDelegateConfiguration implements Parcelable { mPrivateUserIdentifier = source.readString(); mHomeDomain = source.readString(); mImei = source.readString(); - mGruu = source.readParcelable(null, android.net.Uri.class); + mGruu = source.readParcelable(null); mSipAuthHeader = source.readString(); mSipAuthNonce = source.readString(); mServiceRouteHeader = source.readString(); diff --git a/telephony/java/android/telephony/ims/SipMessage.java b/telephony/java/android/telephony/ims/SipMessage.java index 391372ac8ac1..acc62435e07c 100644 --- a/telephony/java/android/telephony/ims/SipMessage.java +++ b/telephony/java/android/telephony/ims/SipMessage.java @@ -57,7 +57,7 @@ public final class SipMessage implements Parcelable { * @param startLine The start line of the message, containing either the request-line or * status-line. * @param headerSection A String containing the full unencoded SIP message header. - * @param content UTF-8 encoded SIP message body. + * @param content SIP message body. */ public SipMessage(@NonNull String startLine, @NonNull String headerSection, @NonNull byte[] content) { @@ -105,7 +105,7 @@ public final class SipMessage implements Parcelable { } /** - * @return only the UTF-8 encoded SIP message body. + * @return the SIP message body. */ public @NonNull byte[] getContent() { return mContent; diff --git a/telephony/java/android/telephony/mbms/DownloadRequest.java b/telephony/java/android/telephony/mbms/DownloadRequest.java index 81d5be8562b6..eb59f87a6c02 100644 --- a/telephony/java/android/telephony/mbms/DownloadRequest.java +++ b/telephony/java/android/telephony/mbms/DownloadRequest.java @@ -242,8 +242,8 @@ public final class DownloadRequest implements Parcelable { private DownloadRequest(Parcel in) { fileServiceId = in.readString(); - sourceUri = in.readParcelable(getClass().getClassLoader(), android.net.Uri.class); - destinationUri = in.readParcelable(getClass().getClassLoader(), android.net.Uri.class); + sourceUri = in.readParcelable(getClass().getClassLoader()); + destinationUri = in.readParcelable(getClass().getClassLoader()); subscriptionId = in.readInt(); serializedResultIntentForApp = in.readString(); version = in.readInt(); diff --git a/telephony/java/android/telephony/mbms/FileInfo.java b/telephony/java/android/telephony/mbms/FileInfo.java index ffd864ebda93..e52b2ce0c505 100644 --- a/telephony/java/android/telephony/mbms/FileInfo.java +++ b/telephony/java/android/telephony/mbms/FileInfo.java @@ -55,7 +55,7 @@ public final class FileInfo implements Parcelable { } private FileInfo(Parcel in) { - uri = in.readParcelable(null, android.net.Uri.class); + uri = in.readParcelable(null); mimeType = in.readString(); } diff --git a/telephony/java/android/telephony/mbms/FileServiceInfo.java b/telephony/java/android/telephony/mbms/FileServiceInfo.java index 0fc3be6de929..8777e7f59e3f 100644 --- a/telephony/java/android/telephony/mbms/FileServiceInfo.java +++ b/telephony/java/android/telephony/mbms/FileServiceInfo.java @@ -58,7 +58,7 @@ public final class FileServiceInfo extends ServiceInfo implements Parcelable { FileServiceInfo(Parcel in) { super(in); files = new ArrayList<FileInfo>(); - in.readList(files, FileInfo.class.getClassLoader(), android.telephony.mbms.FileInfo.class); + in.readList(files, FileInfo.class.getClassLoader()); } @Override diff --git a/telephony/java/android/telephony/mbms/ServiceInfo.java b/telephony/java/android/telephony/mbms/ServiceInfo.java index 02424ff75c82..f78e7a6e54c4 100644 --- a/telephony/java/android/telephony/mbms/ServiceInfo.java +++ b/telephony/java/android/telephony/mbms/ServiceInfo.java @@ -80,7 +80,7 @@ public class ServiceInfo { } names = new HashMap(mapCount); while (mapCount-- > 0) { - Locale locale = (java.util.Locale) in.readSerializable(java.util.Locale.class.getClassLoader(), java.util.Locale.class); + Locale locale = (java.util.Locale) in.readSerializable(); String name = in.readString(); names.put(locale, name); } @@ -91,12 +91,12 @@ public class ServiceInfo { } locales = new ArrayList<Locale>(localesCount); while (localesCount-- > 0) { - Locale l = (java.util.Locale) in.readSerializable(java.util.Locale.class.getClassLoader(), java.util.Locale.class); + Locale l = (java.util.Locale) in.readSerializable(); locales.add(l); } serviceId = in.readString(); - sessionStartTime = (java.util.Date) in.readSerializable(java.util.Date.class.getClassLoader(), java.util.Date.class); - sessionEndTime = (java.util.Date) in.readSerializable(java.util.Date.class.getClassLoader(), java.util.Date.class); + sessionStartTime = (java.util.Date) in.readSerializable(); + sessionEndTime = (java.util.Date) in.readSerializable(); } /** @hide */ diff --git a/telephony/java/android/telephony/mbms/UriPathPair.java b/telephony/java/android/telephony/mbms/UriPathPair.java index 54d9d9e5284e..9258919919b7 100644 --- a/telephony/java/android/telephony/mbms/UriPathPair.java +++ b/telephony/java/android/telephony/mbms/UriPathPair.java @@ -48,8 +48,8 @@ public final class UriPathPair implements Parcelable { /** @hide */ private UriPathPair(Parcel in) { - mFilePathUri = in.readParcelable(Uri.class.getClassLoader(), android.net.Uri.class); - mContentUri = in.readParcelable(Uri.class.getClassLoader(), android.net.Uri.class); + mFilePathUri = in.readParcelable(Uri.class.getClassLoader()); + mContentUri = in.readParcelable(Uri.class.getClassLoader()); } public static final @android.annotation.NonNull Creator<UriPathPair> CREATOR = new Creator<UriPathPair>() { diff --git a/telephony/java/com/android/internal/telephony/NetworkScanResult.java b/telephony/java/com/android/internal/telephony/NetworkScanResult.java index 8b49f4b4593c..d07d77ca742a 100644 --- a/telephony/java/com/android/internal/telephony/NetworkScanResult.java +++ b/telephony/java/com/android/internal/telephony/NetworkScanResult.java @@ -83,7 +83,7 @@ public final class NetworkScanResult implements Parcelable { scanStatus = in.readInt(); scanError = in.readInt(); List<CellInfo> ni = new ArrayList<>(); - in.readParcelableList(ni, Object.class.getClassLoader(), android.telephony.CellInfo.class); + in.readParcelableList(ni, Object.class.getClassLoader()); networkInfos = ni; } diff --git a/telephony/java/com/android/internal/telephony/OperatorInfo.java b/telephony/java/com/android/internal/telephony/OperatorInfo.java index 1820a1dc4d6c..a6f0f667d0cd 100644 --- a/telephony/java/com/android/internal/telephony/OperatorInfo.java +++ b/telephony/java/com/android/internal/telephony/OperatorInfo.java @@ -189,7 +189,7 @@ public class OperatorInfo implements Parcelable { in.readString(), /*operatorAlphaLong*/ in.readString(), /*operatorAlphaShort*/ in.readString(), /*operatorNumeric*/ - (State) in.readSerializable(com.android.internal.telephony.OperatorInfo.State.class.getClassLoader(), com.android.internal.telephony.OperatorInfo.State.class), /*state*/ + (State) in.readSerializable(), /*state*/ in.readInt()); /*ran*/ return opInfo; } diff --git a/tests/AppLaunchWear/Android.bp b/tests/AppLaunchWear/Android.bp deleted file mode 100644 index e2fc4735a7c2..000000000000 --- a/tests/AppLaunchWear/Android.bp +++ /dev/null @@ -1,22 +0,0 @@ -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_base_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_base_license"], -} - -android_test { - name: "AppLaunchWear", - // Only compile source java files in this apk. - srcs: ["src/**/*.java"], - platform_apis: true, - certificate: "platform", - libs: [ - "android.test.base", - "android.test.runner", - ], - static_libs: ["androidx.test.rules"], - test_suites: ["device-tests"], -} diff --git a/tests/AppLaunchWear/AndroidManifest.xml b/tests/AppLaunchWear/AndroidManifest.xml deleted file mode 100644 index 7dfd7bafbaaa..000000000000 --- a/tests/AppLaunchWear/AndroidManifest.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.tests.applaunch" - android:sharedUserId="android.uid.system" > - - <uses-permission android:name="android.permission.REAL_GET_TASKS" /> - <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> - - <uses-sdk - android:minSdkVersion="22" - android:targetSdkVersion="24" /> - - <instrumentation android:label="Measure app start up time" - android:name="android.test.InstrumentationTestRunner" - android:targetPackage="com.android.tests.applaunch" /> - - <application android:label="App Launch Test"> - <uses-library android:name="android.test.runner" /> - </application> -</manifest> diff --git a/tests/AppLaunchWear/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunchWear/src/com/android/tests/applaunch/AppLaunch.java deleted file mode 100644 index 97701c61011e..000000000000 --- a/tests/AppLaunchWear/src/com/android/tests/applaunch/AppLaunch.java +++ /dev/null @@ -1,864 +0,0 @@ -/* - * Copyright (C) 2013 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.tests.applaunch; - -import android.accounts.Account; -import android.accounts.AccountManager; -import android.app.ActivityManager; -import android.app.ActivityManager.ProcessErrorStateInfo; -import android.app.IActivityManager; -import android.app.UiAutomation; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.ResolveInfo; -import android.os.Bundle; -import android.os.ParcelFileDescriptor; -import android.os.RemoteException; -import android.os.UserHandle; -import android.test.InstrumentationTestCase; -import android.test.InstrumentationTestRunner; -import android.util.Log; - -import androidx.test.rule.logging.AtraceLogger; - -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * This test is intended to measure the time it takes for the apps to start. - * Names of the applications are passed in command line, and the - * test starts each application, and reports the start up time in milliseconds. - * The instrumentation expects the following key to be passed on the command line: - * apps - A list of applications to start and their corresponding result keys - * in the following format: - * -e apps <app name>^<result key>|<app name>^<result key> - */ -public class AppLaunch extends InstrumentationTestCase { - - private static final int JOIN_TIMEOUT = 10000; - private static final String TAG = AppLaunch.class.getSimpleName(); - - // optional parameter: comma separated list of required account types before proceeding - // with the app launch - private static final String KEY_REQUIRED_ACCOUNTS = "required_accounts"; - private static final String KEY_APPS = "apps"; - private static final String KEY_TRIAL_LAUNCH = "trial_launch"; - private static final String KEY_LAUNCH_ITERATIONS = "launch_iterations"; - private static final String KEY_LAUNCH_ORDER = "launch_order"; - private static final String KEY_DROP_CACHE = "drop_cache"; - private static final String KEY_SIMPLEPERF_CMD = "simpleperf_cmd"; - private static final String KEY_SIMPLEPERF_APP = "simpleperf_app"; - private static final String KEY_TRACE_ITERATIONS = "trace_iterations"; - private static final String KEY_LAUNCH_DIRECTORY = "launch_directory"; - private static final String KEY_TRACE_DIRECTORY = "trace_directory"; - private static final String KEY_TRACE_CATEGORY = "trace_categories"; - private static final String KEY_TRACE_BUFFERSIZE = "trace_bufferSize"; - private static final String KEY_TRACE_DUMPINTERVAL = "tracedump_interval"; - private static final String KEY_COMPILER_FILTERS = "compiler_filters"; - - private static final String SIMPLEPERF_APP_CMD = - "simpleperf --log fatal stat --csv -e cpu-cycles,major-faults --app %s & %s"; - private static final String WEARABLE_ACTION_GOOGLE = - "com.google.android.wearable.action.GOOGLE"; - private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 5000; // 5s to allow app to idle - private static final int POST_LAUNCH_IDLE_TIMEOUT = 750; // 750ms idle for non initial launches - private static final int BETWEEN_LAUNCH_SLEEP_TIMEOUT = 5000; // 5s between launching apps - private static final String LAUNCH_SUB_DIRECTORY = "launch_logs"; - private static final String LAUNCH_FILE = "applaunch.txt"; - private static final String TRACE_SUB_DIRECTORY = "atrace_logs"; - private static final String DEFAULT_TRACE_CATEGORIES = - "sched,freq,gfx,view,dalvik,webview,input,wm,disk,am,wm"; - private static final String DEFAULT_TRACE_BUFFER_SIZE = "20000"; - private static final String DEFAULT_TRACE_DUMP_INTERVAL = "10"; - private static final String TRIAL_LAUNCH = "TRIAL_LAUNCH"; - private static final String DELIMITER = ","; - private static final String DROP_CACHE_SCRIPT = "/data/local/tmp/dropCache.sh"; - private static final String APP_LAUNCH_CMD = "am start -W -n"; - private static final String SUCCESS_MESSAGE = "Status: ok"; - private static final String WARNING_MESSAGE = "Warning: Activity not started"; - private static final String COMPILE_SUCCESS = "Success"; - private static final String THIS_TIME = "ThisTime:"; - private static final String LAUNCH_ITERATION = "LAUNCH_ITERATION - %d"; - private static final String TRACE_ITERATION = "TRACE_ITERATION-%d"; - private static final String LAUNCH_ITERATION_PREFIX = "LAUNCH_ITERATION"; - private static final String TRACE_ITERATION_PREFIX = "TRACE_ITERATION"; - private static final String LAUNCH_ORDER_CYCLIC = "cyclic"; - private static final String LAUNCH_ORDER_SEQUENTIAL = "sequential"; - private static final String COMPILE_CMD = "cmd package compile -f -m %s %s"; - private static final String SPEED_PROFILE_FILTER = "speed-profile"; - private static final String VERIFY_FILTER = "verify"; - private static final String LAUNCH_SCRIPT_NAME = "appLaunch"; - private static final String WEARABLE_HOME_PACKAGE = "com.google.android.wearable.app"; - - private Map<String, Intent> mNameToIntent; - private List<LaunchOrder> mLaunchOrderList = new ArrayList<LaunchOrder>(); - private Map<String, String> mNameToResultKey; - private Map<String, Map<String, List<AppLaunchResult>>> mNameToLaunchTime; - private IActivityManager mAm; - private String mSimplePerfCmd = null; - private String mLaunchOrder = null; - private boolean mDropCache = false; - private int mLaunchIterations = 10; - private int mTraceLaunchCount = 0; - private String mTraceDirectoryStr = null; - private Bundle mResult = new Bundle(); - private Set<String> mRequiredAccounts; - private boolean mTrialLaunch = false; - private BufferedWriter mBufferedWriter = null; - private boolean mSimplePerfAppOnly = false; - private String[] mCompilerFilters = null; - - @Override - protected void setUp() throws Exception { - super.setUp(); - getInstrumentation().getUiAutomation().setRotation(UiAutomation.ROTATION_FREEZE_0); - } - - @Override - protected void tearDown() throws Exception { - getInstrumentation().getUiAutomation().setRotation(UiAutomation.ROTATION_UNFREEZE); - super.tearDown(); - } - - private void addLaunchResult(LaunchOrder launch, AppLaunchResult result) { - mNameToLaunchTime.get(launch.getApp()).get(launch.getCompilerFilter()).add(result); - } - - private boolean hasFailureOnFirstLaunch(LaunchOrder launch) { - List<AppLaunchResult> results = - mNameToLaunchTime.get(launch.getApp()).get(launch.getCompilerFilter()); - return (results.size() > 0) && (results.get(0).mLaunchTime < 0); - } - - public void testMeasureStartUpTime() throws RemoteException, NameNotFoundException, - IOException, InterruptedException { - InstrumentationTestRunner instrumentation = - (InstrumentationTestRunner)getInstrumentation(); - Bundle args = instrumentation.getArguments(); - mAm = ActivityManager.getService(); - String launchDirectory = args.getString(KEY_LAUNCH_DIRECTORY); - - createMappings(); - parseArgs(args); - checkAccountSignIn(); - - // Root directory for applaunch file to log the app launch output - // Will be useful in case of simpleperf command is used - File launchRootDir = null; - if (null != launchDirectory && !launchDirectory.isEmpty()) { - launchRootDir = new File(launchDirectory); - if (!launchRootDir.exists() && !launchRootDir.mkdirs()) { - throw new IOException("Unable to create the destination directory"); - } - } - - try { - File launchSubDir = new File(launchRootDir, LAUNCH_SUB_DIRECTORY); - - if (!launchSubDir.exists() && !launchSubDir.mkdirs()) { - throw new IOException("Unable to create the lauch file sub directory"); - } - File file = new File(launchSubDir, LAUNCH_FILE); - FileOutputStream outputStream = new FileOutputStream(file); - mBufferedWriter = new BufferedWriter(new OutputStreamWriter( - outputStream)); - - // Root directory for trace file during the launches - File rootTrace = null; - File rootTraceSubDir = null; - int traceBufferSize = 0; - int traceDumpInterval = 0; - Set<String> traceCategoriesSet = null; - if (null != mTraceDirectoryStr && !mTraceDirectoryStr.isEmpty()) { - rootTrace = new File(mTraceDirectoryStr); - if (!rootTrace.exists() && !rootTrace.mkdirs()) { - throw new IOException("Unable to create the trace directory"); - } - rootTraceSubDir = new File(rootTrace, TRACE_SUB_DIRECTORY); - if (!rootTraceSubDir.exists() && !rootTraceSubDir.mkdirs()) { - throw new IOException("Unable to create the trace sub directory"); - } - assertNotNull("Trace iteration parameter is mandatory", - args.getString(KEY_TRACE_ITERATIONS)); - mTraceLaunchCount = Integer.parseInt(args.getString(KEY_TRACE_ITERATIONS)); - String traceCategoriesStr = args - .getString(KEY_TRACE_CATEGORY, DEFAULT_TRACE_CATEGORIES); - traceBufferSize = Integer.parseInt(args.getString(KEY_TRACE_BUFFERSIZE, - DEFAULT_TRACE_BUFFER_SIZE)); - traceDumpInterval = Integer.parseInt(args.getString(KEY_TRACE_DUMPINTERVAL, - DEFAULT_TRACE_DUMP_INTERVAL)); - traceCategoriesSet = new HashSet<String>(); - if (!traceCategoriesStr.isEmpty()) { - String[] traceCategoriesSplit = traceCategoriesStr.split(DELIMITER); - for (int i = 0; i < traceCategoriesSplit.length; i++) { - traceCategoriesSet.add(traceCategoriesSplit[i]); - } - } - } - - // Get the app launch order based on launch order, trial launch, - // launch iterations and trace iterations - setLaunchOrder(); - - for (LaunchOrder launch : mLaunchOrderList) { - if (mNameToIntent.get(launch.getApp()) == null) { - continue; - } - dropCache(); - String appPkgName = mNameToIntent.get(launch.getApp()) - .getComponent().getPackageName(); - Log.v(TAG, String.format("\nApp name: %s", launch.getApp())); - Log.v(TAG, String.format("Adding app package name: %s", appPkgName)); - // App launch times for trial launch will not be used for final - // launch time calculations. - if (launch.getLaunchReason().equals(TRIAL_LAUNCH)) { - // In the "applaunch.txt" file, trail launches is referenced using - // "TRIAL_LAUNCH" - Log.v(TAG, "Trial Launch"); - if (SPEED_PROFILE_FILTER.equals(launch.getCompilerFilter())) { - assertTrue(String.format("Not able to compile the app : %s", appPkgName), - compileApp(VERIFY_FILTER, appPkgName)); - } else if (launch.getCompilerFilter() != null) { - assertTrue(String.format("Not able to compile the app : %s", appPkgName), - compileApp(launch.getCompilerFilter(), appPkgName)); - } - // We only need to run a trial for the speed-profile filter, but we always - // run one for "applaunch.txt" consistency. - AppLaunchResult launchResult = null; - if (appPkgName.contains(WEARABLE_HOME_PACKAGE)) { - Log.v(TAG, "Home package detected. Not killing app"); - launchResult = startApp(launch.getApp(), false, launch.getLaunchReason()); - } else { - Log.v(TAG, "Will kill app before launch"); - launchResult = startApp(launch.getApp(), true, launch.getLaunchReason()); - } - if (launchResult.mLaunchTime < 0) { - addLaunchResult(launch, new AppLaunchResult()); - // simply pass the app if launch isn't successful - // error should have already been logged by startApp - continue; - } - sleep(INITIAL_LAUNCH_IDLE_TIMEOUT); - if (SPEED_PROFILE_FILTER.equals(launch.getCompilerFilter())) { - // Send SIGUSR1 to force dumping a profile. - String sendSignalCommand = - String.format("killall -s SIGUSR1 %s", appPkgName); - getInstrumentation().getUiAutomation().executeShellCommand( - sendSignalCommand); - assertTrue(String.format("Not able to compile the app : %s", appPkgName), - compileApp(launch.getCompilerFilter(), appPkgName)); - } - } - - // App launch times used for final calculation - else if (launch.getLaunchReason().contains(LAUNCH_ITERATION_PREFIX)) { - Log.v(TAG, "Launch iteration prefix."); - AppLaunchResult launchResults = null; - if (hasFailureOnFirstLaunch(launch)) { - // skip if the app has failures while launched first - continue; - } - // In the "applaunch.txt" file app launches are referenced using - // "LAUNCH_ITERATION - ITERATION NUM" - if (appPkgName.contains(WEARABLE_HOME_PACKAGE)) { - Log.v(TAG, "Home package detected. Not killing app"); - launchResults = startApp(launch.getApp(), false, launch.getLaunchReason()); - } else { - Log.v(TAG, "Will kill app before launch"); - launchResults = startApp(launch.getApp(), true, launch.getLaunchReason()); - } - if (launchResults.mLaunchTime < 0) { - addLaunchResult(launch, new AppLaunchResult()); - // if it fails once, skip the rest of the launches - continue; - } else { - addLaunchResult(launch, launchResults); - } - sleep(POST_LAUNCH_IDLE_TIMEOUT); - } - - // App launch times for trace launch will not be used for final - // launch time calculations. - else if (launch.getLaunchReason().contains(TRACE_ITERATION_PREFIX)) { - Log.v(TAG, "Trace iteration prefix"); - AtraceLogger atraceLogger = AtraceLogger - .getAtraceLoggerInstance(getInstrumentation()); - // Start the trace - try { - atraceLogger.atraceStart(traceCategoriesSet, traceBufferSize, - traceDumpInterval, rootTraceSubDir, - String.format("%s-%s", launch.getApp(), launch.getLaunchReason())); - if (appPkgName.contains(WEARABLE_HOME_PACKAGE)) { - Log.v(TAG, "Home package detected. Not killing app"); - startApp(launch.getApp(), false, launch.getLaunchReason()); - } else { - Log.v(TAG, "Will kill app before launch"); - startApp(launch.getApp(), true, launch.getLaunchReason()); - } - sleep(POST_LAUNCH_IDLE_TIMEOUT); - } finally { - // Stop the trace - atraceLogger.atraceStop(); - } - } - closeApp(launch.getApp(), true); - sleep(BETWEEN_LAUNCH_SLEEP_TIMEOUT); - } - } finally { - if (null != mBufferedWriter) { - mBufferedWriter.close(); - } - } - - for (String app : mNameToResultKey.keySet()) { - for (String compilerFilter : mCompilerFilters) { - StringBuilder launchTimes = new StringBuilder(); - StringBuilder cpuCycles = new StringBuilder(); - StringBuilder majorFaults = new StringBuilder(); - for (AppLaunchResult result : mNameToLaunchTime.get(app).get(compilerFilter)) { - launchTimes.append(result.mLaunchTime); - launchTimes.append(","); - if (mSimplePerfAppOnly) { - cpuCycles.append(result.mCpuCycles); - cpuCycles.append(","); - majorFaults.append(result.mMajorFaults); - majorFaults.append(","); - } - } - String filterName = (compilerFilter == null) ? "" : ("-" + compilerFilter); - mResult.putString(mNameToResultKey.get(app) + filterName, launchTimes.toString()); - if (mSimplePerfAppOnly) { - mResult.putString(mNameToResultKey.get(app) + filterName + "-cpuCycles", - cpuCycles.toString()); - mResult.putString(mNameToResultKey.get(app) + filterName + "-majorFaults", - majorFaults.toString()); - } - } - } - instrumentation.sendStatus(0, mResult); - } - - /** - * Compile the app package using compilerFilter and return true or false - * based on status of the compilation command. - */ - private boolean compileApp(String compilerFilter, String appPkgName) throws IOException { - try (ParcelFileDescriptor result = getInstrumentation().getUiAutomation(). - executeShellCommand(String.format(COMPILE_CMD, compilerFilter, appPkgName)); - BufferedReader bufferedReader = new BufferedReader(new InputStreamReader( - new FileInputStream(result.getFileDescriptor())))) { - String line; - while ((line = bufferedReader.readLine()) != null) { - if (line.contains(COMPILE_SUCCESS)) { - return true; - } - } - return false; - } - } - - /** - * If launch order is "cyclic" then apps will be launched one after the - * other for each iteration count. - * If launch order is "sequential" then each app will be launched for given number - * iterations at once before launching the other apps. - */ - private void setLaunchOrder() { - if (LAUNCH_ORDER_CYCLIC.equalsIgnoreCase(mLaunchOrder)) { - for (String compilerFilter : mCompilerFilters) { - if (mTrialLaunch) { - for (String app : mNameToResultKey.keySet()) { - mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, TRIAL_LAUNCH)); - } - } - for (int launchCount = 0; launchCount < mLaunchIterations; launchCount++) { - for (String app : mNameToResultKey.keySet()) { - mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, - String.format(LAUNCH_ITERATION, launchCount))); - } - } - if (mTraceDirectoryStr != null && !mTraceDirectoryStr.isEmpty()) { - for (int traceCount = 0; traceCount < mTraceLaunchCount; traceCount++) { - for (String app : mNameToResultKey.keySet()) { - mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, - String.format(TRACE_ITERATION, traceCount))); - } - } - } - } - } else if (LAUNCH_ORDER_SEQUENTIAL.equalsIgnoreCase(mLaunchOrder)) { - for (String compilerFilter : mCompilerFilters) { - for (String app : mNameToResultKey.keySet()) { - if (mTrialLaunch) { - mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, TRIAL_LAUNCH)); - } - for (int launchCount = 0; launchCount < mLaunchIterations; launchCount++) { - mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, - String.format(LAUNCH_ITERATION, launchCount))); - } - if (mTraceDirectoryStr != null && !mTraceDirectoryStr.isEmpty()) { - for (int traceCount = 0; traceCount < mTraceLaunchCount; traceCount++) { - mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, - String.format(TRACE_ITERATION, traceCount))); - } - } - } - } - } else { - assertTrue("Launch order is not valid parameter", false); - } - } - - private void dropCache() { - if (mDropCache) { - assertNotNull("Issue in dropping the cache", - getInstrumentation().getUiAutomation() - .executeShellCommand(DROP_CACHE_SCRIPT)); - } - } - - private void parseArgs(Bundle args) { - mNameToResultKey = new LinkedHashMap<String, String>(); - mNameToLaunchTime = new HashMap<>(); - String launchIterations = args.getString(KEY_LAUNCH_ITERATIONS); - if (launchIterations != null) { - mLaunchIterations = Integer.parseInt(launchIterations); - } - String appList = args.getString(KEY_APPS); - if (appList == null) - return; - - String appNames[] = appList.split("\\|"); - for (String pair : appNames) { - String[] parts = pair.split("\\^"); - if (parts.length != 2) { - Log.e(TAG, "The apps key is incorrectly formatted"); - fail(); - } - - mNameToResultKey.put(parts[0], parts[1]); - mNameToLaunchTime.put(parts[0], null); - } - String requiredAccounts = args.getString(KEY_REQUIRED_ACCOUNTS); - if (requiredAccounts != null) { - mRequiredAccounts = new HashSet<String>(); - for (String accountType : requiredAccounts.split(",")) { - mRequiredAccounts.add(accountType); - } - } - - String compilerFilterList = args.getString(KEY_COMPILER_FILTERS); - if (compilerFilterList != null) { - // If a compiler filter is passed, we make a trial launch to force compilation - // of the apps. - mTrialLaunch = true; - mCompilerFilters = compilerFilterList.split("\\|"); - } else { - // Just pass a null compiler filter to use the current state of the app. - mCompilerFilters = new String[1]; - } - - // Pre-populate the results map to avoid null checks. - for (String app : mNameToLaunchTime.keySet()) { - HashMap<String, List<AppLaunchResult>> map = new HashMap<>(); - mNameToLaunchTime.put(app, map); - for (String compilerFilter : mCompilerFilters) { - map.put(compilerFilter, new ArrayList<>()); - } - } - - mTraceDirectoryStr = args.getString(KEY_TRACE_DIRECTORY); - mDropCache = Boolean.parseBoolean(args.getString(KEY_DROP_CACHE)); - mSimplePerfCmd = args.getString(KEY_SIMPLEPERF_CMD); - mLaunchOrder = args.getString(KEY_LAUNCH_ORDER, LAUNCH_ORDER_CYCLIC); - mSimplePerfAppOnly = Boolean.parseBoolean(args.getString(KEY_SIMPLEPERF_APP)); - mTrialLaunch = mTrialLaunch || Boolean.parseBoolean(args.getString(KEY_TRIAL_LAUNCH)); - - if (mSimplePerfCmd != null && mSimplePerfAppOnly) { - Log.w(TAG, String.format("Passing both %s and %s is not supported, ignoring %s", - KEY_SIMPLEPERF_CMD, KEY_SIMPLEPERF_APP)); - } - } - - private boolean hasLeanback(Context context) { - return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK); - } - - private void createMappings() { - mNameToIntent = new LinkedHashMap<String, Intent>(); - - PackageManager pm = getInstrumentation().getContext() - .getPackageManager(); - Intent intentToResolve = new Intent(Intent.ACTION_MAIN); - intentToResolve.addCategory(hasLeanback(getInstrumentation().getContext()) ? - Intent.CATEGORY_LEANBACK_LAUNCHER : - Intent.CATEGORY_LAUNCHER); - List<ResolveInfo> ris = pm.queryIntentActivities(intentToResolve, 0); - resolveLoop(ris, intentToResolve, pm); - // For Wear - intentToResolve = new Intent(WEARABLE_ACTION_GOOGLE); - ris = pm.queryIntentActivities(intentToResolve, 0); - resolveLoop(ris, intentToResolve, pm); - } - - private void resolveLoop(List<ResolveInfo> ris, Intent intentToResolve, PackageManager pm) { - if (ris == null || ris.isEmpty()) { - Log.i(TAG, "Could not find any apps"); - } else { - for (ResolveInfo ri : ris) { - Intent startIntent = new Intent(intentToResolve); - startIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); - startIntent.setClassName(ri.activityInfo.packageName, - ri.activityInfo.name); - String appName = ri.loadLabel(pm).toString(); - if (appName != null) { - // Support launching intent using package name or app name - mNameToIntent.put(ri.activityInfo.packageName, startIntent); - mNameToIntent.put(appName, startIntent); - } - } - } - } - - private AppLaunchResult startApp(String appName, boolean forceStopBeforeLaunch, - String launchReason) throws NameNotFoundException, RemoteException { - Log.i(TAG, "Starting " + appName); - - Intent startIntent = mNameToIntent.get(appName); - if (startIntent == null) { - Log.w(TAG, "App does not exist: " + appName); - mResult.putString(mNameToResultKey.get(appName), "App does not exist"); - return new AppLaunchResult(); - } - AppLaunchRunnable runnable = new AppLaunchRunnable(startIntent, forceStopBeforeLaunch, - launchReason); - Thread t = new Thread(runnable); - t.start(); - try { - t.join(JOIN_TIMEOUT); - } catch (InterruptedException e) { - // ignore - } - return runnable.getResult(); - } - - private void checkAccountSignIn() { - // ensure that the device has the required account types before starting test - // e.g. device must have a valid Google account sign in to measure a meaningful launch time - // for Gmail - if (mRequiredAccounts == null || mRequiredAccounts.isEmpty()) { - return; - } - final AccountManager am = - (AccountManager) getInstrumentation().getTargetContext().getSystemService( - Context.ACCOUNT_SERVICE); - Account[] accounts = am.getAccounts(); - // use set here in case device has multiple accounts of the same type - Set<String> foundAccounts = new HashSet<String>(); - for (Account account : accounts) { - if (mRequiredAccounts.contains(account.type)) { - foundAccounts.add(account.type); - } - } - // check if account type matches, if not, fail test with message on what account types - // are missing - if (mRequiredAccounts.size() != foundAccounts.size()) { - mRequiredAccounts.removeAll(foundAccounts); - StringBuilder sb = new StringBuilder("Device missing these accounts:"); - for (String account : mRequiredAccounts) { - sb.append(' '); - sb.append(account); - } - fail(sb.toString()); - } - } - - private void closeApp(String appName, boolean forceStopApp) { - Intent homeIntent = new Intent(Intent.ACTION_MAIN); - homeIntent.addCategory(Intent.CATEGORY_HOME); - homeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); - getInstrumentation().getContext().startActivity(homeIntent); - sleep(POST_LAUNCH_IDLE_TIMEOUT); - if (forceStopApp) { - Intent startIntent = mNameToIntent.get(appName); - if (startIntent != null) { - String packageName = startIntent.getComponent().getPackageName(); - try { - mAm.forceStopPackage(packageName, UserHandle.USER_CURRENT); - } catch (RemoteException e) { - Log.w(TAG, "Error closing app", e); - } - } - } - } - - private void sleep(int time) { - try { - Thread.sleep(time); - } catch (InterruptedException e) { - // ignore - } - } - - private void reportError(String appName, String processName) { - ActivityManager am = (ActivityManager) getInstrumentation() - .getContext().getSystemService(Context.ACTIVITY_SERVICE); - List<ProcessErrorStateInfo> crashes = am.getProcessesInErrorState(); - if (crashes != null) { - for (ProcessErrorStateInfo crash : crashes) { - if (!crash.processName.equals(processName)) - continue; - - Log.w(TAG, appName + " crashed: " + crash.shortMsg); - mResult.putString(mNameToResultKey.get(appName), crash.shortMsg); - return; - } - } - - mResult.putString(mNameToResultKey.get(appName), - "Crashed for unknown reason"); - Log.w(TAG, appName - + " not found in process list, most likely it is crashed"); - } - - private class LaunchOrder { - private String mApp; - private String mCompilerFilter; - private String mLaunchReason; - - LaunchOrder(String app, String compilerFilter, String launchReason){ - mApp = app; - mCompilerFilter = compilerFilter; - mLaunchReason = launchReason; - } - - public String getApp() { - return mApp; - } - - public void setApp(String app) { - mApp = app; - } - - public String getCompilerFilter() { - return mCompilerFilter; - } - - public String getLaunchReason() { - return mLaunchReason; - } - - public void setLaunchReason(String launchReason) { - mLaunchReason = launchReason; - } - } - - private class AppLaunchResult { - long mLaunchTime; - long mCpuCycles; - long mMajorFaults; - - AppLaunchResult() { - mLaunchTime = -1L; - mCpuCycles = -1L; - mMajorFaults = -1L; - } - - AppLaunchResult(String launchTime, String cpuCycles, String majorFaults) { - try { - mLaunchTime = Long.parseLong(launchTime, 10); - mCpuCycles = Long.parseLong(cpuCycles, 10); - mMajorFaults = Long.parseLong(majorFaults, 10); - } catch (NumberFormatException e) { - Log.e(TAG, "Error parsing result", e); - } - } - } - - private class AppLaunchRunnable implements Runnable { - private Intent mLaunchIntent; - private AppLaunchResult mLaunchResult; - private boolean mForceStopBeforeLaunch; - private String mLaunchReason; - - public AppLaunchRunnable(Intent intent, boolean forceStopBeforeLaunch, - String launchReason) { - mLaunchIntent = intent; - mForceStopBeforeLaunch = forceStopBeforeLaunch; - mLaunchReason = launchReason; - mLaunchResult = new AppLaunchResult(); - } - - public AppLaunchResult getResult() { - return mLaunchResult; - } - - public void run() { - File launchFile = null; - try { - String packageName = mLaunchIntent.getComponent().getPackageName(); - String componentName = mLaunchIntent.getComponent().flattenToShortString(); - if (mForceStopBeforeLaunch) { - Log.v(TAG, "Stopping app before launch"); - mAm.forceStopPackage(packageName, UserHandle.USER_CURRENT); - } else { - Log.v(TAG, "Not killing app. Going to Home Screen."); - ParcelFileDescriptor goHome = getInstrumentation().getUiAutomation() - .executeShellCommand("input keyevent 3"); - } - String launchCmd = String.format("%s %s", APP_LAUNCH_CMD, componentName); - if (mSimplePerfAppOnly) { - try { - // executeShellCommand cannot handle shell specific actions, like '&'. - // Therefore, we create a file containing the command and make that - // the command to launch. - launchFile = File.createTempFile(LAUNCH_SCRIPT_NAME, ".sh"); - launchFile.setExecutable(true); - try (FileOutputStream stream = new FileOutputStream(launchFile); - BufferedWriter writer = - new BufferedWriter(new OutputStreamWriter(stream))) { - String cmd = String.format(SIMPLEPERF_APP_CMD, packageName, launchCmd); - writer.write(cmd); - } - launchCmd = launchFile.getAbsolutePath(); - } catch (IOException e) { - Log.w(TAG, "Error writing the launch command", e); - return; - } - } else if (null != mSimplePerfCmd) { - launchCmd = String.format("%s %s", mSimplePerfCmd, launchCmd); - } - Log.v(TAG, "Final launch cmd:" + launchCmd); - ParcelFileDescriptor parcelDesc = getInstrumentation().getUiAutomation() - .executeShellCommand(launchCmd); - mLaunchResult = parseLaunchTimeAndWrite(parcelDesc, String.format - ("App Launch :%s %s", componentName, mLaunchReason)); - } catch (RemoteException e) { - Log.w(TAG, "Error launching app", e); - } finally { - if (launchFile != null) { - launchFile.delete(); - } - } - } - - /** - * Method to parse the launch time info and write the result to file - * - * @param parcelDesc - * @return - */ - private AppLaunchResult parseLaunchTimeAndWrite(ParcelFileDescriptor parcelDesc, - String headerInfo) { - String launchTime = "-1"; - String cpuCycles = "-1"; - String majorFaults = "-1"; - boolean launchSuccess = false; - try { - InputStream inputStream = new FileInputStream(parcelDesc.getFileDescriptor()); - /* SAMPLE OUTPUT : - Starting: Intent { cmp=com.google.android.calculator/com.android.calculator2.Calculator } - Status: ok - Activity: com.google.android.calculator/com.android.calculator2.Calculator - ThisTime: 357 - TotalTime: 357 - WaitTime: 377 - Complete*/ - /* WHEN NOT KILLING HOME : - Starting: Intent { cmp=com.google.android.wearable.app/ - com.google.android.clockwork.home.calendar.AgendaActivity } - Warning: Activity not started, its current task has been brought to the front - Status: ok - Activity: com.google.android.wearable.app/ - com.google.android.clockwork.home.calendar.AgendaActivity - ThisTime: 209 - TotalTime: 209 - WaitTime: 285 - Complete*/ - /* WITH SIMPLEPERF : - Performance counter statistics, - 6595722690,cpu-cycles,4.511040,GHz,(100%), - 0,major-faults,0.000,/sec,(100%), - Total test time,1.462129,seconds,*/ - BufferedReader bufferedReader = new BufferedReader(new InputStreamReader( - inputStream)); - String line = null; - int lineCount = 1; - int addLineForWarning = 0; - mBufferedWriter.newLine(); - mBufferedWriter.write(headerInfo); - mBufferedWriter.newLine(); - while ((line = bufferedReader.readLine()) != null) { - if (lineCount == 2 && line.contains(WARNING_MESSAGE)) { - addLineForWarning = 1; - } - if (lineCount == (2 + addLineForWarning) && line.contains(SUCCESS_MESSAGE)) { - launchSuccess = true; - } - // Parse TotalTime which is the launch time - if (launchSuccess && lineCount == (5 + addLineForWarning)) { - String launchSplit[] = line.split(":"); - launchTime = launchSplit[1].trim(); - } - - if (mSimplePerfAppOnly) { - // Parse simpleperf output. - if (lineCount == (9 + addLineForWarning)) { - if (!line.contains("cpu-cycles")) { - Log.e(TAG, "Error in simpleperf output"); - } else { - cpuCycles = line.split(",")[0].trim(); - } - } else if (lineCount == (10 + addLineForWarning)) { - if (!line.contains("major-faults")) { - Log.e(TAG, "Error in simpleperf output"); - } else { - majorFaults = line.split(",")[0].trim(); - } - } - } - mBufferedWriter.write(line); - mBufferedWriter.newLine(); - lineCount++; - } - mBufferedWriter.flush(); - inputStream.close(); - } catch (IOException e) { - Log.w(TAG, "Error writing the launch file", e); - } - return new AppLaunchResult(launchTime, cpuCycles, majorFaults); - } - - } -} diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java index 46d3a3d669c0..65d168bd5238 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java @@ -84,7 +84,7 @@ public class ColorFiltersMutateActivity extends Activity { mBlendPaint.setColorFilter(new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_OVER)); mRuntimeShader = new RuntimeShader(sSkSL, false); - mRuntimeShader.setUniform("param1", mShaderParam1); + mRuntimeShader.setFloatUniform("param1", mShaderParam1); mRuntimeShader.setInputShader("bitmapShader", new BitmapShader(mBitmap1, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)); @@ -177,7 +177,7 @@ public class ColorFiltersMutateActivity extends Activity { public void setShaderParam1(float value) { mShaderParam1 = value; - mRuntimeShader.setUniform("param1", mShaderParam1); + mRuntimeShader.setFloatUniform("param1", mShaderParam1); invalidate(); } diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java index 83e2de925ceb..73c4b8af99de 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java @@ -110,7 +110,7 @@ public class RippleActivity extends Activity { mPaint = CanvasProperty.createPaint(p); mRuntimeShader = new RuntimeShader(sSkSL, false); - mRuntimeShader.setUniform("in_maxRadius", MAX_RADIUS); + mRuntimeShader.setFloatUniform("in_maxRadius", MAX_RADIUS); } @Override diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java index fcdee63a99a8..12e338c96aea 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java @@ -357,18 +357,18 @@ public class StretchShaderActivity extends Activity { float uScrollX = mScrollX; float uScrollY = mScrollY; - mRuntimeShader.setUniform("uMaxStretchIntensity", mMaxStretchIntensity); - mRuntimeShader.setUniform("uStretchAffectedDist", mStretchAffectedDistance); - mRuntimeShader.setUniform("uDistanceStretchedX", distanceStretchedX); - mRuntimeShader.setUniform("uDistanceStretchedY", distanceStretchedY); - mRuntimeShader.setUniform("uDistDiffX", diffX); - mRuntimeShader.setUniform("uDistDiffY", diffY); - mRuntimeShader.setUniform("uOverscrollX", normOverScrollDistX); - mRuntimeShader.setUniform("uOverscrollY", normOverScrollDistY); - mRuntimeShader.setUniform("uScrollX", uScrollX); - mRuntimeShader.setUniform("uScrollY", uScrollY); - mRuntimeShader.setUniform("viewportWidth", width); - mRuntimeShader.setUniform("viewportHeight", height); + mRuntimeShader.setFloatUniform("uMaxStretchIntensity", mMaxStretchIntensity); + mRuntimeShader.setFloatUniform("uStretchAffectedDist", mStretchAffectedDistance); + mRuntimeShader.setFloatUniform("uDistanceStretchedX", distanceStretchedX); + mRuntimeShader.setFloatUniform("uDistanceStretchedY", distanceStretchedY); + mRuntimeShader.setFloatUniform("uDistDiffX", diffX); + mRuntimeShader.setFloatUniform("uDistDiffY", diffY); + mRuntimeShader.setFloatUniform("uOverscrollX", normOverScrollDistX); + mRuntimeShader.setFloatUniform("uOverscrollY", normOverScrollDistY); + mRuntimeShader.setFloatUniform("uScrollX", uScrollX); + mRuntimeShader.setFloatUniform("uScrollY", uScrollY); + mRuntimeShader.setFloatUniform("viewportWidth", width); + mRuntimeShader.setFloatUniform("viewportHeight", height); mImageView.setRenderEffect(RenderEffect.createShaderEffect(mRuntimeShader)); diff --git a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java index d03aee282ee1..4a724b72f5f9 100644 --- a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java +++ b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java @@ -15,12 +15,13 @@ */ package android.net.vcn; +import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY; +import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN; +import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED; import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_ANY; import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; import org.junit.Test; @@ -32,26 +33,26 @@ public class VcnCellUnderlyingNetworkTemplateTest { private static final Set<Integer> ALLOWED_CARRIER_IDS = new HashSet<>(); // Package private for use in VcnGatewayConnectionConfigTest - static VcnCellUnderlyingNetworkTemplate getTestNetworkPriority() { + static VcnCellUnderlyingNetworkTemplate getTestNetworkTemplate() { return new VcnCellUnderlyingNetworkTemplate.Builder() .setNetworkQuality(NETWORK_QUALITY_OK) - .setAllowMetered(true /* allowMetered */) - .setAllowedOperatorPlmnIds(ALLOWED_PLMN_IDS) - .setAllowedSpecificCarrierIds(ALLOWED_CARRIER_IDS) - .setAllowRoaming(true /* allowRoaming */) - .setRequireOpportunistic(true /* requireOpportunistic */) + .setMetered(MATCH_FORBIDDEN) + .setOperatorPlmnIds(ALLOWED_PLMN_IDS) + .setSimSpecificCarrierIds(ALLOWED_CARRIER_IDS) + .setRoaming(MATCH_FORBIDDEN) + .setOpportunistic(MATCH_REQUIRED) .build(); } @Test public void testBuilderAndGetters() { - final VcnCellUnderlyingNetworkTemplate networkPriority = getTestNetworkPriority(); + final VcnCellUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate(); assertEquals(NETWORK_QUALITY_OK, networkPriority.getNetworkQuality()); - assertTrue(networkPriority.allowMetered()); - assertEquals(ALLOWED_PLMN_IDS, networkPriority.getAllowedOperatorPlmnIds()); - assertEquals(ALLOWED_CARRIER_IDS, networkPriority.getAllowedSpecificCarrierIds()); - assertTrue(networkPriority.allowRoaming()); - assertTrue(networkPriority.requireOpportunistic()); + assertEquals(MATCH_FORBIDDEN, networkPriority.getMetered()); + assertEquals(ALLOWED_PLMN_IDS, networkPriority.getOperatorPlmnIds()); + assertEquals(ALLOWED_CARRIER_IDS, networkPriority.getSimSpecificCarrierIds()); + assertEquals(MATCH_FORBIDDEN, networkPriority.getRoaming()); + assertEquals(MATCH_REQUIRED, networkPriority.getOpportunistic()); } @Test @@ -59,16 +60,16 @@ public class VcnCellUnderlyingNetworkTemplateTest { final VcnCellUnderlyingNetworkTemplate networkPriority = new VcnCellUnderlyingNetworkTemplate.Builder().build(); assertEquals(NETWORK_QUALITY_ANY, networkPriority.getNetworkQuality()); - assertFalse(networkPriority.allowMetered()); - assertEquals(new HashSet<String>(), networkPriority.getAllowedOperatorPlmnIds()); - assertEquals(new HashSet<Integer>(), networkPriority.getAllowedSpecificCarrierIds()); - assertFalse(networkPriority.allowRoaming()); - assertFalse(networkPriority.requireOpportunistic()); + assertEquals(MATCH_ANY, networkPriority.getMetered()); + assertEquals(new HashSet<String>(), networkPriority.getOperatorPlmnIds()); + assertEquals(new HashSet<Integer>(), networkPriority.getSimSpecificCarrierIds()); + assertEquals(MATCH_ANY, networkPriority.getRoaming()); + assertEquals(MATCH_ANY, networkPriority.getOpportunistic()); } @Test public void testPersistableBundle() { - final VcnCellUnderlyingNetworkTemplate networkPriority = getTestNetworkPriority(); + final VcnCellUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate(); assertEquals( networkPriority, VcnUnderlyingNetworkTemplate.fromPersistableBundle( diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java index 1f2905da08f4..2aef9ae7ca32 100644 --- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java +++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java @@ -17,8 +17,8 @@ package android.net.vcn; import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE; -import static android.net.vcn.VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_PRIORITIES; -import static android.net.vcn.VcnGatewayConnectionConfig.UNDERLYING_NETWORK_PRIORITIES_KEY; +import static android.net.vcn.VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES; +import static android.net.vcn.VcnGatewayConnectionConfig.UNDERLYING_NETWORK_TEMPLATES_KEY; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -40,8 +40,9 @@ import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.ArrayList; import java.util.Arrays; -import java.util.LinkedHashSet; +import java.util.List; import java.util.concurrent.TimeUnit; @RunWith(AndroidJUnit4.class) @@ -54,17 +55,17 @@ public class VcnGatewayConnectionConfigTest { }; public static final int[] UNDERLYING_CAPS = new int[] {NetworkCapabilities.NET_CAPABILITY_DUN}; - private static final LinkedHashSet<VcnUnderlyingNetworkTemplate> UNDERLYING_NETWORK_PRIORITIES = - new LinkedHashSet(); + private static final List<VcnUnderlyingNetworkTemplate> UNDERLYING_NETWORK_TEMPLATES = + new ArrayList(); static { Arrays.sort(EXPOSED_CAPS); Arrays.sort(UNDERLYING_CAPS); - UNDERLYING_NETWORK_PRIORITIES.add( - VcnCellUnderlyingNetworkTemplateTest.getTestNetworkPriority()); - UNDERLYING_NETWORK_PRIORITIES.add( - VcnWifiUnderlyingNetworkTemplateTest.getTestNetworkPriority()); + UNDERLYING_NETWORK_TEMPLATES.add( + VcnCellUnderlyingNetworkTemplateTest.getTestNetworkTemplate()); + UNDERLYING_NETWORK_TEMPLATES.add( + VcnWifiUnderlyingNetworkTemplateTest.getTestNetworkTemplate()); } public static final long[] RETRY_INTERVALS_MS = @@ -95,7 +96,7 @@ public class VcnGatewayConnectionConfigTest { // Public for use in VcnGatewayConnectionTest public static VcnGatewayConnectionConfig buildTestConfig() { final VcnGatewayConnectionConfig.Builder builder = - newBuilder().setVcnUnderlyingNetworkPriorities(UNDERLYING_NETWORK_PRIORITIES); + newBuilder().setVcnUnderlyingNetworkPriorities(UNDERLYING_NETWORK_TEMPLATES); return buildTestConfigWithExposedCaps(builder, EXPOSED_CAPS); } @@ -174,10 +175,10 @@ public class VcnGatewayConnectionConfigTest { } @Test - public void testBuilderRequiresNonNullNetworkPriorities() { + public void testBuilderRequiresNonNullNetworkTemplates() { try { newBuilder().setVcnUnderlyingNetworkPriorities(null); - fail("Expected exception due to invalid underlyingNetworkPriorities"); + fail("Expected exception due to invalid underlyingNetworkTemplates"); } catch (NullPointerException e) { } } @@ -219,7 +220,7 @@ public class VcnGatewayConnectionConfigTest { Arrays.sort(exposedCaps); assertArrayEquals(EXPOSED_CAPS, exposedCaps); - assertEquals(UNDERLYING_NETWORK_PRIORITIES, config.getVcnUnderlyingNetworkPriorities()); + assertEquals(UNDERLYING_NETWORK_TEMPLATES, config.getVcnUnderlyingNetworkPriorities()); assertEquals(TUNNEL_CONNECTION_PARAMS, config.getTunnelConnectionParams()); assertArrayEquals(RETRY_INTERVALS_MS, config.getRetryIntervalsMillis()); @@ -234,13 +235,13 @@ public class VcnGatewayConnectionConfigTest { } @Test - public void testParsePersistableBundleWithoutVcnUnderlyingNetworkPriorities() { + public void testParsePersistableBundleWithoutVcnUnderlyingNetworkTemplates() { PersistableBundle configBundle = buildTestConfig().toPersistableBundle(); - configBundle.putPersistableBundle(UNDERLYING_NETWORK_PRIORITIES_KEY, null); + configBundle.putPersistableBundle(UNDERLYING_NETWORK_TEMPLATES_KEY, null); final VcnGatewayConnectionConfig config = new VcnGatewayConnectionConfig(configBundle); assertEquals( - DEFAULT_UNDERLYING_NETWORK_PRIORITIES, config.getVcnUnderlyingNetworkPriorities()); + DEFAULT_UNDERLYING_NETWORK_TEMPLATES, config.getVcnUnderlyingNetworkPriorities()); } private static IkeTunnelConnectionParams buildTunnelConnectionParams(String ikePsk) { @@ -285,39 +286,36 @@ public class VcnGatewayConnectionConfigTest { assertNotEquals(config, anotherConfig); } - private static VcnGatewayConnectionConfig buildTestConfigWithVcnUnderlyingNetworkPriorities( - LinkedHashSet<VcnUnderlyingNetworkTemplate> networkPriorities) { + private static VcnGatewayConnectionConfig buildTestConfigWithVcnUnderlyingNetworkTemplates( + List<VcnUnderlyingNetworkTemplate> networkTemplates) { return buildTestConfigWithExposedCaps( new VcnGatewayConnectionConfig.Builder( - "buildTestConfigWithVcnUnderlyingNetworkPriorities", + "buildTestConfigWithVcnUnderlyingNetworkTemplates", TUNNEL_CONNECTION_PARAMS) - .setVcnUnderlyingNetworkPriorities(networkPriorities), + .setVcnUnderlyingNetworkPriorities(networkTemplates), EXPOSED_CAPS); } @Test - public void testVcnUnderlyingNetworkPrioritiesEquality() throws Exception { + public void testVcnUnderlyingNetworkTemplatesEquality() throws Exception { final VcnGatewayConnectionConfig config = - buildTestConfigWithVcnUnderlyingNetworkPriorities(UNDERLYING_NETWORK_PRIORITIES); + buildTestConfigWithVcnUnderlyingNetworkTemplates(UNDERLYING_NETWORK_TEMPLATES); - final LinkedHashSet<VcnUnderlyingNetworkTemplate> networkPrioritiesEqual = - new LinkedHashSet(); - networkPrioritiesEqual.add(VcnCellUnderlyingNetworkTemplateTest.getTestNetworkPriority()); - networkPrioritiesEqual.add(VcnWifiUnderlyingNetworkTemplateTest.getTestNetworkPriority()); + final List<VcnUnderlyingNetworkTemplate> networkTemplatesEqual = new ArrayList(); + networkTemplatesEqual.add(VcnCellUnderlyingNetworkTemplateTest.getTestNetworkTemplate()); + networkTemplatesEqual.add(VcnWifiUnderlyingNetworkTemplateTest.getTestNetworkTemplate()); final VcnGatewayConnectionConfig configEqual = - buildTestConfigWithVcnUnderlyingNetworkPriorities(networkPrioritiesEqual); + buildTestConfigWithVcnUnderlyingNetworkTemplates(networkTemplatesEqual); - final LinkedHashSet<VcnUnderlyingNetworkTemplate> networkPrioritiesNotEqual = - new LinkedHashSet(); - networkPrioritiesNotEqual.add( - VcnWifiUnderlyingNetworkTemplateTest.getTestNetworkPriority()); + final List<VcnUnderlyingNetworkTemplate> networkTemplatesNotEqual = new ArrayList(); + networkTemplatesNotEqual.add(VcnWifiUnderlyingNetworkTemplateTest.getTestNetworkTemplate()); final VcnGatewayConnectionConfig configNotEqual = - buildTestConfigWithVcnUnderlyingNetworkPriorities(networkPrioritiesNotEqual); + buildTestConfigWithVcnUnderlyingNetworkTemplates(networkTemplatesNotEqual); - assertEquals(UNDERLYING_NETWORK_PRIORITIES, networkPrioritiesEqual); + assertEquals(UNDERLYING_NETWORK_TEMPLATES, networkTemplatesEqual); assertEquals(config, configEqual); - assertNotEquals(UNDERLYING_NETWORK_PRIORITIES, networkPrioritiesNotEqual); + assertNotEquals(UNDERLYING_NETWORK_TEMPLATES, networkTemplatesNotEqual); assertNotEquals(config, configNotEqual); } } diff --git a/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java b/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java index 652057fd48c7..cb5b47bbdc15 100644 --- a/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java +++ b/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java @@ -15,36 +15,38 @@ */ package android.net.vcn; +import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY; +import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN; import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_ANY; import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import org.junit.Test; +import java.util.Set; + public class VcnWifiUnderlyingNetworkTemplateTest { private static final String SSID = "TestWifi"; private static final int INVALID_NETWORK_QUALITY = -1; // Package private for use in VcnGatewayConnectionConfigTest - static VcnWifiUnderlyingNetworkTemplate getTestNetworkPriority() { + static VcnWifiUnderlyingNetworkTemplate getTestNetworkTemplate() { return new VcnWifiUnderlyingNetworkTemplate.Builder() .setNetworkQuality(NETWORK_QUALITY_OK) - .setAllowMetered(true /* allowMetered */) - .setSsid(SSID) + .setMetered(MATCH_FORBIDDEN) + .setSsids(Set.of(SSID)) .build(); } @Test public void testBuilderAndGetters() { - final VcnWifiUnderlyingNetworkTemplate networkPriority = getTestNetworkPriority(); + final VcnWifiUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate(); assertEquals(NETWORK_QUALITY_OK, networkPriority.getNetworkQuality()); - assertTrue(networkPriority.allowMetered()); - assertEquals(SSID, networkPriority.getSsid()); + assertEquals(MATCH_FORBIDDEN, networkPriority.getMetered()); + assertEquals(Set.of(SSID), networkPriority.getSsids()); } @Test @@ -52,8 +54,8 @@ public class VcnWifiUnderlyingNetworkTemplateTest { final VcnWifiUnderlyingNetworkTemplate networkPriority = new VcnWifiUnderlyingNetworkTemplate.Builder().build(); assertEquals(NETWORK_QUALITY_ANY, networkPriority.getNetworkQuality()); - assertFalse(networkPriority.allowMetered()); - assertNull(SSID, networkPriority.getSsid()); + assertEquals(MATCH_ANY, networkPriority.getMetered()); + assertTrue(networkPriority.getSsids().isEmpty()); } @Test @@ -68,7 +70,7 @@ public class VcnWifiUnderlyingNetworkTemplateTest { @Test public void testPersistableBundle() { - final VcnWifiUnderlyingNetworkTemplate networkPriority = getTestNetworkPriority(); + final VcnWifiUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate(); assertEquals( networkPriority, VcnUnderlyingNetworkTemplate.fromPersistableBundle( diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java index f23d5bf67ebf..4bb7de8c1fef 100644 --- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java +++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java @@ -16,6 +16,8 @@ package com.android.server.vcn.routeselection; +import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN; +import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED; import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK; import static com.android.server.vcn.VcnTestUtils.setupSystemService; @@ -145,7 +147,7 @@ public class NetworkPriorityClassifierTest { final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority = new VcnWifiUnderlyingNetworkTemplate.Builder() .setNetworkQuality(NETWORK_QUALITY_OK) - .setAllowMetered(false /* allowMetered */) + .setMetered(MATCH_FORBIDDEN) .build(); assertFalse( @@ -164,7 +166,6 @@ public class NetworkPriorityClassifierTest { final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority = new VcnWifiUnderlyingNetworkTemplate.Builder() .setNetworkQuality(NETWORK_QUALITY_OK) - .setAllowMetered(true /* allowMetered */) .build(); final UnderlyingNetworkRecord selectedNetworkRecord = isSelectedNetwork ? mWifiNetworkRecord : null; @@ -214,8 +215,7 @@ public class NetworkPriorityClassifierTest { final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority = new VcnWifiUnderlyingNetworkTemplate.Builder() .setNetworkQuality(NETWORK_QUALITY_OK) - .setAllowMetered(true /* allowMetered */) - .setSsid(nwPrioritySsid) + .setSsids(Set.of(nwPrioritySsid)) .build(); assertEquals( @@ -238,10 +238,7 @@ public class NetworkPriorityClassifierTest { } private static VcnCellUnderlyingNetworkTemplate.Builder getCellNetworkPriorityBuilder() { - return new VcnCellUnderlyingNetworkTemplate.Builder() - .setNetworkQuality(NETWORK_QUALITY_OK) - .setAllowMetered(true /* allowMetered */) - .setAllowRoaming(true /* allowRoaming */); + return new VcnCellUnderlyingNetworkTemplate.Builder().setNetworkQuality(NETWORK_QUALITY_OK); } @Test @@ -258,9 +255,7 @@ public class NetworkPriorityClassifierTest { @Test public void testMatchOpportunisticCell() { final VcnCellUnderlyingNetworkTemplate opportunisticCellNetworkPriority = - getCellNetworkPriorityBuilder() - .setRequireOpportunistic(true /* requireOpportunistic */) - .build(); + getCellNetworkPriorityBuilder().setOpportunistic(MATCH_REQUIRED).build(); when(mSubscriptionSnapshot.isOpportunistic(SUB_ID)).thenReturn(true); when(mSubscriptionSnapshot.getAllSubIdsInGroup(SUB_GROUP)).thenReturn(new ArraySet<>()); @@ -279,7 +274,7 @@ public class NetworkPriorityClassifierTest { final String networkPriorityPlmnId = useMatchedPlmnId ? PLMN_ID : PLMN_ID_OTHER; final VcnCellUnderlyingNetworkTemplate networkPriority = getCellNetworkPriorityBuilder() - .setAllowedOperatorPlmnIds(Set.of(networkPriorityPlmnId)) + .setOperatorPlmnIds(Set.of(networkPriorityPlmnId)) .build(); assertEquals( @@ -308,7 +303,7 @@ public class NetworkPriorityClassifierTest { final int networkPriorityCarrierId = useMatchedCarrierId ? CARRIER_ID : CARRIER_ID_OTHER; final VcnCellUnderlyingNetworkTemplate networkPriority = getCellNetworkPriorityBuilder() - .setAllowedSpecificCarrierIds(Set.of(networkPriorityCarrierId)) + .setSimSpecificCarrierIds(Set.of(networkPriorityCarrierId)) .build(); assertEquals( @@ -336,7 +331,7 @@ public class NetworkPriorityClassifierTest { @Test public void testMatchWifiFailWithoutNotRoamingBit() { final VcnCellUnderlyingNetworkTemplate networkPriority = - getCellNetworkPriorityBuilder().setAllowRoaming(false /* allowRoaming */).build(); + getCellNetworkPriorityBuilder().setRoaming(MATCH_FORBIDDEN).build(); assertFalse( checkMatchesCellPriorityRule( @@ -353,7 +348,7 @@ public class NetworkPriorityClassifierTest { calculatePriorityClass( mVcnContext, networkRecord, - VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_PRIORITIES, + VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES, SUB_GROUP, mSubscriptionSnapshot, null /* currentlySelected */, diff --git a/tools/aapt2/cmd/Compile_test.cpp b/tools/aapt2/cmd/Compile_test.cpp index 89757134f3a7..fbfbf68b30bb 100644 --- a/tools/aapt2/cmd/Compile_test.cpp +++ b/tools/aapt2/cmd/Compile_test.cpp @@ -19,12 +19,11 @@ #include "android-base/file.h" #include "android-base/stringprintf.h" #include "android-base/utf8.h" - +#include "format/proto/ProtoDeserialize.h" #include "io/StringStream.h" #include "io/ZipArchive.h" #include "java/AnnotationProcessor.h" #include "test/Test.h" -#include "format/proto/ProtoDeserialize.h" namespace aapt { @@ -59,55 +58,56 @@ TEST_F(CompilerTest, MultiplePeriods) { std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build(); const std::string kResDir = BuildPath({android::base::Dirname(android::base::GetExecutablePath()), "integration-tests", "CompileTest", "res"}); + const std::string kOutDir = testing::TempDir(); // Resource files without periods in the file name should not throw errors const std::string path0 = BuildPath({kResDir, "values", "values.xml"}); - const std::string path0_out = BuildPath({kResDir, "values_values.arsc.flat"}); + const std::string path0_out = BuildPath({kOutDir, "values_values.arsc.flat"}); ::android::base::utf8::unlink(path0_out.c_str()); - ASSERT_EQ(TestCompile(path0, kResDir, /** legacy */ false, diag), 0); + ASSERT_EQ(TestCompile(path0, kOutDir, /** legacy */ false, diag), 0); ASSERT_EQ(::android::base::utf8::unlink(path0_out.c_str()), 0); - ASSERT_EQ(TestCompile(path0, kResDir, /** legacy */ true, diag), 0); + ASSERT_EQ(TestCompile(path0, kOutDir, /** legacy */ true, diag), 0); ASSERT_EQ(::android::base::utf8::unlink(path0_out.c_str()), 0); const std::string path1 = BuildPath({kResDir, "drawable", "image.png"}); - const std::string path1_out = BuildPath({kResDir, "drawable_image.png.flat"}); + const std::string path1_out = BuildPath({kOutDir, "drawable_image.png.flat"}); ::android::base::utf8::unlink(path1_out.c_str()); - ASSERT_EQ(TestCompile(path1, kResDir, /** legacy */ false, diag), 0); + ASSERT_EQ(TestCompile(path1, kOutDir, /** legacy */ false, diag), 0); ASSERT_EQ(::android::base::utf8::unlink(path1_out.c_str()), 0); - ASSERT_EQ(TestCompile(path1, kResDir, /** legacy */ true, diag), 0); + ASSERT_EQ(TestCompile(path1, kOutDir, /** legacy */ true, diag), 0); ASSERT_EQ(::android::base::utf8::unlink(path1_out.c_str()), 0); const std::string path2 = BuildPath({kResDir, "drawable", "image.9.png"}); - const std::string path2_out = BuildPath({kResDir, "drawable_image.9.png.flat"}); + const std::string path2_out = BuildPath({kOutDir, "drawable_image.9.png.flat"}); ::android::base::utf8::unlink(path2_out.c_str()); - ASSERT_EQ(TestCompile(path2, kResDir, /** legacy */ false, diag), 0); + ASSERT_EQ(TestCompile(path2, kOutDir, /** legacy */ false, diag), 0); ASSERT_EQ(::android::base::utf8::unlink(path2_out.c_str()), 0); - ASSERT_EQ(TestCompile(path2, kResDir, /** legacy */ true, diag), 0); + ASSERT_EQ(TestCompile(path2, kOutDir, /** legacy */ true, diag), 0); ASSERT_EQ(::android::base::utf8::unlink(path2_out.c_str()), 0); // Resource files with periods in the file name should fail on non-legacy compilations const std::string path3 = BuildPath({kResDir, "values", "values.all.xml"}); - const std::string path3_out = BuildPath({kResDir, "values_values.all.arsc.flat"}); + const std::string path3_out = BuildPath({kOutDir, "values_values.all.arsc.flat"}); ::android::base::utf8::unlink(path3_out.c_str()); - ASSERT_NE(TestCompile(path3, kResDir, /** legacy */ false, diag), 0); + ASSERT_NE(TestCompile(path3, kOutDir, /** legacy */ false, diag), 0); ASSERT_NE(::android::base::utf8::unlink(path3_out.c_str()), 0); - ASSERT_EQ(TestCompile(path3, kResDir, /** legacy */ true, diag), 0); + ASSERT_EQ(TestCompile(path3, kOutDir, /** legacy */ true, diag), 0); ASSERT_EQ(::android::base::utf8::unlink(path3_out.c_str()), 0); const std::string path4 = BuildPath({kResDir, "drawable", "image.small.png"}); - const std::string path4_out = BuildPath({kResDir, "drawable_image.small.png.flat"}); + const std::string path4_out = BuildPath({kOutDir, "drawable_image.small.png.flat"}); ::android::base::utf8::unlink(path4_out.c_str()); - ASSERT_NE(TestCompile(path4, kResDir, /** legacy */ false, diag), 0); + ASSERT_NE(TestCompile(path4, kOutDir, /** legacy */ false, diag), 0); ASSERT_NE(::android::base::utf8::unlink(path4_out.c_str()), 0); - ASSERT_EQ(TestCompile(path4, kResDir, /** legacy */ true, diag), 0); + ASSERT_EQ(TestCompile(path4, kOutDir, /** legacy */ true, diag), 0); ASSERT_EQ(::android::base::utf8::unlink(path4_out.c_str()), 0); const std::string path5 = BuildPath({kResDir, "drawable", "image.small.9.png"}); - const std::string path5_out = BuildPath({kResDir, "drawable_image.small.9.png.flat"}); + const std::string path5_out = BuildPath({kOutDir, "drawable_image.small.9.png.flat"}); ::android::base::utf8::unlink(path5_out.c_str()); - ASSERT_NE(TestCompile(path5, kResDir, /** legacy */ false, diag), 0); + ASSERT_NE(TestCompile(path5, kOutDir, /** legacy */ false, diag), 0); ASSERT_NE(::android::base::utf8::unlink(path5_out.c_str()), 0); - ASSERT_EQ(TestCompile(path5, kResDir, /** legacy */ true, diag), 0); + ASSERT_EQ(TestCompile(path5, kOutDir, /** legacy */ true, diag), 0); ASSERT_EQ(::android::base::utf8::unlink(path5_out.c_str()), 0); } @@ -116,9 +116,7 @@ TEST_F(CompilerTest, DirInput) { std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build(); const std::string kResDir = BuildPath({android::base::Dirname(android::base::GetExecutablePath()), "integration-tests", "CompileTest", "DirInput", "res"}); - const std::string kOutputFlata = - BuildPath({android::base::Dirname(android::base::GetExecutablePath()), "integration-tests", - "CompileTest", "DirInput", "compiled.flata"}); + const std::string kOutputFlata = BuildPath({testing::TempDir(), "compiled.flata"}); ::android::base::utf8::unlink(kOutputFlata.c_str()); std::vector<android::StringPiece> args; @@ -147,9 +145,7 @@ TEST_F(CompilerTest, ZipInput) { const std::string kResZip = BuildPath({android::base::Dirname(android::base::GetExecutablePath()), "integration-tests", "CompileTest", "ZipInput", "res.zip"}); - const std::string kOutputFlata = - BuildPath({android::base::Dirname(android::base::GetExecutablePath()), "integration-tests", - "CompileTest", "ZipInput", "compiled.flata"}); + const std::string kOutputFlata = BuildPath({testing::TempDir(), "compiled.flata"}); ::android::base::utf8::unlink(kOutputFlata.c_str()); @@ -257,9 +253,9 @@ TEST_F(CompilerTest, DoNotTranslateTest) { TEST_F(CompilerTest, RelativePathTest) { StdErrDiagnostics diag; - const std::string res_path = BuildPath( - {android::base::Dirname(android::base::GetExecutablePath()), - "integration-tests", "CompileTest", "res"}); + const std::string res_path = + BuildPath({android::base::Dirname(android::base::GetExecutablePath()), "integration-tests", + "CompileTest", "res"}); const std::string path_values_colors = GetTestPath("values/colors.xml"); WriteFile(path_values_colors, "<resources>" @@ -272,9 +268,8 @@ TEST_F(CompilerTest, RelativePathTest) { "<TextBox android:id=\"@+id/text_one\" android:background=\"@color/color_one\"/>" "</LinearLayout>"); - const std::string compiled_files_dir = BuildPath( - {android::base::Dirname(android::base::GetExecutablePath()), - "integration-tests", "CompileTest", "compiled"}); + const std::string compiled_files_dir = + BuildPath({testing::TempDir(), "integration-tests", "CompileTest", "compiled"}); CHECK(file::mkdirs(compiled_files_dir.data())); const std::string path_values_colors_out = @@ -283,9 +278,8 @@ TEST_F(CompilerTest, RelativePathTest) { BuildPath({compiled_files_dir, "layout_layout_one.flat"}); ::android::base::utf8::unlink(path_values_colors_out.c_str()); ::android::base::utf8::unlink(path_layout_layout_one_out.c_str()); - const std::string apk_path = BuildPath( - {android::base::Dirname(android::base::GetExecutablePath()), - "integration-tests", "CompileTest", "out.apk"}); + const std::string apk_path = + BuildPath({testing::TempDir(), "integration-tests", "CompileTest", "out.apk"}); const std::string source_set_res = BuildPath({"main", "res"}); const std::string relative_path_values_colors = diff --git a/tools/aapt2/test/Fixture.cpp b/tools/aapt2/test/Fixture.cpp index e2f71dc45075..ddc1853ca13c 100644 --- a/tools/aapt2/test/Fixture.cpp +++ b/tools/aapt2/test/Fixture.cpp @@ -67,8 +67,7 @@ void ClearDirectory(const android::StringPiece& path) { } void TestDirectoryFixture::SetUp() { - temp_dir_ = file::BuildPath({android::base::GetExecutableDirectory(), - "_temp", + temp_dir_ = file::BuildPath({testing::TempDir(), "_temp", testing::UnitTest::GetInstance()->current_test_case()->name(), testing::UnitTest::GetInstance()->current_test_info()->name()}); ASSERT_TRUE(file::mkdirs(temp_dir_)); @@ -236,4 +235,4 @@ std::vector<std::string> LinkCommandBuilder::Build(const std::string& out_apk) { return args_; } -} // namespace aapt
\ No newline at end of file +} // namespace aapt diff --git a/tools/fonts/fontchain_linter.py b/tools/fonts/fontchain_linter.py index fe2b0186c57f..3131f5687c6a 100755 --- a/tools/fonts/fontchain_linter.py +++ b/tools/fonts/fontchain_linter.py @@ -57,6 +57,7 @@ LANG_TO_SCRIPT = { 'sk': 'Latn', 'sl': 'Latn', 'sq': 'Latn', + 'sv': 'Latn', 'ta': 'Taml', 'te': 'Telu', 'tk': 'Latn', |