Merge "Remove CARD_PREVIEW_MODE settings as it's no longer needed"
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index a3eac2e..ec29fb1 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -8081,8 +8081,8 @@
   }
 
   public final class BatteryStatsManager {
-    method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.os.connectivity.CellularBatteryStats getCellularBatteryStats();
-    method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.os.connectivity.WifiBatteryStats getWifiBatteryStats();
+    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.BATTERY_STATS, android.Manifest.permission.UPDATE_DEVICE_STATS}) public android.os.connectivity.CellularBatteryStats getCellularBatteryStats();
+    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.BATTERY_STATS, android.Manifest.permission.UPDATE_DEVICE_STATS}) public android.os.connectivity.WifiBatteryStats getWifiBatteryStats();
     method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportFullWifiLockAcquiredFromSource(@NonNull android.os.WorkSource);
     method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportFullWifiLockReleasedFromSource(@NonNull android.os.WorkSource);
     method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportMobileRadioPowerState(boolean, int);
diff --git a/core/java/android/os/BatteryStatsManager.java b/core/java/android/os/BatteryStatsManager.java
index 2c088e2..4d55906 100644
--- a/core/java/android/os/BatteryStatsManager.java
+++ b/core/java/android/os/BatteryStatsManager.java
@@ -322,7 +322,9 @@
      *
      * @return Instance of {@link CellularBatteryStats}.
      */
-    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.BATTERY_STATS,
+            android.Manifest.permission.UPDATE_DEVICE_STATS})
     public @NonNull CellularBatteryStats getCellularBatteryStats() {
         try {
             return mBatteryStats.getCellularBatteryStats();
@@ -337,7 +339,9 @@
      *
      * @return Instance of {@link WifiBatteryStats}.
      */
-    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.BATTERY_STATS,
+            android.Manifest.permission.UPDATE_DEVICE_STATS})
     public @NonNull WifiBatteryStats getWifiBatteryStats() {
         try {
             return mBatteryStats.getWifiBatteryStats();
diff --git a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
index c5e35a4..8b394bf 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
@@ -16,13 +16,18 @@
 
 package com.android.systemui.toast;
 
+import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
+import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+
 import android.animation.Animator;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.Application;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -53,7 +58,7 @@
     final ToastPlugin.Toast mPluginToast;
 
     private final String mPackageName;
-    private final int mUserId;
+    @UserIdInt private final int mUserId;
     private final LayoutInflater mLayoutInflater;
 
     final int mDefaultX = 0;
@@ -74,7 +79,7 @@
     }
 
     SystemUIToast(LayoutInflater layoutInflater, Context context, CharSequence text,
-            ToastPlugin.Toast pluginToast, String packageName, int userId,
+            ToastPlugin.Toast pluginToast, String packageName, @UserIdInt int userId,
             int orientation) {
         mLayoutInflater = layoutInflater;
         mContext = context;
@@ -248,6 +253,15 @@
             return null;
         }
 
+        final Context userContext;
+        try {
+            userContext = context.createPackageContextAsUser("android",
+                0, new UserHandle(userId));
+        } catch (NameNotFoundException e) {
+            Log.e(TAG, "Could not create user package context");
+            return null;
+        }
+
         final ApplicationsState appState =
                 ApplicationsState.getInstance((Application) context.getApplicationContext());
         if (!appState.isUserAdded(userId)) {
@@ -255,9 +269,11 @@
                     + "packageName=" + packageName);
             return null;
         }
+
+        final PackageManager packageManager = userContext.getPackageManager();
         final AppEntry appEntry = appState.getEntry(packageName, userId);
         if (appEntry == null || appEntry.info == null
-                || !ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(appEntry)) {
+                || !showApplicationIcon(appEntry.info, packageManager)) {
             return null;
         }
 
@@ -265,7 +281,20 @@
         UserHandle user = UserHandle.getUserHandleForUid(appInfo.uid);
         IconFactory iconFactory = IconFactory.obtain(context);
         Bitmap iconBmp = iconFactory.createBadgedIconBitmap(
-                appInfo.loadUnbadgedIcon(context.getPackageManager()), user, true).icon;
+                appInfo.loadUnbadgedIcon(packageManager), user, true).icon;
         return new BitmapDrawable(context.getResources(), iconBmp);
     }
