summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xapi/current.txt10
-rw-r--r--api/system-current.txt2
-rw-r--r--api/test-current.txt3
-rw-r--r--core/java/android/app/ActivityThread.java134
-rw-r--r--core/java/android/app/Notification.java38
-rw-r--r--core/java/android/app/NotificationChannel.java125
-rw-r--r--core/java/android/app/NotificationChannelGroup.java113
-rw-r--r--core/java/android/content/ContentResolver.java38
-rw-r--r--core/java/android/content/pm/PackageManagerInternal.java12
-rw-r--r--core/java/android/os/FileUtils.java20
-rw-r--r--core/java/android/os/storage/StorageManagerInternal.java4
-rw-r--r--core/java/android/provider/MediaStore.java15
-rw-r--r--core/jni/Android.bp1
-rw-r--r--core/jni/android_app_ActivityThread.cpp55
-rw-r--r--core/jni/android_view_DisplayListCanvas.cpp22
-rw-r--r--core/proto/android/app/notification_channel.proto3
-rw-r--r--core/proto/android/app/notification_channel_group.proto3
-rw-r--r--core/tests/coretests/src/android/content/ContentResolverTest.java15
-rw-r--r--core/tests/coretests/src/android/os/FileUtilsTest.java14
-rw-r--r--packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java2
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java6
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java202
-rw-r--r--services/core/java/com/android/server/accounts/AccountManagerService.java65
-rw-r--r--services/core/java/com/android/server/accounts/AccountsDb.java77
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java15
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java51
-rw-r--r--services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java29
-rw-r--r--services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java41
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java129
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java19
-rwxr-xr-xtelephony/java/com/android/internal/telephony/ISub.aidl6
31 files changed, 909 insertions, 360 deletions
diff --git a/api/current.txt b/api/current.txt
index 15392b8fe29a..5f399598689d 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -5217,6 +5217,7 @@ package android.app {
ctor public Notification(android.os.Parcel);
method public android.app.Notification clone();
method public int describeContents();
+ method public android.app.PendingIntent getAppOverlayIntent();
method public int getBadgeIconType();
method public java.lang.String getChannelId();
method public java.lang.String getGroup();
@@ -5446,6 +5447,7 @@ package android.app {
method public android.app.Notification.Style getStyle();
method public static android.app.Notification.Builder recoverBuilder(android.content.Context, android.app.Notification);
method public android.app.Notification.Builder setActions(android.app.Notification.Action...);
+ method public android.app.Notification.Builder setAppOverlayIntent(android.app.PendingIntent);
method public android.app.Notification.Builder setAutoCancel(boolean);
method public android.app.Notification.Builder setBadgeIconType(int);
method public android.app.Notification.Builder setCategory(java.lang.String);
@@ -5663,6 +5665,7 @@ package android.app {
public final class NotificationChannel implements android.os.Parcelable {
ctor public NotificationChannel(java.lang.String, java.lang.CharSequence, int);
method public boolean canBypassDnd();
+ method public boolean canOverlayApps();
method public boolean canShowBadge();
method public int describeContents();
method public void enableLights(boolean);
@@ -5678,6 +5681,7 @@ package android.app {
method public android.net.Uri getSound();
method public long[] getVibrationPattern();
method public boolean hasUserSetImportance();
+ method public void setAllowAppOverlay(boolean);
method public void setBypassDnd(boolean);
method public void setDescription(java.lang.String);
method public void setGroup(java.lang.String);
@@ -5697,6 +5701,7 @@ package android.app {
public final class NotificationChannelGroup implements android.os.Parcelable {
ctor public NotificationChannelGroup(java.lang.String, java.lang.CharSequence);
+ method public boolean canOverlayApps();
method public android.app.NotificationChannelGroup clone();
method public int describeContents();
method public java.util.List<android.app.NotificationChannel> getChannels();
@@ -5704,6 +5709,7 @@ package android.app {
method public java.lang.String getId();
method public java.lang.CharSequence getName();
method public boolean isBlocked();
+ method public void setAllowAppOverlay(boolean);
method public void setDescription(java.lang.String);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.NotificationChannelGroup> CREATOR;
@@ -36803,10 +36809,12 @@ package android.provider {
method public static android.net.Uri getMediaScannerUri();
method public static android.net.Uri getMediaUri(android.content.Context, android.net.Uri);
method public static java.lang.String getVersion(android.content.Context);
+ method public static java.lang.String getVolumeName(android.net.Uri);
field public static final java.lang.String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE";
field public static final java.lang.String ACTION_IMAGE_CAPTURE_SECURE = "android.media.action.IMAGE_CAPTURE_SECURE";
field public static final java.lang.String ACTION_VIDEO_CAPTURE = "android.media.action.VIDEO_CAPTURE";
field public static final java.lang.String AUTHORITY = "media";
+ field public static final android.net.Uri AUTHORITY_URI;
field public static final java.lang.String EXTRA_DURATION_LIMIT = "android.intent.extra.durationLimit";
field public static final java.lang.String EXTRA_FINISH_ON_COMPLETION = "android.intent.extra.finishOnCompletion";
field public static final java.lang.String EXTRA_FULL_SCREEN = "android.intent.extra.fullScreen";
@@ -43141,7 +43149,7 @@ package android.telephony {
method public static int getDefaultSmsSubscriptionId();
method public static int getDefaultSubscriptionId();
method public static int getDefaultVoiceSubscriptionId();
- method public java.util.List<android.telephony.SubscriptionInfo> getOpportunisticSubscriptions(int);
+ method public java.util.List<android.telephony.SubscriptionInfo> getOpportunisticSubscriptions();
method public static int getSlotIndex(int);
method public static int[] getSubscriptionIds(int);
method public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int);
diff --git a/api/system-current.txt b/api/system-current.txt
index 4512fc3b9c2b..fb97643129bd 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -987,8 +987,8 @@ package android.content {
field public static final java.lang.String ACTION_PRE_BOOT_COMPLETED = "android.intent.action.PRE_BOOT_COMPLETED";
field public static final java.lang.String ACTION_QUERY_PACKAGE_RESTART = "android.intent.action.QUERY_PACKAGE_RESTART";
field public static final java.lang.String ACTION_RESOLVE_INSTANT_APP_PACKAGE = "android.intent.action.RESOLVE_INSTANT_APP_PACKAGE";
- field public static final java.lang.String ACTION_REVIEW_PERMISSION_USAGE = "android.intent.action.REVIEW_PERMISSION_USAGE";
field public static final java.lang.String ACTION_REVIEW_PERMISSIONS = "android.intent.action.REVIEW_PERMISSIONS";
+ field public static final java.lang.String ACTION_REVIEW_PERMISSION_USAGE = "android.intent.action.REVIEW_PERMISSION_USAGE";
field public static final java.lang.String ACTION_SHOW_SUSPENDED_APP_DETAILS = "android.intent.action.SHOW_SUSPENDED_APP_DETAILS";
field public static final deprecated java.lang.String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED";
field public static final java.lang.String ACTION_SPLIT_CONFIGURATION_CHANGED = "android.intent.action.SPLIT_CONFIGURATION_CHANGED";
diff --git a/api/test-current.txt b/api/test-current.txt
index 0e3d84efa0ac..8f08c7108721 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -140,7 +140,10 @@ package android.app {
}
public final class NotificationChannelGroup implements android.os.Parcelable {
+ method public int getUserLockedFields();
+ method public void lockFields(int);
method public void setBlocked(boolean);
+ field public static final int USER_LOCKED_ALLOW_APP_OVERLAY = 2; // 0x2
}
public class NotificationManager {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 4756bf40bad3..3b2a21e61e54 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -23,6 +23,8 @@ import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME;
import static android.app.servertransaction.ActivityLifecycleItem.ON_START;
import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
+import static android.content.ContentResolver.DEPRECATE_DATA_COLUMNS;
+import static android.content.ContentResolver.DEPRECATE_DATA_PREFIX;
import static android.view.Display.INVALID_DISPLAY;
import android.annotation.NonNull;
@@ -45,6 +47,7 @@ import android.content.BroadcastReceiver;
import android.content.ComponentCallbacks2;
import android.content.ComponentName;
import android.content.ContentProvider;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.IContentProvider;
import android.content.IIntentReceiver;
@@ -84,6 +87,7 @@ import android.os.Bundle;
import android.os.Debug;
import android.os.DropBoxManager;
import android.os.Environment;
+import android.os.FileUtils;
import android.os.GraphicsEnvironment;
import android.os.Handler;
import android.os.HandlerExecutor;
@@ -114,6 +118,9 @@ import android.provider.Settings;
import android.renderscript.RenderScriptCacheDir;
import android.security.NetworkSecurityPolicy;
import android.security.net.config.NetworkSecurityConfigProvider;
+import android.system.ErrnoException;
+import android.system.OsConstants;
+import android.system.StructStat;
import android.util.AndroidRuntimeException;
import android.util.ArrayMap;
import android.util.DisplayMetrics;
@@ -162,13 +169,16 @@ import dalvik.system.VMRuntime;
import libcore.io.DropBox;
import libcore.io.EventLogger;
+import libcore.io.ForwardingOs;
import libcore.io.IoUtils;
+import libcore.io.Os;
import libcore.net.event.NetworkEventDispatcher;
import org.apache.harmony.dalvik.ddmc.DdmVmInternal;
import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
@@ -392,6 +402,9 @@ public final class ActivityThread extends ClientTransactionHandler {
= new ArrayMap<Activity, ArrayList<OnActivityPausedListener>>();
final GcIdler mGcIdler = new GcIdler();
+ final PurgeIdler mPurgeIdler = new PurgeIdler();
+
+ boolean mPurgeIdlerScheduled = false;
boolean mGcIdlerScheduled = false;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@@ -1681,6 +1694,7 @@ public final class ActivityThread extends ClientTransactionHandler {
public static final int RUN_ISOLATED_ENTRY_POINT = 158;
public static final int EXECUTE_TRANSACTION = 159;
public static final int RELAUNCH_ACTIVITY = 160;
+ public static final int PURGE_RESOURCES = 161;
String codeToString(int code) {
if (DEBUG_MESSAGES) {
@@ -1724,6 +1738,7 @@ public final class ActivityThread extends ClientTransactionHandler {
case RUN_ISOLATED_ENTRY_POINT: return "RUN_ISOLATED_ENTRY_POINT";
case EXECUTE_TRANSACTION: return "EXECUTE_TRANSACTION";
case RELAUNCH_ACTIVITY: return "RELAUNCH_ACTIVITY";
+ case PURGE_RESOURCES: return "PURGE_RESOURCES";
}
}
return Integer.toString(code);
@@ -1761,6 +1776,7 @@ public final class ActivityThread extends ClientTransactionHandler {
case UNBIND_SERVICE:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceUnbind");
handleUnbindService((BindServiceData)msg.obj);
+ schedulePurgeIdler();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case SERVICE_ARGS:
@@ -1771,6 +1787,7 @@ public final class ActivityThread extends ClientTransactionHandler {
case STOP_SERVICE:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceStop");
handleStopService((IBinder)msg.obj);
+ schedulePurgeIdler();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
case CONFIGURATION_CHANGED:
@@ -1904,6 +1921,9 @@ public final class ActivityThread extends ClientTransactionHandler {
case RELAUNCH_ACTIVITY:
handleRelaunchActivityLocally((IBinder) msg.obj);
break;
+ case PURGE_RESOURCES:
+ schedulePurgeIdler();
+ break;
}
Object obj = msg.obj;
if (obj instanceof SomeArgs) {
@@ -1956,6 +1976,17 @@ public final class ActivityThread extends ClientTransactionHandler {
@Override
public final boolean queueIdle() {
doGcIfNeeded();
+ nPurgePendingResources();
+ return false;
+ }
+ }
+
+ final class PurgeIdler implements MessageQueue.IdleHandler {
+ @Override
+ public boolean queueIdle() {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "purgePendingResources");
+ nPurgePendingResources();
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
return false;
}
}
@@ -2285,6 +2316,22 @@ public final class ActivityThread extends ClientTransactionHandler {
mH.removeMessages(H.GC_WHEN_IDLE);
}
+ void schedulePurgeIdler() {
+ if (!mPurgeIdlerScheduled) {
+ mPurgeIdlerScheduled = true;
+ Looper.myQueue().addIdleHandler(mPurgeIdler);
+ }
+ mH.removeMessages(H.PURGE_RESOURCES);
+ }
+
+ void unschedulePurgeIdler() {
+ if (mPurgeIdlerScheduled) {
+ mPurgeIdlerScheduled = false;
+ Looper.myQueue().removeIdleHandler(mPurgeIdler);
+ }
+ mH.removeMessages(H.PURGE_RESOURCES);
+ }
+
void doGcIfNeeded() {
mGcIdlerScheduled = false;
final long now = SystemClock.uptimeMillis();
@@ -4586,6 +4633,7 @@ public final class ActivityThread extends ClientTransactionHandler {
}
r.setState(ON_DESTROY);
}
+ schedulePurgeIdler();
mActivities.remove(token);
StrictMode.decrementExpectedActivityCount(activityClass);
return r;
@@ -6749,7 +6797,7 @@ public final class ActivityThread extends ClientTransactionHandler {
}
}
- private class DropBoxReporter implements DropBox.Reporter {
+ private static class DropBoxReporter implements DropBox.Reporter {
private DropBoxManager dropBox;
@@ -6769,7 +6817,84 @@ public final class ActivityThread extends ClientTransactionHandler {
private synchronized void ensureInitialized() {
if (dropBox == null) {
- dropBox = (DropBoxManager) getSystemContext().getSystemService(Context.DROPBOX_SERVICE);
+ dropBox = currentActivityThread().getApplication()
+ .getSystemService(DropBoxManager.class);
+ }
+ }
+ }
+
+ private static class AndroidOs extends ForwardingOs {
+ /**
+ * Install selective syscall interception. For example, this is used to
+ * implement special filesystem paths that will be redirected to
+ * {@link ContentResolver#openFileDescriptor(Uri, String)}.
+ */
+ public static void install() {
+ // If feature is disabled, we don't need to install
+ if (!DEPRECATE_DATA_COLUMNS) return;
+
+ // If app is modern enough, we don't need to install
+ if (VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.Q) return;
+
+ // Install interception and make sure it sticks!
+ Os def = null;
+ do {
+ def = Os.getDefault();
+ } while (!Os.compareAndSetDefault(def, new AndroidOs(def)));
+ }
+
+ private AndroidOs(Os os) {
+ super(os);
+ }
+
+ private FileDescriptor openDeprecatedDataPath(String path, int mode) throws ErrnoException {
+ final Uri uri = ContentResolver.translateDeprecatedDataPath(path);
+ Log.v(TAG, "Redirecting " + path + " to " + uri);
+
+ final ContentResolver cr = currentActivityThread().getApplication()
+ .getContentResolver();
+ try {
+ final FileDescriptor fd = new FileDescriptor();
+ fd.setInt$(cr.openFileDescriptor(uri,
+ FileUtils.translateModePosixToString(mode)).detachFd());
+ return fd;
+ } catch (FileNotFoundException e) {
+ throw new ErrnoException(e.getMessage(), OsConstants.ENOENT);
+ }
+ }
+
+ @Override
+ public boolean access(String path, int mode) throws ErrnoException {
+ if (path != null && path.startsWith(DEPRECATE_DATA_PREFIX)) {
+ // If we opened it okay, then access check succeeded
+ IoUtils.closeQuietly(
+ openDeprecatedDataPath(path, FileUtils.translateModeAccessToPosix(mode)));
+ return true;
+ } else {
+ return super.access(path, mode);
+ }
+ }
+
+ @Override
+ public FileDescriptor open(String path, int flags, int mode) throws ErrnoException {
+ if (path != null && path.startsWith(DEPRECATE_DATA_PREFIX)) {
+ return openDeprecatedDataPath(path, mode);
+ } else {
+ return super.open(path, flags, mode);
+ }
+ }
+
+ @Override
+ public StructStat stat(String path) throws ErrnoException {
+ if (path != null && path.startsWith(DEPRECATE_DATA_PREFIX)) {
+ final FileDescriptor fd = openDeprecatedDataPath(path, OsConstants.O_RDONLY);
+ try {
+ return android.system.Os.fstat(fd);
+ } finally {
+ IoUtils.closeQuietly(fd);
+ }
+ } else {
+ return super.stat(path);
}
}
}
@@ -6777,6 +6902,9 @@ public final class ActivityThread extends ClientTransactionHandler {
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
+ // Install selective syscall interception
+ AndroidOs.install();
+
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
@@ -6826,6 +6954,6 @@ public final class ActivityThread extends ClientTransactionHandler {
}
// ------------------ Regular JNI ------------------------
-
+ private native void nPurgePendingResources();
private native void nDumpGraphicsInfo(FileDescriptor fd);
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 4f41da6e52fb..6d464fb190c1 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1275,6 +1275,8 @@ public class Notification implements Parcelable
private String mShortcutId;
private CharSequence mSettingsText;
+ private PendingIntent mAppOverlayIntent;
+
/** @hide */
@IntDef(prefix = { "GROUP_ALERT_" }, value = {
GROUP_ALERT_ALL, GROUP_ALERT_CHILDREN, GROUP_ALERT_SUMMARY
@@ -2225,6 +2227,9 @@ public class Notification implements Parcelable
}
mGroupAlertBehavior = parcel.readInt();
+ if (parcel.readInt() != 0) {
+ mAppOverlayIntent = PendingIntent.CREATOR.createFromParcel(parcel);
+ }
}
@Override
@@ -2339,6 +2344,7 @@ public class Notification implements Parcelable
that.mBadgeIcon = this.mBadgeIcon;
that.mSettingsText = this.mSettingsText;
that.mGroupAlertBehavior = this.mGroupAlertBehavior;
+ that.mAppOverlayIntent = this.mAppOverlayIntent;
if (!heavy) {
that.lightenPayload(); // will clean out extras
@@ -2660,6 +2666,13 @@ public class Notification implements Parcelable
parcel.writeInt(mGroupAlertBehavior);
+ if (mAppOverlayIntent != null) {
+ parcel.writeInt(1);
+ mAppOverlayIntent.writeToParcel(parcel, 0);
+ } else {
+ parcel.writeInt(0);
+ }
+
// mUsesStandardHeader is not written because it should be recomputed in listeners
}
@@ -3073,6 +3086,14 @@ public class Notification implements Parcelable
}
/**
+ * Returns the intent that will be used to display app content in a floating window over the
+ * existing foreground activity.
+ */
+ public PendingIntent getAppOverlayIntent() {
+ return mAppOverlayIntent;
+ }
+
+ /**
* The small icon representing this notification in the status bar and content view.
*
* @return the small icon representing this notification.
@@ -3406,6 +3427,23 @@ public class Notification implements Parcelable
return this;
}
+ /**
+ * Sets the intent that will be used to display app content in a floating window
+ * over the existing foreground activity.
+ *
+ * <p>This intent will be ignored unless this notification is posted to a channel that
+ * allows {@link NotificationChannel#canOverlayApps() app overlays}.</p>
+ *
+ * <p>Notifications with a valid and allowed app overlay intent will be displayed as
+ * floating windows outside of the notification shade on unlocked devices. When a user
+ * interacts with one of these windows, this app overlay intent will be invoked and
+ * displayed.</p>
+ */
+ public Builder setAppOverlayIntent(PendingIntent intent) {
+ mN.mAppOverlayIntent = intent;
+ return this;
+ }
+
/** @removed */
@Deprecated
public Builder setChannel(String channelId) {
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 9f93e1765da3..41ceaafa56a9 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -15,6 +15,8 @@
*/
package android.app;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
@@ -41,6 +43,7 @@ import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
+import java.util.Objects;
/**
* A representation of settings that apply to a collection of similarly themed notifications.
@@ -81,6 +84,7 @@ public final class NotificationChannel implements Parcelable {
private static final String ATT_FG_SERVICE_SHOWN = "fgservice";
private static final String ATT_GROUP = "group";
private static final String ATT_BLOCKABLE_SYSTEM = "blockable_system";
+ private static final String ATT_ALLOW_APP_OVERLAY = "app_overlay";
private static final String DELIMITER = ",";
/**
@@ -116,6 +120,11 @@ public final class NotificationChannel implements Parcelable {
/**
* @hide
*/
+ public static final int USER_LOCKED_ALLOW_APP_OVERLAY = 0x00000100;
+
+ /**
+ * @hide
+ */
public static final int[] LOCKABLE_FIELDS = new int[] {
USER_LOCKED_PRIORITY,
USER_LOCKED_VISIBILITY,
@@ -124,6 +133,7 @@ public final class NotificationChannel implements Parcelable {
USER_LOCKED_VIBRATION,
USER_LOCKED_SOUND,
USER_LOCKED_SHOW_BADGE,
+ USER_LOCKED_ALLOW_APP_OVERLAY
};
private static final int DEFAULT_LIGHT_COLOR = 0;
@@ -133,6 +143,7 @@ public final class NotificationChannel implements Parcelable {
NotificationManager.IMPORTANCE_UNSPECIFIED;
private static final boolean DEFAULT_DELETED = false;
private static final boolean DEFAULT_SHOW_BADGE = true;
+ private static final boolean DEFAULT_ALLOW_APP_OVERLAY = true;
@UnsupportedAppUsage
private final String mId;
@@ -156,6 +167,7 @@ public final class NotificationChannel implements Parcelable {
private AudioAttributes mAudioAttributes = Notification.AUDIO_ATTRIBUTES_DEFAULT;
// If this is a blockable system notification channel.
private boolean mBlockableSystem = false;
+ private boolean mAllowAppOverlay = DEFAULT_ALLOW_APP_OVERLAY;
/**
* Creates a notification channel.
@@ -217,6 +229,7 @@ public final class NotificationChannel implements Parcelable {
mAudioAttributes = in.readInt() > 0 ? AudioAttributes.CREATOR.createFromParcel(in) : null;
mLightColor = in.readInt();
mBlockableSystem = in.readBoolean();
+ mAllowAppOverlay = in.readBoolean();
}
@Override
@@ -269,6 +282,7 @@ public final class NotificationChannel implements Parcelable {
}
dest.writeInt(mLightColor);
dest.writeBoolean(mBlockableSystem);
+ dest.writeBoolean(mAllowAppOverlay);
}
/**
@@ -461,6 +475,22 @@ public final class NotificationChannel implements Parcelable {
}
/**
+ * Sets whether notifications posted to this channel can appear outside of the notification
+ * shade, floating over other apps' content.
+ *
+ * <p>This value will be ignored for channels that aren't allowed to pop on screen (that is,
+ * channels whose {@link #getImportance() importance} is <
+ * {@link NotificationManager#IMPORTANCE_HIGH}.</p>
+ *
+ * <p>Only modifiable before the channel is submitted to
+ * * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.</p>
+ * @see Notification#getAppOverlayIntent()
+ */
+ public void setAllowAppOverlay(boolean allowAppOverlay) {
+ mAllowAppOverlay = allowAppOverlay;
+ }
+
+ /**
* Returns the id of this channel.
*/
public String getId() {
@@ -573,6 +603,22 @@ public final class NotificationChannel implements Parcelable {
}
/**
+ * Returns whether notifications posted to this channel can display outside of the notification
+ * shade, in a floating window on top of other apps.
+ */
+ public boolean canOverlayApps() {
+ return isAppOverlayAllowed() && getImportance() >= IMPORTANCE_HIGH;
+ }
+
+ /**
+ * Like {@link #canOverlayApps()}, but only checks the permission, not the importance.
+ * @hide
+ */
+ public boolean isAppOverlayAllowed() {
+ return mAllowAppOverlay;
+ }
+
+ /**
* @hide
*/
@SystemApi
@@ -605,6 +651,7 @@ public final class NotificationChannel implements Parcelable {
/**
* Returns whether the user has chosen the importance of this channel, either to affirm the
* initial selection from the app, or changed it to be higher or lower.
+ * @see #getImportance()
*/
public boolean hasUserSetImportance() {
return (mUserLockedFields & USER_LOCKED_IMPORTANCE) != 0;
@@ -652,6 +699,7 @@ public final class NotificationChannel implements Parcelable {
lockFields(safeInt(parser, ATT_USER_LOCKED, 0));
setFgServiceShown(safeBool(parser, ATT_FG_SERVICE_SHOWN, false));
setBlockableSystem(safeBool(parser, ATT_BLOCKABLE_SYSTEM, false));
+ setAllowAppOverlay(safeBool(parser, ATT_ALLOW_APP_OVERLAY, DEFAULT_ALLOW_APP_OVERLAY));
}
@Nullable
@@ -770,6 +818,9 @@ public final class NotificationChannel implements Parcelable {
if (isBlockableSystem()) {
out.attribute(null, ATT_BLOCKABLE_SYSTEM, Boolean.toString(isBlockableSystem()));
}
+ if (canOverlayApps() != DEFAULT_ALLOW_APP_OVERLAY) {
+ out.attribute(null, ATT_ALLOW_APP_OVERLAY, Boolean.toString(canOverlayApps()));
+ }
out.endTag(null, TAG_CHANNEL);
}
@@ -812,6 +863,7 @@ public final class NotificationChannel implements Parcelable {
record.put(ATT_DELETED, Boolean.toString(isDeleted()));
record.put(ATT_GROUP, getGroup());
record.put(ATT_BLOCKABLE_SYSTEM, isBlockableSystem());
+ record.put(ATT_ALLOW_APP_OVERLAY, canOverlayApps());
return record;
}
@@ -899,58 +951,36 @@ public final class NotificationChannel implements Parcelable {
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
-
NotificationChannel that = (NotificationChannel) o;
-
- if (getImportance() != that.getImportance()) return false;
- if (mBypassDnd != that.mBypassDnd) return false;
- if (getLockscreenVisibility() != that.getLockscreenVisibility()) return false;
- if (mLights != that.mLights) return false;
- if (getLightColor() != that.getLightColor()) return false;
- if (getUserLockedFields() != that.getUserLockedFields()) return false;
- if (mVibrationEnabled != that.mVibrationEnabled) return false;
- if (mShowBadge != that.mShowBadge) return false;
- if (isDeleted() != that.isDeleted()) return false;
- if (isBlockableSystem() != that.isBlockableSystem()) return false;
- if (getId() != null ? !getId().equals(that.getId()) : that.getId() != null) return false;
- if (getName() != null ? !getName().equals(that.getName()) : that.getName() != null) {
- return false;
- }
- if (getDescription() != null ? !getDescription().equals(that.getDescription())
- : that.getDescription() != null) {
- return false;
- }
- if (getSound() != null ? !getSound().equals(that.getSound()) : that.getSound() != null) {
- return false;
- }
- if (!Arrays.equals(mVibration, that.mVibration)) return false;
- if (getGroup() != null ? !getGroup().equals(that.getGroup()) : that.getGroup() != null) {
- return false;
- }
- return getAudioAttributes() != null ? getAudioAttributes().equals(that.getAudioAttributes())
- : that.getAudioAttributes() == null;
-
+ return getImportance() == that.getImportance() &&
+ mBypassDnd == that.mBypassDnd &&
+ getLockscreenVisibility() == that.getLockscreenVisibility() &&
+ mLights == that.mLights &&
+ getLightColor() == that.getLightColor() &&
+ getUserLockedFields() == that.getUserLockedFields() &&
+ isFgServiceShown() == that.isFgServiceShown() &&
+ mVibrationEnabled == that.mVibrationEnabled &&
+ mShowBadge == that.mShowBadge &&
+ isDeleted() == that.isDeleted() &&
+ isBlockableSystem() == that.isBlockableSystem() &&
+ mAllowAppOverlay == that.mAllowAppOverlay &&
+ Objects.equals(getId(), that.getId()) &&
+ Objects.equals(getName(), that.getName()) &&
+ Objects.equals(mDesc, that.mDesc) &&
+ Objects.equals(getSound(), that.getSound()) &&
+ Arrays.equals(mVibration, that.mVibration) &&
+ Objects.equals(getGroup(), that.getGroup()) &&
+ Objects.equals(getAudioAttributes(), that.getAudioAttributes());
}
@Override
public int hashCode() {
- int result = getId() != null ? getId().hashCode() : 0;
- result = 31 * result + (getName() != null ? getName().hashCode() : 0);
- result = 31 * result + (getDescription() != null ? getDescription().hashCode() : 0);
- result = 31 * result + getImportance();
- result = 31 * result + (mBypassDnd ? 1 : 0);
- result = 31 * result + getLockscreenVisibility();
- result = 31 * result + (getSound() != null ? getSound().hashCode() : 0);
- result = 31 * result + (mLights ? 1 : 0);
- result = 31 * result + getLightColor();
+ int result = Objects.hash(getId(), getName(), mDesc, getImportance(), mBypassDnd,
+ getLockscreenVisibility(), getSound(), mLights, getLightColor(),
+ getUserLockedFields(),
+ isFgServiceShown(), mVibrationEnabled, mShowBadge, isDeleted(), getGroup(),
+ getAudioAttributes(), isBlockableSystem(), mAllowAppOverlay);
result = 31 * result + Arrays.hashCode(mVibration);
- result = 31 * result + getUserLockedFields();
- result = 31 * result + (mVibrationEnabled ? 1 : 0);
- result = 31 * result + (mShowBadge ? 1 : 0);
- result = 31 * result + (isDeleted() ? 1 : 0);
- result = 31 * result + (getGroup() != null ? getGroup().hashCode() : 0);
- result = 31 * result + (getAudioAttributes() != null ? getAudioAttributes().hashCode() : 0);
- result = 31 * result + (isBlockableSystem() ? 1 : 0);
return result;
}
@@ -976,6 +1006,7 @@ public final class NotificationChannel implements Parcelable {
+ ", mGroup='" + mGroup + '\''
+ ", mAudioAttributes=" + mAudioAttributes
+ ", mBlockableSystem=" + mBlockableSystem
+ + ", mAllowAppOverlay=" + mAllowAppOverlay
+ '}';
pw.println(prefix + output);
}
@@ -1001,6 +1032,7 @@ public final class NotificationChannel implements Parcelable {
+ ", mGroup='" + mGroup + '\''
+ ", mAudioAttributes=" + mAudioAttributes
+ ", mBlockableSystem=" + mBlockableSystem
+ + ", mAllowAppOverlay=" + mAllowAppOverlay
+ '}';
}
@@ -1034,6 +1066,7 @@ public final class NotificationChannel implements Parcelable {
mAudioAttributes.writeToProto(proto, NotificationChannelProto.AUDIO_ATTRIBUTES);
}
proto.write(NotificationChannelProto.IS_BLOCKABLE_SYSTEM, mBlockableSystem);
+ proto.write(NotificationChannelProto.ALLOW_APP_OVERLAY, mAllowAppOverlay);
proto.end(token);
}
diff --git a/core/java/android/app/NotificationChannelGroup.java b/core/java/android/app/NotificationChannelGroup.java
index 17c5cba3ed2c..2322a42c93d5 100644
--- a/core/java/android/app/NotificationChannelGroup.java
+++ b/core/java/android/app/NotificationChannelGroup.java
@@ -32,6 +32,7 @@ import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
/**
* A grouping of related notification channels. e.g., channels that all belong to a single account.
@@ -49,13 +50,33 @@ public final class NotificationChannelGroup implements Parcelable {
private static final String ATT_DESC = "desc";
private static final String ATT_ID = "id";
private static final String ATT_BLOCKED = "blocked";
+ private static final String ATT_ALLOW_APP_OVERLAY = "app_overlay";
+ private static final String ATT_USER_LOCKED = "locked";
+ private static final boolean DEFAULT_ALLOW_APP_OVERLAY = true;
+
+ /**
+ * @hide
+ */
+ public static final int USER_LOCKED_BLOCKED_STATE = 0x00000001;
+ /**
+ * @hide
+ */
+ @TestApi
+ public static final int USER_LOCKED_ALLOW_APP_OVERLAY = 0x00000002;
+
+ /**
+ * @see #getId()
+ */
@UnsupportedAppUsage
private final String mId;
private CharSequence mName;
private String mDescription;
private boolean mBlocked;
private List<NotificationChannel> mChannels = new ArrayList<>();
+ private boolean mAllowAppOverlay = DEFAULT_ALLOW_APP_OVERLAY;
+ // Bitwise representation of fields that have been changed by the user
+ private int mUserLockedFields;
/**
* Creates a notification channel group.
@@ -89,6 +110,8 @@ public final class NotificationChannelGroup implements Parcelable {
}
in.readParcelableList(mChannels, NotificationChannel.class.getClassLoader());
mBlocked = in.readBoolean();
+ mAllowAppOverlay = in.readBoolean();
+ mUserLockedFields = in.readInt();
}
private String getTrimmedString(String input) {
@@ -115,6 +138,8 @@ public final class NotificationChannelGroup implements Parcelable {
}
dest.writeParcelableList(mChannels, flags);
dest.writeBoolean(mBlocked);
+ dest.writeBoolean(mAllowAppOverlay);
+ dest.writeInt(mUserLockedFields);
}
/**
@@ -156,6 +181,15 @@ public final class NotificationChannelGroup implements Parcelable {
}
/**
+ * Returns whether notifications posted to this channel group can display outside of the
+ * notification shade, in a floating window on top of other apps. These may additionally be
+ * blocked at the notification channel level, see {@link NotificationChannel#canOverlayApps()}.
+ */
+ public boolean canOverlayApps() {
+ return mAllowAppOverlay;
+ }
+
+ /**
* Sets the user visible description of this group.
*
* <p>The recommended maximum length is 300 characters; the value may be truncated if it is too
@@ -166,6 +200,21 @@ public final class NotificationChannelGroup implements Parcelable {
}
/**
+ * Sets whether notifications posted to this channel group can appear outside of the
+ * notification shade, floating over other apps' content.
+ *
+ * <p>This value will be ignored for notifications that are posted to channels that do not
+ * allow app overlays ({@link NotificationChannel#canOverlayApps()}.
+ *
+ * <p>Only modifiable before the channel is submitted to
+ * {@link NotificationManager#createNotificationChannelGroup(NotificationChannelGroup)}.</p>
+ * @see Notification#getAppOverlayIntent()
+ */
+ public void setAllowAppOverlay(boolean allowAppOverlay) {
+ mAllowAppOverlay = allowAppOverlay;
+ }
+
+ /**
* @hide
*/
@TestApi
@@ -190,10 +239,34 @@ public final class NotificationChannelGroup implements Parcelable {
/**
* @hide
*/
+ @TestApi
+ public void lockFields(int field) {
+ mUserLockedFields |= field;
+ }
+
+ /**
+ * @hide
+ */
+ public void unlockFields(int field) {
+ mUserLockedFields &= ~field;
+ }
+
+ /**
+ * @hide
+ */
+ @TestApi
+ public int getUserLockedFields() {
+ return mUserLockedFields;
+ }
+
+ /**
+ * @hide
+ */
public void populateFromXml(XmlPullParser parser) {
// Name, id, and importance are set in the constructor.
setDescription(parser.getAttributeValue(null, ATT_DESC));
setBlocked(safeBool(parser, ATT_BLOCKED, false));
+ setAllowAppOverlay(safeBool(parser, ATT_ALLOW_APP_OVERLAY, DEFAULT_ALLOW_APP_OVERLAY));
}
private static boolean safeBool(XmlPullParser parser, String att, boolean defValue) {
@@ -216,6 +289,10 @@ public final class NotificationChannelGroup implements Parcelable {
out.attribute(null, ATT_DESC, getDescription().toString());
}
out.attribute(null, ATT_BLOCKED, Boolean.toString(isBlocked()));
+ if (canOverlayApps() != DEFAULT_ALLOW_APP_OVERLAY) {
+ out.attribute(null, ATT_ALLOW_APP_OVERLAY, Boolean.toString(canOverlayApps()));
+ }
+ out.attribute(null, ATT_USER_LOCKED, Integer.toString(mUserLockedFields));
out.endTag(null, TAG_GROUP);
}
@@ -230,6 +307,8 @@ public final class NotificationChannelGroup implements Parcelable {
record.put(ATT_NAME, getName());
record.put(ATT_DESC, getDescription());
record.put(ATT_BLOCKED, isBlocked());
+ record.put(ATT_ALLOW_APP_OVERLAY, canOverlayApps());
+ record.put(ATT_USER_LOCKED, mUserLockedFields);
return record;
}
@@ -255,30 +334,20 @@ public final class NotificationChannelGroup implements Parcelable {
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
-
NotificationChannelGroup that = (NotificationChannelGroup) o;
-
- if (isBlocked() != that.isBlocked()) return false;
- if (getId() != null ? !getId().equals(that.getId()) : that.getId() != null) return false;
- if (getName() != null ? !getName().equals(that.getName()) : that.getName() != null) {
- return false;
- }
- if (getDescription() != null ? !getDescription().equals(that.getDescription())
- : that.getDescription() != null) {
- return false;
- }
- return getChannels() != null ? getChannels().equals(that.getChannels())
- : that.getChannels() == null;
+ return isBlocked() == that.isBlocked() &&
+ mAllowAppOverlay == that.mAllowAppOverlay &&
+ mUserLockedFields == that.mUserLockedFields &&
+ Objects.equals(getId(), that.getId()) &&
+ Objects.equals(getName(), that.getName()) &&
+ Objects.equals(getDescription(), that.getDescription()) &&
+ Objects.equals(getChannels(), that.getChannels());
}
@Override
public int hashCode() {
- int result = getId() != null ? getId().hashCode() : 0;
- result = 31 * result + (getName() != null ? getName().hashCode() : 0);
- result = 31 * result + (getDescription() != null ? getDescription().hashCode() : 0);
- result = 31 * result + (isBlocked() ? 1 : 0);
- result = 31 * result + (getChannels() != null ? getChannels().hashCode() : 0);
- return result;
+ return Objects.hash(getId(), getName(), getDescription(), isBlocked(), getChannels(),
+ mAllowAppOverlay, mUserLockedFields);
}
@Override
@@ -287,6 +356,8 @@ public final class NotificationChannelGroup implements Parcelable {
cloned.setDescription(getDescription());
cloned.setBlocked(isBlocked());
cloned.setChannels(getChannels());
+ cloned.setAllowAppOverlay(canOverlayApps());
+ cloned.lockFields(mUserLockedFields);
return cloned;
}
@@ -298,6 +369,8 @@ public final class NotificationChannelGroup implements Parcelable {
+ ", mDescription=" + (!TextUtils.isEmpty(mDescription) ? "hasDescription " : "")
+ ", mBlocked=" + mBlocked
+ ", mChannels=" + mChannels
+ + ", mAllowAppOverlay=" + mAllowAppOverlay
+ + ", mUserLockedFields=" + mUserLockedFields
+ '}';
}
@@ -312,7 +385,7 @@ public final class NotificationChannelGroup implements Parcelable {
for (NotificationChannel channel : mChannels) {
channel.writeToProto(proto, NotificationChannelGroupProto.CHANNELS);
}
-
+ proto.write(NotificationChannelGroupProto.ALLOW_APP_OVERLAY, mAllowAppOverlay);
proto.end(token);
}
}
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index a2a6b9b4a762..4de1dfcc12ba 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -52,7 +52,9 @@ import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.SystemProperties;
import android.os.UserHandle;
+import android.os.storage.StorageManager;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
@@ -88,6 +90,30 @@ import java.util.concurrent.atomic.AtomicBoolean;
*/
public abstract class ContentResolver {
/**
+ * Enables logic that supports deprecation of {@code _data} columns,
+ * typically by replacing values with fake paths that the OS then offers to
+ * redirect to {@link #openFileDescriptor(Uri, String)}, which developers
+ * should be using directly.
+ *
+ * @hide
+ */
+ public static final boolean DEPRECATE_DATA_COLUMNS = SystemProperties
+ .getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false);
+
+ /**
+ * Special filesystem path prefix which indicates that a path should be
+ * treated as a {@code content://} {@link Uri} when
+ * {@link #DEPRECATE_DATA_COLUMNS} is enabled.
+ * <p>
+ * The remainder of the path after this prefix is a
+ * {@link Uri#getSchemeSpecificPart()} value, which includes authority, path
+ * segments, and query parameters.
+ *
+ * @hide
+ */
+ public static final String DEPRECATE_DATA_PREFIX = "/mnt/content/";
+
+ /**
* @deprecated instead use
* {@link #requestSync(android.accounts.Account, String, android.os.Bundle)}
*/
@@ -3261,4 +3287,16 @@ public abstract class ContentResolver {
e.rethrowFromSystemServer();
}
}
+
+ /** {@hide} */
+ public static Uri translateDeprecatedDataPath(String path) {
+ final String ssp = "//" + path.substring(DEPRECATE_DATA_PREFIX.length());
+ return Uri.parse(new Uri.Builder().scheme(SCHEME_CONTENT)
+ .encodedOpaquePart(ssp).build().toString());
+ }
+
+ /** {@hide} */
+ public static String translateDeprecatedDataPath(Uri uri) {
+ return DEPRECATE_DATA_PREFIX + uri.getEncodedSchemeSpecificPart().substring(2);
+ }
}
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 4f58321da2f4..6f49cc42f6f6 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -703,6 +703,18 @@ public abstract class PackageManagerInternal {
public abstract SparseArray<String> getAppsWithSharedUserIds();
/**
+ * Get the value of attribute android:sharedUserId for the given packageName if specified,
+ * otherwise {@code null}.
+ */
+ public abstract String getSharedUserIdForPackage(@NonNull String packageName);
+
+ /**
+ * Get all packages which specified the given sharedUserId as android:sharedUserId attribute
+ * or an empty array if no package specified it.
+ */
+ public abstract String[] getPackagesForSharedUserId(@NonNull String sharedUserId, int userId);
+
+ /**
* Return if device is currently in a "core" boot environment, typically
* used to support full-disk encryption. Only apps marked with
* {@code coreApp} attribute are available.
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index f71fdd7fdac1..0b90f5437826 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -22,16 +22,19 @@ import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
import static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY;
+import static android.system.OsConstants.F_OK;
import static android.system.OsConstants.O_APPEND;
import static android.system.OsConstants.O_CREAT;
import static android.system.OsConstants.O_RDONLY;
import static android.system.OsConstants.O_RDWR;
import static android.system.OsConstants.O_TRUNC;
import static android.system.OsConstants.O_WRONLY;
+import static android.system.OsConstants.R_OK;
import static android.system.OsConstants.SPLICE_F_MORE;
import static android.system.OsConstants.SPLICE_F_MOVE;
import static android.system.OsConstants.S_ISFIFO;
import static android.system.OsConstants.S_ISREG;
+import static android.system.OsConstants.W_OK;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -1300,6 +1303,23 @@ public class FileUtils {
}
/** {@hide} */
+ public static int translateModeAccessToPosix(int mode) {
+ if (mode == F_OK) {
+ // There's not an exact mapping, so we attempt a read-only open to
+ // determine if a file exists
+ return O_RDONLY;
+ } else if ((mode & (R_OK | W_OK)) == (R_OK | W_OK)) {
+ return O_RDWR;
+ } else if ((mode & R_OK) == R_OK) {
+ return O_RDONLY;
+ } else if ((mode & W_OK) == W_OK) {
+ return O_WRONLY;
+ } else {
+ throw new IllegalArgumentException("Bad mode: " + mode);
+ }
+ }
+
+ /** {@hide} */
@VisibleForTesting
public static class MemoryPipe extends Thread implements AutoCloseable {
private final FileDescriptor[] pipe;
diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java
index 63ff7b22f364..816a730716e1 100644
--- a/core/java/android/os/storage/StorageManagerInternal.java
+++ b/core/java/android/os/storage/StorageManagerInternal.java
@@ -101,9 +101,11 @@ public abstract class StorageManagerInternal {
* Delete storage sandbox for the given package.
*
* @param packageName The package for which the sandbox needs to be destroyed.
+ * @param sharedUserId The sharedUserId if specified by the package.
* @param userId The userId in which the sandbox needs to be destroyed.
*/
- public abstract void destroySandboxForApp(@NonNull String packageName, int userId);
+ public abstract void destroySandboxForApp(@NonNull String packageName,
+ @Nullable String sharedUserId, int userId);
/**
* @return Labels of storage volumes that are visible to the given userId.
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 0aab76ebd0e0..1fce8e6c9ac2 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -60,7 +60,10 @@ import java.util.List;
public final class MediaStore {
private final static String TAG = "MediaStore";
+ /** The authority for the media provider */
public static final String AUTHORITY = "media";
+ /** A content:// style uri to the authority for the media provider */
+ public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
private static final String CONTENT_AUTHORITY_SLASH = "content://" + AUTHORITY + "/";
@@ -2254,6 +2257,18 @@ public final class MediaStore {
}
/**
+ * Return the volume name that the given {@link Uri} references.
+ */
+ public static @NonNull String getVolumeName(@NonNull Uri uri) {
+ final List<String> segments = uri.getPathSegments();
+ if (uri.getAuthority().equals(AUTHORITY) && segments != null && segments.size() > 0) {
+ return segments.get(0);
+ } else {
+ throw new IllegalArgumentException("Not a media Uri: " + uri);
+ }
+ }
+
+ /**
* Uri for querying the state of the media scanner.
*/
public static Uri getMediaScannerUri() {
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 59c29e2626c7..c96aaba60775 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -42,6 +42,7 @@ cc_library_shared {
"com_google_android_gles_jni_EGLImpl.cpp",
"com_google_android_gles_jni_GLImpl.cpp", // TODO: .arm
"android_app_Activity.cpp",
+ "android_app_ActivityThread.cpp",
"android_app_NativeActivity.cpp",
"android_app_admin_SecurityLog.cpp",
"android_opengl_EGL14.cpp",
diff --git a/core/jni/android_app_ActivityThread.cpp b/core/jni/android_app_ActivityThread.cpp
new file mode 100644
index 000000000000..d56e4c51124d
--- /dev/null
+++ b/core/jni/android_app_ActivityThread.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include "jni.h"
+#include "GraphicsJNI.h"
+#include <nativehelper/JNIHelp.h>
+
+#include <minikin/Layout.h>
+#include <renderthread/RenderProxy.h>
+
+#include "core_jni_helpers.h"
+#include <unistd.h>
+
+namespace android {
+
+static void android_app_ActivityThread_purgePendingResources(JNIEnv* env, jobject clazz) {
+ // Don't care about return values.
+ mallopt(M_PURGE, 0);
+}
+
+static void
+android_app_ActivityThread_dumpGraphics(JNIEnv* env, jobject clazz, jobject javaFileDescriptor) {
+ int fd = jniGetFDFromFileDescriptor(env, javaFileDescriptor);
+ android::uirenderer::renderthread::RenderProxy::dumpGraphicsMemory(fd);
+ minikin::Layout::dumpMinikinStats(fd);
+}
+
+
+static JNINativeMethod gActivityThreadMethods[] = {
+ // ------------ Regular JNI ------------------
+ { "nPurgePendingResources", "()V",
+ (void*) android_app_ActivityThread_purgePendingResources },
+ { "nDumpGraphicsInfo", "(Ljava/io/FileDescriptor;)V",
+ (void*) android_app_ActivityThread_dumpGraphics }
+};
+
+int register_android_app_ActivityThread(JNIEnv* env) {
+ return RegisterMethodsOrDie(env, "android/app/ActivityThread",
+ gActivityThreadMethods, NELEM(gActivityThreadMethods));
+}
+
+};
diff --git a/core/jni/android_view_DisplayListCanvas.cpp b/core/jni/android_view_DisplayListCanvas.cpp
index 8998cd706dd8..e0fd1a634557 100644
--- a/core/jni/android_view_DisplayListCanvas.cpp
+++ b/core/jni/android_view_DisplayListCanvas.cpp
@@ -88,17 +88,6 @@ private:
sp<InvokeRunnableMessage> mMessage;
};
-
-// ---------------- Regular JNI -----------------------------
-
-static void
-android_app_ActivityThread_dumpGraphics(JNIEnv* env, jobject clazz, jobject javaFileDescriptor) {
- int fd = jniGetFDFromFileDescriptor(env, javaFileDescriptor);
- android::uirenderer::renderthread::RenderProxy::dumpGraphicsMemory(fd);
- minikin::Layout::dumpMinikinStats(fd);
-}
-
-
// ---------------- @FastNative -----------------------------
static void android_view_DisplayListCanvas_callDrawGLFunction(JNIEnv* env, jobject clazz,
@@ -205,12 +194,6 @@ static JNINativeMethod gMethods[] = {
{ "nDrawRoundRect", "(JJJJJJJJ)V",(void*) android_view_DisplayListCanvas_drawRoundRectProps },
};
-static JNINativeMethod gActivityThreadMethods[] = {
- // ------------ Regular JNI ------------------
- { "nDumpGraphicsInfo", "(Ljava/io/FileDescriptor;)V",
- (void*) android_app_ActivityThread_dumpGraphics }
-};
-
int register_android_view_DisplayListCanvas(JNIEnv* env) {
jclass runnableClass = FindClassOrDie(env, "java/lang/Runnable");
gRunnableMethodId = GetMethodIDOrDie(env, runnableClass, "run", "()V");
@@ -218,9 +201,4 @@ int register_android_view_DisplayListCanvas(JNIEnv* env) {
return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
}
-int register_android_app_ActivityThread(JNIEnv* env) {
- return RegisterMethodsOrDie(env, "android/app/ActivityThread",
- gActivityThreadMethods, NELEM(gActivityThreadMethods));
-}
-
};
diff --git a/core/proto/android/app/notification_channel.proto b/core/proto/android/app/notification_channel.proto
index 75cc18bf7a07..435d32f59a35 100644
--- a/core/proto/android/app/notification_channel.proto
+++ b/core/proto/android/app/notification_channel.proto
@@ -57,4 +57,7 @@ message NotificationChannelProto {
// If this is a blockable system notification channel.
optional bool is_blockable_system = 17;
optional bool fg_service_shown = 18;
+ // Default is true.
+ // Allows the notifications to appear outside of the shade in floating windows
+ optional bool allow_app_overlay = 19;
}
diff --git a/core/proto/android/app/notification_channel_group.proto b/core/proto/android/app/notification_channel_group.proto
index 4fb27b0d6e4a..6d6ceb2f7cbe 100644
--- a/core/proto/android/app/notification_channel_group.proto
+++ b/core/proto/android/app/notification_channel_group.proto
@@ -36,4 +36,7 @@ message NotificationChannelGroupProto {
optional string description = 3;
optional bool is_blocked = 4;
repeated NotificationChannelProto channels = 5;
+ // Default is true.
+ // Allows the notifications to appear outside of the shade in floating windows
+ optional bool allow_app_overlay = 6;
}
diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java
index 0036186994fe..9940bf7dd692 100644
--- a/core/tests/coretests/src/android/content/ContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/ContentResolverTest.java
@@ -158,4 +158,19 @@ public class ContentResolverTest {
assertImageAspectAndContents(res);
}
+
+ @Test
+ public void testTranslateDeprecatedDataPath() throws Exception {
+ assertTranslate(Uri.parse("content://com.example/path/?foo=bar&baz=meow"));
+ assertTranslate(Uri.parse("content://com.example/path/subpath/12/"));
+ assertTranslate(Uri.parse("content://com.example/path/subpath/12"));
+ assertTranslate(Uri.parse("content://com.example/path/12"));
+ assertTranslate(Uri.parse("content://com.example/"));
+ assertTranslate(Uri.parse("content://com.example"));
+ }
+
+ private static void assertTranslate(Uri uri) {
+ assertEquals(uri, ContentResolver
+ .translateDeprecatedDataPath(ContentResolver.translateDeprecatedDataPath(uri)));
+ }
}
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index 6966448f7d63..55e21a76f170 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -17,6 +17,7 @@
package android.os;
import static android.os.FileUtils.roundStorageSize;
+import static android.os.FileUtils.translateModeAccessToPosix;
import static android.os.FileUtils.translateModePfdToPosix;
import static android.os.FileUtils.translateModePosixToPfd;
import static android.os.FileUtils.translateModePosixToString;
@@ -27,12 +28,16 @@ import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
import static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY;
+import static android.system.OsConstants.F_OK;
import static android.system.OsConstants.O_APPEND;
import static android.system.OsConstants.O_CREAT;
import static android.system.OsConstants.O_RDONLY;
import static android.system.OsConstants.O_RDWR;
import static android.system.OsConstants.O_TRUNC;
import static android.system.OsConstants.O_WRONLY;
+import static android.system.OsConstants.R_OK;
+import static android.system.OsConstants.W_OK;
+import static android.system.OsConstants.X_OK;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.WEEK_IN_MILLIS;
@@ -525,6 +530,15 @@ public class FileUtilsTest {
}
}
+ @Test
+ public void testTranslateMode_Access() throws Exception {
+ assertEquals(O_RDONLY, translateModeAccessToPosix(F_OK));
+ assertEquals(O_RDONLY, translateModeAccessToPosix(R_OK));
+ assertEquals(O_WRONLY, translateModeAccessToPosix(W_OK));
+ assertEquals(O_RDWR, translateModeAccessToPosix(R_OK | W_OK));
+ assertEquals(O_RDWR, translateModeAccessToPosix(R_OK | W_OK | X_OK));
+ }
+
private static void assertTranslate(String string, int posix, int pfd) {
assertEquals(posix, translateModeStringToPosix(string));
assertEquals(string, translateModePosixToString(posix));
diff --git a/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java b/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java
index 29ee920d4dde..ca3daad8d109 100644
--- a/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java
+++ b/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java
@@ -37,6 +37,8 @@ public final class ChannelImpressions implements Parcelable {
static final String ATT_DISMISSALS = "dismisses";
static final String ATT_VIEWS = "views";
static final String ATT_STREAK = "streak";
+ static final String ATT_SENT = "sent";
+ static final String ATT_INTERRUPTIVE = "interruptive";
private int mDismissals = 0;
private int mViews = 0;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
index 87f29342b167..82e8b3e48cfc 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
@@ -246,7 +246,11 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage
SystemMessage.NOTE_PLUGIN, nb.build(), UserHandle.ALL);
}
if (clearClassLoader(pkg)) {
- Toast.makeText(mContext, "Reloading " + pkg, Toast.LENGTH_LONG).show();
+ if (Build.IS_ENG) {
+ Toast.makeText(mContext, "Reloading " + pkg, Toast.LENGTH_LONG).show();
+ } else {
+ Log.v(TAG, "Reloading " + pkg);
+ }
}
if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
for (PluginInstanceManager manager : mPluginMap.values()) {
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 858dcedd03cc..5643a6a66251 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -112,7 +112,6 @@ import android.util.TimeUtils;
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.AppFuseMount;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.FuseUnavailableMountException;
@@ -323,12 +322,6 @@ class StorageManagerService extends IStorageManager.Stub
@GuardedBy("mPackagesLock")
private final SparseArray<ArraySet<String>> mPackages = new SparseArray<>();
- @GuardedBy("mPackagesLock")
- private final ArrayMap<String, Integer> mAppIds = new ArrayMap<>();
-
- @GuardedBy("mPackagesLock")
- private final SparseArray<String> mSandboxIds = new SparseArray<>();
-
/**
* List of volumes visible to any user.
* TODO: may be have a map of userId -> volumes?
@@ -876,13 +869,12 @@ class StorageManagerService extends IStorageManager.Stub
try {
mVold.reset();
- pushPackagesInfo();
// Tell vold about all existing and started users
for (UserInfo user : users) {
mVold.onUserAdded(user.id, user.serialNumber);
}
for (int userId : systemUnlockedUsers) {
- mVold.onUserStarted(userId, getPackagesArrayForUser(userId));
+ sendUserStartedCallback(userId);
mStoraged.onUserStarted(userId);
}
mVold.onSecureKeyguardStateChanged(mSecureKeyguardShowing);
@@ -899,7 +891,7 @@ class StorageManagerService extends IStorageManager.Stub
// staging area is ready so it's ready for zygote-forked apps to
// bind mount against.
try {
- mVold.onUserStarted(userId, getPackagesArrayForUser(userId));
+ sendUserStartedCallback(userId);
mStoraged.onUserStarted(userId);
} catch (Exception e) {
Slog.wtf(TAG, e);
@@ -932,11 +924,52 @@ class StorageManagerService extends IStorageManager.Stub
Slog.wtf(TAG, e);
}
+ synchronized (mPackagesLock) {
+ mPackages.delete(userId);
+ }
+
synchronized (mLock) {
mSystemUnlockedUsers = ArrayUtils.removeInt(mSystemUnlockedUsers, userId);
}
}
+ private void sendUserStartedCallback(int userId) throws Exception {
+ if (!ENABLE_ISOLATED_STORAGE) {
+ mVold.onUserStarted(userId, EmptyArray.STRING, EmptyArray.INT, EmptyArray.STRING);
+ }
+
+ final String[] packages;
+ final int[] appIds;
+ final String[] sandboxIds;
+ final SparseArray<String> sharedUserIds = mPmInternal.getAppsWithSharedUserIds();
+ final List<ApplicationInfo> appInfos =
+ mContext.getPackageManager().getInstalledApplicationsAsUser(
+ PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
+ synchronized (mPackagesLock) {
+ final ArraySet<String> userPackages = new ArraySet<>();
+ final ArrayMap<String, Integer> packageToAppId = new ArrayMap<>();
+ for (int i = appInfos.size() - 1; i >= 0; --i) {
+ final ApplicationInfo appInfo = appInfos.get(i);
+ if (appInfo.isInstantApp()) {
+ continue;
+ }
+ userPackages.add(appInfo.packageName);
+ packageToAppId.put(appInfo.packageName, UserHandle.getAppId(appInfo.uid));
+ }
+ mPackages.put(userId, userPackages);
+
+ packages = new String[userPackages.size()];
+ appIds = new int[userPackages.size()];
+ sandboxIds = new String[userPackages.size()];
+ for (int i = userPackages.size() - 1; i >= 0; --i) {
+ packages[i] = userPackages.valueAt(i);
+ appIds[i] = packageToAppId.get(packages[i]);
+ sandboxIds[i] = getSandboxId(packages[i], sharedUserIds.get(appIds[i]));
+ }
+ }
+ mVold.onUserStarted(userId, packages, appIds, sandboxIds);
+ }
+
@Override
public void onAwakeStateChanged(boolean isAwake) {
// Ignored
@@ -1454,111 +1487,12 @@ class StorageManagerService extends IStorageManager.Stub
}
private void start() {
- collectPackagesInfo();
connect();
}
- @VisibleForTesting
- void collectPackagesInfo() {
- if (!ENABLE_ISOLATED_STORAGE) return;
-
- resetPackageData();
- final SparseArray<String> sharedUserIds = mPmInternal.getAppsWithSharedUserIds();
- final int[] userIds = mUmInternal.getUserIds();
- for (int userId : userIds) {
- final List<ApplicationInfo> appInfos
- = mContext.getPackageManager().getInstalledApplicationsAsUser(
- PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
- synchronized (mPackagesLock) {
- final ArraySet<String> userPackages = getAvailablePackagesForUserPL(userId);
- for (int i = appInfos.size() - 1; i >= 0; --i) {
- if (appInfos.get(i).isInstantApp()) {
- continue;
- }
- final String packageName = appInfos.get(i).packageName;
- userPackages.add(packageName);
-
- final int appId = UserHandle.getAppId(appInfos.get(i).uid);
- mAppIds.put(packageName, appId);
- mSandboxIds.put(appId, getSandboxId(packageName, sharedUserIds.get(appId)));
- }
- }
- }
- }
-
- private void resetPackageData() {
- synchronized (mPackagesLock) {
- mPackages.clear();
- mAppIds.clear();
- mSandboxIds.clear();
- }
- }
-
private static String getSandboxId(String packageName, String sharedUserId) {
return sharedUserId == null ? packageName : SHARED_SANDBOX_ID_PREFIX + sharedUserId;
}
- private void pushPackagesInfo() throws RemoteException {
- if (!ENABLE_ISOLATED_STORAGE) return;
-
- // Arrays to fill up from {@link #mAppIds}
- final String[] allPackageNames;
- final int[] appIdsForPackages;
-
- // Arrays to fill up from {@link #mSandboxIds}
- final int[] allAppIds;
- final String[] sandboxIdsForApps;
- synchronized (mPackagesLock) {
- allPackageNames = new String[mAppIds.size()];
- appIdsForPackages = new int[mAppIds.size()];
- for (int i = mAppIds.size() - 1; i >= 0; --i) {
- allPackageNames[i] = mAppIds.keyAt(i);
- appIdsForPackages[i] = mAppIds.valueAt(i);
- }
-
- allAppIds = new int[mSandboxIds.size()];
- sandboxIdsForApps = new String[mSandboxIds.size()];
- for (int i = mSandboxIds.size() - 1; i >= 0; --i) {
- allAppIds[i] = mSandboxIds.keyAt(i);
- sandboxIdsForApps[i] = mSandboxIds.valueAt(i);
- }
- }
- mVold.addAppIds(allPackageNames, appIdsForPackages);
- mVold.addSandboxIds(allAppIds, sandboxIdsForApps);
- }
-
- @GuardedBy("mPackagesLock")
- private ArraySet<String> getAvailablePackagesForUserPL(int userId) {
- ArraySet<String> userPackages = mPackages.get(userId);
- if (userPackages == null) {
- userPackages = new ArraySet<>();
- mPackages.put(userId, userPackages);
- }
- return userPackages;
- }
-
- private String[] getPackagesArrayForUser(int userId) {
- if (!ENABLE_ISOLATED_STORAGE) return EmptyArray.STRING;
-
- final ArraySet<String> userPackages;
- synchronized (mPackagesLock) {
- userPackages = getAvailablePackagesForUserPL(userId);
- if (!userPackages.isEmpty()) {
- return userPackages.toArray(new String[0]);
- }
- }
- final List<ApplicationInfo> appInfos =
- mContext.getPackageManager().getInstalledApplicationsAsUser(
- PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
- synchronized (mPackagesLock) {
- for (int i = appInfos.size() - 1; i >= 0; --i) {
- if (appInfos.get(i).isInstantApp()) {
- continue;
- }
- userPackages.add(appInfos.get(i).packageName);
- }
- return userPackages.toArray(new String[0]);
- }
- }
private void connect() {
IBinder binder = ServiceManager.getService("storaged");
@@ -3122,15 +3056,8 @@ class StorageManagerService extends IStorageManager.Stub
throw new SecurityException("Shady looking path " + path);
}
- final int uid = mPmInternal.getPackageUid(packageName,
- PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
- final String sandboxId;
- synchronized (mPackagesLock) {
- sandboxId = mSandboxIds.get(UserHandle.getAppId(uid));
- }
- if (uid < 0 || sandboxId == null) {
- throw new IllegalArgumentException("Unknown package " + packageName);
- }
+ final String sharedUserId = mPmInternal.getSharedUserIdForPackage(packageName);
+ final String sandboxId = getSandboxId(packageName, sharedUserId);
final Matcher m = PATTERN_TRANSLATE.matcher(path);
if (m.matches()) {
@@ -3139,7 +3066,9 @@ class StorageManagerService extends IStorageManager.Stub
// Does path belong to any packages belonging to this UID? If so,
// they get to go straight through to legacy paths.
- final String[] pkgs = mContext.getPackageManager().getPackagesForUid(uid);
+ final String[] pkgs = (sharedUserId == null)
+ ? new String[] {packageName}
+ : mPmInternal.getPackagesForSharedUserId(sharedUserId, userId);
for (String pkg : pkgs) {
if (devicePath.startsWith("Android/data/" + pkg + "/") ||
devicePath.startsWith("Android/media/" + pkg + "/") ||
@@ -3758,16 +3687,14 @@ class StorageManagerService extends IStorageManager.Stub
int userId) {
final String sandboxId;
synchronized (mPackagesLock) {
- final ArraySet<String> userPackages = getAvailablePackagesForUserPL(userId);
+ final ArraySet<String> userPackages = mPackages.get(userId);
// If userPackages is empty, it means the user is not started yet, so no need to
// do anything now.
- if (userPackages.isEmpty() || userPackages.contains(packageName)) {
+ if (userPackages == null || userPackages.contains(packageName)) {
return;
}
userPackages.add(packageName);
- mAppIds.put(packageName, appId);
sandboxId = getSandboxId(packageName, sharedUserId);
- mSandboxIds.put(appId, sandboxId);
}
try {
@@ -3778,34 +3705,21 @@ class StorageManagerService extends IStorageManager.Stub
}
@Override
- public void destroySandboxForApp(String packageName, int userId) {
+ public void destroySandboxForApp(String packageName, String sharedUserId, int userId) {
if (!ENABLE_ISOLATED_STORAGE) {
return;
}
- final int appId;
- final String sandboxId;
+ final String sandboxId = getSandboxId(packageName, sharedUserId);
synchronized (mPackagesLock) {
- final ArraySet<String> userPackages = getAvailablePackagesForUserPL(userId);
- userPackages.remove(packageName);
- appId = mAppIds.get(packageName);
- sandboxId = mSandboxIds.get(appId);
-
- // If the package is not uninstalled in any other users, remove appId and sandboxId
- // corresponding to it from the internal state.
- boolean installedInAnyUser = false;
- for (int i = mPackages.size() - 1; i >= 0; --i) {
- if (mPackages.valueAt(i).contains(packageName)) {
- installedInAnyUser = true;
- break;
- }
- }
- if (!installedInAnyUser) {
- mAppIds.remove(packageName);
- mSandboxIds.remove(appId);
+ final ArraySet<String> userPackages = mPackages.get(userId);
+ // If the userPackages is null, it means the user is not started but we still
+ // need to delete the sandbox data though.
+ if (userPackages != null) {
+ userPackages.remove(packageName);
}
}
try {
- mVold.destroySandboxForApp(packageName, appId, sandboxId, userId);
+ mVold.destroySandboxForApp(packageName, sandboxId, userId);
} catch (Exception e) {
Slog.wtf(TAG, e);
}
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 9c60b8c00019..e879efd644d1 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -241,9 +241,6 @@ public class AccountManagerService
private final HashMap<Account, AtomicReference<String>> previousNameCache =
new HashMap<Account, AtomicReference<String>>();
- private int debugDbInsertionPoint = -1;
- private SQLiteStatement statementForLogging; // TODO Move to AccountsDb
-
UserAccounts(Context context, int userId, File preNDbFile, File deDbFile) {
this.userId = userId;
synchronized (dbLock) {
@@ -1299,7 +1296,6 @@ public class AccountManagerService
File preNDbFile = new File(mInjector.getPreNDatabaseName(userId));
File deDbFile = new File(mInjector.getDeDatabaseName(userId));
accounts = new UserAccounts(mContext, userId, preNDbFile, deDbFile);
- initializeDebugDbSizeAndCompileSqlStatementForLogging(accounts);
mUsers.append(userId, accounts);
purgeOldGrants(accounts);
validateAccounts = true;
@@ -1400,7 +1396,7 @@ public class AccountManagerService
if (accounts != null) {
synchronized (accounts.dbLock) {
synchronized (accounts.cacheLock) {
- accounts.statementForLogging.close();
+ accounts.accountsDb.closeDebugStatement();
accounts.accountsDb.close();
}
}
@@ -5124,41 +5120,36 @@ public class AccountManagerService
@Override
public void run() {
- SQLiteStatement logStatement = userAccount.statementForLogging;
- logStatement.bindLong(1, accountId);
- logStatement.bindString(2, action);
- logStatement.bindString(3, mDateFormat.format(new Date()));
- logStatement.bindLong(4, callingUid);
- logStatement.bindString(5, tableName);
- logStatement.bindLong(6, userDebugDbInsertionPoint);
- try {
- logStatement.execute();
- } catch (IllegalStateException e) {
- // Guard against crash, DB can already be closed
- // since this statement is executed on a handler thread
- Slog.w(TAG, "Failed to insert a log record. accountId=" + accountId
- + " action=" + action + " tableName=" + tableName + " Error: " + e);
- } finally {
- logStatement.clearBindings();
+ synchronized (userAccount.accountsDb.mDebugStatementLock) {
+ SQLiteStatement logStatement = userAccount.accountsDb.getStatementForLogging();
+ if (logStatement == null) {
+ return; // Can't log.
+ }
+ logStatement.bindLong(1, accountId);
+ logStatement.bindString(2, action);
+ logStatement.bindString(3, mDateFormat.format(new Date()));
+ logStatement.bindLong(4, callingUid);
+ logStatement.bindString(5, tableName);
+ logStatement.bindLong(6, userDebugDbInsertionPoint);
+ try {
+ logStatement.execute();
+ } catch (IllegalStateException e) {
+ // Guard against crash, DB can already be closed
+ // since this statement is executed on a handler thread
+ Slog.w(TAG, "Failed to insert a log record. accountId=" + accountId
+ + " action=" + action + " tableName=" + tableName + " Error: " + e);
+ } finally {
+ logStatement.clearBindings();
+ }
}
}
}
-
- LogRecordTask logTask = new LogRecordTask(action, tableName, accountId, userAccount,
- callingUid, userAccount.debugDbInsertionPoint);
- userAccount.debugDbInsertionPoint = (userAccount.debugDbInsertionPoint + 1)
- % AccountsDb.MAX_DEBUG_DB_SIZE;
- mHandler.post(logTask);
- }
-
- /*
- * This should only be called once to compile the sql statement for logging
- * and to find the insertion point.
- */
- private void initializeDebugDbSizeAndCompileSqlStatementForLogging(UserAccounts userAccount) {
- userAccount.debugDbInsertionPoint = userAccount.accountsDb
- .calculateDebugTableInsertionPoint();
- userAccount.statementForLogging = userAccount.accountsDb.compileSqlStatementForLogging();
+ long insertionPoint = userAccount.accountsDb.reserveDebugDbInsertionPoint();
+ if (insertionPoint != -1) {
+ LogRecordTask logTask = new LogRecordTask(action, tableName, accountId, userAccount,
+ callingUid, insertionPoint);
+ mHandler.post(logTask);
+ }
}
public IBinder onBind(@SuppressWarnings("unused") Intent intent) {
diff --git a/services/core/java/com/android/server/accounts/AccountsDb.java b/services/core/java/com/android/server/accounts/AccountsDb.java
index 0c3d268f4b0f..712edcced5da 100644
--- a/services/core/java/com/android/server/accounts/AccountsDb.java
+++ b/services/core/java/com/android/server/accounts/AccountsDb.java
@@ -17,11 +17,13 @@
package com.android.server.accounts;
import android.accounts.Account;
+import android.annotation.Nullable;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteStatement;
import android.os.FileUtils;
@@ -183,6 +185,10 @@ class AccountsDb implements AutoCloseable {
private final Context mContext;
private final File mPreNDatabaseFile;
+ final Object mDebugStatementLock = new Object();
+ private volatile long mDebugDbInsertionPoint = -1;
+ private volatile SQLiteStatement mDebugStatementForLogging; // not thread safe.
+
AccountsDb(DeDatabaseHelper deDatabase, Context context, File preNDatabaseFile) {
mDeDatabase = deDatabase;
mContext = context;
@@ -1278,31 +1284,72 @@ class AccountsDb implements AutoCloseable {
* Finds the row key where the next insertion should take place. Returns number of rows
* if it is less {@link #MAX_DEBUG_DB_SIZE}, otherwise finds the lowest number available.
*/
- int calculateDebugTableInsertionPoint() {
- SQLiteDatabase db = mDeDatabase.getReadableDatabase();
- String queryCountDebugDbRows = "SELECT COUNT(*) FROM " + TABLE_DEBUG;
- int size = (int) DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
- if (size < MAX_DEBUG_DB_SIZE) {
- return size;
- }
+ long calculateDebugTableInsertionPoint() {
+ try {
+ SQLiteDatabase db = mDeDatabase.getReadableDatabase();
+ String queryCountDebugDbRows = "SELECT COUNT(*) FROM " + TABLE_DEBUG;
+ int size = (int) DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
+ if (size < MAX_DEBUG_DB_SIZE) {
+ return size;
+ }
- // This query finds the smallest timestamp value (and if 2 records have
- // same timestamp, the choose the lower id).
- queryCountDebugDbRows = "SELECT " + DEBUG_TABLE_KEY +
- " FROM " + TABLE_DEBUG +
- " ORDER BY " + DEBUG_TABLE_TIMESTAMP + "," + DEBUG_TABLE_KEY +
- " LIMIT 1";
- return (int) DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
+ // This query finds the smallest timestamp value (and if 2 records have
+ // same timestamp, the choose the lower id).
+ queryCountDebugDbRows =
+ "SELECT " + DEBUG_TABLE_KEY
+ + " FROM " + TABLE_DEBUG
+ + " ORDER BY " + DEBUG_TABLE_TIMESTAMP + ","
+ + DEBUG_TABLE_KEY
+ + " LIMIT 1";
+ return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
+ } catch (SQLiteException e) {
+ Log.e(TAG, "Failed to open debug table" + e);
+ return -1;
+ }
}
SQLiteStatement compileSqlStatementForLogging() {
- // TODO b/31708085 Fix debug logging - it eagerly opens database for write without a need
SQLiteDatabase db = mDeDatabase.getWritableDatabase();
String sql = "INSERT OR REPLACE INTO " + AccountsDb.TABLE_DEBUG
+ " VALUES (?,?,?,?,?,?)";
return db.compileStatement(sql);
}
+ /**
+ * Returns statement for logging or {@code null} on database open failure.
+ * Returned value must be guarded by {link #debugStatementLock}
+ */
+ @Nullable SQLiteStatement getStatementForLogging() {
+ if (mDebugStatementForLogging != null) {
+ return mDebugStatementForLogging;
+ }
+ try {
+ mDebugStatementForLogging = compileSqlStatementForLogging();
+ return mDebugStatementForLogging;
+ } catch (SQLiteException e) {
+ Log.e(TAG, "Failed to open debug table" + e);
+ return null;
+ }
+ }
+
+ void closeDebugStatement() {
+ synchronized (mDebugStatementLock) {
+ if (mDebugStatementForLogging != null) {
+ mDebugStatementForLogging.close();
+ mDebugStatementForLogging = null;
+ }
+ }
+ }
+
+ long reserveDebugDbInsertionPoint() {
+ if (mDebugDbInsertionPoint == -1) {
+ mDebugDbInsertionPoint = calculateDebugTableInsertionPoint();
+ return mDebugDbInsertionPoint;
+ }
+ mDebugDbInsertionPoint = (mDebugDbInsertionPoint + 1) % MAX_DEBUG_DB_SIZE;
+ return mDebugDbInsertionPoint;
+ }
+
void dumpDebugTable(PrintWriter pw) {
SQLiteDatabase db = mDeDatabase.getReadableDatabase();
Cursor cursor = db.query(TABLE_DEBUG, null,
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 3a0ab77c1313..8fce5e3acdc2 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -515,8 +515,20 @@ public class PreferencesHelper implements RankingConfig {
if (oldGroup != null) {
group.setChannels(oldGroup.getChannels());
+ // apps can't update the blocked status or app overlay permission
if (fromTargetApp) {
group.setBlocked(oldGroup.isBlocked());
+ group.setAllowAppOverlay(oldGroup.canOverlayApps());
+ group.unlockFields(group.getUserLockedFields());
+ group.lockFields(oldGroup.getUserLockedFields());
+ } else {
+ // but the system can
+ if (group.isBlocked() != oldGroup.isBlocked()) {
+ group.lockFields(NotificationChannelGroup.USER_LOCKED_BLOCKED_STATE);
+ }
+ if (group.canOverlayApps() != oldGroup.canOverlayApps()) {
+ group.lockFields(NotificationChannelGroup.USER_LOCKED_ALLOW_APP_OVERLAY);
+ }
}
}
r.groups.put(group.getId(), group);
@@ -1071,6 +1083,9 @@ public class PreferencesHelper implements RankingConfig {
if (original.canShowBadge() != update.canShowBadge()) {
update.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE);
}
+ if (original.isAppOverlayAllowed() != update.isAppOverlayAllowed()) {
+ update.lockFields(NotificationChannel.USER_LOCKED_ALLOW_APP_OVERLAY);
+ }
}
public void dump(PrintWriter pw, String prefix,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f5a18b0ccc16..b988c6a1c739 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -322,6 +322,7 @@ import dalvik.system.CloseGuard;
import dalvik.system.VMRuntime;
import libcore.io.IoUtils;
+import libcore.util.EmptyArray;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -9530,7 +9531,8 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
if (deleteSandboxData && getStorageManagerInternal() != null) {
- getStorageManagerInternal().destroySandboxForApp(pkg.packageName, realUserId);
+ getStorageManagerInternal().destroySandboxForApp(pkg.packageName,
+ pkg.mSharedUserId, realUserId);
}
} catch (PackageManagerException e) {
// Should not happen
@@ -22942,6 +22944,20 @@ public class PackageManagerService extends IPackageManager.Stub
}
@Override
+ public String getSharedUserIdForPackage(String packageName) {
+ synchronized (mPackages) {
+ return getSharedUserIdForPackageLocked(packageName);
+ }
+ }
+
+ @Override
+ public String[] getPackagesForSharedUserId(String sharedUserId, int userId) {
+ synchronized (mPackages) {
+ return getPackagesForSharedUserIdLocked(sharedUserId, userId);
+ }
+ }
+
+ @Override
public boolean isOnlyCoreApps() {
return PackageManagerService.this.isOnlyCoreApps();
}
@@ -22953,6 +22969,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
+ @GuardedBy("mPackages")
private SparseArray<String> getAppsWithSharedUserIdsLocked() {
final SparseArray<String> sharedUserIds = new SparseArray<>();
synchronized (mPackages) {
@@ -22963,6 +22980,38 @@ public class PackageManagerService extends IPackageManager.Stub
return sharedUserIds;
}
+ @GuardedBy("mPackages")
+ private String getSharedUserIdForPackageLocked(String packageName) {
+ final PackageSetting ps = mSettings.mPackages.get(packageName);
+ return (ps != null && ps.isSharedUser()) ? ps.sharedUser.name : null;
+ }
+
+ @GuardedBy("mPackages")
+ private String[] getPackagesForSharedUserIdLocked(String sharedUserId, int userId) {
+ try {
+ final SharedUserSetting sus = mSettings.getSharedUserLPw(
+ sharedUserId, 0, 0, false);
+ if (sus == null) {
+ return EmptyArray.STRING;
+ }
+ String[] res = new String[sus.packages.size()];
+ final Iterator<PackageSetting> it = sus.packages.iterator();
+ int i = 0;
+ while (it.hasNext()) {
+ PackageSetting ps = it.next();
+ if (ps.getInstalled(userId)) {
+ res[i++] = ps.name;
+ } else {
+ res = ArrayUtils.removeElement(String.class, res, res[i]);
+ }
+ }
+ return res;
+ } catch (PackageManagerException e) {
+ // Should not happen
+ }
+ return EmptyArray.STRING;
+ }
+
@Override
public void grantDefaultPermissionsToEnabledCarrierApps(String[] packageNames, int userId) {
enforceSystemOrPhoneCaller("grantPermissionsToEnabledCarrierApps");
diff --git a/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java
index 802253280614..e53518cca21f 100644
--- a/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java
@@ -30,7 +30,6 @@ import android.os.UserManagerInternal;
import android.os.storage.StorageManagerInternal;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
-import android.util.SparseArray;
import org.junit.Before;
import org.junit.Test;
@@ -38,9 +37,6 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.ArrayList;
-import java.util.List;
-
@SmallTest
@RunWith(AndroidJUnit4.class)
public class StorageManagerServiceTest {
@@ -83,29 +79,14 @@ public class StorageManagerServiceTest {
when(mUmi.getUserIds()).thenReturn(new int[] { 0 });
- {
- final SparseArray<String> res = new SparseArray<>();
- res.put(UID_COLORS, NAME_COLORS);
- when(mPmi.getAppsWithSharedUserIds()).thenReturn(res);
- }
-
- {
- final List<ApplicationInfo> res = new ArrayList<>();
- res.add(buildApplicationInfo(PKG_GREY, UID_GREY));
- res.add(buildApplicationInfo(PKG_RED, UID_COLORS));
- res.add(buildApplicationInfo(PKG_BLUE, UID_COLORS));
- when(mPm.getInstalledApplicationsAsUser(anyInt(), anyInt())).thenReturn(res);
- }
-
- when(mPmi.getPackageUid(eq(PKG_GREY), anyInt(), anyInt())).thenReturn(UID_GREY);
- when(mPmi.getPackageUid(eq(PKG_RED), anyInt(), anyInt())).thenReturn(UID_COLORS);
- when(mPmi.getPackageUid(eq(PKG_BLUE), anyInt(), anyInt())).thenReturn(UID_COLORS);
+ when(mPmi.getSharedUserIdForPackage(eq(PKG_GREY))).thenReturn(null);
+ when(mPmi.getSharedUserIdForPackage(eq(PKG_RED))).thenReturn(NAME_COLORS);
+ when(mPmi.getSharedUserIdForPackage(eq(PKG_BLUE))).thenReturn(NAME_COLORS);
- when(mPm.getPackagesForUid(eq(UID_GREY))).thenReturn(new String[] { PKG_GREY });
- when(mPm.getPackagesForUid(eq(UID_COLORS))).thenReturn(new String[] { PKG_RED, PKG_BLUE });
+ when(mPmi.getPackagesForSharedUserId(eq(NAME_COLORS), anyInt()))
+ .thenReturn(new String[] { PKG_RED, PKG_BLUE });
mService = new StorageManagerService(mContext);
- mService.collectPackagesInfo();
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java
index 72c22fd2b135..29a920a0386f 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java
@@ -22,9 +22,14 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
import android.accounts.Account;
import android.content.Context;
import android.database.Cursor;
+import android.database.sqlite.SQLiteStatement;
import android.os.Build;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Pair;
@@ -37,7 +42,11 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
import java.io.File;
+import java.io.PrintWriter;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@@ -64,8 +73,11 @@ public class AccountsDbTest {
private File deDb;
private File ceDb;
+ @Mock private PrintWriter mockWriter;
+
@Before
public void setUp() {
+ MockitoAnnotations.initMocks(this);
Context context = InstrumentationRegistry.getContext();
preNDb = new File(context.getCacheDir(), PREN_DB);
ceDb = new File(context.getCacheDir(), CE_DB);
@@ -444,4 +456,33 @@ public class AccountsDbTest {
assertTrue(mAccountsDb.deleteDeAccount(accId)); // Trigger should remove visibility.
assertNull(mAccountsDb.findAccountVisibility(account, packageName1));
}
+
+ @Test
+ public void testDumpDebugTable() {
+ long accId = 10;
+ long insertionPoint = mAccountsDb.reserveDebugDbInsertionPoint();
+
+ SQLiteStatement logStatement = mAccountsDb.getStatementForLogging();
+
+ logStatement.bindLong(1, accId);
+ logStatement.bindString(2, "action");
+ logStatement.bindString(3, "date");
+ logStatement.bindLong(4, 10);
+ logStatement.bindString(5, "table");
+ logStatement.bindLong(6, insertionPoint);
+ logStatement.execute();
+
+ mAccountsDb.dumpDebugTable(mockWriter);
+
+ verify(mockWriter, times(3)).println(anyString());
+ }
+
+ @Test
+ public void testReserveDebugDbInsertionPoint() {
+ long insertionPoint = mAccountsDb.reserveDebugDbInsertionPoint();
+ long insertionPoint2 = mAccountsDb.reserveDebugDbInsertionPoint();
+
+ assertEquals(0, insertionPoint);
+ assertEquals(1, insertionPoint2);
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 79998a5bf754..3fe381b0abe2 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -280,7 +280,8 @@ public class PreferencesHelperTest extends UiServiceTestCase {
assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
assertTrue(mHelper.getIsAppImportanceLocked(PKG_N_MR1, UID_N_MR1));
- assertEquals(channel1, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId(), false));
+ assertEquals(channel1,
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId(), false));
compareChannels(channel2,
mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId(), false));
@@ -348,7 +349,8 @@ public class PreferencesHelperTest extends UiServiceTestCase {
assertEquals(IMPORTANCE_NONE, mHelper.getImportance(PKG_O, UID_O));
assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
- assertEquals(channel1, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId(), false));
+ assertEquals(channel1,
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId(), false));
compareChannels(channel2,
mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId(), false));
compareChannels(channel3,
@@ -487,7 +489,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
NotificationChannel channel1 =
new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
NotificationChannel channel2 =
- new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+ new NotificationChannel("id2", "name2", IMPORTANCE_HIGH);
NotificationChannel channel3 =
new NotificationChannel("id3", "name3", IMPORTANCE_LOW);
channel3.setGroup(ncg.getId());
@@ -500,7 +502,8 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId());
mHelper.deleteNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg.getId());
- assertEquals(channel2, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId(), false));
+ assertEquals(channel2,
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId(), false));
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, channel1.getId(),
channel2.getId(), channel3.getId(), NotificationChannel.DEFAULT_CHANNEL_ID);
@@ -516,8 +519,8 @@ public class PreferencesHelperTest extends UiServiceTestCase {
assertNull(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId(), false));
assertNull(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3.getId(), false));
assertNull(mHelper.getNotificationChannelGroup(ncg.getId(), PKG_N_MR1, UID_N_MR1));
- //assertEquals(ncg2, mHelper.getNotificationChannelGroup(ncg2.getId(), PKG_N_MR1, UID_N_MR1));
- assertEquals(channel2, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId(), false));
+ assertEquals(channel2,
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId(), false));
}
@Test
@@ -799,14 +802,15 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
- public void testCreateChannel_CannotChangeHiddenFields() throws Exception {
+ public void testCreateChannel_CannotChangeHiddenFields() {
final NotificationChannel channel =
- new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+ new NotificationChannel("id2", "name2", IMPORTANCE_HIGH);
channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
channel.enableLights(true);
channel.setBypassDnd(true);
channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
channel.setShowBadge(true);
+ channel.setAllowAppOverlay(false);
int lockMask = 0;
for (int i = 0; i < NotificationChannel.LOCKABLE_FIELDS.length; i++) {
lockMask |= NotificationChannel.LOCKABLE_FIELDS[i];
@@ -823,19 +827,21 @@ public class PreferencesHelperTest extends UiServiceTestCase {
assertFalse(savedChannel.canBypassDnd());
assertFalse(Notification.VISIBILITY_SECRET == savedChannel.getLockscreenVisibility());
assertEquals(channel.canShowBadge(), savedChannel.canShowBadge());
+ assertEquals(channel.canOverlayApps(), savedChannel.canOverlayApps());
verify(mHandler, never()).requestSort();
}
@Test
- public void testCreateChannel_CannotChangeHiddenFieldsAssistant() throws Exception {
+ public void testCreateChannel_CannotChangeHiddenFieldsAssistant() {
final NotificationChannel channel =
- new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+ new NotificationChannel("id2", "name2", IMPORTANCE_HIGH);
channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
channel.enableLights(true);
channel.setBypassDnd(true);
channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
channel.setShowBadge(true);
+ channel.setAllowAppOverlay(false);
int lockMask = 0;
for (int i = 0; i < NotificationChannel.LOCKABLE_FIELDS.length; i++) {
lockMask |= NotificationChannel.LOCKABLE_FIELDS[i];
@@ -852,10 +858,11 @@ public class PreferencesHelperTest extends UiServiceTestCase {
assertFalse(savedChannel.canBypassDnd());
assertFalse(Notification.VISIBILITY_SECRET == savedChannel.getLockscreenVisibility());
assertEquals(channel.canShowBadge(), savedChannel.canShowBadge());
+ assertEquals(channel.canOverlayApps(), savedChannel.canOverlayApps());
}
@Test
- public void testClearLockedFields() throws Exception {
+ public void testClearLockedFields() {
final NotificationChannel channel = getChannel();
mHelper.clearLockedFields(channel);
assertEquals(0, channel.getUserLockedFields());
@@ -867,7 +874,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
- public void testLockFields_soundAndVibration() throws Exception {
+ public void testLockFields_soundAndVibration() {
mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false);
final NotificationChannel update1 = getChannel();
@@ -891,7 +898,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
- public void testLockFields_vibrationAndLights() throws Exception {
+ public void testLockFields_vibrationAndLights() {
mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false);
final NotificationChannel update1 = getChannel();
@@ -911,7 +918,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
- public void testLockFields_lightsAndImportance() throws Exception {
+ public void testLockFields_lightsAndImportance() {
mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false);
final NotificationChannel update1 = getChannel();
@@ -931,7 +938,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
- public void testLockFields_visibilityAndDndAndBadge() throws Exception {
+ public void testLockFields_visibilityAndDndAndBadge() {
mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false);
assertEquals(0,
mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel().getId(), false)
@@ -963,6 +970,21 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
+ public void testLockFields_appOverlay() {
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false);
+ assertEquals(0,
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel().getId(), false)
+ .getUserLockedFields());
+
+ final NotificationChannel update = getChannel();
+ update.setAllowAppOverlay(false);
+ mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, update, true);
+ assertEquals(NotificationChannel.USER_LOCKED_ALLOW_APP_OVERLAY,
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, update.getId(), false)
+ .getUserLockedFields());
+ }
+
+ @Test
public void testDeleteNonExistentChannel() throws Exception {
mHelper.deleteNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, "does not exist");
}
@@ -1255,21 +1277,24 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, notDeleted, true);
mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, deleted, true);
- mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, nonGroupedNonDeletedChannel, true, false);
+ mHelper.createNotificationChannel(
+ PKG_N_MR1, UID_N_MR1, nonGroupedNonDeletedChannel, true, false);
mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, groupedAndDeleted, true, false);
mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, groupedButNotDeleted, true, false);
mHelper.deleteNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, deleted.getId());
assertNull(mHelper.getNotificationChannelGroup(deleted.getId(), PKG_N_MR1, UID_N_MR1));
- assertNotNull(mHelper.getNotificationChannelGroup(notDeleted.getId(), PKG_N_MR1, UID_N_MR1));
+ assertNotNull(
+ mHelper.getNotificationChannelGroup(notDeleted.getId(), PKG_N_MR1, UID_N_MR1));
- assertNull(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, groupedAndDeleted.getId(), false));
- compareChannels(groupedAndDeleted,
- mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, groupedAndDeleted.getId(), true));
+ assertNull(mHelper.getNotificationChannel(
+ PKG_N_MR1, UID_N_MR1, groupedAndDeleted.getId(), false));
+ compareChannels(groupedAndDeleted, mHelper.getNotificationChannel(
+ PKG_N_MR1, UID_N_MR1, groupedAndDeleted.getId(), true));
- compareChannels(groupedButNotDeleted,
- mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, groupedButNotDeleted.getId(), false));
+ compareChannels(groupedButNotDeleted, mHelper.getNotificationChannel(
+ PKG_N_MR1, UID_N_MR1, groupedButNotDeleted.getId(), false));
compareChannels(nonGroupedNonDeletedChannel, mHelper.getNotificationChannel(
PKG_N_MR1, UID_N_MR1, nonGroupedNonDeletedChannel.getId(), false));
@@ -1381,15 +1406,49 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
- public void testCreateGroup() throws Exception {
+ public void testCreateGroup() {
NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
- assertEquals(ncg, mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1).iterator().next());
+ assertEquals(ncg,
+ mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1).iterator().next());
verify(mHandler, never()).requestSort();
}
@Test
- public void testCannotCreateChannel_badGroup() throws Exception {
+ public void testUpdateGroup_fromSystem_appOverlay() {
+ NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
+
+ // from system, allowed
+ NotificationChannelGroup update = ncg.clone();
+ update.setAllowAppOverlay(false);
+
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, update, false);
+ NotificationChannelGroup updated =
+ mHelper.getNotificationChannelGroup("group1", PKG_N_MR1, UID_N_MR1);
+ assertFalse(updated.canOverlayApps());
+ assertEquals(NotificationChannelGroup.USER_LOCKED_ALLOW_APP_OVERLAY,
+ updated.getUserLockedFields());
+ }
+
+ @Test
+ public void testUpdateGroup_fromApp_appOverlay() {
+ NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
+
+ // from app, not allowed
+ NotificationChannelGroup update = new NotificationChannelGroup("group1", "name1");
+ update.setAllowAppOverlay(false);
+
+ mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
+ NotificationChannelGroup updated =
+ mHelper.getNotificationChannelGroup("group1", PKG_N_MR1, UID_N_MR1);
+ assertTrue(updated.canOverlayApps());
+ assertEquals(0, updated.getUserLockedFields());
+ }
+
+ @Test
+ public void testCannotCreateChannel_badGroup() {
NotificationChannel channel1 =
new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
channel1.setGroup("garbage");
@@ -1401,7 +1460,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
- public void testCannotCreateChannel_goodGroup() throws Exception {
+ public void testCannotCreateChannel_goodGroup() {
NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
NotificationChannel channel1 =
@@ -1409,12 +1468,12 @@ public class PreferencesHelperTest extends UiServiceTestCase {
channel1.setGroup(ncg.getId());
mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false);
- assertEquals(ncg.getId(),
- mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId(), false).getGroup());
+ assertEquals(ncg.getId(), mHelper.getNotificationChannel(
+ PKG_N_MR1, UID_N_MR1, channel1.getId(), false).getGroup());
}
@Test
- public void testGetChannelGroups() throws Exception {
+ public void testGetChannelGroups() {
NotificationChannelGroup unused = new NotificationChannelGroup("unused", "s");
mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, unused, true);
NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
@@ -1465,7 +1524,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
- public void testGetChannelGroups_noSideEffects() throws Exception {
+ public void testGetChannelGroups_noSideEffects() {
NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
@@ -1516,10 +1575,11 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
- public void testCreateChannel_updateName() throws Exception {
+ public void testCreateChannel_updateName() {
NotificationChannel nc = new NotificationChannel("id", "hello", IMPORTANCE_DEFAULT);
mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, nc, true, false);
- NotificationChannel actual = mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, "id", false);
+ NotificationChannel actual =
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, "id", false);
assertEquals("hello", actual.getName());
nc = new NotificationChannel("id", "goodbye", IMPORTANCE_HIGH);
@@ -1533,12 +1593,13 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
- public void testCreateChannel_addToGroup() throws Exception {
+ public void testCreateChannel_addToGroup() {
NotificationChannelGroup group = new NotificationChannelGroup("group", "");
mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, group, true);
NotificationChannel nc = new NotificationChannel("id", "hello", IMPORTANCE_DEFAULT);
mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, nc, true, false);
- NotificationChannel actual = mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, "id", false);
+ NotificationChannel actual =
+ mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, "id", false);
assertNull(actual.getGroup());
nc = new NotificationChannel("id", "hello", IMPORTANCE_HIGH);
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 002d8135c4dd..0b54d39632db 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -2229,22 +2229,27 @@ public class SubscriptionManager {
}
/**
- * Get opportunistic data Profiles.
+ * Return opportunistic subscriptions that can be visible to the caller.
+ * Opportunistic subscriptions are for opportunistic networks, which are cellular
+ * networks with limited capabilities and coverage, for example, CBRS.
*
- * Provide all available user downloaded profiles on phone which are used only for
- * opportunistic data.
- * @param slotIndex slot on which the profiles are queried from.
- * @return the list of opportunistic subscription info. If none exists, an empty list.
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ * or that the calling app has carrier privileges (see
+ * {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @return the list of opportunistic subscription info. If none exists, an empty list.
*/
+ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
- public @NonNull List<SubscriptionInfo> getOpportunisticSubscriptions(int slotIndex) {
+ public @NonNull List<SubscriptionInfo> getOpportunisticSubscriptions() {
String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
List<SubscriptionInfo> subInfoList = null;
try {
ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
if (iSub != null) {
- subInfoList = iSub.getOpportunisticSubscriptions(slotIndex, pkgForDebug);
+ subInfoList = iSub.getOpportunisticSubscriptions(pkgForDebug);
}
} catch (RemoteException ex) {
// ignore it
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index 85b4941cf971..4bdec088b6a1 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -186,10 +186,10 @@ interface ISub {
/**
* Get User downloaded Profiles.
*
- * Provide all available user downloaded profile on the phone.
- * @param slotId on which phone the switch will operate on
+ * Return opportunistic subscriptions that can be visible to the caller.
+ * @return the list of opportunistic subscription info. If none exists, an empty list.
*/
- List<SubscriptionInfo> getOpportunisticSubscriptions(int slotId, String callingPackage);
+ List<SubscriptionInfo> getOpportunisticSubscriptions(String callingPackage);
int getSlotIndex(int subId);