diff options
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); |