diff options
115 files changed, 1400 insertions, 1055 deletions
diff --git a/core/java/android/accounts/ChooseAccountActivity.java b/core/java/android/accounts/ChooseAccountActivity.java index 4af22bfac92d..b826def55e92 100644 --- a/core/java/android/accounts/ChooseAccountActivity.java +++ b/core/java/android/accounts/ChooseAccountActivity.java @@ -16,7 +16,6 @@ package android.accounts; import android.app.Activity; -import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.content.Context; import android.content.pm.PackageManager; @@ -25,8 +24,8 @@ import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.IBinder; import android.os.Parcelable; -import android.os.RemoteException; import android.os.Process; +import android.os.RemoteException; import android.os.UserHandle; import android.util.Log; import android.view.LayoutInflater; @@ -37,6 +36,7 @@ import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; + import com.android.internal.R; import java.util.HashMap; @@ -60,6 +60,9 @@ public class ChooseAccountActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + getWindow().addSystemFlags( + android.view.WindowManager.LayoutParams + .SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); mAccounts = getIntent().getParcelableArrayExtra(AccountManager.KEY_ACCOUNTS); mAccountManagerResponse = getIntent().getParcelableExtra(AccountManager.KEY_ACCOUNT_MANAGER_RESPONSE); diff --git a/core/java/android/accounts/ChooseAccountTypeActivity.java b/core/java/android/accounts/ChooseAccountTypeActivity.java index e3352bc85668..300fdd4ee419 100644 --- a/core/java/android/accounts/ChooseAccountTypeActivity.java +++ b/core/java/android/accounts/ChooseAccountTypeActivity.java @@ -31,6 +31,7 @@ import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; + import com.android.internal.R; import java.util.ArrayList; @@ -51,7 +52,9 @@ public class ChooseAccountTypeActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - + getWindow().addSystemFlags( + android.view.WindowManager.LayoutParams + .SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "ChooseAccountTypeActivity.onCreate(savedInstanceState=" + savedInstanceState + ")"); diff --git a/core/java/android/accounts/ChooseTypeAndAccountActivity.java b/core/java/android/accounts/ChooseTypeAndAccountActivity.java index 4b4ef002ae06..9bf4170e767d 100644 --- a/core/java/android/accounts/ChooseTypeAndAccountActivity.java +++ b/core/java/android/accounts/ChooseTypeAndAccountActivity.java @@ -15,11 +15,8 @@ */ package android.accounts; -import android.app.ActivityTaskManager; -import com.google.android.collect.Sets; - import android.app.Activity; -import android.app.ActivityManager; +import android.app.ActivityTaskManager; import android.content.Intent; import android.os.Bundle; import android.os.IBinder; @@ -39,6 +36,8 @@ import android.widget.TextView; import com.android.internal.R; +import com.google.android.collect.Sets; + import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; @@ -143,7 +142,9 @@ public class ChooseTypeAndAccountActivity extends Activity Log.v(TAG, "ChooseTypeAndAccountActivity.onCreate(savedInstanceState=" + savedInstanceState + ")"); } - + getWindow().addSystemFlags( + android.view.WindowManager.LayoutParams + .SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); String message = null; try { diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 4c16072cefd3..2c44c8d7cf2e 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -5183,12 +5183,6 @@ public class Activity extends ContextThemeWrapper * #checkSelfPermission(String)}. * </p> * <p> - * Calling this API for permissions already granted to your app would show UI - * to the user to decide whether the app can still hold these permissions. This - * can be useful if the way your app uses data guarded by the permissions - * changes significantly. - * </p> - * <p> * You cannot request a permission if your activity sets {@link * android.R.styleable#AndroidManifestActivity_noHistory noHistory} to * <code>true</code> because in this case the activity would not receive diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 694c51980e21..c4cdbbcbf9d2 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -7636,8 +7636,8 @@ public class AppOpsManager { } else if (collectionMode == COLLECT_SYNC // Only collect app-ops when the proxy is trusted && (mContext.checkPermission(Manifest.permission.UPDATE_APP_OPS_STATS, -1, - myUid) == PackageManager.PERMISSION_GRANTED - || isTrustedVoiceServiceProxy(mContext, mContext.getOpPackageName(), op))) { + myUid) == PackageManager.PERMISSION_GRANTED || isTrustedVoiceServiceProxy( + mContext, mContext.getOpPackageName(), op, mContext.getUserId()))) { collectNotedOpSync(op, proxiedAttributionTag); } } @@ -7655,7 +7655,7 @@ public class AppOpsManager { * @hide */ public static boolean isTrustedVoiceServiceProxy(Context context, String packageName, - int code) { + int code, int userId) { // This is a workaround for R QPR, new API change is not allowed. We only allow the current // voice recognizer is also the voice interactor to noteproxy op. if (code != OP_RECORD_AUDIO) { @@ -7667,7 +7667,7 @@ public class AppOpsManager { final String voiceRecognitionServicePackageName = getComponentPackageNameFromString(voiceRecognitionComponent); return (Objects.equals(packageName, voiceRecognitionServicePackageName)) - && isPackagePreInstalled(context, packageName); + && isPackagePreInstalled(context, packageName, userId); } private static String getComponentPackageNameFromString(String from) { @@ -7675,10 +7675,10 @@ public class AppOpsManager { return componentName != null ? componentName.getPackageName() : ""; } - private static boolean isPackagePreInstalled(Context context, String packageName) { + private static boolean isPackagePreInstalled(Context context, String packageName, int userId) { try { final PackageManager pm = context.getPackageManager(); - final ApplicationInfo info = pm.getApplicationInfo(packageName, 0); + final ApplicationInfo info = pm.getApplicationInfoAsUser(packageName, 0, userId); return ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0); } catch (PackageManager.NameNotFoundException e) { return false; diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java index ec17e4497ba4..aca74ce78971 100644 --- a/core/java/android/app/admin/DevicePolicyManagerInternal.java +++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java @@ -16,6 +16,7 @@ package android.app.admin; +import android.annotation.Nullable; import android.annotation.UserIdInt; import android.content.ComponentName; import android.content.Intent; @@ -241,6 +242,7 @@ public abstract class DevicePolicyManagerInternal { /** * Returns the profile owner component for the given user, or {@code null} if there is not one. */ + @Nullable public abstract ComponentName getProfileOwnerAsUser(int userHandle); /** @@ -254,4 +256,9 @@ public abstract class DevicePolicyManagerInternal { * {@link #supportsResetOp(int)} is true. */ public abstract void resetOp(int op, String packageName, @UserIdInt int userId); + + /** + * Returns whether the given package is a device owner or a profile owner in the calling user. + */ + public abstract boolean isDeviceOrProfileOwnerInCallingUser(String packageName); } diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java index f932bc250e28..2da93ca34019 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java +++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java @@ -98,6 +98,8 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { public static ForInternedStringValueMap sForInternedStringValueMap = Parcelling.Cache.getOrCreate(ForInternedStringValueMap.class); public static ForStringSet sForStringSet = Parcelling.Cache.getOrCreate(ForStringSet.class); + public static ForInternedStringSet sForInternedStringSet = + Parcelling.Cache.getOrCreate(ForInternedStringSet.class); protected static ParsedIntentInfo.StringPairListParceler sForIntentInfoPairs = Parcelling.Cache.getOrCreate(ParsedIntentInfo.StringPairListParceler.class); @@ -1026,6 +1028,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { dest.writeBoolean(this.forceQueryable); dest.writeParcelableList(this.queriesIntents, flags); sForInternedStringList.parcel(this.queriesPackages, dest, flags); + sForInternedStringSet.parcel(this.queriesProviders, dest, flags); dest.writeString(this.appComponentFactory); dest.writeString(this.backupAgentName); dest.writeInt(this.banner); @@ -1188,6 +1191,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { this.forceQueryable = in.readBoolean(); this.queriesIntents = in.createTypedArrayList(Intent.CREATOR); this.queriesPackages = sForInternedStringList.unparcel(in); + this.queriesProviders = sForInternedStringSet.unparcel(in); this.appComponentFactory = in.readString(); this.backupAgentName = in.readString(); this.banner = in.readInt(); diff --git a/core/java/android/hardware/usb/AccessoryFilter.java b/core/java/android/hardware/usb/AccessoryFilter.java index f22dad4124d2..f4c73d56e433 100644 --- a/core/java/android/hardware/usb/AccessoryFilter.java +++ b/core/java/android/hardware/usb/AccessoryFilter.java @@ -101,7 +101,7 @@ public class AccessoryFilter { public boolean matches(UsbAccessory acc) { if (mManufacturer != null && !acc.getManufacturer().equals(mManufacturer)) return false; if (mModel != null && !acc.getModel().equals(mModel)) return false; - return !(mVersion != null && !acc.getVersion().equals(mVersion)); + return !(mVersion != null && !mVersion.equals(acc.getVersion())); } /** diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 8c2ca6d3e509..86302d9d1119 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -31,8 +31,12 @@ import dalvik.system.VMRuntime; import libcore.io.IoUtils; +import java.io.BufferedReader; import java.io.FileDescriptor; +import java.io.FileReader; +import java.io.IOException; import java.util.Map; +import java.util.StringTokenizer; import java.util.concurrent.TimeoutException; /** @@ -1392,4 +1396,43 @@ public class Process { } private static native int nativePidFdOpen(int pid, int flags) throws ErrnoException; + + /** + * Checks if a process corresponding to a specific pid owns any file locks. + * @param pid The process ID for which we want to know the existence of file locks. + * @return true If the process holds any file locks, false otherwise. + * @throws IOException if /proc/locks can't be accessed. + * + * @hide + */ + public static boolean hasFileLocks(int pid) throws Exception { + BufferedReader br = null; + + try { + br = new BufferedReader(new FileReader("/proc/locks")); + String line; + + while ((line = br.readLine()) != null) { + StringTokenizer st = new StringTokenizer(line); + + for (int i = 0; i < 5 && st.hasMoreTokens(); i++) { + String str = st.nextToken(); + try { + if (i == 4 && Integer.parseInt(str) == pid) { + return true; + } + } catch (NumberFormatException nfe) { + throw new Exception("Exception parsing /proc/locks at \" " + + line + " \", token #" + i); + } + } + } + + return false; + } finally { + if (br != null) { + br.close(); + } + } + } } diff --git a/core/java/android/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java index 6ed32d9c55c9..ab7d3b1b5b25 100644 --- a/core/java/android/provider/CalendarContract.java +++ b/core/java/android/provider/CalendarContract.java @@ -40,6 +40,7 @@ import android.database.DatabaseUtils; import android.net.Uri; import android.os.Build; import android.os.RemoteException; +import android.os.StrictMode; import android.text.format.DateUtils; import android.text.format.TimeMigrationUtils; import android.util.Log; @@ -2619,7 +2620,13 @@ public final class CalendarContract { intent.setData(ContentUris.withAppendedId(CalendarContract.CONTENT_URI, alarmTime)); intent.putExtra(ALARM_TIME, alarmTime); intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); + + // Disable strict mode VM policy violations temporarily for intents that contain a + // content URI but don't have FLAG_GRANT_READ_URI_PERMISSION. + StrictMode.VmPolicy oldVmPolicy = StrictMode.allowVmViolations(); PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0); + StrictMode.setVmPolicy(oldVmPolicy); + manager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, alarmTime, pi); } diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java index 123604489da4..09e4557135b5 100644 --- a/core/java/android/view/InsetsAnimationThreadControlRunner.java +++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java @@ -108,6 +108,9 @@ public class InsetsAnimationThreadControlRunner implements InsetsAnimationContro mControl = new InsetsAnimationControlImpl(controls, frame, state, listener, types, mCallbacks, durationMs, interpolator, animationType); InsetsAnimationThread.getHandler().post(() -> { + if (mControl.isCancelled()) { + return; + } Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "InsetsAsyncAnimation: " + WindowInsets.Type.toString(types), types); listener.onReady(mControl, types); diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java index 700dc66fab55..ba40459692f7 100644 --- a/core/java/android/view/InsetsSourceConsumer.java +++ b/core/java/android/view/InsetsSourceConsumer.java @@ -113,13 +113,20 @@ public class InsetsSourceConsumer { InsetsState.typeToString(control.getType()), mController.getHost().getRootViewTitle())); } - // We are loosing control if (mSourceControl == null) { + // We are loosing control mController.notifyControlRevoked(this); - // Restore server visibility. - mState.getSource(getType()).setVisible( - mController.getLastDispatchedState().getSource(getType()).isVisible()); + // Check if we need to restore server visibility. + final InsetsSource source = mState.getSource(mType); + final boolean serverVisibility = + mController.getLastDispatchedState().getSourceOrDefaultVisibility(mType); + if (source.isVisible() != serverVisibility) { + source.setVisible(serverVisibility); + mController.notifyVisibilityChanged(); + } + + // For updateCompatSysUiVisibility applyLocalVisibilityOverride(); } else { // We are gaining control, and need to run an animation since previous state diff --git a/core/java/com/android/internal/app/ProcessMap.java b/core/java/com/android/internal/app/ProcessMap.java index 81036f7ecba8..4917a47eb000 100644 --- a/core/java/com/android/internal/app/ProcessMap.java +++ b/core/java/com/android/internal/app/ProcessMap.java @@ -22,7 +22,7 @@ import android.util.SparseArray; public class ProcessMap<E> { final ArrayMap<String, SparseArray<E>> mMap = new ArrayMap<String, SparseArray<E>>(); - + public E get(String name, int uid) { SparseArray<E> uids = mMap.get(name); if (uids == null) return null; @@ -58,4 +58,6 @@ public class ProcessMap<E> { public int size() { return mMap.size(); } + + public void putAll(ProcessMap<E> other) { mMap.putAll(other.mMap); } } diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java index a23fc4b57b45..7ee846e9d8c1 100644 --- a/core/java/com/android/internal/util/ScreenshotHelper.java +++ b/core/java/com/android/internal/util/ScreenshotHelper.java @@ -1,12 +1,15 @@ package com.android.internal.util; +import static android.content.Intent.ACTION_USER_SWITCHED; import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_OTHER; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.ServiceConnection; import android.graphics.Insets; import android.graphics.Rect; @@ -161,8 +164,21 @@ public class ScreenshotHelper { private ServiceConnection mScreenshotConnection = null; private final Context mContext; + private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + synchronized (mScreenshotLock) { + if (ACTION_USER_SWITCHED.equals(intent.getAction())) { + resetConnection(); + } + } + } + }; + public ScreenshotHelper(Context context) { mContext = context; + IntentFilter filter = new IntentFilter(ACTION_USER_SWITCHED); + mContext.registerReceiver(mBroadcastReceiver, filter); } /** @@ -279,9 +295,8 @@ public class ScreenshotHelper { final Runnable mScreenshotTimeout = () -> { synchronized (mScreenshotLock) { if (mScreenshotConnection != null) { - mContext.unbindService(mScreenshotConnection); - mScreenshotConnection = null; - mScreenshotService = null; + Log.e(TAG, "Timed out before getting screenshot capture response"); + resetConnection(); notifyScreenshotError(); } } @@ -304,11 +319,7 @@ public class ScreenshotHelper { break; case SCREENSHOT_MSG_PROCESS_COMPLETE: synchronized (mScreenshotLock) { - if (mScreenshotConnection != null) { - mContext.unbindService(mScreenshotConnection); - mScreenshotConnection = null; - mScreenshotService = null; - } + resetConnection(); } break; } @@ -348,9 +359,7 @@ public class ScreenshotHelper { public void onServiceDisconnected(ComponentName name) { synchronized (mScreenshotLock) { if (mScreenshotConnection != null) { - mContext.unbindService(mScreenshotConnection); - mScreenshotConnection = null; - mScreenshotService = null; + resetConnection(); // only log an error if we're still within the timeout period if (handler.hasCallbacks(mScreenshotTimeout)) { handler.removeCallbacks(mScreenshotTimeout); @@ -383,6 +392,17 @@ public class ScreenshotHelper { } /** + * Unbinds the current screenshot connection (if any). + */ + private void resetConnection() { + if (mScreenshotConnection != null) { + mContext.unbindService(mScreenshotConnection); + mScreenshotConnection = null; + mScreenshotService = null; + } + } + + /** * Notifies the screenshot service to show an error. */ private void notifyScreenshotError() { diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 91c1548bfcc2..04ee6349f11d 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -115,6 +115,12 @@ <protected-broadcast android:name="android.app.action.EXIT_DESK_MODE" /> <protected-broadcast android:name="android.app.action.NEXT_ALARM_CLOCK_CHANGED" /> + <protected-broadcast android:name="android.app.action.USER_ADDED" /> + <protected-broadcast android:name="android.app.action.USER_REMOVED" /> + <protected-broadcast android:name="android.app.action.USER_STARTED" /> + <protected-broadcast android:name="android.app.action.USER_STOPPED" /> + <protected-broadcast android:name="android.app.action.USER_SWITCHED" /> + <protected-broadcast android:name="android.app.action.BUGREPORT_SHARING_DECLINED" /> <protected-broadcast android:name="android.app.action.BUGREPORT_FAILED" /> <protected-broadcast android:name="android.app.action.BUGREPORT_SHARE" /> diff --git a/core/res/res/color-car/car_borderless_button_text_color.xml b/core/res/res/color-car/car_borderless_button_text_color.xml index 1cdd6cd901af..0a86e4012e99 100644 --- a/core/res/res/color-car/car_borderless_button_text_color.xml +++ b/core/res/res/color-car/car_borderless_button_text_color.xml @@ -16,5 +16,6 @@ limitations under the License. <!-- Default text colors for car buttons when enabled/disabled. --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:color="@*android:color/car_grey_700" android:state_enabled="false"/> + <item android:color="@*android:color/car_grey_700" android:state_ux_restricted="true"/> <item android:color="?android:attr/colorButtonNormal"/> </selector> diff --git a/core/res/res/color-car/car_switch_track.xml b/core/res/res/color-car/car_switch_track.xml new file mode 100644 index 000000000000..8ca67dd8dda9 --- /dev/null +++ b/core/res/res/color-car/car_switch_track.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 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. +--> +<!-- copy of switch_track_material, but with a ux restricted state --> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_enabled="false" + android:color="?attr/colorForeground" + android:alpha="?attr/disabledAlpha" /> + <item android:state_ux_restricted="true" + android:color="?attr/colorForeground" + android:alpha="?attr/disabledAlpha" /> + <item android:state_checked="true" + android:color="?attr/colorControlActivated" /> + <item android:color="?attr/colorForeground" /> +</selector> diff --git a/core/res/res/drawable-car/car_button_background.xml b/core/res/res/drawable-car/car_button_background.xml index e568aebfe81d..13b0ec13f355 100644 --- a/core/res/res/drawable-car/car_button_background.xml +++ b/core/res/res/drawable-car/car_button_background.xml @@ -25,6 +25,22 @@ limitations under the License. android:color="#0059B3"/> </shape> </item> + <item android:state_focused="true" android:state_pressed="true" android:state_ux_restricted="true"> + <shape android:shape="rectangle"> + <corners android:radius="@*android:dimen/car_button_radius"/> + <solid android:color="@*android:color/car_grey_300"/> + <stroke android:width="4dp" + android:color="#0059B3"/> + </shape> + </item> + <item android:state_focused="true" android:state_ux_restricted="true"> + <shape android:shape="rectangle"> + <corners android:radius="@*android:dimen/car_button_radius"/> + <solid android:color="@*android:color/car_grey_300"/> + <stroke android:width="8dp" + android:color="#0059B3"/> + </shape> + </item> <item android:state_focused="true" android:state_pressed="true"> <shape android:shape="rectangle"> <corners android:radius="@*android:dimen/car_button_radius"/> @@ -47,6 +63,12 @@ limitations under the License. <solid android:color="@*android:color/car_grey_300"/> </shape> </item> + <item android:state_ux_restricted="true"> + <shape android:shape="rectangle"> + <corners android:radius="@*android:dimen/car_button_radius"/> + <solid android:color="@*android:color/car_grey_300"/> + </shape> + </item> <item> <ripple android:color="?android:attr/colorControlHighlight"> <item> diff --git a/core/res/res/drawable-car/car_switch_track.xml b/core/res/res/drawable-car/car_switch_track.xml index cb0b9beeeab6..51e9f7eb4ebc 100644 --- a/core/res/res/drawable-car/car_switch_track.xml +++ b/core/res/res/drawable-car/car_switch_track.xml @@ -41,7 +41,7 @@ android:right="@dimen/car_switch_track_margin_size"> <shape android:shape="rectangle" - android:tint="@color/switch_track_material"> + android:tint="@color/car_switch_track"> <corners android:radius="7dp" /> <solid android:color="@color/white_disabled_material" /> <size android:height="14dp" /> diff --git a/core/res/res/values/attrs_car.xml b/core/res/res/values/attrs_car.xml new file mode 100644 index 000000000000..6bfea9728b69 --- /dev/null +++ b/core/res/res/values/attrs_car.xml @@ -0,0 +1,24 @@ +<?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. +--> + +<!-- Formatting note: terminate all comments with a period, to avoid breaking + the documentation output. To suppress comment lines from the documentation + output, insert an eat-comment element after the comment lines. +--> + +<resources> + <attr name="state_ux_restricted" format="boolean"/> +</resources> diff --git a/core/res/res/values/required_apps_managed_device.xml b/core/res/res/values/required_apps_managed_device.xml index 40db9dfbcd2c..4c5cd6861601 100644 --- a/core/res/res/values/required_apps_managed_device.xml +++ b/core/res/res/values/required_apps_managed_device.xml @@ -27,5 +27,6 @@ <item>com.android.providers.downloads</item> <item>com.android.providers.downloads.ui</item> <item>com.android.documentsui</item> + <item>com.android.cellbroadcastreceiver</item> </string-array> </resources> diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp index 4181163610ec..a9b34af2ab0e 100644 --- a/data/etc/car/Android.bp +++ b/data/etc/car/Android.bp @@ -138,13 +138,6 @@ prebuilt_etc { } prebuilt_etc { - name: "privapp_whitelist_com.android.car.companiondevicesupport", - sub_dir: "permissions", - src: "com.android.car.companiondevicesupport.xml", - filename_from_src: true, -} - -prebuilt_etc { name: "privapp_whitelist_com.google.android.car.kitchensink", sub_dir: "permissions", src: "com.google.android.car.kitchensink.xml", @@ -160,13 +153,6 @@ prebuilt_etc { } prebuilt_etc { - name: "privapp_whitelist_com.android.car.floatingcardslauncher", - sub_dir: "permissions", - src: "com.android.car.floatingcardslauncher.xml", - filename_from_src: true, -} - -prebuilt_etc { name: "privapp_allowlist_com.google.android.car.networking.preferenceupdater", sub_dir: "permissions", src: "com.google.android.car.networking.preferenceupdater.xml", @@ -186,3 +172,10 @@ prebuilt_etc { src: "com.android.car.shell.xml", filename_from_src: true, } + +prebuilt_etc { + name: "allowed_privapp_com.android.car.activityresolver", + sub_dir: "permissions", + src: "com.android.car.activityresolver.xml", + filename_from_src: true, +} diff --git a/data/etc/car/com.android.car.floatingcardslauncher.xml b/data/etc/car/com.android.car.activityresolver.xml index 2755fee4eb55..63f83b4afb62 100644 --- a/data/etc/car/com.android.car.floatingcardslauncher.xml +++ b/data/etc/car/com.android.car.activityresolver.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - ~ Copyright (C) 2019 The Android Open Source Project + ~ 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. @@ -12,14 +12,10 @@ ~ 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. --> <permissions> - <privapp-permissions package="com.android.car.floatingcardslauncher"> - <permission name="android.permission.ACTIVITY_EMBEDDING"/> - <permission name="android.permission.INTERACT_ACROSS_USERS"/> + <privapp-permissions package="com.android.car.activityresolver"> <permission name="android.permission.MANAGE_USERS"/> - <permission name="android.permission.MEDIA_CONTENT_CONTROL"/> - <permission name="android.permission.MODIFY_PHONE_STATE"/> - </privapp-permissions> + </privapp-permissions> </permissions> diff --git a/data/etc/car/com.android.car.companiondevicesupport.xml b/data/etc/car/com.android.car.companiondevicesupport.xml deleted file mode 100644 index 2067bab20d3d..000000000000 --- a/data/etc/car/com.android.car.companiondevicesupport.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2019 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 - --> -<permissions> - <privapp-permissions package="com.android.car.companiondevicesupport"> - <permission name="android.permission.INTERACT_ACROSS_USERS"/> - <permission name="android.permission.MANAGE_USERS"/> - <permission name="android.permission.PROVIDE_TRUST_AGENT"/> - <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/> - </privapp-permissions> -</permissions> diff --git a/data/etc/car/com.android.car.shell.xml b/data/etc/car/com.android.car.shell.xml index 32666c8d9f68..6132d53b4651 100644 --- a/data/etc/car/com.android.car.shell.xml +++ b/data/etc/car/com.android.car.shell.xml @@ -15,7 +15,9 @@ ~ limitations under the License --> <permissions> - <privapp-permissions package="com.android.car.shell"> + <!-- CarShell now overrides the shell package and adding permission here + is ok. --> + <privapp-permissions package="com.android.shell"> <permission name="android.permission.INSTALL_PACKAGES" /> <permission name="android.permission.MEDIA_CONTENT_CONTROL"/> </privapp-permissions> diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java index 1a367d9b1734..f9785e9fd16e 100644 --- a/media/java/android/media/MediaRouter.java +++ b/media/java/android/media/MediaRouter.java @@ -381,7 +381,12 @@ public class MediaRouter { } public Display[] getAllPresentationDisplays() { - return mDisplayService.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION); + try { + return mDisplayService.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION); + } catch (RuntimeException ex) { + Log.e(TAG, "Unable to get displays.", ex); + return null; + } } private void updatePresentationDisplays(int changedDisplayId) { @@ -2085,6 +2090,9 @@ public class MediaRouter { private Display choosePresentationDisplay() { if ((mSupportedTypes & ROUTE_TYPE_LIVE_VIDEO) != 0) { Display[] displays = sStatic.getAllPresentationDisplays(); + if (displays == null || displays.length == 0) { + return null; + } // Ensure that the specified display is valid for presentations. // This check will normally disallow the default display unless it was diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java index ed56b4398c22..798bf6e2f8ee 100755 --- a/media/java/android/mtp/MtpDatabase.java +++ b/media/java/android/mtp/MtpDatabase.java @@ -19,8 +19,7 @@ package android.mtp; import android.annotation.NonNull; import android.content.BroadcastReceiver; import android.content.ContentProviderClient; -import android.content.ContentUris; -import android.content.ContentValues; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -32,7 +31,6 @@ import android.media.ExifInterface; import android.media.ThumbnailUtils; import android.net.Uri; import android.os.BatteryManager; -import android.os.RemoteException; import android.os.SystemProperties; import android.os.storage.StorageVolume; import android.provider.MediaStore; @@ -103,8 +101,6 @@ public class MtpDatabase implements AutoCloseable { private MtpStorageManager mManager; private static final String PATH_WHERE = Files.FileColumns.DATA + "=?"; - private static final String[] ID_PROJECTION = new String[] {Files.FileColumns._ID}; - private static final String[] PATH_PROJECTION = new String[] {Files.FileColumns.DATA}; private static final String NO_MEDIA = ".nomedia"; static { @@ -431,7 +427,7 @@ public class MtpDatabase implements AutoCloseable { } // Add the new file to MediaProvider if (succeeded) { - MediaStore.scanFile(mContext.getContentResolver(), obj.getPath().toFile()); + updateMediaStore(mContext, obj.getPath().toFile()); } } @@ -580,32 +576,8 @@ public class MtpDatabase implements AutoCloseable { return MtpConstants.RESPONSE_GENERAL_ERROR; } - // finally update MediaProvider - ContentValues values = new ContentValues(); - values.put(Files.FileColumns.DATA, newPath.toString()); - String[] whereArgs = new String[]{oldPath.toString()}; - try { - // note - we are relying on a special case in MediaProvider.update() to update - // the paths for all children in the case where this is a directory. - final Uri objectsUri = MediaStore.Files.getContentUri(obj.getVolumeName()); - mMediaProvider.update(objectsUri, values, PATH_WHERE, whereArgs); - } catch (RemoteException e) { - Log.e(TAG, "RemoteException in mMediaProvider.update", e); - } - - // check if nomedia status changed - if (obj.isDir()) { - // for directories, check if renamed from something hidden to something non-hidden - if (oldPath.getFileName().startsWith(".") && !newPath.startsWith(".")) { - MediaStore.scanFile(mContext.getContentResolver(), newPath.toFile()); - } - } else { - // for files, check if renamed from .nomedia to something else - if (oldPath.getFileName().toString().toLowerCase(Locale.US).equals(NO_MEDIA) - && !newPath.getFileName().toString().toLowerCase(Locale.US).equals(NO_MEDIA)) { - MediaStore.scanFile(mContext.getContentResolver(), newPath.getParent().toFile()); - } - } + updateMediaStore(mContext, oldPath.toFile()); + updateMediaStore(mContext, newPath.toFile()); return MtpConstants.RESPONSE_OK; } @@ -635,48 +607,15 @@ public class MtpDatabase implements AutoCloseable { Log.e(TAG, "Failed to end move object"); return; } - obj = mManager.getObject(objId); if (!success || obj == null) return; - // Get parent info from MediaProvider, since the id is different from MTP's - ContentValues values = new ContentValues(); + Path path = newParentObj.getPath().resolve(name); Path oldPath = oldParentObj.getPath().resolve(name); - values.put(Files.FileColumns.DATA, path.toString()); - if (obj.getParent().isRoot()) { - values.put(Files.FileColumns.PARENT, 0); - } else { - int parentId = findInMedia(newParentObj, path.getParent()); - if (parentId != -1) { - values.put(Files.FileColumns.PARENT, parentId); - } else { - // The new parent isn't in MediaProvider, so delete the object instead - deleteFromMedia(obj, oldPath, obj.isDir()); - return; - } - } - // update MediaProvider - Cursor c = null; - String[] whereArgs = new String[]{oldPath.toString()}; - try { - int parentId = -1; - if (!oldParentObj.isRoot()) { - parentId = findInMedia(oldParentObj, oldPath.getParent()); - } - if (oldParentObj.isRoot() || parentId != -1) { - // Old parent exists in MediaProvider - perform a move - // note - we are relying on a special case in MediaProvider.update() to update - // the paths for all children in the case where this is a directory. - final Uri objectsUri = MediaStore.Files.getContentUri(obj.getVolumeName()); - mMediaProvider.update(objectsUri, values, PATH_WHERE, whereArgs); - } else { - // Old parent doesn't exist - add the object - MediaStore.scanFile(mContext.getContentResolver(), path.toFile()); - } - } catch (RemoteException e) { - Log.e(TAG, "RemoteException in mMediaProvider.update", e); - } + + updateMediaStore(mContext, oldPath.toFile()); + updateMediaStore(mContext, path.toFile()); } @VisibleForNative @@ -699,7 +638,19 @@ public class MtpDatabase implements AutoCloseable { if (!success) { return; } - MediaStore.scanFile(mContext.getContentResolver(), obj.getPath().toFile()); + + updateMediaStore(mContext, obj.getPath().toFile()); + } + + private static void updateMediaStore(@NonNull Context context, @NonNull File file) { + final ContentResolver resolver = context.getContentResolver(); + // For file, check whether the file name is .nomedia or not. + // If yes, scan the parent directory to update all files in the directory. + if (!file.isDirectory() && file.getName().toLowerCase(Locale.ROOT).endsWith(NO_MEDIA)) { + MediaStore.scanFile(resolver, file.getParentFile()); + } else { + MediaStore.scanFile(resolver, file); + } } @VisibleForNative @@ -928,26 +879,6 @@ public class MtpDatabase implements AutoCloseable { deleteFromMedia(obj, obj.getPath(), obj.isDir()); } - private int findInMedia(MtpStorageManager.MtpObject obj, Path path) { - final Uri objectsUri = MediaStore.Files.getContentUri(obj.getVolumeName()); - - int ret = -1; - Cursor c = null; - try { - c = mMediaProvider.query(objectsUri, ID_PROJECTION, PATH_WHERE, - new String[]{path.toString()}, null, null); - if (c != null && c.moveToNext()) { - ret = c.getInt(0); - } - } catch (RemoteException e) { - Log.e(TAG, "Error finding " + path + " in MediaProvider"); - } finally { - if (c != null) - c.close(); - } - return ret; - } - private void deleteFromMedia(MtpStorageManager.MtpObject obj, Path path, boolean isDir) { final Uri objectsUri = MediaStore.Files.getContentUri(obj.getVolumeName()); try { @@ -963,13 +894,10 @@ public class MtpDatabase implements AutoCloseable { } String[] whereArgs = new String[]{path.toString()}; - if (mMediaProvider.delete(objectsUri, PATH_WHERE, whereArgs) > 0) { - if (!isDir && path.toString().toLowerCase(Locale.US).endsWith(NO_MEDIA)) { - MediaStore.scanFile(mContext.getContentResolver(), path.getParent().toFile()); - } - } else { - Log.i(TAG, "Mediaprovider didn't delete " + path); + if (mMediaProvider.delete(objectsUri, PATH_WHERE, whereArgs) == 0) { + Log.i(TAG, "MediaProvider didn't delete " + path); } + updateMediaStore(mContext, path.toFile()); } catch (Exception e) { Log.d(TAG, "Failed to delete " + path + " from MediaProvider"); } diff --git a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml index cdc29eec21cd..cb1aadaf7fe7 100644 --- a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml +++ b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml @@ -77,8 +77,8 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:background="@null" - systemui:intent="intent:#Intent;component=com.android.car.settings/.common.CarSettingActivities$QuickSettingActivity;launchFlags=0x24000000;end" - /> + android:focusedByDefault="true" + systemui:intent="intent:#Intent;component=com.android.car.settings/.common.CarSettingActivities$QuickSettingActivity;launchFlags=0x24000000;end"/> <com.android.systemui.statusbar.policy.Clock android:id="@+id/clock" android:layout_width="wrap_content" diff --git a/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml index 9634950e4748..cfa02a2023a1 100644 --- a/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml +++ b/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml @@ -74,7 +74,8 @@ android:id="@+id/qs" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@null"/> + android:background="@null" + android:focusedByDefault="true"/> <com.android.systemui.statusbar.policy.Clock android:id="@+id/clock" android:layout_width="wrap_content" diff --git a/packages/CarSystemUI/res/layout/headsup_container_bottom.xml b/packages/CarSystemUI/res/layout/headsup_container_bottom.xml index f43f02dfcc8c..c28da39f04b2 100644 --- a/packages/CarSystemUI/res/layout/headsup_container_bottom.xml +++ b/packages/CarSystemUI/res/layout/headsup_container_bottom.xml @@ -35,7 +35,8 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent"/> + app:layout_constraintTop_toTopOf="parent" + app:shouldRestoreFocus="false"/> <View android:id="@+id/scrim" diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java index fd804c71c9d0..b83fcf4bf8b5 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java @@ -28,6 +28,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import android.view.GestureDetector; +import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; @@ -245,6 +246,17 @@ public class NotificationPanelViewController extends OverlayPanelViewController mNotificationView = (CarNotificationView) LayoutInflater.from(mContext).inflate( R.layout.notification_center_activity, container, /* attachToRoot= */ false); + mNotificationView.setKeyEventHandler( + event -> { + if (event.getKeyCode() != KeyEvent.KEYCODE_BACK) { + return false; + } + + if (event.getAction() == KeyEvent.ACTION_UP && isPanelExpanded()) { + toggle(); + } + return true; + }); container.addView(mNotificationView); onNotificationViewInflated(); diff --git a/packages/CompanionDeviceManager/AndroidManifest.xml b/packages/CompanionDeviceManager/AndroidManifest.xml index ea9b52cbf3d5..e4e5b9fa781a 100644 --- a/packages/CompanionDeviceManager/AndroidManifest.xml +++ b/packages/CompanionDeviceManager/AndroidManifest.xml @@ -31,6 +31,7 @@ <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/> <uses-permission android:name="android.permission.RADIO_SCAN_WITHOUT_LOCATION"/> <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/> + <uses-permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS"/> <application android:allowClearUserData="true" diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java index e501e1269aeb..5ac059be2010 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java @@ -17,6 +17,7 @@ package com.android.companiondevicemanager; import static android.companion.BluetoothDeviceFilterUtils.getDeviceMacAddress; +import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; import static java.util.Objects.requireNonNull; @@ -58,6 +59,8 @@ public class DeviceChooserActivity extends Activity { Log.e(LOG_TAG, "About to show UI, but no devices to show"); } + getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); + if (getService().mRequest.isSingleDevice()) { setContentView(R.layout.device_confirmation); final DeviceFilterPair selectedDevice = getService().mDevicesFound.get(0); diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java index 5675c9986ac9..665d262d8067 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java @@ -343,17 +343,19 @@ public class PackageInstallerActivity extends AlertActivity { if (!wasSetUp) { return; } - - // load dummy layout with OK button disabled until we override this layout in - // startInstallConfirm - bindUi(); - checkIfAllowedAndInitiateInstall(); } @Override protected void onResume() { super.onResume(); + if (mAppSnippet != null) { + // load dummy layout with OK button disabled until we override this layout in + // startInstallConfirm + bindUi(); + checkIfAllowedAndInitiateInstall(); + } + if (mOk != null) { mOk.setEnabled(mEnableOk); } diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java index ce60fafaa31b..b3205d7563b2 100644 --- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java +++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java @@ -25,6 +25,7 @@ import static android.os.BatteryManager.EXTRA_LEVEL; import static android.os.BatteryManager.EXTRA_MAX_CHARGING_CURRENT; import static android.os.BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE; import static android.os.BatteryManager.EXTRA_PLUGGED; +import static android.os.BatteryManager.EXTRA_PRESENT; import static android.os.BatteryManager.EXTRA_STATUS; import android.content.Context; @@ -50,14 +51,16 @@ public class BatteryStatus { public final int plugged; public final int health; public final int maxChargingWattage; + public final boolean present; public BatteryStatus(int status, int level, int plugged, int health, - int maxChargingWattage) { + int maxChargingWattage, boolean present) { this.status = status; this.level = level; this.plugged = plugged; this.health = health; this.maxChargingWattage = maxChargingWattage; + this.present = present; } public BatteryStatus(Intent batteryChangedIntent) { @@ -65,6 +68,7 @@ public class BatteryStatus { plugged = batteryChangedIntent.getIntExtra(EXTRA_PLUGGED, 0); level = batteryChangedIntent.getIntExtra(EXTRA_LEVEL, 0); health = batteryChangedIntent.getIntExtra(EXTRA_HEALTH, BATTERY_HEALTH_UNKNOWN); + present = batteryChangedIntent.getBooleanExtra(EXTRA_PRESENT, true); final int maxChargingMicroAmp = batteryChangedIntent.getIntExtra(EXTRA_MAX_CHARGING_CURRENT, -1); diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java index cb610fc61142..bcde58494838 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java @@ -89,7 +89,7 @@ public class SystemSettingsValidators { return value == null || value.length() < MAX_LENGTH; } }); - VALIDATORS.put(System.FONT_SCALE, new InclusiveFloatRangeValidator(0.85f, 1.3f)); + VALIDATORS.put(System.FONT_SCALE, new InclusiveFloatRangeValidator(0.25f, 5.0f)); VALIDATORS.put(System.DIM_SCREEN, BOOLEAN_VALIDATOR); VALIDATORS.put( System.DISPLAY_COLOR_MODE, diff --git a/packages/Shell/Android.bp b/packages/Shell/Android.bp index 279cb84436d5..092c5439d7ca 100644 --- a/packages/Shell/Android.bp +++ b/packages/Shell/Android.bp @@ -7,13 +7,17 @@ package { default_applicable_licenses: ["frameworks_base_license"], } +// used both for the android_app and android_library +shell_srcs = ["src/**/*.java",":dumpstate_aidl"] +shell_static_libs = ["androidx.legacy_legacy-support-v4"] + android_app { name: "Shell", - srcs: ["src/**/*.java",":dumpstate_aidl"], + srcs: shell_srcs, aidl: { include_dirs: ["frameworks/native/cmds/dumpstate/binder"], }, - static_libs: ["androidx.legacy_legacy-support-v4"], + static_libs: shell_static_libs, platform_apis: true, certificate: "platform", privileged: true, @@ -21,3 +25,17 @@ android_app { include_filter: ["com.android.shell.*"], }, } + +// A library for product type like auto to create a new shell package +// with product specific permissions. +android_library { + name: "Shell-package-library", + srcs: shell_srcs, + aidl: { + include_dirs: ["frameworks/native/cmds/dumpstate/binder"], + }, + resource_dirs: ["res"], + static_libs: shell_static_libs, + platform_apis: true, + manifest: "AndroidManifest.xml", +} diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java index 9d52098f37d5..63f8b1f5dbb8 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java @@ -30,7 +30,7 @@ import java.io.PrintWriter; */ @ProvidesInterface(version = FalsingManager.VERSION) public interface FalsingManager { - int VERSION = 4; + int VERSION = 5; void onSuccessfulUnlock(); @@ -42,7 +42,8 @@ public interface FalsingManager { boolean isUnlockingDisabled(); - boolean isFalseTouch(); + /** Returns true if the gesture should be rejected. */ + boolean isFalseTouch(int interactionType); void onNotificatonStopDraggingDown(); diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java index 02c4c5eff26e..4b6efa91a7c8 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java @@ -14,16 +14,16 @@ package com.android.systemui.plugins.statusbar; -import com.android.systemui.plugins.annotations.DependsOn; -import com.android.systemui.plugins.annotations.ProvidesInterface; -import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption; - import android.service.notification.SnoozeCriterion; import android.service.notification.StatusBarNotification; import android.view.MotionEvent; import android.view.View; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; +import com.android.systemui.plugins.annotations.DependsOn; +import com.android.systemui.plugins.annotations.ProvidesInterface; +import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption; + @ProvidesInterface(version = NotificationSwipeActionHelper.VERSION) @DependsOn(target = SnoozeOption.class) public interface NotificationSwipeActionHelper { @@ -52,7 +52,8 @@ public interface NotificationSwipeActionHelper { public boolean isDismissGesture(MotionEvent ev); - public boolean isFalseGesture(MotionEvent ev); + /** Returns true if the gesture should be rejected. */ + boolean isFalseGesture(); public boolean swipedFarEnough(float translation, float viewSize); diff --git a/packages/SystemUI/res/drawable/ic_battery_unknown.xml b/packages/SystemUI/res/drawable/ic_battery_unknown.xml new file mode 100644 index 000000000000..8b2ba12fe0be --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_battery_unknown.xml @@ -0,0 +1,24 @@ +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="12dp" + android:height="24dp" + android:viewportWidth="12.0" + android:viewportHeight="24.0"> + <path + android:pathData="M10.404,2.4L8.4,2.4L8.4,0L3.6,0L3.6,2.4L1.596,2.4C0.72,2.4 0,3.12 0,3.996L0,22.392C0,23.28 0.72,24 1.596,24L10.392,24C11.28,24 12,23.28 12,22.404L12,3.996C12,3.12 11.28,2.4 10.404,2.4ZM7.14,19.14L4.86,19.14L4.86,16.86L7.14,16.86L7.14,19.14ZM8.76,12.828C8.76,12.828 8.304,13.332 7.956,13.68C7.38,14.256 6.96,15.06 6.96,15.6L5.04,15.6C5.04,14.604 5.592,13.776 6.156,13.2L7.272,12.072C7.596,11.748 7.8,11.292 7.8,10.8C7.8,9.804 6.996,9 6,9C5.004,9 4.2,9.804 4.2,10.8L2.4,10.8C2.4,8.808 4.008,7.2 6,7.2C7.992,7.2 9.6,8.808 9.6,10.8C9.6,11.592 9.276,12.312 8.76,12.828L8.76,12.828Z" + android:fillColor="#ffffff" /> +</vector> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 009773849c5f..827721ce923b 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -581,4 +581,9 @@ <integer name="controls_max_columns_adjust_below_width_dp">320</integer> <!-- If the config font scale is >= this value, potentially adjust the number of columns--> <item name="controls_max_columns_adjust_above_font_scale" translatable="false" format="float" type="dimen">1.25</item> + + <!-- Whether or not to show a notification for an unknown battery state --> + <bool name="config_showNotificationForUnknownBatteryState">false</bool> + <!-- content URL in a notification when ACTION_BATTERY_CHANGED.EXTRA_PRESENT field is false --> + <string translatable="false" name="config_batteryStateUnknownUrl"></string> </resources> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 824521ecd1e7..174f5c737d14 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -437,6 +437,8 @@ <string name="accessibility_battery_three_bars">Battery three bars.</string> <!-- Content description of the battery when it is full for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> <string name="accessibility_battery_full">Battery full.</string> + <!-- Content description of the battery when battery state is unknown for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_battery_unknown">Battery percentage unknown.</string> <!-- Content description of the phone signal when no signal for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> <string name="accessibility_no_phone">No phone.</string> @@ -2870,4 +2872,11 @@ <string name="media_output_dialog_connect_failed">Couldn\'t connect. Try again.</string> <!-- Title for pairing item [CHAR LIMIT=60] --> <string name="media_output_dialog_pairing_new">Pair new device</string> + + <!-- Title to display in a notification when ACTION_BATTERY_CHANGED.EXTRA_PRESENT field is false + [CHAR LIMIT=NONE] --> + <string name="battery_state_unknown_notification_title">Problem reading your battery meter</string> + <!-- Text to display in a notification when ACTION_BATTERY_CHANGED.EXTRA_PRESENT field is false + [CHAR LIMIT=NONE] --> + <string name="battery_state_unknown_notification_text">Tap for more information</string> </resources> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 60cd24019b97..deaa4255c2f1 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -1696,7 +1696,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } // Take a guess at initial SIM state, battery status and PLMN until we get an update - mBatteryStatus = new BatteryStatus(BATTERY_STATUS_UNKNOWN, 100, 0, 0, 0); + mBatteryStatus = new BatteryStatus(BATTERY_STATUS_UNKNOWN, 100, 0, 0, 0, true); // Watch for interesting updates final IntentFilter filter = new IntentFilter(); @@ -2563,6 +2563,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab final boolean wasPluggedIn = old.isPluggedIn(); final boolean stateChangedWhilePluggedIn = wasPluggedIn && nowPluggedIn && (old.status != current.status); + final boolean nowPresent = current.present; + final boolean wasPresent = old.present; // change in plug state is always interesting if (wasPluggedIn != nowPluggedIn || stateChangedWhilePluggedIn) { @@ -2584,6 +2586,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab return true; } + // Battery either showed up or disappeared + if (wasPresent != nowPresent) { + return true; + } + return false; } diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java index 5235a451d021..10f9f5475f19 100644 --- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java @@ -33,6 +33,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.database.ContentObserver; import android.graphics.Rect; +import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Handler; import android.provider.Settings; @@ -95,12 +96,15 @@ public class BatteryMeterView extends LinearLayout implements private int mTextColor; private int mLevel; private int mShowPercentMode = MODE_DEFAULT; - private boolean mForceShowPercent; private boolean mShowPercentAvailable; // Some places may need to show the battery conditionally, and not obey the tuner private boolean mIgnoreTunerUpdates; private boolean mIsSubscribedForTunerUpdates; private boolean mCharging; + // Error state where we know nothing about the current battery state + private boolean mBatteryStateUnknown; + // Lazily-loaded since this is expected to be a rare-if-ever state + private Drawable mUnknownStateDrawable; private DualToneHandler mDualToneHandler; private int mUser; @@ -350,6 +354,11 @@ public class BatteryMeterView extends LinearLayout implements } private void updatePercentText() { + if (mBatteryStateUnknown) { + setContentDescription(getContext().getString(R.string.accessibility_battery_unknown)); + return; + } + if (mBatteryController == null) { return; } @@ -390,9 +399,13 @@ public class BatteryMeterView extends LinearLayout implements final boolean systemSetting = 0 != whitelistIpcs(() -> Settings.System .getIntForUser(getContext().getContentResolver(), SHOW_BATTERY_PERCENT, 0, mUser)); + boolean shouldShow = + (mShowPercentAvailable && systemSetting && mShowPercentMode != MODE_OFF) + || mShowPercentMode == MODE_ON + || mShowPercentMode == MODE_ESTIMATE; + shouldShow = shouldShow && !mBatteryStateUnknown; - if ((mShowPercentAvailable && systemSetting && mShowPercentMode != MODE_OFF) - || mShowPercentMode == MODE_ON || mShowPercentMode == MODE_ESTIMATE) { + if (shouldShow) { if (!showing) { mBatteryPercentView = loadPercentView(); if (mPercentageStyleId != 0) { // Only set if specified as attribute @@ -418,6 +431,32 @@ public class BatteryMeterView extends LinearLayout implements scaleBatteryMeterViews(); } + private Drawable getUnknownStateDrawable() { + if (mUnknownStateDrawable == null) { + mUnknownStateDrawable = mContext.getDrawable(R.drawable.ic_battery_unknown); + mUnknownStateDrawable.setTint(mTextColor); + } + + return mUnknownStateDrawable; + } + + @Override + public void onBatteryUnknownStateChanged(boolean isUnknown) { + if (mBatteryStateUnknown == isUnknown) { + return; + } + + mBatteryStateUnknown = isUnknown; + + if (mBatteryStateUnknown) { + mBatteryIconView.setImageDrawable(getUnknownStateDrawable()); + } else { + mBatteryIconView.setImageDrawable(mDrawable); + } + + updateShowPercent(); + } + /** * Looks up the scale factor for status bar icons and scales the battery view by that amount. */ @@ -458,6 +497,10 @@ public class BatteryMeterView extends LinearLayout implements if (mBatteryPercentView != null) { mBatteryPercentView.setTextColor(singleToneColor); } + + if (mUnknownStateDrawable != null) { + mUnknownStateDrawable.setTint(singleToneColor); + } } public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { @@ -467,8 +510,8 @@ public class BatteryMeterView extends LinearLayout implements pw.println(" mDrawable.getPowerSave: " + powerSave); pw.println(" mBatteryPercentView.getText(): " + percent); pw.println(" mTextColor: #" + Integer.toHexString(mTextColor)); + pw.println(" mBatteryStateUnknown: " + mBatteryStateUnknown); pw.println(" mLevel: " + mLevel); - pw.println(" mForceShowPercent: " + mForceShowPercent); } private final class SettingObserver extends ContentObserver { diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java index d17ca4041b31..0bb8c9cd81f3 100644 --- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java @@ -16,6 +16,8 @@ package com.android.systemui; +import static com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; @@ -697,14 +699,15 @@ public class SwipeHelper implements Gefingerpoken { float translation = getTranslation(mCurrView); return ev.getActionMasked() == MotionEvent.ACTION_UP && !mFalsingManager.isUnlockingDisabled() - && !isFalseGesture(ev) && (swipedFastEnough() || swipedFarEnough()) + && !isFalseGesture() && (swipedFastEnough() || swipedFarEnough()) && mCallback.canChildBeDismissedInDirection(mCurrView, translation > 0); } - public boolean isFalseGesture(MotionEvent ev) { + /** Returns true if the gesture should be rejected. */ + public boolean isFalseGesture() { boolean falsingDetected = mCallback.isAntiFalsingNeeded(); if (mFalsingManager.isClassifierEnabled()) { - falsingDetected = falsingDetected && mFalsingManager.isFalseTouch(); + falsingDetected = falsingDetected && mFalsingManager.isFalseTouch(NOTIFICATION_DISMISS); } else { falsingDetected = falsingDetected && !mTouchAboveFalsingThreshold; } diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java index 708002d5b946..1f41038c260f 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIService.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java @@ -32,6 +32,7 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpHandler; import com.android.systemui.dump.LogBufferFreezer; import com.android.systemui.dump.SystemUIAuxiliaryDumpService; +import com.android.systemui.statusbar.policy.BatteryStateNotifier; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -44,18 +45,21 @@ public class SystemUIService extends Service { private final DumpHandler mDumpHandler; private final BroadcastDispatcher mBroadcastDispatcher; private final LogBufferFreezer mLogBufferFreezer; + private final BatteryStateNotifier mBatteryStateNotifier; @Inject public SystemUIService( @Main Handler mainHandler, DumpHandler dumpHandler, BroadcastDispatcher broadcastDispatcher, - LogBufferFreezer logBufferFreezer) { + LogBufferFreezer logBufferFreezer, + BatteryStateNotifier batteryStateNotifier) { super(); mMainHandler = mainHandler; mDumpHandler = dumpHandler; mBroadcastDispatcher = broadcastDispatcher; mLogBufferFreezer = logBufferFreezer; + mBatteryStateNotifier = batteryStateNotifier; } @Override @@ -68,6 +72,11 @@ public class SystemUIService extends Service { // Finish initializing dump logic mLogBufferFreezer.attach(mBroadcastDispatcher); + // If configured, set up a battery notification + if (getResources().getBoolean(R.bool.config_showNotificationForUnknownBatteryState)) { + mBatteryStateNotifier.startListening(); + } + // For debugging RescueParty if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean("debug.crash_sysui", false)) { throw new RuntimeException(); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java index 1d47fc520ec2..7daad1ca7e79 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java @@ -160,12 +160,12 @@ public class AuthBiometricFaceView extends AuthBiometricView { @Override protected void handleResetAfterError() { - resetErrorView(mContext, mIndicatorView); + resetErrorView(); } @Override protected void handleResetAfterHelp() { - resetErrorView(mContext, mIndicatorView); + resetErrorView(); } @Override @@ -185,7 +185,7 @@ public class AuthBiometricFaceView extends AuthBiometricView { if (newState == STATE_AUTHENTICATING_ANIMATING_IN || (newState == STATE_AUTHENTICATING && mSize == AuthDialog.SIZE_MEDIUM)) { - resetErrorView(mContext, mIndicatorView); + resetErrorView(); } // Do this last since the state variable gets updated. @@ -204,9 +204,8 @@ public class AuthBiometricFaceView extends AuthBiometricView { super.onAuthenticationFailed(failureReason); } - static void resetErrorView(Context context, TextView textView) { - textView.setTextColor(context.getResources().getColor( - R.color.biometric_dialog_gray, context.getTheme())); - textView.setVisibility(View.INVISIBLE); + private void resetErrorView() { + mIndicatorView.setTextColor(mTextColorHint); + mIndicatorView.setVisibility(View.INVISIBLE); } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java index 176e9e6b1c9b..45ee4ad9ae50 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java @@ -78,7 +78,7 @@ public class AuthBiometricFingerprintView extends AuthBiometricView { private void showTouchSensorString() { mIndicatorView.setText(R.string.fingerprint_dialog_touch_sensor); - mIndicatorView.setTextColor(R.color.biometric_dialog_gray); + mIndicatorView.setTextColor(mTextColorHint); } private void updateIcon(int lastState, int newState) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java index 7c25d2811793..f9c6d32d1d19 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java @@ -82,7 +82,7 @@ public abstract class AuthBiometricView extends LinearLayout { * Authenticated, dialog animating away soon. */ protected static final int STATE_AUTHENTICATED = 6; - + @Retention(RetentionPolicy.SOURCE) @IntDef({STATE_IDLE, STATE_AUTHENTICATING_ANIMATING_IN, STATE_AUTHENTICATING, STATE_HELP, STATE_ERROR, STATE_PENDING_CONFIRMATION, STATE_AUTHENTICATED}) @@ -155,8 +155,8 @@ public abstract class AuthBiometricView extends LinearLayout { private final Injector mInjector; private final Handler mHandler; private final AccessibilityManager mAccessibilityManager; - private final int mTextColorError; - private final int mTextColorHint; + protected final int mTextColorError; + protected final int mTextColorHint; private AuthPanelController mPanelController; private Bundle mBiometricPromptBundle; @@ -169,7 +169,7 @@ public abstract class AuthBiometricView extends LinearLayout { private TextView mSubtitleView; private TextView mDescriptionView; protected ImageView mIconView; - @VisibleForTesting protected TextView mIndicatorView; + protected TextView mIndicatorView; @VisibleForTesting Button mNegativeButton; @VisibleForTesting Button mPositiveButton; @VisibleForTesting Button mTryAgainButton; diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java index 646e62062dfb..6961b45c3c37 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java @@ -70,7 +70,7 @@ public class FalsingManagerFake implements FalsingManager { } @Override - public boolean isFalseTouch() { + public boolean isFalseTouch(@Classifier.InteractionType int interactionType) { return mIsFalseTouch; } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java index cc64fb53f15f..decaec10e572 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java @@ -262,7 +262,7 @@ public class FalsingManagerImpl implements FalsingManager { /** * @return true if the classifier determined that this is not a human interacting with the phone */ - public boolean isFalseTouch() { + public boolean isFalseTouch(@Classifier.InteractionType int interactionType) { if (FalsingLog.ENABLED) { // We're getting some false wtfs from touches that happen after the device went // to sleep. Only report missing sessions that happen when the device is interactive. diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java index f35322bd2a77..be6c5f9a905f 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java @@ -187,8 +187,8 @@ public class FalsingManagerProxy implements FalsingManager, Dumpable { } @Override - public boolean isFalseTouch() { - return mInternalFalsingManager.isFalseTouch(); + public boolean isFalseTouch(@Classifier.InteractionType int interactionType) { + return mInternalFalsingManager.isFalseTouch(interactionType); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java index a50f9ce9713b..9d847ca62465 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java @@ -189,7 +189,8 @@ public class BrightLineFalsingManager implements FalsingManager { } @Override - public boolean isFalseTouch() { + public boolean isFalseTouch(@Classifier.InteractionType int interactionType) { + mDataProvider.setInteractionType(interactionType); if (!mDataProvider.isDirty()) { return mPreviousResult; } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java index ea46441c8fbe..8d067489a8cc 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java @@ -116,7 +116,10 @@ public class FalsingDataProvider { * interactionType is defined by {@link com.android.systemui.classifier.Classifier}. */ final void setInteractionType(@Classifier.InteractionType int interactionType) { - this.mInteractionType = interactionType; + if (mInteractionType != interactionType) { + mInteractionType = interactionType; + mDirty = true; + } } public boolean isDirty() { diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java index aebf41b884c4..2eadbd0365e4 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java @@ -46,6 +46,7 @@ import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.UiEventLoggerImpl; import com.android.internal.logging.nano.MetricsProto; +import com.android.internal.util.IndentingPrintWriter; import com.android.systemui.plugins.SensorManagerPlugin; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.util.sensors.AsyncSensorManager; @@ -80,6 +81,7 @@ public class DozeSensors { private long mDebounceFrom; private boolean mSettingRegistered; private boolean mListening; + private boolean mListeningTouchScreenSensors; @VisibleForTesting public enum DozeSensorsUiEvent implements UiEventLogger.UiEventEnum { @@ -222,22 +224,25 @@ public class DozeSensors { /** * If sensors should be registered and sending signals. */ - public void setListening(boolean listen) { - if (mListening == listen) { + public void setListening(boolean listen, boolean includeTouchScreenSensors) { + if (mListening == listen && mListeningTouchScreenSensors == includeTouchScreenSensors) { return; } mListening = listen; + mListeningTouchScreenSensors = includeTouchScreenSensors; updateListening(); } /** * Registers/unregisters sensors based on internal state. */ - public void updateListening() { + private void updateListening() { boolean anyListening = false; for (TriggerSensor s : mSensors) { - s.setListening(mListening); - if (mListening) { + boolean listen = mListening + && (!s.mRequiresTouchscreen || mListeningTouchScreenSensors); + s.setListening(listen); + if (listen) { anyListening = true; } } @@ -309,10 +314,14 @@ public class DozeSensors { /** Dump current state */ public void dump(PrintWriter pw) { + pw.println("mListening=" + mListening); + pw.println("mListeningTouchScreenSensors=" + mListeningTouchScreenSensors); + IndentingPrintWriter idpw = new IndentingPrintWriter(pw, " "); + idpw.increaseIndent(); for (TriggerSensor s : mSensors) { - pw.println(" Sensor: " + s.toString()); + idpw.println("Sensor: " + s.toString()); } - pw.println(" ProxSensor: " + mProximitySensor.toString()); + idpw.println("ProxSensor: " + mProximitySensor.toString()); } /** diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index cbf8f578744c..043edee44492 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -38,6 +38,7 @@ import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.UiEventLoggerImpl; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.util.IndentingPrintWriter; import com.android.systemui.Dependency; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dock.DockManager; @@ -408,15 +409,12 @@ public class DozeTriggers implements DozeMachine.Part { break; case DOZE_PULSE_DONE: mDozeSensors.requestTemporaryDisable(); - // A pulse will temporarily disable sensors that require a touch screen. - // Let's make sure that they are re-enabled when the pulse is over. - mDozeSensors.updateListening(); break; case FINISH: mBroadcastReceiver.unregister(mBroadcastDispatcher); mDozeHost.removeCallback(mHostCallback); mDockManager.removeListener(mDockEventListener); - mDozeSensors.setListening(false); + mDozeSensors.setListening(false, false); mDozeSensors.setProxListening(false); mWantSensors = false; mWantProx = false; @@ -424,20 +422,16 @@ public class DozeTriggers implements DozeMachine.Part { break; default: } + mDozeSensors.setListening(mWantSensors, mWantTouchScreenSensors); } @Override public void onScreenState(int state) { mDozeSensors.onScreenState(state); - if (state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND - || state == Display.STATE_OFF) { - mDozeSensors.setProxListening(mWantProx); - mDozeSensors.setListening(mWantSensors); - mDozeSensors.setTouchscreenSensorsListening(mWantTouchScreenSensors); - } else { - mDozeSensors.setProxListening(false); - mDozeSensors.setListening(mWantSensors); - } + mDozeSensors.setProxListening(mWantProx && (state == Display.STATE_DOZE + || state == Display.STATE_DOZE_SUSPEND + || state == Display.STATE_OFF)); + mDozeSensors.setListening(mWantSensors, mWantTouchScreenSensors); } private void checkTriggersAtInit() { @@ -513,7 +507,9 @@ public class DozeTriggers implements DozeMachine.Part { pw.println(" pulsePending=" + mPulsePending); pw.println("DozeSensors:"); - mDozeSensors.dump(pw); + IndentingPrintWriter idpw = new IndentingPrintWriter(pw, " "); + idpw.increaseIndent(); + mDozeSensors.dump(idpw); } private class TriggerReceiver extends BroadcastReceiver { diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt index 77cac5023db3..486399979db7 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt @@ -30,6 +30,7 @@ import com.android.settingslib.Utils import com.android.systemui.Gefingerpoken import com.android.systemui.qs.PageIndicator import com.android.systemui.R +import com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS import com.android.systemui.plugins.FalsingManager import com.android.systemui.util.animation.PhysicsAnimator import com.android.systemui.util.concurrency.DelayableExecutor @@ -315,7 +316,8 @@ class MediaCarouselScrollHandler( return false } - private fun isFalseTouch() = falsingProtectionNeeded && falsingManager.isFalseTouch + private fun isFalseTouch() = falsingProtectionNeeded && + falsingManager.isFalseTouch(NOTIFICATION_DISMISS) private fun getMaxTranslation() = if (showsSettingsButton) { settingsButton.width diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt index 833d08808a0b..2cc31f0eba4d 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt @@ -70,6 +70,7 @@ private const val DEBUG = true private const val DEFAULT_LUMINOSITY = 0.25f private const val LUMINOSITY_THRESHOLD = 0.05f private const val SATURATION_MULTIPLIER = 0.8f +const val DEFAULT_COLOR = Color.DKGRAY private val LOADING = MediaData(-1, false, 0, null, null, null, null, null, emptyList(), emptyList(), "INVALID", null, null, null, true, null) @@ -380,7 +381,7 @@ class MediaDataManager( } else { null } - val bgColor = artworkBitmap?.let { computeBackgroundColor(it) } ?: Color.DKGRAY + val bgColor = artworkBitmap?.let { computeBackgroundColor(it) } ?: DEFAULT_COLOR val mediaAction = getResumeMediaAction(resumeAction) foregroundExecutor.execute { @@ -560,12 +561,14 @@ class MediaDataManager( private fun computeBackgroundColor(artworkBitmap: Bitmap?): Int { var color = Color.WHITE - if (artworkBitmap != null) { - // If we have art, get colors from that + if (artworkBitmap != null && artworkBitmap.width > 1 && artworkBitmap.height > 1) { + // If we have valid art, get colors from that val p = MediaNotificationProcessor.generateArtworkPaletteBuilder(artworkBitmap) .generate() val swatch = MediaNotificationProcessor.findBackgroundSwatch(p) color = swatch.rgb + } else { + return DEFAULT_COLOR } // Adapt background color, so it's always subdued and text is legible val tmpHsl = floatArrayOf(0f, 0f, 0f) diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt index 59118bf3534e..cd01cb703877 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt @@ -104,9 +104,9 @@ class PrivacyItemController @Inject constructor( uiExecutor.execute(notifyChanges) } - var allIndicatorsAvailable = isAllIndicatorsEnabled() + var allIndicatorsAvailable = false private set - var micCameraAvailable = isMicCameraEnabled() + var micCameraAvailable = false private set private val devicePropertiesChangedListener = @@ -158,10 +158,6 @@ class PrivacyItemController @Inject constructor( } init { - deviceConfigProxy.addOnPropertiesChangedListener( - DeviceConfig.NAMESPACE_PRIVACY, - uiExecutor, - devicePropertiesChangedListener) dumpManager.registerDumpable(TAG, this) } diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java index b6c6afd523b3..be4009838fcc 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java @@ -618,7 +618,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, mEntranceAnimationRunning = false; mExitAnimationRunning = false; if (!dismissed && !wasMinimizeInteraction) { - WindowManagerProxy.applyResizeSplits(snapTarget.position, mSplitLayout); + mWindowManagerProxy.applyResizeSplits(snapTarget.position, mSplitLayout); } if (mCallback != null) { mCallback.onDraggingEnd(); @@ -889,7 +889,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, t.hide(sc).apply(); mTiles.releaseTransaction(t); int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position; - WindowManagerProxy.applyResizeSplits(midPos, mSplitLayout); + mWindowManagerProxy.applyResizeSplits(midPos, mSplitLayout); } public void setMinimizedDockStack(boolean minimized, long animDuration, diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java index 410e3dd39a0b..e593db86bd12 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java @@ -37,7 +37,6 @@ import android.view.WindowManagerGlobal; import android.window.TaskOrganizer; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; -import android.window.WindowOrganizer; import com.android.internal.annotations.GuardedBy; import com.android.systemui.TransactionPool; @@ -112,10 +111,10 @@ public class WindowManagerProxy { mExecutor.execute(mSetTouchableRegionRunnable); } - static void applyResizeSplits(int position, SplitDisplayLayout splitLayout) { + void applyResizeSplits(int position, SplitDisplayLayout splitLayout) { WindowContainerTransaction t = new WindowContainerTransaction(); splitLayout.resizeSplits(position, t); - WindowOrganizer.applyTransaction(t); + applySyncTransaction(t); } private static boolean getHomeAndRecentsTasks(List<ActivityManager.RunningTaskInfo> out, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java index 4fa782269c2d..e61e05a7dc2f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar; +import static com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; @@ -163,7 +165,7 @@ public class DragDownHelper implements Gefingerpoken { if (!mDragDownCallback.isFalsingCheckNeeded()) { return false; } - return mFalsingManager.isFalseTouch() || !mDraggedFarEnough; + return mFalsingManager.isFalseTouch(NOTIFICATION_DRAG_DOWN) || !mDraggedFarEnough; } private void captureStartingChild(float x, float y) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 39d2f71e7e0b..155ec5ea72de 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -123,6 +123,7 @@ public class KeyguardIndicationController implements StateListener, private int mChargingSpeed; private int mChargingWattage; private int mBatteryLevel; + private boolean mBatteryPresent = true; private long mChargingTimeRemaining; private float mDisclosureMaxAlpha; private String mMessageToShowOnScreenOn; @@ -391,86 +392,103 @@ public class KeyguardIndicationController implements StateListener, mWakeLock.setAcquired(false); } - if (mVisible) { - // Walk down a precedence-ordered list of what indication - // should be shown based on user or device state - if (mDozing) { - // When dozing we ignore any text color and use white instead, because - // colors can be hard to read in low brightness. - mTextView.setTextColor(Color.WHITE); - if (!TextUtils.isEmpty(mTransientIndication)) { - mTextView.switchIndication(mTransientIndication); - } else if (!TextUtils.isEmpty(mAlignmentIndication)) { - mTextView.switchIndication(mAlignmentIndication); - mTextView.setTextColor(mContext.getColor(R.color.misalignment_text_color)); - } else if (mPowerPluggedIn || mEnableBatteryDefender) { - String indication = computePowerIndication(); - if (animate) { - animateText(mTextView, indication); - } else { - mTextView.switchIndication(indication); - } - } else { - String percentage = NumberFormat.getPercentInstance() - .format(mBatteryLevel / 100f); - mTextView.switchIndication(percentage); - } - return; - } + if (!mVisible) { + return; + } - int userId = KeyguardUpdateMonitor.getCurrentUser(); - String trustGrantedIndication = getTrustGrantedIndication(); - String trustManagedIndication = getTrustManagedIndication(); + // A few places might need to hide the indication, so always start by making it visible + mIndicationArea.setVisibility(View.VISIBLE); - String powerIndication = null; - if (mPowerPluggedIn || mEnableBatteryDefender) { - powerIndication = computePowerIndication(); - } - - boolean isError = false; - if (!mKeyguardUpdateMonitor.isUserUnlocked(userId)) { - mTextView.switchIndication(com.android.internal.R.string.lockscreen_storage_locked); - } else if (!TextUtils.isEmpty(mTransientIndication)) { - if (powerIndication != null && !mTransientIndication.equals(powerIndication)) { - String indication = mContext.getResources().getString( - R.string.keyguard_indication_trust_unlocked_plugged_in, - mTransientIndication, powerIndication); - mTextView.switchIndication(indication); - } else { - mTextView.switchIndication(mTransientIndication); - } - isError = mTransientTextIsError; - } else if (!TextUtils.isEmpty(trustGrantedIndication) - && mKeyguardUpdateMonitor.getUserHasTrust(userId)) { - if (powerIndication != null) { - String indication = mContext.getResources().getString( - R.string.keyguard_indication_trust_unlocked_plugged_in, - trustGrantedIndication, powerIndication); - mTextView.switchIndication(indication); - } else { - mTextView.switchIndication(trustGrantedIndication); - } + // Walk down a precedence-ordered list of what indication + // should be shown based on user or device state + if (mDozing) { + // When dozing we ignore any text color and use white instead, because + // colors can be hard to read in low brightness. + mTextView.setTextColor(Color.WHITE); + if (!TextUtils.isEmpty(mTransientIndication)) { + mTextView.switchIndication(mTransientIndication); + } else if (!mBatteryPresent) { + // If there is no battery detected, hide the indication and bail + mIndicationArea.setVisibility(View.GONE); } else if (!TextUtils.isEmpty(mAlignmentIndication)) { mTextView.switchIndication(mAlignmentIndication); - isError = true; + mTextView.setTextColor(mContext.getColor(R.color.misalignment_text_color)); } else if (mPowerPluggedIn || mEnableBatteryDefender) { - if (DEBUG_CHARGING_SPEED) { - powerIndication += ", " + (mChargingWattage / 1000) + " mW"; - } + String indication = computePowerIndication(); if (animate) { - animateText(mTextView, powerIndication); + animateText(mTextView, indication); } else { - mTextView.switchIndication(powerIndication); + mTextView.switchIndication(indication); } - } else if (!TextUtils.isEmpty(trustManagedIndication) - && mKeyguardUpdateMonitor.getUserTrustIsManaged(userId) - && !mKeyguardUpdateMonitor.getUserHasTrust(userId)) { - mTextView.switchIndication(trustManagedIndication); } else { - mTextView.switchIndication(mRestingIndication); + String percentage = NumberFormat.getPercentInstance() + .format(mBatteryLevel / 100f); + mTextView.switchIndication(percentage); } - mTextView.setTextColor(isError ? Utils.getColorError(mContext) - : mInitialTextColorState); + return; + } + + int userId = KeyguardUpdateMonitor.getCurrentUser(); + String trustGrantedIndication = getTrustGrantedIndication(); + String trustManagedIndication = getTrustManagedIndication(); + + String powerIndication = null; + if (mPowerPluggedIn || mEnableBatteryDefender) { + powerIndication = computePowerIndication(); + } + + // Some cases here might need to hide the indication (if the battery is not present) + boolean hideIndication = false; + boolean isError = false; + if (!mKeyguardUpdateMonitor.isUserUnlocked(userId)) { + mTextView.switchIndication(com.android.internal.R.string.lockscreen_storage_locked); + } else if (!TextUtils.isEmpty(mTransientIndication)) { + if (powerIndication != null && !mTransientIndication.equals(powerIndication)) { + String indication = mContext.getResources().getString( + R.string.keyguard_indication_trust_unlocked_plugged_in, + mTransientIndication, powerIndication); + mTextView.switchIndication(indication); + hideIndication = !mBatteryPresent; + } else { + mTextView.switchIndication(mTransientIndication); + } + isError = mTransientTextIsError; + } else if (!TextUtils.isEmpty(trustGrantedIndication) + && mKeyguardUpdateMonitor.getUserHasTrust(userId)) { + if (powerIndication != null) { + String indication = mContext.getResources().getString( + R.string.keyguard_indication_trust_unlocked_plugged_in, + trustGrantedIndication, powerIndication); + mTextView.switchIndication(indication); + hideIndication = !mBatteryPresent; + } else { + mTextView.switchIndication(trustGrantedIndication); + } + } else if (!TextUtils.isEmpty(mAlignmentIndication)) { + mTextView.switchIndication(mAlignmentIndication); + isError = true; + hideIndication = !mBatteryPresent; + } else if (mPowerPluggedIn || mEnableBatteryDefender) { + if (DEBUG_CHARGING_SPEED) { + powerIndication += ", " + (mChargingWattage / 1000) + " mW"; + } + if (animate) { + animateText(mTextView, powerIndication); + } else { + mTextView.switchIndication(powerIndication); + } + hideIndication = !mBatteryPresent; + } else if (!TextUtils.isEmpty(trustManagedIndication) + && mKeyguardUpdateMonitor.getUserTrustIsManaged(userId) + && !mKeyguardUpdateMonitor.getUserHasTrust(userId)) { + mTextView.switchIndication(trustManagedIndication); + } else { + mTextView.switchIndication(mRestingIndication); + } + mTextView.setTextColor(isError ? Utils.getColorError(mContext) + : mInitialTextColorState); + if (hideIndication) { + mIndicationArea.setVisibility(View.GONE); } } @@ -654,6 +672,7 @@ public class KeyguardIndicationController implements StateListener, pw.println(" mMessageToShowOnScreenOn: " + mMessageToShowOnScreenOn); pw.println(" mDozing: " + mDozing); pw.println(" mBatteryLevel: " + mBatteryLevel); + pw.println(" mBatteryPresent: " + mBatteryPresent); pw.println(" mTextView.getText(): " + (mTextView == null ? null : mTextView.getText())); pw.println(" computePowerIndication(): " + computePowerIndication()); } @@ -694,6 +713,7 @@ public class KeyguardIndicationController implements StateListener, mBatteryLevel = status.level; mBatteryOverheated = status.isOverheated(); mEnableBatteryDefender = mBatteryOverheated && status.isPluggedIn(); + mBatteryPresent = status.present; try { mChargingTimeRemaining = mPowerPluggedIn ? mBatteryInfo.computeChargeTimeRemaining() : -1; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt index 7b2585324b9f..38f96f42bdd4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt @@ -30,6 +30,7 @@ import android.view.ViewConfiguration import com.android.systemui.Gefingerpoken import com.android.systemui.Interpolators import com.android.systemui.R +import com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator @@ -106,7 +107,7 @@ constructor( private var velocityTracker: VelocityTracker? = null private val isFalseTouch: Boolean - get() = falsingManager.isFalseTouch + get() = falsingManager.isFalseTouch(NOTIFICATION_DRAG_DOWN) var qsExpanded: Boolean = false var pulseExpandAbortListener: Runnable? = null var bouncerShowing: Boolean = false diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java index d7d09e05c238..ac528b245ce9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java @@ -209,7 +209,7 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc || (isFastNonDismissGesture && isAbleToShowMenu); int menuSnapTarget = menuRow.getMenuSnapTarget(); boolean isNonFalseMenuRevealingGesture = - !isFalseGesture(ev) && isMenuRevealingGestureAwayFromMenu; + !isFalseGesture() && isMenuRevealingGestureAwayFromMenu; if ((isNonDismissGestureTowardsMenu || isNonFalseMenuRevealingGesture) && menuSnapTarget != 0) { // Menu has not been snapped to previously and this is menu revealing gesture diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java index 858023dc6c62..ba9420265849 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java @@ -27,6 +27,7 @@ import android.view.ViewConfiguration; import com.android.systemui.Interpolators; import com.android.systemui.R; +import com.android.systemui.classifier.Classifier; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.statusbar.FlingAnimationUtils; import com.android.systemui.statusbar.KeyguardAffordanceView; @@ -317,7 +318,9 @@ public class KeyguardAffordanceHelper { // We snap back if the current translation is not far enough boolean snapBack = false; if (mCallback.needsAntiFalsing()) { - snapBack = snapBack || mFalsingManager.isFalseTouch(); + snapBack = snapBack || mFalsingManager.isFalseTouch( + mTargetedView == mRightIcon + ? Classifier.RIGHT_AFFORDANCE : Classifier.LEFT_AFFORDANCE); } snapBack = snapBack || isBelowFalsingThreshold(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index 799e16cc6d8d..53cbf02e57f0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.phone; import static android.view.View.GONE; +import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL; @@ -69,6 +70,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.DejankUtils; import com.android.systemui.Interpolators; import com.android.systemui.R; +import com.android.systemui.classifier.Classifier; import com.android.systemui.dagger.qualifiers.DisplayId; import com.android.systemui.doze.DozeLog; import com.android.systemui.fragments.FragmentHostManager; @@ -1204,7 +1206,7 @@ public class NotificationPanelViewController extends PanelViewController { } private boolean flingExpandsQs(float vel) { - if (mFalsingManager.isUnlockingDisabled() || isFalseTouch()) { + if (mFalsingManager.isUnlockingDisabled() || isFalseTouch(QUICK_SETTINGS)) { return false; } if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) { @@ -1214,12 +1216,12 @@ public class NotificationPanelViewController extends PanelViewController { } } - private boolean isFalseTouch() { + private boolean isFalseTouch(@Classifier.InteractionType int interactionType) { if (!mKeyguardAffordanceHelperCallback.needsAntiFalsing()) { return false; } if (mFalsingManager.isClassifierEnabled()) { - return mFalsingManager.isFalseTouch(); + return mFalsingManager.isFalseTouch(interactionType); } return !mQsTouchAboveFalsingThreshold; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java index e942d85790c1..a4f9b06b86a5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java @@ -16,6 +16,10 @@ package com.android.systemui.statusbar.phone; +import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK; +import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; +import static com.android.systemui.classifier.Classifier.UNLOCK; + import static java.lang.Float.isNaN; import android.animation.Animator; @@ -41,6 +45,7 @@ import com.android.internal.util.LatencyTracker; import com.android.systemui.DejankUtils; import com.android.systemui.Interpolators; import com.android.systemui.R; +import com.android.systemui.classifier.Classifier; import com.android.systemui.doze.DozeLog; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.statusbar.FlingAnimationUtils; @@ -397,7 +402,12 @@ public abstract class PanelViewController { mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_UNLOCK, heightDp, velocityDp); mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_UNLOCK); } - fling(vel, expand, isFalseTouch(x, y)); + @Classifier.InteractionType int interactionType = vel > 0 + ? QUICK_SETTINGS : ( + mKeyguardStateController.canDismissLockScreen() + ? UNLOCK : BOUNCER_UNLOCK); + + fling(vel, expand, isFalseTouch(x, y, interactionType)); onTrackingStopped(expand); mUpdateFlingOnLayout = expand && mPanelClosedOnDown && !mHasLayoutedSinceDown; if (mUpdateFlingOnLayout) { @@ -492,7 +502,11 @@ public abstract class PanelViewController { return true; } - if (isFalseTouch(x, y)) { + @Classifier.InteractionType int interactionType = vel > 0 + ? QUICK_SETTINGS : ( + mKeyguardStateController.canDismissLockScreen() ? UNLOCK : BOUNCER_UNLOCK); + + if (isFalseTouch(x, y, interactionType)) { return true; } if (Math.abs(vectorVel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) { @@ -511,12 +525,13 @@ public abstract class PanelViewController { * @param y the final y-coordinate when the finger was lifted * @return whether this motion should be regarded as a false touch */ - private boolean isFalseTouch(float x, float y) { + private boolean isFalseTouch(float x, float y, + @Classifier.InteractionType int interactionType) { if (!mStatusBar.isFalsingThresholdNeeded()) { return false; } if (mFalsingManager.isClassifierEnabled()) { - return mFalsingManager.isFalseTouch(); + return mFalsingManager.isFalseTouch(interactionType); } if (!mTouchAboveFalsingThreshold) { return true; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java index e5a46797d035..5009fce5216d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java @@ -97,6 +97,9 @@ public interface BatteryController extends DemoMode, Dumpable, default void onPowerSaveChanged(boolean isPowerSave) { } + default void onBatteryUnknownStateChanged(boolean isUnknown) { + } + default void onReverseChanged(boolean isReverse, int level, String name) { } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java index d30f01a658f6..ea79c246f014 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.policy; +import static android.os.BatteryManager.EXTRA_PRESENT; + import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -70,6 +72,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC protected int mLevel; protected boolean mPluggedIn; protected boolean mCharging; + private boolean mStateUnknown = false; private boolean mCharged; protected boolean mPowerSave; private boolean mAodPowerSave; @@ -126,6 +129,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC pw.print(" mCharging="); pw.println(mCharging); pw.print(" mCharged="); pw.println(mCharged); pw.print(" mPowerSave="); pw.println(mPowerSave); + pw.print(" mStateUnknown="); pw.println(mStateUnknown); } @Override @@ -139,8 +143,11 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC mChangeCallbacks.add(cb); } if (!mHasReceivedBattery) return; + + // Make sure new callbacks get the correct initial state cb.onBatteryLevelChanged(mLevel, mPluggedIn, mCharging); cb.onPowerSaveChanged(mPowerSave); + cb.onBatteryUnknownStateChanged(mStateUnknown); } @Override @@ -168,6 +175,13 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC mWirelessCharging = mCharging && intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) == BatteryManager.BATTERY_PLUGGED_WIRELESS; + boolean present = intent.getBooleanExtra(EXTRA_PRESENT, true); + boolean unknown = !present; + if (unknown != mStateUnknown) { + mStateUnknown = unknown; + fireBatteryUnknownStateChanged(); + } + fireBatteryLevelChanged(); } else if (action.equals(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)) { updatePowerSave(); @@ -316,6 +330,15 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC } } + private void fireBatteryUnknownStateChanged() { + synchronized (mChangeCallbacks) { + final int n = mChangeCallbacks.size(); + for (int i = 0; i < n; i++) { + mChangeCallbacks.get(i).onBatteryUnknownStateChanged(mStateUnknown); + } + } + } + private void firePowerSaveChanged() { synchronized (mChangeCallbacks) { final int N = mChangeCallbacks.size(); @@ -340,6 +363,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC String level = args.getString("level"); String plugged = args.getString("plugged"); String powerSave = args.getString("powersave"); + String present = args.getString("present"); if (level != null) { mLevel = Math.min(Math.max(Integer.parseInt(level), 0), 100); } @@ -350,6 +374,10 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC mPowerSave = powerSave.equals("true"); firePowerSaveChanged(); } + if (present != null) { + mStateUnknown = !present.equals("true"); + fireBatteryUnknownStateChanged(); + } fireBatteryLevelChanged(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt new file mode 100644 index 000000000000..92e5b78f776a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy + +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import android.net.Uri +import com.android.systemui.R +import com.android.systemui.util.concurrency.DelayableExecutor +import javax.inject.Inject + +/** + * Listens for important battery states and sends non-dismissible system notifications if there is a + * problem + */ +class BatteryStateNotifier @Inject constructor( + val controller: BatteryController, + val noMan: NotificationManager, + val delayableExecutor: DelayableExecutor, + val context: Context +) : BatteryController.BatteryStateChangeCallback { + var stateUnknown = false + + fun startListening() { + controller.addCallback(this) + } + + fun stopListening() { + controller.removeCallback(this) + } + + override fun onBatteryUnknownStateChanged(isUnknown: Boolean) { + stateUnknown = isUnknown + if (stateUnknown) { + val channel = NotificationChannel("battery_status", "Battery status", + NotificationManager.IMPORTANCE_DEFAULT) + noMan.createNotificationChannel(channel) + + val intent = Intent(Intent.ACTION_VIEW, + Uri.parse(context.getString(R.string.config_batteryStateUnknownUrl))) + val pi = PendingIntent.getActivity(context, 0, intent, 0) + + val builder = Notification.Builder(context, channel.id) + .setAutoCancel(false) + .setContentTitle( + context.getString(R.string.battery_state_unknown_notification_title)) + .setContentText( + context.getString(R.string.battery_state_unknown_notification_text)) + .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb) + .setContentIntent(pi) + .setAutoCancel(true) + .setOngoing(true) + + noMan.notify(TAG, ID, builder.build()) + } else { + scheduleNotificationCancel() + } + } + + private fun scheduleNotificationCancel() { + val r = { + if (!stateUnknown) { + noMan.cancel(ID) + } + } + delayableExecutor.executeDelayed(r, DELAY_MILLIS) + } +} + +private const val TAG = "BatteryStateNotifier" +private const val ID = 666 +private const val DELAY_MILLIS: Long = 4 * 60 * 60 * 1000
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java b/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java index 08cd6e383897..8d77c4a194a9 100644 --- a/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java +++ b/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLock.java @@ -33,7 +33,7 @@ public interface WakeLock { static final String REASON_WRAP = "wrap"; /** - * Default wake-lock timeout, to avoid battery regressions. + * Default wake-lock timeout in milliseconds, to avoid battery regressions. */ long DEFAULT_MAX_TIMEOUT = 20000; @@ -104,6 +104,7 @@ public interface WakeLock { if (count == null) { Log.wtf(TAG, "Releasing WakeLock with invalid reason: " + why, new Throwable()); + return; } else if (count == 1) { mActiveClients.remove(why); } else { diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java index dd4ea578dafe..ab4da83518e4 100644 --- a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java +++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java @@ -203,6 +203,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged return; } + mImeShowing = insetsState.getSourceOrDefaultVisibility(InsetsState.ITYPE_IME); + final InsetsSource newSource = insetsState.getSource(InsetsState.ITYPE_IME); final Rect newFrame = newSource.getFrame(); final Rect oldFrame = mInsetsState.getSource(InsetsState.ITYPE_IME).getFrame(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java index ebd2c3afe646..35e3bb392f7c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java @@ -77,8 +77,6 @@ public class DozeSensorsTest extends SysuiTestCase { @Mock private Consumer<Boolean> mProxCallback; @Mock - private AlwaysOnDisplayPolicy mAlwaysOnDisplayPolicy; - @Mock private TriggerSensor mTriggerSensor; @Mock private DozeLog mDozeLog; @@ -110,7 +108,7 @@ public class DozeSensorsTest extends SysuiTestCase { @Test public void testSensorDebounce() { - mDozeSensors.setListening(true); + mDozeSensors.setListening(true, true); mWakeLockScreenListener.onSensorChanged(mock(SensorManagerPlugin.SensorEvent.class)); mTestableLooper.processAllMessages(); @@ -128,7 +126,7 @@ public class DozeSensorsTest extends SysuiTestCase { @Test public void testSetListening_firstTrue_registerSettingsObserver() { verify(mSensorManager, never()).registerListener(any(), any(Sensor.class), anyInt()); - mDozeSensors.setListening(true); + mDozeSensors.setListening(true, true); verify(mTriggerSensor).registerSettingsObserver(any(ContentObserver.class)); } @@ -136,8 +134,8 @@ public class DozeSensorsTest extends SysuiTestCase { @Test public void testSetListening_twiceTrue_onlyRegisterSettingsObserverOnce() { verify(mSensorManager, never()).registerListener(any(), any(Sensor.class), anyInt()); - mDozeSensors.setListening(true); - mDozeSensors.setListening(true); + mDozeSensors.setListening(true, true); + mDozeSensors.setListening(true, true); verify(mTriggerSensor, times(1)).registerSettingsObserver(any(ContentObserver.class)); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java index 655f933d28fe..be4fdb0babc6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java @@ -149,6 +149,7 @@ public class DozeTriggersTest extends SysuiTestCase { clearInvocations(mSensors); mTriggers.transitionTo(DozeMachine.State.DOZE_PULSING, DozeMachine.State.DOZE_PULSE_DONE); + mTriggers.transitionTo(DozeMachine.State.DOZE_PULSE_DONE, DozeMachine.State.DOZE_AOD); waitForSensorManager(); verify(mSensors).requestTriggerSensor(any(), eq(mTapSensor)); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt index b47ee2921380..2f99b2a2a449 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt @@ -2,6 +2,7 @@ package com.android.systemui.media import android.app.Notification.MediaStyle import android.app.PendingIntent +import android.graphics.Bitmap import android.media.MediaDescription import android.media.MediaMetadata import android.media.session.MediaController @@ -246,4 +247,26 @@ class MediaDataManagerTest : SysuiTestCase() { verify(listener).onMediaDataRemoved(eq(KEY)) } + + @Test + fun testBadArtwork_doesNotUse() { + // WHEN notification has a too-small artwork + val artwork = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888) + val notif = SbnBuilder().run { + setPkg(PACKAGE_NAME) + modifyNotification(context).also { + it.setSmallIcon(android.R.drawable.ic_media_pause) + it.setStyle(MediaStyle().apply { setMediaSession(session.sessionToken) }) + it.setLargeIcon(artwork) + } + build() + } + mediaDataManager.onNotificationAdded(KEY, notif) + + // THEN it loads and uses the default background color + assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) + assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor)) + assertThat(mediaDataCaptor.value!!.backgroundColor).isEqualTo(DEFAULT_COLOR) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt deleted file mode 100644 index 4ba29e6e02a6..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt +++ /dev/null @@ -1,219 +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.systemui.privacy - -import android.os.UserManager -import android.provider.DeviceConfig -import android.testing.AndroidTestingRunner -import androidx.test.filters.SmallTest -import com.android.internal.config.sysui.SystemUiDeviceConfigFlags -import com.android.systemui.SysuiTestCase -import com.android.systemui.appops.AppOpsController -import com.android.systemui.broadcast.BroadcastDispatcher -import com.android.systemui.dump.DumpManager -import com.android.systemui.util.DeviceConfigProxy -import com.android.systemui.util.DeviceConfigProxyFake -import com.android.systemui.util.concurrency.FakeExecutor -import com.android.systemui.util.time.FakeSystemClock -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.ArgumentCaptor -import org.mockito.Mock -import org.mockito.Mockito -import org.mockito.Mockito.anyBoolean -import org.mockito.Mockito.atLeastOnce -import org.mockito.Mockito.never -import org.mockito.Mockito.verify -import org.mockito.MockitoAnnotations - -@RunWith(AndroidTestingRunner::class) -@SmallTest -class PrivacyItemControllerFlagsTest : SysuiTestCase() { - companion object { - fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture() - fun <T> eq(value: T): T = Mockito.eq(value) ?: value - fun <T> any(): T = Mockito.any<T>() - - private const val ALL_INDICATORS = - SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED - private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED - } - - @Mock - private lateinit var appOpsController: AppOpsController - @Mock - private lateinit var callback: PrivacyItemController.Callback - @Mock - private lateinit var userManager: UserManager - @Mock - private lateinit var broadcastDispatcher: BroadcastDispatcher - @Mock - private lateinit var dumpManager: DumpManager - - private lateinit var privacyItemController: PrivacyItemController - private lateinit var executor: FakeExecutor - private lateinit var deviceConfigProxy: DeviceConfigProxy - - fun PrivacyItemController(): PrivacyItemController { - return PrivacyItemController( - appOpsController, - executor, - executor, - broadcastDispatcher, - deviceConfigProxy, - userManager, - dumpManager - ) - } - - @Before - fun setup() { - MockitoAnnotations.initMocks(this) - executor = FakeExecutor(FakeSystemClock()) - deviceConfigProxy = DeviceConfigProxyFake() - - privacyItemController = PrivacyItemController() - privacyItemController.addCallback(callback) - - executor.runAllReady() - } - - @Test - fun testNotListeningByDefault() { - assertFalse(privacyItemController.allIndicatorsAvailable) - assertFalse(privacyItemController.micCameraAvailable) - - verify(appOpsController, never()).addCallback(any(), any()) - } - - @Test - fun testMicCameraChanged() { - changeMicCamera(true) - executor.runAllReady() - - verify(callback).onFlagMicCameraChanged(true) - verify(callback, never()).onFlagAllChanged(anyBoolean()) - - assertTrue(privacyItemController.micCameraAvailable) - assertFalse(privacyItemController.allIndicatorsAvailable) - } - - @Test - fun testAllChanged() { - changeAll(true) - executor.runAllReady() - - verify(callback).onFlagAllChanged(true) - verify(callback, never()).onFlagMicCameraChanged(anyBoolean()) - - assertTrue(privacyItemController.allIndicatorsAvailable) - assertFalse(privacyItemController.micCameraAvailable) - } - - @Test - fun testBothChanged() { - changeAll(true) - changeMicCamera(true) - executor.runAllReady() - - verify(callback, atLeastOnce()).onFlagAllChanged(true) - verify(callback, atLeastOnce()).onFlagMicCameraChanged(true) - - assertTrue(privacyItemController.allIndicatorsAvailable) - assertTrue(privacyItemController.micCameraAvailable) - } - - @Test - fun testAll_listeningToAll() { - changeAll(true) - executor.runAllReady() - - verify(appOpsController).addCallback(eq(PrivacyItemController.OPS), any()) - } - - @Test - fun testMicCamera_listening() { - changeMicCamera(true) - executor.runAllReady() - - verify(appOpsController).addCallback(eq(PrivacyItemController.OPS), any()) - } - - @Test - fun testAll_listening() { - changeAll(true) - executor.runAllReady() - - verify(appOpsController).addCallback(eq(PrivacyItemController.OPS), any()) - } - - @Test - fun testAllFalse_notListening() { - changeAll(true) - executor.runAllReady() - changeAll(false) - executor.runAllReady() - - verify(appOpsController).removeCallback(any(), any()) - } - - @Test - fun testSomeListening_stillListening() { - changeAll(true) - changeMicCamera(true) - executor.runAllReady() - changeAll(false) - executor.runAllReady() - - verify(appOpsController, never()).removeCallback(any(), any()) - } - - @Test - fun testAllDeleted_stopListening() { - changeAll(true) - executor.runAllReady() - changeAll(null) - executor.runAllReady() - - verify(appOpsController).removeCallback(any(), any()) - } - - @Test - fun testMicDeleted_stopListening() { - changeMicCamera(true) - executor.runAllReady() - changeMicCamera(null) - executor.runAllReady() - - verify(appOpsController).removeCallback(any(), any()) - } - - private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value) - private fun changeAll(value: Boolean?) = changeProperty(ALL_INDICATORS, value) - - private fun changeProperty(name: String, value: Boolean?) { - deviceConfigProxy.setProperty( - DeviceConfig.NAMESPACE_PRIVACY, - name, - value?.toString(), - false - ) - } -}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt deleted file mode 100644 index 5c5df2639cdd..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt +++ /dev/null @@ -1,329 +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.systemui.privacy - -import android.app.ActivityManager -import android.app.AppOpsManager -import android.content.Intent -import android.content.pm.UserInfo -import android.os.UserHandle -import android.os.UserManager -import android.provider.DeviceConfig -import android.testing.AndroidTestingRunner -import android.testing.TestableLooper.RunWithLooper -import androidx.test.filters.SmallTest -import com.android.internal.config.sysui.SystemUiDeviceConfigFlags -import com.android.systemui.SysuiTestCase -import com.android.systemui.appops.AppOpItem -import com.android.systemui.appops.AppOpsController -import com.android.systemui.broadcast.BroadcastDispatcher -import com.android.systemui.dump.DumpManager -import com.android.systemui.util.DeviceConfigProxy -import com.android.systemui.util.DeviceConfigProxyFake -import com.android.systemui.util.concurrency.FakeExecutor -import com.android.systemui.util.time.FakeSystemClock -import org.hamcrest.Matchers.hasItem -import org.hamcrest.Matchers.not -import org.hamcrest.Matchers.nullValue -import org.junit.Assert.assertEquals -import org.junit.Assert.assertThat -import org.junit.Assert.assertTrue -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.ArgumentCaptor -import org.mockito.ArgumentMatchers.anyInt -import org.mockito.ArgumentMatchers.anyList -import org.mockito.Captor -import org.mockito.Mock -import org.mockito.Mockito -import org.mockito.Mockito.atLeastOnce -import org.mockito.Mockito.doReturn -import org.mockito.Mockito.mock -import org.mockito.Mockito.never -import org.mockito.Mockito.reset -import org.mockito.Mockito.verify -import org.mockito.Mockito.verifyNoMoreInteractions -import org.mockito.MockitoAnnotations - -@RunWith(AndroidTestingRunner::class) -@SmallTest -@RunWithLooper -class PrivacyItemControllerTest : SysuiTestCase() { - - companion object { - val CURRENT_USER_ID = ActivityManager.getCurrentUser() - val TEST_UID = CURRENT_USER_ID * UserHandle.PER_USER_RANGE - const val TEST_PACKAGE_NAME = "test" - - private const val ALL_INDICATORS = - SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED - private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED - fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture() - fun <T> eq(value: T): T = Mockito.eq(value) ?: value - fun <T> any(): T = Mockito.any<T>() - } - - @Mock - private lateinit var appOpsController: AppOpsController - @Mock - private lateinit var callback: PrivacyItemController.Callback - @Mock - private lateinit var userManager: UserManager - @Mock - private lateinit var broadcastDispatcher: BroadcastDispatcher - @Mock - private lateinit var dumpManager: DumpManager - @Captor - private lateinit var argCaptor: ArgumentCaptor<List<PrivacyItem>> - @Captor - private lateinit var argCaptorCallback: ArgumentCaptor<AppOpsController.Callback> - - private lateinit var privacyItemController: PrivacyItemController - private lateinit var executor: FakeExecutor - private lateinit var deviceConfigProxy: DeviceConfigProxy - - fun PrivacyItemController(): PrivacyItemController { - return PrivacyItemController( - appOpsController, - executor, - executor, - broadcastDispatcher, - deviceConfigProxy, - userManager, - dumpManager - ) - } - - @Before - fun setup() { - MockitoAnnotations.initMocks(this) - executor = FakeExecutor(FakeSystemClock()) - deviceConfigProxy = DeviceConfigProxyFake() - - changeAll(true) - - doReturn(listOf(object : UserInfo() { - init { - id = CURRENT_USER_ID - } - })).`when`(userManager).getProfiles(anyInt()) - - privacyItemController = PrivacyItemController() - } - - @Test - fun testSetListeningTrueByAddingCallback() { - privacyItemController.addCallback(callback) - executor.runAllReady() - verify(appOpsController).addCallback(eq(PrivacyItemController.OPS), - any()) - verify(callback).onPrivacyItemsChanged(anyList()) - } - - @Test - fun testSetListeningFalseByRemovingLastCallback() { - privacyItemController.addCallback(callback) - executor.runAllReady() - verify(appOpsController, never()).removeCallback(any(), - any()) - privacyItemController.removeCallback(callback) - executor.runAllReady() - verify(appOpsController).removeCallback(eq(PrivacyItemController.OPS), - any()) - verify(callback).onPrivacyItemsChanged(emptyList()) - } - - @Test - fun testDistinctItems() { - doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 0), - AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 1))) - .`when`(appOpsController).getActiveAppOpsForUser(anyInt()) - - privacyItemController.addCallback(callback) - executor.runAllReady() - verify(callback).onPrivacyItemsChanged(capture(argCaptor)) - assertEquals(1, argCaptor.value.size) - } - - @Test - fun testRegisterReceiver_allUsers() { - privacyItemController.addCallback(callback) - executor.runAllReady() - verify(broadcastDispatcher, atLeastOnce()).registerReceiver( - eq(privacyItemController.userSwitcherReceiver), any(), eq(null), eq(UserHandle.ALL)) - verify(broadcastDispatcher, never()) - .unregisterReceiver(eq(privacyItemController.userSwitcherReceiver)) - } - - @Test - fun testReceiver_ACTION_USER_FOREGROUND() { - privacyItemController.userSwitcherReceiver.onReceive(context, - Intent(Intent.ACTION_USER_SWITCHED)) - executor.runAllReady() - verify(userManager).getProfiles(anyInt()) - } - - @Test - fun testReceiver_ACTION_MANAGED_PROFILE_ADDED() { - privacyItemController.userSwitcherReceiver.onReceive(context, - Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)) - executor.runAllReady() - verify(userManager).getProfiles(anyInt()) - } - - @Test - fun testReceiver_ACTION_MANAGED_PROFILE_REMOVED() { - privacyItemController.userSwitcherReceiver.onReceive(context, - Intent(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)) - executor.runAllReady() - verify(userManager).getProfiles(anyInt()) - } - - @Test - fun testAddMultipleCallbacks() { - val otherCallback = mock(PrivacyItemController.Callback::class.java) - privacyItemController.addCallback(callback) - executor.runAllReady() - verify(callback).onPrivacyItemsChanged(anyList()) - - privacyItemController.addCallback(otherCallback) - executor.runAllReady() - verify(otherCallback).onPrivacyItemsChanged(anyList()) - // Adding a callback should not unnecessarily call previous ones - verifyNoMoreInteractions(callback) - } - - @Test - fun testMultipleCallbacksAreUpdated() { - doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOpsForUser(anyInt()) - - val otherCallback = mock(PrivacyItemController.Callback::class.java) - privacyItemController.addCallback(callback) - privacyItemController.addCallback(otherCallback) - executor.runAllReady() - reset(callback) - reset(otherCallback) - - verify(appOpsController).addCallback(any(), capture(argCaptorCallback)) - argCaptorCallback.value.onActiveStateChanged(0, TEST_UID, "", true) - executor.runAllReady() - verify(callback).onPrivacyItemsChanged(anyList()) - verify(otherCallback).onPrivacyItemsChanged(anyList()) - } - - @Test - fun testRemoveCallback() { - doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOpsForUser(anyInt()) - val otherCallback = mock(PrivacyItemController.Callback::class.java) - privacyItemController.addCallback(callback) - privacyItemController.addCallback(otherCallback) - executor.runAllReady() - executor.runAllReady() - reset(callback) - reset(otherCallback) - - verify(appOpsController).addCallback(any(), capture(argCaptorCallback)) - privacyItemController.removeCallback(callback) - argCaptorCallback.value.onActiveStateChanged(0, TEST_UID, "", true) - executor.runAllReady() - verify(callback, never()).onPrivacyItemsChanged(anyList()) - verify(otherCallback).onPrivacyItemsChanged(anyList()) - } - - @Test - fun testListShouldNotHaveNull() { - doReturn(listOf(AppOpItem(AppOpsManager.OP_ACTIVATE_VPN, TEST_UID, "", 0), - AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, "", 0))) - .`when`(appOpsController).getActiveAppOpsForUser(anyInt()) - privacyItemController.addCallback(callback) - executor.runAllReady() - executor.runAllReady() - - verify(callback).onPrivacyItemsChanged(capture(argCaptor)) - assertEquals(1, argCaptor.value.size) - assertThat(argCaptor.value, not(hasItem(nullValue()))) - } - - @Test - fun testListShouldBeCopy() { - val list = listOf(PrivacyItem(PrivacyType.TYPE_CAMERA, - PrivacyApplication("", TEST_UID))) - privacyItemController.privacyList = list - val privacyList = privacyItemController.privacyList - assertEquals(list, privacyList) - assertTrue(list !== privacyList) - } - - @Test - fun testNotListeningWhenIndicatorsDisabled() { - changeAll(false) - privacyItemController.addCallback(callback) - executor.runAllReady() - verify(appOpsController, never()).addCallback(eq(PrivacyItemController.OPS), - any()) - } - - @Test - fun testNotSendingLocationWhenOnlyMicCamera() { - changeAll(false) - changeMicCamera(true) - executor.runAllReady() - - doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 0), - AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, "", 0))) - .`when`(appOpsController).getActiveAppOpsForUser(anyInt()) - - privacyItemController.addCallback(callback) - executor.runAllReady() - - verify(callback).onPrivacyItemsChanged(capture(argCaptor)) - - assertEquals(1, argCaptor.value.size) - assertEquals(PrivacyType.TYPE_CAMERA, argCaptor.value[0].privacyType) - } - - @Test - fun testNotUpdated_LocationChangeWhenOnlyMicCamera() { - doReturn(listOf(AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, "", 0))) - .`when`(appOpsController).getActiveAppOpsForUser(anyInt()) - - privacyItemController.addCallback(callback) - changeAll(false) - changeMicCamera(true) - executor.runAllReady() - reset(callback) // Clean callback - - verify(appOpsController).addCallback(any(), capture(argCaptorCallback)) - argCaptorCallback.value.onActiveStateChanged( - AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true) - - verify(callback, never()).onPrivacyItemsChanged(any()) - } - - private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value) - private fun changeAll(value: Boolean?) = changeProperty(ALL_INDICATORS, value) - - private fun changeProperty(name: String, value: Boolean?) { - deviceConfigProxy.setProperty( - DeviceConfig.NAMESPACE_PRIVACY, - name, - value?.toString(), - false - ) - } -}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java index 052f3382099f..91144be7347d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java @@ -495,7 +495,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { createController(); BatteryStatus status = new BatteryStatus(BatteryManager.BATTERY_STATUS_CHARGING, 80 /* level */, BatteryManager.BATTERY_PLUGGED_WIRELESS, 100 /* health */, - 0 /* maxChargingWattage */); + 0 /* maxChargingWattage */, true /* present */); mController.getKeyguardCallback().onRefreshBatteryInfo(status); verify(mIBatteryStats).computeChargeTimeRemaining(); @@ -507,7 +507,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { createController(); BatteryStatus status = new BatteryStatus(BatteryManager.BATTERY_STATUS_CHARGING, 80 /* level */, 0 /* plugged */, 100 /* health */, - 0 /* maxChargingWattage */); + 0 /* maxChargingWattage */, true /* present */); mController.getKeyguardCallback().onRefreshBatteryInfo(status); verify(mIBatteryStats, never()).computeChargeTimeRemaining(); @@ -553,7 +553,8 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { createController(); BatteryStatus status = new BatteryStatus(BatteryManager.BATTERY_STATUS_CHARGING, 80 /* level */, BatteryManager.BATTERY_PLUGGED_AC, - BatteryManager.BATTERY_HEALTH_OVERHEAT, 0 /* maxChargingWattage */); + BatteryManager.BATTERY_HEALTH_OVERHEAT, 0 /* maxChargingWattage */, + true /* present */); mController.getKeyguardCallback().onRefreshBatteryInfo(status); mController.setVisible(true); @@ -569,7 +570,8 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { createController(); BatteryStatus status = new BatteryStatus(BatteryManager.BATTERY_STATUS_DISCHARGING, 80 /* level */, BatteryManager.BATTERY_PLUGGED_AC, - BatteryManager.BATTERY_HEALTH_OVERHEAT, 0 /* maxChargingWattage */); + BatteryManager.BATTERY_HEALTH_OVERHEAT, 0 /* maxChargingWattage */, + true /* present */); mController.getKeyguardCallback().onRefreshBatteryInfo(status); mController.setVisible(true); @@ -585,7 +587,8 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { createController(); BatteryStatus status = new BatteryStatus(BatteryManager.BATTERY_STATUS_CHARGING, 100 /* level */, BatteryManager.BATTERY_PLUGGED_AC, - BatteryManager.BATTERY_HEALTH_OVERHEAT, 0 /* maxChargingWattage */); + BatteryManager.BATTERY_HEALTH_OVERHEAT, 0 /* maxChargingWattage */, + true /* present */); mController.getKeyguardCallback().onRefreshBatteryInfo(status); mController.setVisible(true); @@ -599,7 +602,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { createController(); BatteryStatus status = new BatteryStatus(BatteryManager.BATTERY_STATUS_DISCHARGING, 90 /* level */, 0 /* plugged */, BatteryManager.BATTERY_HEALTH_OVERHEAT, - 0 /* maxChargingWattage */); + 0 /* maxChargingWattage */, true /* present */); mController.getKeyguardCallback().onRefreshBatteryInfo(status); mController.setDozing(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java index eca48c8c2ee1..e985a61c7a56 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java @@ -16,7 +16,11 @@ package com.android.systemui.statusbar.policy; +import static android.os.BatteryManager.EXTRA_PRESENT; + +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Intent; @@ -30,6 +34,7 @@ import android.testing.TestableLooper; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.power.EnhancedEstimates; +import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback; import org.junit.Assert; import org.junit.Before; @@ -93,4 +98,36 @@ public class BatteryControllerTest extends SysuiTestCase { Assert.assertFalse(mBatteryController.isAodPowerSave()); } + @Test + public void testBatteryPresentState_notPresent() { + // GIVEN a battery state callback listening for changes + BatteryStateChangeCallback cb = mock(BatteryStateChangeCallback.class); + mBatteryController.addCallback(cb); + + // WHEN the state of the battery becomes unknown + Intent i = new Intent(Intent.ACTION_BATTERY_CHANGED); + i.putExtra(EXTRA_PRESENT, false); + mBatteryController.onReceive(getContext(), i); + + // THEN the callback is notified + verify(cb, atLeastOnce()).onBatteryUnknownStateChanged(true); + } + + @Test + public void testBatteryPresentState_callbackAddedAfterStateChange() { + // GIVEN a battery state callback + BatteryController.BatteryStateChangeCallback cb = + mock(BatteryController.BatteryStateChangeCallback.class); + + // GIVEN the state has changed before adding a new callback + Intent i = new Intent(Intent.ACTION_BATTERY_CHANGED); + i.putExtra(EXTRA_PRESENT, false); + mBatteryController.onReceive(getContext(), i); + + // WHEN a callback is added + mBatteryController.addCallback(cb); + + // THEN it is informed about the battery state + verify(cb, atLeastOnce()).onBatteryUnknownStateChanged(true); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryStateNotifierTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryStateNotifierTest.kt new file mode 100644 index 000000000000..dcd57f137d71 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryStateNotifierTest.kt @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy + +import android.app.NotificationManager +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper.RunWithLooper + +import androidx.test.filters.SmallTest + +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.time.FakeSystemClock + +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.ArgumentMatchers.anyString +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +private fun <T> anyObject(): T { + return Mockito.anyObject<T>() +} + +@RunWith(AndroidTestingRunner::class) +@RunWithLooper() +@SmallTest +class BatteryStateNotifierTest : SysuiTestCase() { + @Mock private lateinit var batteryController: BatteryController + @Mock private lateinit var noMan: NotificationManager + + private val clock = FakeSystemClock() + private val executor = FakeExecutor(clock) + + private lateinit var notifier: BatteryStateNotifier + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + notifier = BatteryStateNotifier(batteryController, noMan, executor, context) + notifier.startListening() + + context.ensureTestableResources() + } + + @Test + fun testNotifyWhenStateUnknown() { + notifier.onBatteryUnknownStateChanged(true) + verify(noMan).notify(anyString(), anyInt(), anyObject()) + } + + @Test + fun testCancelAfterDelay() { + notifier.onBatteryUnknownStateChanged(true) + notifier.onBatteryUnknownStateChanged(false) + + clock.advanceTime(DELAY_MILLIS + 1) + verify(noMan).cancel(anyInt()) + } +} + +// From BatteryStateNotifier.kt +private const val DELAY_MILLIS: Long = 40 * 60 * 60 * 1000 diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 4e2519b47a47..13d26920c917 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -58,6 +58,7 @@ import static org.xmlpull.v1.XmlPullParser.START_TAG; import android.Manifest; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.AppOpsManager; @@ -511,14 +512,21 @@ class StorageManagerService extends IStorageManager.Stub } } - private @Nullable VolumeInfo findStorageForUuid(String volumeUuid) { + private @Nullable VolumeInfo findStorageForUuidAsUser(String volumeUuid, + @UserIdInt int userId) { final StorageManager storage = mContext.getSystemService(StorageManager.class); if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) { - return storage.findVolumeById(VolumeInfo.ID_EMULATED_INTERNAL + ";" + 0); + return storage.findVolumeById(VolumeInfo.ID_EMULATED_INTERNAL + ";" + userId); } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) { return storage.getPrimaryPhysicalVolume(); } else { - return storage.findEmulatedForPrivate(storage.findVolumeByUuid(volumeUuid)); + VolumeInfo info = storage.findVolumeByUuid(volumeUuid); + if (info == null) { + Slog.w(TAG, "findStorageForUuidAsUser cannot find volumeUuid:" + volumeUuid); + return null; + } + String emulatedUuid = info.getId().replace("private", "emulated") + ";" + userId; + return storage.findVolumeById(emulatedUuid); } } @@ -2766,8 +2774,9 @@ class StorageManagerService extends IStorageManager.Stub return; } else { - from = findStorageForUuid(mPrimaryStorageUuid); - to = findStorageForUuid(volumeUuid); + int currentUserId = mCurrentUserId; + from = findStorageForUuidAsUser(mPrimaryStorageUuid, currentUserId); + to = findStorageForUuidAsUser(volumeUuid, currentUserId); if (from == null) { Slog.w(TAG, "Failing move due to missing from volume " + mPrimaryStorageUuid); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index bc96824a171b..88aa46cd7292 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -6003,9 +6003,7 @@ public class ActivityManagerService extends IActivityManager.Stub } private boolean isAppBad(ApplicationInfo info) { - synchronized (this) { - return mAppErrors.isBadProcessLocked(info); - } + return mAppErrors.isBadProcess(info.processName, info.uid); } // NOTE: this is an internal method used by the OnShellCommand implementation only and should @@ -20530,7 +20528,7 @@ public class ActivityManagerService extends IActivityManager.Stub int callerUid = Binder.getCallingUid(); // Only system can toggle the freezer state - if (callerUid == SYSTEM_UID) { + if (callerUid == SYSTEM_UID || Build.IS_DEBUGGABLE) { return mOomAdjuster.mCachedAppOptimizer.enableFreezer(enable); } else { throw new SecurityException("Caller uid " + callerUid + " cannot set freezer state "); diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java index 50d2cab0af81..9838f01510a3 100644 --- a/services/core/java/com/android/server/am/AppErrors.java +++ b/services/core/java/com/android/server/am/AppErrors.java @@ -97,9 +97,19 @@ class AppErrors { * a minimum amount of time; they are removed from it when they are * later restarted (hopefully due to some user action). The value is the * time it was added to the list. + * + * Read access is UNLOCKED, and must either be based on a single lookup + * call on the current mBadProcesses instance, or a local copy of that + * reference must be made and the local copy treated as the source of + * truth. Mutations are performed by synchronizing on mBadProcessLock, + * cloning the existing mBadProcesses instance, performing the mutation, + * then changing the volatile "live" mBadProcesses reference to point to the + * mutated version. These operations are very rare compared to lookups: + * we intentionally trade additional cost for mutations for eliminating + * lock operations from the simple lookup cases. */ - private final ProcessMap<BadProcessInfo> mBadProcesses = new ProcessMap<>(); - + private volatile ProcessMap<BadProcessInfo> mBadProcesses = new ProcessMap<>(); + private final Object mBadProcessLock = new Object(); AppErrors(Context context, ActivityManagerService service, PackageWatchdog watchdog) { context.assertRuntimeOverlayThemable(); @@ -109,7 +119,8 @@ class AppErrors { } void dumpDebug(ProtoOutputStream proto, long fieldId, String dumpPackage) { - if (mProcessCrashTimes.getMap().isEmpty() && mBadProcesses.getMap().isEmpty()) { + final ProcessMap<BadProcessInfo> badProcesses = mBadProcesses; + if (mProcessCrashTimes.getMap().isEmpty() && badProcesses.getMap().isEmpty()) { return; } @@ -144,8 +155,8 @@ class AppErrors { } - if (!mBadProcesses.getMap().isEmpty()) { - final ArrayMap<String, SparseArray<BadProcessInfo>> pmap = mBadProcesses.getMap(); + if (!badProcesses.getMap().isEmpty()) { + final ArrayMap<String, SparseArray<BadProcessInfo>> pmap = badProcesses.getMap(); final int processCount = pmap.size(); for (int ip = 0; ip < processCount; ip++) { final long btoken = proto.start(AppErrorsProto.BAD_PROCESSES); @@ -209,9 +220,10 @@ class AppErrors { } } - if (!mBadProcesses.getMap().isEmpty()) { + final ProcessMap<BadProcessInfo> badProcesses = mBadProcesses; + if (!badProcesses.getMap().isEmpty()) { boolean printed = false; - final ArrayMap<String, SparseArray<BadProcessInfo>> pmap = mBadProcesses.getMap(); + final ArrayMap<String, SparseArray<BadProcessInfo>> pmap = badProcesses.getMap(); final int processCount = pmap.size(); for (int ip = 0; ip < processCount; ip++) { final String pname = pmap.keyAt(ip); @@ -263,12 +275,27 @@ class AppErrors { return needSep; } - boolean isBadProcessLocked(ApplicationInfo info) { - return mBadProcesses.get(info.processName, info.uid) != null; + boolean isBadProcess(final String processName, final int uid) { + // NO LOCKING for the simple lookup + return mBadProcesses.get(processName, uid) != null; + } + + void clearBadProcess(final String processName, final int uid) { + synchronized (mBadProcessLock) { + final ProcessMap<BadProcessInfo> badProcesses = new ProcessMap<>(); + badProcesses.putAll(mBadProcesses); + badProcesses.remove(processName, uid); + mBadProcesses = badProcesses; + } } - void clearBadProcessLocked(ApplicationInfo info) { - mBadProcesses.remove(info.processName, info.uid); + void markBadProcess(final String processName, final int uid, BadProcessInfo info) { + synchronized (mBadProcessLock) { + final ProcessMap<BadProcessInfo> badProcesses = new ProcessMap<>(); + badProcesses.putAll(mBadProcesses); + badProcesses.put(processName, uid, info); + mBadProcesses = badProcesses; + } } void resetProcessCrashTimeLocked(ApplicationInfo info) { @@ -737,10 +764,10 @@ class AppErrors { app.info.processName); if (!app.isolated) { // XXX We don't have a way to mark isolated processes - // as bad, since they don't have a peristent identity. - mBadProcesses.put(app.info.processName, app.uid, + // as bad, since they don't have a persistent identity. + markBadProcess(app.info.processName, app.uid, new BadProcessInfo(now, shortMsg, longMsg, stackTrace)); - mProcessCrashTimes.remove(app.info.processName, app.uid); + mProcessCrashTimes.remove(app.processName, app.uid); } app.bad = true; app.removed = true; diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java index 36d4a38c1624..edd0c5b72c5e 100644 --- a/services/core/java/com/android/server/am/CachedAppOptimizer.java +++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java @@ -133,7 +133,7 @@ public final class CachedAppOptimizer { static final int REPORT_UNFREEZE_MSG = 4; //TODO:change this static definition into a configurable flag. - static final int FREEZE_TIMEOUT_MS = 10000; + static final long FREEZE_TIMEOUT_MS = 600000; static final int DO_FREEZE = 1; static final int REPORT_UNFREEZE = 2; @@ -722,10 +722,12 @@ public final class CachedAppOptimizer { // This will ensure app will be out of the freezer for at least FREEZE_TIMEOUT_MS void unfreezeTemporarily(ProcessRecord app) { - synchronized (mAm) { - if (app.frozen) { - unfreezeAppLocked(app); - freezeAppAsync(app); + if (mUseFreezer) { + synchronized (mAm) { + if (app.frozen) { + unfreezeAppLocked(app); + freezeAppAsync(app); + } } } } @@ -1101,15 +1103,26 @@ public final class CachedAppOptimizer { } private void freezeProcess(ProcessRecord proc) { - final int pid; - final String name; + final int pid = proc.pid; + final String name = proc.processName; final long unfrozenDuration; final boolean frozen; - synchronized (mAm) { - pid = proc.pid; - name = proc.processName; + try { + // pre-check for locks to avoid unnecessary freeze/unfreeze operations + if (Process.hasFileLocks(pid)) { + if (DEBUG_FREEZER) { + Slog.d(TAG_AM, name + " (" + pid + ") holds file locks, not freezing"); + } + return; + } + } catch (Exception e) { + Slog.e(TAG_AM, "Not freezing. Unable to check file locks for " + name + "(" + pid + + "): " + e); + return; + } + synchronized (mAm) { if (proc.curAdj < ProcessList.CACHED_APP_MIN_ADJ || proc.shouldNotFreeze) { if (DEBUG_FREEZER) { @@ -1141,29 +1154,50 @@ public final class CachedAppOptimizer { frozen = proc.frozen; } - if (frozen) { - if (DEBUG_FREEZER) { - Slog.d(TAG_AM, "froze " + pid + " " + name); - } + if (!frozen) { + return; + } - EventLog.writeEvent(EventLogTags.AM_FREEZE, pid, name); - try { - freezeBinder(pid, true); - } catch (RuntimeException e) { - Slog.e(TAG_AM, "Unable to freeze binder for " + pid + " " + name); - proc.kill("Unable to freeze binder interface", - ApplicationExitInfo.REASON_OTHER, - ApplicationExitInfo.SUBREASON_INVALID_STATE, true); - } + if (DEBUG_FREEZER) { + Slog.d(TAG_AM, "froze " + pid + " " + name); + } + + EventLog.writeEvent(EventLogTags.AM_FREEZE, pid, name); - // See above for why we're not taking mPhenotypeFlagLock here - if (mRandom.nextFloat() < mFreezerStatsdSampleRate) { - FrameworkStatsLog.write(FrameworkStatsLog.APP_FREEZE_CHANGED, - FrameworkStatsLog.APP_FREEZE_CHANGED__ACTION__FREEZE_APP, - pid, - name, - unfrozenDuration); + try { + freezeBinder(pid, true); + } catch (RuntimeException e) { + Slog.e(TAG_AM, "Unable to freeze binder for " + pid + " " + name); + proc.kill("Unable to freeze binder interface", + ApplicationExitInfo.REASON_OTHER, + ApplicationExitInfo.SUBREASON_INVALID_STATE, true); + } + + // See above for why we're not taking mPhenotypeFlagLock here + if (mRandom.nextFloat() < mFreezerStatsdSampleRate) { + FrameworkStatsLog.write(FrameworkStatsLog.APP_FREEZE_CHANGED, + FrameworkStatsLog.APP_FREEZE_CHANGED__ACTION__FREEZE_APP, + pid, + name, + unfrozenDuration); + } + + try { + // post-check to prevent races + if (Process.hasFileLocks(pid)) { + if (DEBUG_FREEZER) { + Slog.d(TAG_AM, name + " (" + pid + ") holds file locks, reverting freeze"); + } + + synchronized (mAm) { + unfreezeAppLocked(proc); + } + } + } catch (Exception e) { + Slog.e(TAG_AM, "Unable to check file locks for " + name + "(" + pid + "): " + e); + synchronized (mAm) { + unfreezeAppLocked(proc); } } } diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index b6e632d42d8e..3fda6d279fef 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -2379,7 +2379,7 @@ public final class ProcessList { if ((intentFlags & Intent.FLAG_FROM_BACKGROUND) != 0) { // If we are in the background, then check to see if this process // is bad. If so, we will just silently fail. - if (mService.mAppErrors.isBadProcessLocked(info)) { + if (mService.mAppErrors.isBadProcess(info.processName, info.uid)) { if (DEBUG_PROCESSES) Slog.v(TAG, "Bad process: " + info.uid + "/" + info.processName); return null; @@ -2392,11 +2392,11 @@ public final class ProcessList { if (DEBUG_PROCESSES) Slog.v(TAG, "Clearing bad process: " + info.uid + "/" + info.processName); mService.mAppErrors.resetProcessCrashTimeLocked(info); - if (mService.mAppErrors.isBadProcessLocked(info)) { + if (mService.mAppErrors.isBadProcess(info.processName, info.uid)) { EventLog.writeEvent(EventLogTags.AM_PROC_GOOD, UserHandle.getUserId(info.uid), info.uid, info.processName); - mService.mAppErrors.clearBadProcessLocked(info); + mService.mAppErrors.clearBadProcess(info.processName, info.uid); if (app != null) { app.bad = false; } diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index b7bc2ab36fba..a5d2ca3ad4ff 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -3014,8 +3014,8 @@ public class AppOpsService extends IAppOpsService.Stub { // This is a workaround for R QPR, new API change is not allowed. We only allow the current // voice recognizer is also the voice interactor to noteproxy op. - final boolean isTrustVoiceServiceProxy = - AppOpsManager.isTrustedVoiceServiceProxy(mContext, proxyPackageName, code); + final boolean isTrustVoiceServiceProxy = AppOpsManager.isTrustedVoiceServiceProxy(mContext, + proxyPackageName, code, UserHandle.getUserId(proxyUid)); final boolean isSelfBlame = Binder.getCallingUid() == proxiedUid; final boolean isProxyTrusted = mContext.checkPermission( Manifest.permission.UPDATE_APP_OPS_STATS, -1, proxyUid) diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 852868616afd..713f9c8f173d 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -1484,6 +1484,14 @@ public final class DisplayManagerService extends SystemService { } } + void setDisplayModeDirectorLoggingEnabled(boolean enabled) { + synchronized (mSyncRoot) { + if (mDisplayModeDirector != null) { + mDisplayModeDirector.setLoggingEnabled(enabled); + } + } + } + void setAmbientColorTemperatureOverride(float cct) { if (mDisplayPowerController != null) { synchronized (mSyncRoot) { diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java index 0c6c797b917a..d1d04966bdec 100644 --- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java +++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java @@ -54,6 +54,10 @@ class DisplayManagerShellCommand extends ShellCommand { return setDisplayWhiteBalanceLoggingEnabled(true); case "dwb-logging-disable": return setDisplayWhiteBalanceLoggingEnabled(false); + case "dmd-logging-enable": + return setDisplayModeDirectorLoggingEnabled(true); + case "dmd-logging-disable": + return setDisplayModeDirectorLoggingEnabled(false); case "dwb-set-cct": return setAmbientColorTemperatureOverride(); default: @@ -80,6 +84,10 @@ class DisplayManagerShellCommand extends ShellCommand { pw.println(" Enable display white-balance logging."); pw.println(" dwb-logging-disable"); pw.println(" Disable display white-balance logging."); + pw.println(" dmd-logging-enable"); + pw.println(" Enable display mode director logging."); + pw.println(" dmd-logging-disable"); + pw.println(" Disable display mode director logging."); pw.println(" dwb-set-cct CCT"); pw.println(" Sets the ambient color temperature override to CCT (use -1 to disable)."); pw.println(); @@ -132,6 +140,11 @@ class DisplayManagerShellCommand extends ShellCommand { return 0; } + private int setDisplayModeDirectorLoggingEnabled(boolean enabled) { + mService.setDisplayModeDirectorLoggingEnabled(enabled); + return 0; + } + private int setAmbientColorTemperatureOverride() { String cctText = getNextArg(); if (cctText == null) { diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index e30b1ebdbe2a..dfa87bc5e18f 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -52,9 +52,12 @@ import com.android.server.display.utils.AmbientFilterFactory; import com.android.server.utils.DeviceConfigInterface; import java.io.PrintWriter; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; +import java.util.Date; import java.util.List; +import java.util.Locale; import java.util.Objects; /** @@ -63,7 +66,7 @@ import java.util.Objects; */ public class DisplayModeDirector { private static final String TAG = "DisplayModeDirector"; - private static final boolean DEBUG = false; + private boolean mLoggingEnabled; private static final int MSG_REFRESH_RATE_RANGE_CHANGED = 1; private static final int MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED = 2; @@ -143,6 +146,14 @@ public class DisplayModeDirector { } + public void setLoggingEnabled(boolean loggingEnabled) { + if (mLoggingEnabled == loggingEnabled) { + return; + } + mLoggingEnabled = loggingEnabled; + mBrightnessObserver.setLoggingEnabled(loggingEnabled); + } + @NonNull private SparseArray<Vote> getVotesLocked(int displayId) { SparseArray<Vote> displayVotes = mVotesByDisplay.get(displayId); @@ -244,7 +255,7 @@ public class DisplayModeDirector { availableModes = filterModes(modes, primarySummary); if (availableModes.length > 0) { - if (DEBUG) { + if (mLoggingEnabled) { Slog.w(TAG, "Found available modes=" + Arrays.toString(availableModes) + " with lowest priority considered " + Vote.priorityToString(lowestConsideredPriority) @@ -257,7 +268,7 @@ public class DisplayModeDirector { break; } - if (DEBUG) { + if (mLoggingEnabled) { Slog.w(TAG, "Couldn't find available modes with lowest priority set to " + Vote.priorityToString(lowestConsideredPriority) + " and with the following constraints: " @@ -279,7 +290,7 @@ public class DisplayModeDirector { Math.min(appRequestSummary.minRefreshRate, primarySummary.minRefreshRate); appRequestSummary.maxRefreshRate = Math.max(appRequestSummary.maxRefreshRate, primarySummary.maxRefreshRate); - if (DEBUG) { + if (mLoggingEnabled) { Slog.i(TAG, String.format("App request range: [%.0f %.0f]", appRequestSummary.minRefreshRate, @@ -306,7 +317,7 @@ public class DisplayModeDirector { for (Display.Mode mode : supportedModes) { if (mode.getPhysicalWidth() != summary.width || mode.getPhysicalHeight() != summary.height) { - if (DEBUG) { + if (mLoggingEnabled) { Slog.w(TAG, "Discarding mode " + mode.getModeId() + ", wrong size" + ": desiredWidth=" + summary.width + ": desiredHeight=" + summary.height @@ -321,7 +332,7 @@ public class DisplayModeDirector { // comparison. if (refreshRate < (summary.minRefreshRate - FLOAT_TOLERANCE) || refreshRate > (summary.maxRefreshRate + FLOAT_TOLERANCE)) { - if (DEBUG) { + if (mLoggingEnabled) { Slog.w(TAG, "Discarding mode " + mode.getModeId() + ", outside refresh rate bounds" + ": minRefreshRate=" + summary.minRefreshRate @@ -421,7 +432,7 @@ public class DisplayModeDirector { } private void updateVoteLocked(int displayId, int priority, Vote vote) { - if (DEBUG) { + if (mLoggingEnabled) { Slog.i(TAG, "updateVoteLocked(displayId=" + displayId + ", priority=" + Vote.priorityToString(priority) + ", vote=" + vote + ")"); @@ -442,7 +453,7 @@ public class DisplayModeDirector { } if (votes.size() == 0) { - if (DEBUG) { + if (mLoggingEnabled) { Slog.i(TAG, "No votes left for display " + displayId + ", removing."); } mVotesByDisplay.remove(displayId); @@ -1144,6 +1155,7 @@ public class DisplayModeDirector { private boolean mShouldObserveAmbientLowChange; private boolean mShouldObserveDisplayHighChange; private boolean mShouldObserveAmbientHighChange; + private boolean mLoggingEnabled; private SensorManager mSensorManager; private Sensor mLightSensor; @@ -1159,7 +1171,7 @@ public class DisplayModeDirector { // mShouldObserveAmbientHighChange is true, screen is on, peak refresh rate // changeable and low power mode off. After initialization, these states will // be updated from the same handler thread. - private boolean mDefaultDisplayOn = false; + private int mDefaultDisplayState = Display.STATE_UNKNOWN; private boolean mRefreshRateChangeable = false; private boolean mLowPowerModeEnabled = false; @@ -1271,6 +1283,14 @@ public class DisplayModeDirector { mDeviceConfigDisplaySettings.startListening(); } + public void setLoggingEnabled(boolean loggingEnabled) { + if (mLoggingEnabled == loggingEnabled) { + return; + } + mLoggingEnabled = loggingEnabled; + mLightSensorListener.setLoggingEnabled(loggingEnabled); + } + public void onRefreshRateSettingChangedLocked(float min, float max) { boolean changeable = (max - min > 1f && max > 60f); if (mRefreshRateChangeable != changeable) { @@ -1340,7 +1360,7 @@ public class DisplayModeDirector { pw.println(" BrightnessObserver"); pw.println(" mAmbientLux: " + mAmbientLux); pw.println(" mBrightness: " + mBrightness); - pw.println(" mDefaultDisplayOn: " + mDefaultDisplayOn); + pw.println(" mDefaultDisplayState: " + mDefaultDisplayState); pw.println(" mLowPowerModeEnabled: " + mLowPowerModeEnabled); pw.println(" mRefreshRateChangeable: " + mRefreshRateChangeable); pw.println(" mShouldObserveDisplayLowChange: " + mShouldObserveDisplayLowChange); @@ -1546,7 +1566,7 @@ public class DisplayModeDirector { vote = Vote.forRefreshRates(mRefreshRateInHighZone, mRefreshRateInHighZone); } - if (DEBUG) { + if (mLoggingEnabled) { Slog.d(TAG, "Display brightness " + mBrightness + ", ambient lux " + mAmbientLux + ", Vote " + vote); } @@ -1566,14 +1586,22 @@ public class DisplayModeDirector { private void updateDefaultDisplayState() { Display display = mContext.getSystemService(DisplayManager.class) .getDisplay(Display.DEFAULT_DISPLAY); - boolean defaultDisplayOn = display != null && display.getState() != Display.STATE_OFF; - setDefaultDisplayState(defaultDisplayOn); + if (display == null) { + return; + } + + setDefaultDisplayState(display.getState()); } @VisibleForTesting - public void setDefaultDisplayState(boolean on) { - if (mDefaultDisplayOn != on) { - mDefaultDisplayOn = on; + public void setDefaultDisplayState(int state) { + if (mLoggingEnabled) { + Slog.d(TAG, "setDefaultDisplayState: mDefaultDisplayState = " + + mDefaultDisplayState + ", state = " + state); + } + + if (mDefaultDisplayState != state) { + mDefaultDisplayState = state; updateSensorStatus(); } } @@ -1583,32 +1611,58 @@ public class DisplayModeDirector { return; } + if (mLoggingEnabled) { + Slog.d(TAG, "updateSensorStatus: mShouldObserveAmbientLowChange = " + + mShouldObserveAmbientLowChange + ", mShouldObserveAmbientHighChange = " + + mShouldObserveAmbientHighChange); + Slog.d(TAG, "updateSensorStatus: mLowPowerModeEnabled = " + + mLowPowerModeEnabled + ", mRefreshRateChangeable = " + + mRefreshRateChangeable); + } + if ((mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange) && isDeviceActive() && !mLowPowerModeEnabled && mRefreshRateChangeable) { mSensorManager.registerListener(mLightSensorListener, mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler); + if (mLoggingEnabled) { + Slog.d(TAG, "updateSensorStatus: registerListener"); + } } else { mLightSensorListener.removeCallbacks(); mSensorManager.unregisterListener(mLightSensorListener); + if (mLoggingEnabled) { + Slog.d(TAG, "updateSensorStatus: unregisterListener"); + } } } private boolean isDeviceActive() { - return mDefaultDisplayOn && mInjector.isDeviceInteractive(mContext); + return mDefaultDisplayState == Display.STATE_ON; } private final class LightSensorEventListener implements SensorEventListener { final private static int INJECT_EVENTS_INTERVAL_MS = LIGHT_SENSOR_RATE_MS; private float mLastSensorData; + private long mTimestamp; + private boolean mLoggingEnabled; public void dumpLocked(PrintWriter pw) { pw.println(" mLastSensorData: " + mLastSensorData); + pw.println(" mTimestamp: " + formatTimestamp(mTimestamp)); + } + + + public void setLoggingEnabled(boolean loggingEnabled) { + if (mLoggingEnabled == loggingEnabled) { + return; + } + mLoggingEnabled = loggingEnabled; } @Override public void onSensorChanged(SensorEvent event) { mLastSensorData = event.values[0]; - if (DEBUG) { + if (mLoggingEnabled) { Slog.d(TAG, "On sensor changed: " + mLastSensorData); } @@ -1626,6 +1680,7 @@ public class DisplayModeDirector { } long now = SystemClock.uptimeMillis(); + mTimestamp = System.currentTimeMillis(); if (mAmbientFilter != null) { mAmbientFilter.addValue(now, mLastSensorData); } @@ -1652,6 +1707,12 @@ public class DisplayModeDirector { mHandler.removeCallbacks(mInjectSensorEventRunnable); } + private String formatTimestamp(long time) { + SimpleDateFormat dateFormat = + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US); + return dateFormat.format(new Date(time)); + } + private void processSensorData(long now) { if (mAmbientFilter != null) { mAmbientLux = mAmbientFilter.getEstimate(now); @@ -1850,8 +1911,6 @@ public class DisplayModeDirector { void registerPeakRefreshRateObserver(@NonNull ContentResolver cr, @NonNull ContentObserver observer); - - boolean isDeviceInteractive(@NonNull Context context); } @VisibleForTesting @@ -1882,11 +1941,6 @@ public class DisplayModeDirector { cr.registerContentObserver(PEAK_REFRESH_RATE_URI, false /*notifyDescendants*/, observer, UserHandle.USER_SYSTEM); } - - @Override - public boolean isDeviceInteractive(@NonNull Context ctx) { - return ctx.getSystemService(PowerManager.class).isInteractive(); - } } } diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java index 5bd3c5707fd2..8017a442d8e7 100644 --- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java @@ -841,6 +841,9 @@ public class GnssLocationProvider extends AbstractLocationProvider implements } private void injectBestLocation(Location location) { + if (location.isFromMockProvider()) { + return; + } if (DEBUG) { Log.d(TAG, "injectBestLocation: " + location); } @@ -942,6 +945,9 @@ public class GnssLocationProvider extends AbstractLocationProvider implements } private void injectLocation(Location location) { + if (location.isFromMockProvider()) { + return; + } if (location.hasAccuracy()) { if (DEBUG) { Log.d(TAG, "injectLocation: " + location); diff --git a/services/core/java/com/android/server/media/MediaShellCommand.java b/services/core/java/com/android/server/media/MediaShellCommand.java index 20df271a1de2..b199325798f4 100644 --- a/services/core/java/com/android/server/media/MediaShellCommand.java +++ b/services/core/java/com/android/server/media/MediaShellCommand.java @@ -64,7 +64,7 @@ public class MediaShellCommand extends ShellCommand { } if (sThread == null) { Looper.prepare(); - sThread = ActivityThread.systemMain(); + sThread = ActivityThread.currentActivityThread(); Context context = sThread.getSystemContext(); sMediaSessionManager = (MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE); diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java index 9a9e733cb390..da472be81156 100644 --- a/services/core/java/com/android/server/notification/SnoozeHelper.java +++ b/services/core/java/com/android/server/notification/SnoozeHelper.java @@ -37,6 +37,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; import com.android.internal.util.XmlUtils; +import com.android.server.pm.PackageManagerService; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -463,6 +464,7 @@ public class SnoozeHelper { return PendingIntent.getBroadcast(mContext, REQUEST_CODE_REPOST, new Intent(REPOST_ACTION) + .setPackage(PackageManagerService.PLATFORM_PACKAGE_NAME) .setData(new Uri.Builder().scheme(REPOST_SCHEME).appendPath(key).build()) .addFlags(Intent.FLAG_RECEIVER_FOREGROUND) .putExtra(EXTRA_KEY, key) diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 55be2e365369..3281698f7830 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -20,6 +20,7 @@ import static android.Manifest.permission.DELETE_PACKAGES; import static android.Manifest.permission.INSTALL_PACKAGES; import static android.Manifest.permission.MANAGE_DEVICE_ADMINS; import static android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS; +import static android.Manifest.permission.QUERY_ALL_PACKAGES; import static android.Manifest.permission.READ_EXTERNAL_STORAGE; import static android.Manifest.permission.REQUEST_DELETE_PACKAGES; import static android.Manifest.permission.SET_HARMFUL_APP_WARNINGS; @@ -28,6 +29,7 @@ import static android.app.AppOpsManager.MODE_ALLOWED; import static android.app.AppOpsManager.MODE_DEFAULT; import static android.app.AppOpsManager.MODE_IGNORED; import static android.content.Intent.ACTION_MAIN; +import static android.content.Intent.CATEGORY_BROWSABLE; import static android.content.Intent.CATEGORY_DEFAULT; import static android.content.Intent.CATEGORY_HOME; import static android.content.Intent.EXTRA_LONG_VERSION_CODE; @@ -6483,14 +6485,10 @@ public class PackageManagerService extends IPackageManager.Stub true /*allowDynamicSplits*/); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - final boolean queryMayBeFiltered = - UserHandle.getAppId(filterCallingUid) >= Process.FIRST_APPLICATION_UID - && !resolveForStart; - final ResolveInfo bestChoice = chooseBestActivity( intent, resolvedType, flags, privateResolveFlags, query, userId, - queryMayBeFiltered); + queryMayBeFiltered(filterCallingUid, resolveForStart)); final boolean nonBrowserOnly = (privateResolveFlags & PackageManagerInternal.RESOLVE_NON_BROWSER_ONLY) != 0; if (nonBrowserOnly && bestChoice != null && bestChoice.handleAllWebDataURI) { @@ -6502,6 +6500,25 @@ public class PackageManagerService extends IPackageManager.Stub } } + /** + * Returns whether the query may be filtered to packages which are visible to the caller. + * Filtering occurs except in the following cases: + * <ul> + * <li>system processes + * <li>applications granted {@link android.Manifest.permission#QUERY_ALL_PACKAGES} + * <li>when querying to start an app + * </ul> + * + * @param filterCallingUid the UID of the calling application + * @param queryForStart whether query is to start an app + * @return whether filtering may occur + */ + private boolean queryMayBeFiltered(int filterCallingUid, boolean queryForStart) { + return UserHandle.getAppId(filterCallingUid) >= Process.FIRST_APPLICATION_UID + && checkUidPermission(QUERY_ALL_PACKAGES, filterCallingUid) != PERMISSION_GRANTED + && !queryForStart; + } + @Override public ResolveInfo findPersistentPreferredActivity(Intent intent, int userId) { if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.SYSTEM_UID)) { @@ -6867,7 +6884,7 @@ public class PackageManagerService extends IPackageManager.Stub boolean removeMatches, boolean debug, int userId) { return findPreferredActivityNotLocked( intent, resolvedType, flags, query, priority, always, removeMatches, debug, userId, - UserHandle.getAppId(Binder.getCallingUid()) >= Process.FIRST_APPLICATION_UID); + queryMayBeFiltered(Binder.getCallingUid(), /* queryForStart= */ false)); } // TODO: handle preferred activities missing while user has amnesia @@ -7733,6 +7750,13 @@ public class PackageManagerService extends IPackageManager.Stub Slog.i(TAG, " + always: " + info.activityInfo.packageName + " : linkgen=" + linkGeneration); } + + if (!intent.hasCategory(CATEGORY_BROWSABLE) + || !intent.hasCategory(CATEGORY_DEFAULT)) { + undefinedList.add(info); + continue; + } + // Use link-enabled generation as preferredOrder, i.e. // prefer newly-enabled over earlier-enabled. info.preferredOrder = linkGeneration; 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 8b3aaf7be871..e24d6ec6d25b 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -1565,9 +1565,6 @@ public class StatsPullAtomService extends SystemService { // Aggregate times for the same uids. SparseArray<long[]> aggregated = new SparseArray<>(); mCpuUidFreqTimeReader.readAbsolute((uid, cpuFreqTimeMs) -> { - // For uids known to be aggregated from many entries allow mutating in place to avoid - // many copies. Otherwise, copy before aggregating. - boolean mutateInPlace = false; if (UserHandle.isIsolated(uid)) { // Skip individual isolated uids because they are recycled and quickly removed from // the underlying data source. @@ -1575,26 +1572,18 @@ public class StatsPullAtomService extends SystemService { } else if (UserHandle.isSharedAppGid(uid)) { // All shared app gids are accounted together. uid = LAST_SHARED_APPLICATION_GID; - mutateInPlace = true; - } else if (UserHandle.isApp(uid)) { - // Apps are accounted under their app id. + } else { + // Everything else is accounted under their base uid. uid = UserHandle.getAppId(uid); } long[] aggCpuFreqTimeMs = aggregated.get(uid); - if (aggCpuFreqTimeMs != null) { - if (!mutateInPlace) { - aggCpuFreqTimeMs = Arrays.copyOf(aggCpuFreqTimeMs, cpuFreqTimeMs.length); - aggregated.put(uid, aggCpuFreqTimeMs); - } - for (int freqIndex = 0; freqIndex < cpuFreqTimeMs.length; ++freqIndex) { - aggCpuFreqTimeMs[freqIndex] += cpuFreqTimeMs[freqIndex]; - } - } else { - if (mutateInPlace) { - cpuFreqTimeMs = Arrays.copyOf(cpuFreqTimeMs, cpuFreqTimeMs.length); - } - aggregated.put(uid, cpuFreqTimeMs); + if (aggCpuFreqTimeMs == null) { + aggCpuFreqTimeMs = new long[cpuFreqTimeMs.length]; + aggregated.put(uid, aggCpuFreqTimeMs); + } + for (int freqIndex = 0; freqIndex < cpuFreqTimeMs.length; ++freqIndex) { + aggCpuFreqTimeMs[freqIndex] += cpuFreqTimeMs[freqIndex]; } }); @@ -2621,7 +2610,6 @@ public class StatsPullAtomService extends SystemService { try { // force procstats to flush & combine old files into one store long lastHighWaterMark = readProcStatsHighWaterMark(section); - List<ParcelFileDescriptor> statsFiles = new ArrayList<>(); ProtoOutputStream[] protoStreams = new ProtoOutputStream[MAX_PROCSTATS_SHARDS]; for (int i = 0; i < protoStreams.length; i++) { @@ -2631,7 +2619,7 @@ public class StatsPullAtomService extends SystemService { ProcessStats procStats = new ProcessStats(false); // Force processStatsService to aggregate all in-storage and in-memory data. long highWaterMark = processStatsService.getCommittedStatsMerged( - lastHighWaterMark, section, true, statsFiles, procStats); + lastHighWaterMark, section, true, null, procStats); procStats.dumpAggregatedProtoForStatsd(protoStreams, MAX_PROCSTATS_RAW_SHARD_SIZE); for (int i = 0; i < protoStreams.length; i++) { diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 8fe88538d03a..882688355bdf 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -39,7 +39,7 @@ import android.app.WallpaperColors; import android.app.WallpaperInfo; import android.app.WallpaperManager; import android.app.WallpaperManager.SetWallpaperFlags; -import android.app.admin.DevicePolicyManager; +import android.app.admin.DevicePolicyManagerInternal; import android.app.backup.WallpaperBackupHelper; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -2861,10 +2861,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub if (!uidMatchPackage) { return false; // callingPackage was faked. } - - // TODO(b/144048540): DPM needs to take into account the userId, not just the package. - final DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class); - if (dpm.isDeviceOwnerApp(callingPackage) || dpm.isProfileOwnerApp(callingPackage)) { + DevicePolicyManagerInternal devicePolicyManagerInternal = + LocalServices.getService(DevicePolicyManagerInternal.class); + if (devicePolicyManagerInternal != null && + devicePolicyManagerInternal.isDeviceOrProfileOwnerInCallingUser(callingPackage)) { return true; } final int callingUserId = UserHandle.getCallingUserId(); diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index 8f59eef49516..eec2e41eb5db 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -264,7 +264,7 @@ class ActivityMetricsLogger { return; } mLastLaunchedActivity = r; - if (!r.noDisplay) { + if (!r.noDisplay && !r.mDrawn) { if (DEBUG_METRICS) Slog.i(TAG, "Add pending draw " + r); mPendingDrawActivities.add(r); } @@ -546,7 +546,7 @@ class ActivityMetricsLogger { + " processSwitch=" + processSwitch + " info=" + info); } - if (launchedActivity.mDrawn) { + if (launchedActivity.mDrawn && launchedActivity.isVisible()) { // Launched activity is already visible. We cannot measure windows drawn delay. abort(info, "launched activity already visible"); return; diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 8ae955adbcc9..fb2afb116f14 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1513,6 +1513,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } final int rotation = rotationForActivityInDifferentOrientation(r); if (rotation == ROTATION_UNDEFINED) { + // The display rotation won't be changed by current top activity. The client side + // adjustments of previous rotated activity should be cleared earlier. Otherwise if + // the current top is in the same process, it may get the rotated state. The transform + // will be cleared later with transition callback to ensure smooth animation. + if (hasTopFixedRotationLaunchingApp()) { + mFixedRotationLaunchingApp.notifyFixedRotationTransform(false /* enabled */); + } return false; } if (!r.getParent().matchParentBounds()) { @@ -5877,6 +5884,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo Slog.w(TAG, "Failed to deliver showInsets", e); } } + + @Override + public boolean getImeRequestedVisibility(@InternalInsetsType int type) { + return getInsetsStateController().getImeSourceProvider().isImeShowing(); + } } /** diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java index 5ab48e158c4d..d9dde754b736 100644 --- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java @@ -35,6 +35,7 @@ class ImeInsetsSourceProvider extends InsetsSourceProvider { private InsetsControlTarget mImeTargetFromIme; private Runnable mShowImeRunner; private boolean mIsImeLayoutDrawn; + private boolean mImeShowing; ImeInsetsSourceProvider(InsetsSource source, InsetsStateController stateController, DisplayContent displayContent) { @@ -74,6 +75,7 @@ class ImeInsetsSourceProvider extends InsetsSourceProvider { ProtoLog.i(WM_DEBUG_IME, "call showInsets(ime) on %s", target.getWindow() != null ? target.getWindow().getName() : ""); + setImeShowing(true); target.showInsets(WindowInsets.Type.ime(), true /* fromIme */); if (target != mImeTargetFromIme && mImeTargetFromIme != null) { ProtoLog.w(WM_DEBUG_IME, @@ -147,11 +149,29 @@ class ImeInsetsSourceProvider extends InsetsSourceProvider { @Override public void dump(PrintWriter pw, String prefix) { super.dump(pw, prefix); + pw.print(prefix); + pw.print("mImeShowing="); + pw.print(mImeShowing); if (mImeTargetFromIme != null) { - pw.print(prefix); - pw.print("showImePostLayout pending for mImeTargetFromIme="); + pw.print(" showImePostLayout pending for mImeTargetFromIme="); pw.print(mImeTargetFromIme); - pw.println(); } + pw.println(); + } + + /** + * Sets whether the IME is currently supposed to be showing according to + * InputMethodManagerService. + */ + public void setImeShowing(boolean imeShowing) { + mImeShowing = imeShowing; + } + + /** + * Returns whether the IME is currently supposed to be showing according to + * InputMethodManagerService. + */ + public boolean isImeShowing() { + return mImeShowing; } } diff --git a/services/core/java/com/android/server/wm/InsetsControlTarget.java b/services/core/java/com/android/server/wm/InsetsControlTarget.java index 5e7ed3f80e43..2af2a9792337 100644 --- a/services/core/java/com/android/server/wm/InsetsControlTarget.java +++ b/services/core/java/com/android/server/wm/InsetsControlTarget.java @@ -39,6 +39,13 @@ interface InsetsControlTarget { } /** + * @return The requested visibility of this target. + */ + default boolean getImeRequestedVisibility(@InsetsState.InternalInsetsType int type) { + return InsetsState.getDefaultVisibility(type); + } + + /** * @return The requested {@link InsetsState} of this target. */ default InsetsState getRequestedInsetsState() { diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index be1d0fc0ecc8..d0012d0faf08 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -27,6 +27,7 @@ import static android.view.InsetsState.ITYPE_STATUS_BAR; import static android.view.SyncRtSurfaceTransactionApplier.applyParams; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import android.annotation.Nullable; import android.app.StatusBarManager; @@ -128,6 +129,9 @@ class InsetsPolicy { /** Updates the target which can control system bars. */ void updateBarControlTarget(@Nullable WindowState focusedWin) { + if (focusedWin != null && (focusedWin.mAttrs.type == TYPE_APPLICATION_STARTING)) { + return; + } if (mFocusedWin != focusedWin){ abortTransient(); } diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index 9fdfbd0a09da..ca83d541a5e8 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -279,7 +279,7 @@ class InsetsSourceProvider { } mAdapter = new ControlAdapter(); if (getSource().getType() == ITYPE_IME) { - setClientVisible(InsetsState.getDefaultVisibility(mSource.getType())); + setClientVisible(target.getImeRequestedVisibility(mSource.getType())); } final Transaction t = mDisplayContent.getPendingTransaction(); mWin.startAnimation(t, mAdapter, !mClientVisible /* hidden */, diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index d9594a40bde3..744afb9bb8ab 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -7637,6 +7637,9 @@ public class WindowManagerService extends IWindowManager.Stub dc.mInputMethodControlTarget.hideInsets( WindowInsets.Type.ime(), true /* fromIme */); } + if (dc != null) { + dc.getInsetsStateController().getImeSourceProvider().setImeShowing(false); + } } } diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index d86f6c998baa..9b1526e9b96f 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -622,11 +622,6 @@ class WindowToken extends WindowContainer<WindowState> { state.mIsTransforming = false; if (applyDisplayRotation != null) { applyDisplayRotation.run(); - } else { - // The display will not rotate to the rotation of this container, let's cancel them. - for (int i = state.mAssociatedTokens.size() - 1; i >= 0; i--) { - state.mAssociatedTokens.get(i).cancelFixedRotationTransform(); - } } // The state is cleared at the end, because it is used to indicate that other windows can // use seamless rotation when applying rotation to display. @@ -634,11 +629,15 @@ class WindowToken extends WindowContainer<WindowState> { final WindowToken token = state.mAssociatedTokens.get(i); token.mFixedRotationTransformState = null; token.notifyFixedRotationTransform(false /* enabled */); + if (applyDisplayRotation == null) { + // Notify cancellation because the display does not change rotation. + token.cancelFixedRotationTransform(); + } } } /** Notifies application side to enable or disable the rotation adjustment of display info. */ - private void notifyFixedRotationTransform(boolean enabled) { + void notifyFixedRotationTransform(boolean enabled) { FixedRotationAdjustments adjustments = null; // A token may contain windows of the same processes or different processes. The list is // used to avoid sending the same adjustments to a process multiple times. @@ -682,7 +681,6 @@ class WindowToken extends WindowContainer<WindowState> { // The window may be detached or detaching. return; } - notifyFixedRotationTransform(false /* enabled */); final int originalRotation = getWindowConfiguration().getRotation(); onConfigurationChanged(parent.getConfiguration()); onCancelFixedRotationTransform(originalRotation); diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp index 5fde550dc19d..7a6d310c2520 100644 --- a/services/core/jni/com_android_server_power_PowerManagerService.cpp +++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp @@ -191,19 +191,18 @@ static void setPowerBoostWithHandle(sp<IPowerAidl> handle, Boost boost, int32_t static std::array<std::atomic<HalSupport>, static_cast<int32_t>(Boost::DISPLAY_UPDATE_IMMINENT) + 1> boostSupportedArray = {HalSupport::UNKNOWN}; + size_t idx = static_cast<size_t>(boost); // Quick return if boost is not supported by HAL - if (boost > Boost::DISPLAY_UPDATE_IMMINENT || - boostSupportedArray[static_cast<int32_t>(boost)] == HalSupport::OFF) { + if (idx >= boostSupportedArray.size() || boostSupportedArray[idx] == HalSupport::OFF) { ALOGV("Skipped setPowerBoost %s because HAL doesn't support it", toString(boost).c_str()); return; } - if (boostSupportedArray[static_cast<int32_t>(boost)] == HalSupport::UNKNOWN) { + if (boostSupportedArray[idx] == HalSupport::UNKNOWN) { bool isSupported = false; handle->isBoostSupported(boost, &isSupported); - boostSupportedArray[static_cast<int32_t>(boost)] = - isSupported ? HalSupport::ON : HalSupport::OFF; + boostSupportedArray[idx] = isSupported ? HalSupport::ON : HalSupport::OFF; if (!isSupported) { ALOGV("Skipped setPowerBoost %s because HAL doesn't support it", toString(boost).c_str()); @@ -231,19 +230,18 @@ static bool setPowerModeWithHandle(sp<IPowerAidl> handle, Mode mode, bool enable // Need to increase the array if more mode supported. static std::array<std::atomic<HalSupport>, static_cast<int32_t>(Mode::DISPLAY_INACTIVE) + 1> modeSupportedArray = {HalSupport::UNKNOWN}; + size_t idx = static_cast<size_t>(mode); // Quick return if mode is not supported by HAL - if (mode > Mode::DISPLAY_INACTIVE || - modeSupportedArray[static_cast<int32_t>(mode)] == HalSupport::OFF) { + if (idx >= modeSupportedArray.size() || modeSupportedArray[idx] == HalSupport::OFF) { ALOGV("Skipped setPowerMode %s because HAL doesn't support it", toString(mode).c_str()); return false; } - if (modeSupportedArray[static_cast<int32_t>(mode)] == HalSupport::UNKNOWN) { + if (modeSupportedArray[idx] == HalSupport::UNKNOWN) { bool isSupported = false; handle->isModeSupported(mode, &isSupported); - modeSupportedArray[static_cast<int32_t>(mode)] = - isSupported ? HalSupport::ON : HalSupport::OFF; + modeSupportedArray[idx] = isSupported ? HalSupport::ON : HalSupport::OFF; if (!isSupported) { ALOGV("Skipped setPowerMode %s because HAL doesn't support it", toString(mode).c_str()); return false; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 6badafa34dfd..a35361884566 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -12872,6 +12872,26 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { ? AppOpsManager.MODE_ALLOWED : AppOpsManager.opToDefaultMode(AppOpsManager.OP_INTERACT_ACROSS_PROFILES); } + + public boolean isDeviceOrProfileOwnerInCallingUser(String packageName) { + return isDeviceOwnerInCallingUser(packageName) + || isProfileOwnerInCallingUser(packageName); + } + + private boolean isDeviceOwnerInCallingUser(String packageName) { + final ComponentName deviceOwnerInCallingUser = + DevicePolicyManagerService.this.getDeviceOwnerComponent( + /* callingUserOnly= */ true); + return deviceOwnerInCallingUser != null + && packageName.equals(deviceOwnerInCallingUser.getPackageName()); + } + + private boolean isProfileOwnerInCallingUser(String packageName) { + final ComponentName profileOwnerInCallingUser = + getProfileOwnerAsUser(UserHandle.getCallingUserId()); + return profileOwnerInCallingUser != null + && packageName.equals(profileOwnerInCallingUser.getPackageName()); + } } private Intent createShowAdminSupportIntent(ComponentName admin, int userId) { diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java index c467ee949aeb..c6f6fa81db75 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java @@ -34,6 +34,7 @@ import static org.mockito.Mockito.any; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.mockito.internal.verification.VerificationModeFactory.times; import android.annotation.NonNull; import android.content.ContentResolver; @@ -410,7 +411,7 @@ public class DisplayModeDirectorTest { createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0); setPeakRefreshRate(90); director.getSettingsObserver().setDefaultRefreshRate(90); - director.getBrightnessObserver().setDefaultDisplayState(true); + director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON); final FakeDeviceConfig config = mInjector.getDeviceConfig(); config.setRefreshRateInLowZone(90); @@ -453,7 +454,7 @@ public class DisplayModeDirectorTest { createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0); setPeakRefreshRate(90 /*fps*/); director.getSettingsObserver().setDefaultRefreshRate(90); - director.getBrightnessObserver().setDefaultDisplayState(true); + director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON); final FakeDeviceConfig config = mInjector.getDeviceConfig(); config.setRefreshRateInHighZone(60); @@ -490,6 +491,43 @@ public class DisplayModeDirectorTest { assertVoteForRefreshRateLocked(vote, 60 /*fps*/); } + @Test + public void testSensorRegistration() { + DisplayModeDirector director = + createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0); + setPeakRefreshRate(90 /*fps*/); + director.getSettingsObserver().setDefaultRefreshRate(90); + director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON); + + Sensor lightSensor = createLightSensor(); + SensorManager sensorManager = createMockSensorManager(lightSensor); + + director.start(sensorManager); + ArgumentCaptor<SensorEventListener> listenerCaptor = + ArgumentCaptor.forClass(SensorEventListener.class); + Mockito.verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1))) + .registerListener( + listenerCaptor.capture(), + eq(lightSensor), + anyInt(), + any(Handler.class)); + + // Dispaly state changed from On to Doze + director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_DOZE); + Mockito.verify(sensorManager) + .unregisterListener(listenerCaptor.capture()); + + // Dispaly state changed from Doze to On + director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON); + Mockito.verify(sensorManager, times(2)) + .registerListener( + listenerCaptor.capture(), + eq(lightSensor), + anyInt(), + any(Handler.class)); + + } + private void assertVoteForRefreshRateLocked(Vote vote, float refreshRate) { assertThat(vote).isNotNull(); final DisplayModeDirector.RefreshRateRange expectedRange = @@ -685,10 +723,5 @@ public class DisplayModeDirectorTest { PEAK_REFRESH_RATE_URI); } } - - @Override - public boolean isDeviceInteractive(@NonNull Context context) { - return true; - } } } 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 4259831c15f8..cea1acfcbd69 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -178,7 +178,6 @@ import com.android.server.wm.WindowManagerInternal; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java index 3deeea2d4577..c2ead5f15ceb 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java @@ -49,6 +49,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.FastXmlSerializer; import com.android.server.UiServiceTestCase; +import com.android.server.pm.PackageManagerService; import org.junit.Before; import org.junit.Test; @@ -257,6 +258,17 @@ public class SnoozeHelperTest extends UiServiceTestCase { } @Test + public void testSnoozeSentToAndroid() throws Exception { + NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM); + mSnoozeHelper.snooze(r, 1000); + ArgumentCaptor<PendingIntent> captor = ArgumentCaptor.forClass(PendingIntent.class); + verify(mAm, times(1)).setExactAndAllowWhileIdle( + anyInt(), anyLong(), captor.capture()); + assertEquals(PackageManagerService.PLATFORM_PACKAGE_NAME, + captor.getValue().getIntent().getPackage()); + } + + @Test public void testSnooze() throws Exception { NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM); mSnoozeHelper.snooze(r, (String) null); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java index 5b516a9de350..53a6d3ff599f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java @@ -312,6 +312,22 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase { } @Test + public void testActivityDrawnBeforeTransition() { + mTopActivity.setVisible(false); + notifyActivityLaunching(mTopActivity.intent); + // Assume the activity is launched the second time consecutively. The drawn event is from + // the first time (omitted in test) launch that is earlier than transition. + mTopActivity.mDrawn = true; + notifyWindowsDrawn(mTopActivity); + notifyActivityLaunched(START_SUCCESS, mTopActivity); + // If the launching activity was drawn when starting transition, the launch event should + // be reported successfully. + notifyTransitionStarting(mTopActivity); + + verifyOnActivityLaunchFinished(mTopActivity); + } + + @Test public void testActivityRecordProtoIsNotTooBig() { // The ActivityRecordProto must not be too big, otherwise converting it at runtime // will become prohibitively expensive. 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 d99606b704e4..b28220585c5c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -88,6 +88,7 @@ import static org.mockito.Mockito.doCallRealMethod; import android.annotation.SuppressLint; import android.app.ActivityTaskManager; import android.app.WindowConfiguration; +import android.app.servertransaction.FixedRotationAdjustmentsItem; import android.content.res.Configuration; import android.graphics.Rect; import android.graphics.Region; @@ -1346,6 +1347,36 @@ public class DisplayContentTests extends WindowTestsBase { } @Test + public void testClearIntermediateFixedRotationAdjustments() throws RemoteException { + final ActivityRecord activity = new ActivityTestsBase.StackBuilder(mWm.mRoot) + .setDisplay(mDisplayContent).build().getTopMostActivity(); + mDisplayContent.setFixedRotationLaunchingApp(activity, + (mDisplayContent.getRotation() + 1) % 4); + // Create a window so FixedRotationAdjustmentsItem can be sent. + createWindow(null, TYPE_APPLICATION_STARTING, activity, "AppWin"); + final ActivityRecord activity2 = new ActivityTestsBase.StackBuilder(mWm.mRoot) + .setDisplay(mDisplayContent).build().getTopMostActivity(); + activity2.setVisible(false); + clearInvocations(mWm.mAtmService.getLifecycleManager()); + // The first activity has applied fixed rotation but the second activity becomes the top + // before the transition is done and it has the same rotation as display, so the dispatched + // rotation adjustment of first activity must be cleared. + mDisplayContent.handleTopActivityLaunchingInDifferentOrientation(activity2, + false /* checkOpening */); + + final ArgumentCaptor<FixedRotationAdjustmentsItem> adjustmentsCaptor = + ArgumentCaptor.forClass(FixedRotationAdjustmentsItem.class); + verify(mWm.mAtmService.getLifecycleManager(), atLeastOnce()).scheduleTransaction( + eq(activity.app.getThread()), adjustmentsCaptor.capture()); + // The transformation is kept for animation in real case. + assertTrue(activity.hasFixedRotationTransform()); + final FixedRotationAdjustmentsItem clearAdjustments = FixedRotationAdjustmentsItem.obtain( + activity.token, null /* fixedRotationAdjustments */); + // The captor may match other items. The first one must be the item to clear adjustments. + assertEquals(clearAdjustments, adjustmentsCaptor.getAllValues().get(0)); + } + + @Test public void testRemoteRotation() { DisplayContent dc = createNewDisplay(); diff --git a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java index 91cfd4e6a89d..59d195b670a8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java @@ -18,7 +18,9 @@ package com.android.server.wm; import static android.view.InsetsState.ITYPE_IME; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import android.graphics.PixelFormat; @@ -64,4 +66,22 @@ public class ImeInsetsSourceProviderTest extends WindowTestsBase { mImeProvider.scheduleShowImePostLayout(target); assertTrue(mImeProvider.isImeTargetFromDisplayContentAndImeSame()); } + + @Test + public void testIsImeShowing() { + WindowState ime = createWindow(null, TYPE_INPUT_METHOD, "ime"); + makeWindowVisibleAndDrawn(ime); + mImeProvider.setWindow(ime, null, null); + + WindowState target = createWindow(null, TYPE_APPLICATION, "app"); + mDisplayContent.mInputMethodTarget = target; + mDisplayContent.mInputMethodControlTarget = target; + + mImeProvider.scheduleShowImePostLayout(target); + assertFalse(mImeProvider.isImeShowing()); + mImeProvider.checkShowImePostLayout(); + assertTrue(mImeProvider.isImeShowing()); + mImeProvider.setImeShowing(false); + assertFalse(mImeProvider.isImeShowing()); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 156298c86d41..60242faf3598 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -37,6 +37,8 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN; + import static org.mockito.Mockito.mock; import android.content.Context; @@ -261,6 +263,13 @@ class WindowTestsBase extends SystemServiceTestsBase { } } + static void makeWindowVisibleAndDrawn(WindowState... windows) { + makeWindowVisible(windows); + for (WindowState win : windows) { + win.mWinAnimator.mDrawState = HAS_DRAWN; + } + } + /** Creates a {@link ActivityStack} and adds it to the specified {@link DisplayContent}. */ ActivityStack createTaskStackOnDisplay(DisplayContent dc) { return createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, dc); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 04e8f6345dee..acde6f64b03b 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -4721,7 +4721,7 @@ public class CarrierConfigManager { sDefaults.putString(KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING, ""); sDefaults.putBoolean(KEY_USE_LOWER_MTU_VALUE_IF_BOTH_RECEIVED, false); sDefaults.putBoolean(KEY_USE_ACS_FOR_RCS_BOOL, false); - sDefaults.putBoolean(KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL, false); + sDefaults.putBoolean(KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL, true); sDefaults.putInt(KEY_DEFAULT_RTT_MODE_INT, 0); sDefaults.putStringArray(KEY_ALLOWED_INITIAL_ATTACH_APN_TYPES_STRING_ARRAY, new String[]{"ia", "default", "ims", "mms", "dun", "emergency"}); diff --git a/telephony/java/android/telephony/ims/ImsReasonInfo.java b/telephony/java/android/telephony/ims/ImsReasonInfo.java index c14024975cf5..a5a8958ce908 100644 --- a/telephony/java/android/telephony/ims/ImsReasonInfo.java +++ b/telephony/java/android/telephony/ims/ImsReasonInfo.java @@ -891,6 +891,13 @@ public final class ImsReasonInfo implements Parcelable { public static final int CODE_WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION = 1623; /** + * Call failed because of network congestion, resource is not available, + * or no circuit or channel available, etc. + * @hide + */ + public static final int CODE_NETWORK_CONGESTION = 1624; + + /** * The dialed RTT call should be retried without RTT * @hide */ @@ -1076,6 +1083,7 @@ public final class ImsReasonInfo implements Parcelable { CODE_REJECT_VT_AVPF_NOT_ALLOWED, CODE_REJECT_ONGOING_ENCRYPTED_CALL, CODE_REJECT_ONGOING_CS_CALL, + CODE_NETWORK_CONGESTION, CODE_RETRY_ON_IMS_WITHOUT_RTT, CODE_OEM_CAUSE_1, CODE_OEM_CAUSE_2, |