+
+    private static boolean showApplicationIcon(ApplicationInfo appInfo,
+            PackageManager packageManager) {
+        if (hasFlag(appInfo.flags, FLAG_UPDATED_SYSTEM_APP)) {
+            return packageManager.getLaunchIntentForPackage(appInfo.packageName)
+                != null;
+        }
+        return !hasFlag(appInfo.flags, FLAG_SYSTEM);
+    }
+
+    private static boolean hasFlag(int flags, int flag) {
+        return (flags & flag) != 0;
+    }
 }
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 312cde6..de5f47d 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -662,6 +662,26 @@
         return false;
     }
 
+    /**
+     * Requests a count of saved passwords from the current service.
+     *
+     * @return {@code true} if the request succeeded
+     */
+    // Called by Shell command
+    boolean requestSavedPasswordCount(@UserIdInt int userId, @NonNull IResultReceiver receiver) {
+        enforceCallingPermissionForManagement();
+        synchronized (mLock) {
+            final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
+            if (service != null) {
+                service.requestSavedPasswordCount(receiver);
+                return true;
+            } else if (sVerbose) {
+                Slog.v(TAG, "requestSavedPasswordCount(): no service for " + userId);
+            }
+        }
+        return false;
+    }
+
     private void setLoggingLevelsLocked(boolean debug, boolean verbose) {
         com.android.server.autofill.Helper.sDebug = debug;
         android.view.autofill.Helper.sDebug = debug;
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index a80efc4..5f2d4e8 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -76,6 +76,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.os.IResultReceiver;
 import com.android.server.LocalServices;
 import com.android.server.autofill.AutofillManagerService.AutofillCompatState;
 import com.android.server.autofill.AutofillManagerService.DisabledInfoCache;
@@ -1181,6 +1182,15 @@
     }
 
     @GuardedBy("mLock")
+    void requestSavedPasswordCount(IResultReceiver receiver) {
+        RemoteFillService remoteService =
+                new RemoteFillService(
+                        getContext(), mInfo.getServiceInfo().getComponentName(), mUserId,
+                        /* callbacks= */ null, mMaster.isInstantServiceAllowed());
+        remoteService.onSavedPasswordCountRequest(receiver);
+    }
+
+    @GuardedBy("mLock")
     @Nullable RemoteAugmentedAutofillService getRemoteAugmentedAutofillServiceLocked() {
         if (mRemoteAugmentedAutofillService == null) {
             final String serviceName = mMaster.mAugmentedAutofillResolver.getServiceName(mUserId);
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
index 68e6290..1eaa59a 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
@@ -17,6 +17,7 @@
 package com.android.server.autofill;
 
 import static android.service.autofill.AutofillFieldClassificationService.EXTRA_SCORES;
+import static android.service.autofill.AutofillService.EXTRA_RESULT;
 
 import static com.android.server.autofill.AutofillManagerService.RECEIVER_BUNDLE_EXTRA_SESSIONS;
 
@@ -89,6 +90,9 @@
             pw.println("  get bind-instant-service-allowed");
             pw.println("    Gets whether binding to services provided by instant apps is allowed");
             pw.println("");
+            pw.println("  get saved-password-count");
+            pw.println("    Gets the number of saved passwords in the current service.");
+            pw.println("");
             pw.println("  set log_level [off | debug | verbose]");
             pw.println("    Sets the Autofill log level.");
             pw.println("");
@@ -145,6 +149,8 @@
                 return getBindInstantService(pw);
             case "default-augmented-service-enabled":
                 return getDefaultAugmentedServiceEnabled(pw);
+            case "saved-password-count":
+                return getSavedPasswordCount(pw);
             default:
                 pw.println("Invalid set: " + what);
                 return -1;
@@ -342,6 +348,25 @@
         return 0;
     }
 
+    private int getSavedPasswordCount(PrintWriter pw) {
+        final int userId = getNextIntArgRequired();
+        CountDownLatch latch = new CountDownLatch(1);
+        IResultReceiver resultReceiver = new IResultReceiver.Stub() {
+            @Override
+            public void send(int resultCode, Bundle resultData) {
+                pw.println("resultCode=" + resultCode);
+                if (resultCode == 0 && resultData != null) {
+                    pw.println("value=" + resultData.getInt(EXTRA_RESULT));
+                }
+                latch.countDown();
+            }
+        };
+        if (mService.requestSavedPasswordCount(userId, resultReceiver)) {
+            waitForLatch(pw, latch);
+        }
+        return 0;
+    }
+
     private int requestDestroy(PrintWriter pw) {
         if (!isNextArgSessions(pw)) {
             return -1;
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index b0755ac..94872b0 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -41,6 +41,7 @@
 
 import com.android.internal.infra.AbstractRemoteService;
 import com.android.internal.infra.ServiceConnector;
+import com.android.internal.os.IResultReceiver;
 
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.CompletableFuture;
@@ -225,6 +226,10 @@
                 }));
     }
 
+    void onSavedPasswordCountRequest(IResultReceiver receiver) {
+        run(service -> service.onSavedPasswordCountRequest(receiver));
+    }
+
     public void destroy() {
         unbind();
     }
diff --git a/services/core/java/com/android/server/appop/DiscreteRegistry.java b/services/core/java/com/android/server/appop/DiscreteRegistry.java
index 8b2d9e7..0439660 100644
--- a/services/core/java/com/android/server/appop/DiscreteRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteRegistry.java
@@ -542,7 +542,7 @@
 
         private OpEvent getLastVisible() {
             // Search all nodes but the first one, which is the start node
-            for (int i = mChain.size() - 1; i > 0; i++) {
+            for (int i = mChain.size() - 1; i > 0; i--) {
                 OpEvent event = mChain.get(i);
                 if (!mExemptPkgs.contains(event.mPkgName)) {
                     return event;
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index bad7e5c..dab980a 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -641,7 +641,7 @@
         // Cell Broadcast Receiver
         grantSystemFixedPermissionsToSystemPackage(pm,
                 getDefaultSystemHandlerActivityPackage(pm, Intents.SMS_CB_RECEIVED_ACTION, userId),
-                userId, SMS_PERMISSIONS);
+                userId, SMS_PERMISSIONS, NEARBY_DEVICES_PERMISSIONS);
 
         // Carrier Provisioning Service
         grantPermissionsToSystemPackage(pm,
diff --git a/services/core/java/com/android/server/vibrator/StepToRampAdapter.java b/services/core/java/com/android/server/vibrator/StepToRampAdapter.java
index d439b94..1d8c64b 100644
--- a/services/core/java/com/android/server/vibrator/StepToRampAdapter.java
+++ b/services/core/java/com/android/server/vibrator/StepToRampAdapter.java
@@ -22,6 +22,7 @@
 import android.os.vibrator.StepSegment;
 import android.os.vibrator.VibrationEffectSegment;
 
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -59,6 +60,7 @@
         convertStepsToRamps(segments);
         int newRepeatIndex = addRampDownToZeroAmplitudeSegments(segments, repeatIndex);
         newRepeatIndex = addRampDownToLoop(segments, newRepeatIndex);
+        newRepeatIndex = splitLongRampSegments(info, segments, newRepeatIndex);
         return newRepeatIndex;
     }
 
@@ -210,11 +212,70 @@
         return repeatIndex;
     }
 
+    /**
+     * Split {@link RampSegment} entries that have duration longer than {@link
+     * VibratorInfo#getPwlePrimitiveDurationMax()}.
+     */
+    private int splitLongRampSegments(VibratorInfo info, List<VibrationEffectSegment> segments,
+            int repeatIndex) {
+        int maxDuration = info.getPwlePrimitiveDurationMax();
+        if (maxDuration <= 0) {
+            // No limit set to PWLE primitive duration.
+            return repeatIndex;
+        }
+
+        int segmentCount = segments.size();
+        for (int i = 0; i < segmentCount; i++) {
+            if (!(segments.get(i) instanceof RampSegment)) {
+                continue;
+            }
+            RampSegment ramp = (RampSegment) segments.get(i);
+            int splits = ((int) ramp.getDuration() + maxDuration - 1) / maxDuration;
+            if (splits <= 1) {
+                continue;
+            }
+            segments.remove(i);
+            segments.addAll(i, splitRampSegment(ramp, splits));
+            int addedSegments = splits - 1;
+            if (repeatIndex > i) {
+                repeatIndex += addedSegments;
+            }
+            i += addedSegments;
+            segmentCount += addedSegments;
+        }
+
+        return repeatIndex;
+    }
+
     private static RampSegment apply(StepSegment segment) {
         return new RampSegment(segment.getAmplitude(), segment.getAmplitude(),
                 segment.getFrequency(), segment.getFrequency(), (int) segment.getDuration());
     }
 
+    private static List<RampSegment> splitRampSegment(RampSegment ramp, int splits) {
+        List<RampSegment> ramps = new ArrayList<>(splits);
+        long splitDuration = ramp.getDuration() / splits;
+        float previousAmplitude = ramp.getStartAmplitude();
+        float previousFrequency = ramp.getStartFrequency();
+        long accumulatedDuration = 0;
+
+        for (int i = 1; i < splits; i++) {
+            accumulatedDuration += splitDuration;
+            RampSegment rampSplit = new RampSegment(
+                    previousAmplitude, interpolateAmplitude(ramp, accumulatedDuration),
+                    previousFrequency, interpolateFrequency(ramp, accumulatedDuration),
+                    (int) splitDuration);
+            ramps.add(rampSplit);
+            previousAmplitude = rampSplit.getEndAmplitude();
+            previousFrequency = rampSplit.getEndFrequency();
+        }
+
+        ramps.add(new RampSegment(previousAmplitude, ramp.getEndAmplitude(), previousFrequency,
+                ramp.getEndFrequency(), (int) (ramp.getDuration() - accumulatedDuration)));
+
+        return ramps;
+    }
+
     private static RampSegment createRampDown(float amplitude, float frequency, long duration) {
         return new RampSegment(amplitude, /* endAmplitude= */ 0, frequency, frequency,
                 (int) duration);
@@ -244,4 +305,19 @@
         }
         return false;
     }
+
+    private static float interpolateAmplitude(RampSegment ramp, long duration) {
+        return interpolate(ramp.getStartAmplitude(), ramp.getEndAmplitude(), duration,
+                ramp.getDuration());
+    }
+
+    private static float interpolateFrequency(RampSegment ramp, long duration) {
+        return interpolate(ramp.getStartFrequency(), ramp.getEndFrequency(), duration,
+                ramp.getDuration());
+    }
+
+    private static float interpolate(float start, float end, long duration, long totalDuration) {
+        float position = (float) duration / totalDuration;
+        return start + position * (end - start);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java b/services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java
index f4eb2de..32988ef 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java
@@ -68,6 +68,38 @@
     }
 
     @Test
+    public void testRampSegments_withPwleDurationLimit_splitsLongRamps() {
+        List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
+                new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f,
+                        /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10),
+                new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 1,
+                        /* startFrequency= */ 0, /* endFrequency= */ -1, /* duration= */ 25),
+                new RampSegment(/* startAmplitude= */ 1, /* endAmplitude*/ 1,
+                        /* startFrequency= */ 0, /* endFrequency= */ 1, /* duration= */ 5)));
+        List<VibrationEffectSegment> expectedSegments = Arrays.asList(
+                new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f,
+                        /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10),
+                new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 0.32f,
+                        /* startFrequency= */ 0, /* endFrequency= */ -0.32f, /* duration= */ 8),
+                new RampSegment(/* startAmplitude= */ 0.32f, /* endAmplitude= */ 0.64f,
+                        /* startFrequency= */ -0.32f, /* endFrequency= */ -0.64f,
+                        /* duration= */ 8),
+                new RampSegment(/* startAmplitude= */ 0.64f, /* endAmplitude= */ 1,
+                        /* startFrequency= */ -0.64f, /* endFrequency= */ -1, /* duration= */ 9),
+                new RampSegment(/* startAmplitude= */ 1, /* endAmplitude*/ 1,
+                        /* startFrequency= */ 0, /* endFrequency= */ 1, /* duration= */ 5));
+
+        VibratorInfo vibratorInfo = new VibratorInfo.Builder(0)
+                .setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)
+                .setPwlePrimitiveDurationMax(10)
+                .build();
+
+        // Update repeat index to skip the ramp splits.
+        assertEquals(4, mAdapter.apply(segments, 2, vibratorInfo));
+        assertEquals(expectedSegments, segments);
+    }
+
+    @Test
     public void testStepAndRampSegments_withoutPwleCapability_keepsListUnchanged() {
         mAdapter = new StepToRampAdapter(50);