diff options
42 files changed, 1442 insertions, 580 deletions
diff --git a/api/current.txt b/api/current.txt index 5d94dbd50bb0..9b6227f5dfac 100644 --- a/api/current.txt +++ b/api/current.txt @@ -696,6 +696,7 @@ package android { field public static final int hyphenationFrequency = 16843998; // 0x10104de field public static final int icon = 16842754; // 0x1010002 field public static final int iconPreview = 16843337; // 0x1010249 + field public static final int iconSpaceReserved = 16844132; // 0x1010564 field public static final int iconTint = 16844129; // 0x1010561 field public static final int iconTintMode = 16844130; // 0x1010562 field public static final int iconifiedByDefault = 16843514; // 0x10102fa @@ -20890,7 +20891,8 @@ package android.media { method public int getContentType(); method public int getFlags(); method public int getUsage(); - method public static int getVolumeControlStream(android.media.AudioAttributes); + method public static deprecated int getVolumeControlStream(android.media.AudioAttributes); + method public int getVolumeControlStream(); method public void writeToParcel(android.os.Parcel, int); field public static final int CONTENT_TYPE_MOVIE = 3; // 0x3 field public static final int CONTENT_TYPE_MUSIC = 2; // 0x2 @@ -24560,6 +24562,7 @@ package android.media.tv { field public static final java.lang.String COLUMN_REVIEW_RATING_STYLE = "review_rating_style"; field public static final java.lang.String COLUMN_STARTING_PRICE = "starting_price"; field public static final java.lang.String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio"; + field public static final java.lang.String COLUMN_TRANSIENT = "transient"; field public static final java.lang.String COLUMN_TYPE = "type"; field public static final java.lang.String INTERACTION_TYPE_FANS = "INTERACTION_TYPE_FANS"; field public static final java.lang.String INTERACTION_TYPE_FOLLOWERS = "INTERACTION_TYPE_FOLLOWERS"; @@ -24637,6 +24640,7 @@ package android.media.tv { field public static final java.lang.String COLUMN_SEARCHABLE = "searchable"; field public static final java.lang.String COLUMN_SERVICE_ID = "service_id"; field public static final java.lang.String COLUMN_SERVICE_TYPE = "service_type"; + field public static final java.lang.String COLUMN_TRANSIENT = "transient"; field public static final java.lang.String COLUMN_TRANSPORT_STREAM_ID = "transport_stream_id"; field public static final java.lang.String COLUMN_TYPE = "type"; field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number"; @@ -31896,6 +31900,7 @@ package android.os.storage { method public boolean isObbMounted(java.lang.String); method public boolean mountObb(java.lang.String, java.lang.String, android.os.storage.OnObbStateChangeListener); method public android.os.ParcelFileDescriptor openProxyFileDescriptor(int, android.os.ProxyFileDescriptorCallback) throws java.io.IOException; + method public android.os.ParcelFileDescriptor openProxyFileDescriptor(int, android.os.ProxyFileDescriptorCallback, android.os.Handler) throws java.io.IOException; method public void setCacheBehaviorGroup(java.io.File, boolean) throws java.io.IOException; method public void setCacheBehaviorTombstone(java.io.File, boolean) throws java.io.IOException; method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener); @@ -32044,6 +32049,7 @@ package android.preference { method public int getWidgetLayoutResource(); method public boolean hasKey(); method public boolean isEnabled(); + method public boolean isIconSpaceReserved(); method public boolean isPersistent(); method public boolean isRecycleEnabled(); method public boolean isSelectable(); @@ -32078,6 +32084,7 @@ package android.preference { method public void setFragment(java.lang.String); method public void setIcon(android.graphics.drawable.Drawable); method public void setIcon(int); + method public void setIconSpaceReserved(boolean); method public void setIntent(android.content.Intent); method public void setKey(java.lang.String); method public void setLayoutResource(int); @@ -41075,9 +41082,9 @@ package android.text { public static final class FontConfig.Font implements android.os.Parcelable { method public int describeContents(); method public android.text.FontConfig.Axis[] getAxes(); - method public android.os.ParcelFileDescriptor getFd(); method public java.lang.String getFontName(); method public int getTtcIndex(); + method public android.net.Uri getUri(); method public int getWeight(); method public boolean isItalic(); method public void writeToParcel(android.os.Parcel, int); @@ -48824,6 +48831,7 @@ package android.webkit { method public int getRendererRequestedPriority(); method public deprecated float getScale(); method public android.webkit.WebSettings getSettings(); + method public android.view.textclassifier.TextClassifier getTextClassifier(); method public java.lang.String getTitle(); method public java.lang.String getUrl(); method public android.webkit.WebChromeClient getWebChromeClient(); @@ -48870,6 +48878,7 @@ package android.webkit { method public void setNetworkAvailable(boolean); method public deprecated void setPictureListener(android.webkit.WebView.PictureListener); method public void setRendererPriorityPolicy(int, boolean); + method public void setTextClassifier(android.view.textclassifier.TextClassifier); method public deprecated void setVerticalScrollbarOverlay(boolean); method public void setWebChromeClient(android.webkit.WebChromeClient); method public static void setWebContentsDebuggingEnabled(boolean); diff --git a/api/system-current.txt b/api/system-current.txt index c7b377e3d6fb..515cf975e156 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -810,6 +810,7 @@ package android { field public static final int hyphenationFrequency = 16843998; // 0x10104de field public static final int icon = 16842754; // 0x1010002 field public static final int iconPreview = 16843337; // 0x1010249 + field public static final int iconSpaceReserved = 16844132; // 0x1010564 field public static final int iconTint = 16844129; // 0x1010561 field public static final int iconTintMode = 16844130; // 0x1010562 field public static final int iconifiedByDefault = 16843514; // 0x10102fa @@ -22626,7 +22627,8 @@ package android.media { method public int getContentType(); method public int getFlags(); method public int getUsage(); - method public static int getVolumeControlStream(android.media.AudioAttributes); + method public static deprecated int getVolumeControlStream(android.media.AudioAttributes); + method public int getVolumeControlStream(); method public void writeToParcel(android.os.Parcel, int); field public static final int CONTENT_TYPE_MOVIE = 3; // 0x3 field public static final int CONTENT_TYPE_MUSIC = 2; // 0x2 @@ -34731,6 +34733,7 @@ package android.os.storage { method public boolean isObbMounted(java.lang.String); method public boolean mountObb(java.lang.String, java.lang.String, android.os.storage.OnObbStateChangeListener); method public android.os.ParcelFileDescriptor openProxyFileDescriptor(int, android.os.ProxyFileDescriptorCallback) throws java.io.IOException; + method public android.os.ParcelFileDescriptor openProxyFileDescriptor(int, android.os.ProxyFileDescriptorCallback, android.os.Handler) throws java.io.IOException; method public void setCacheBehaviorGroup(java.io.File, boolean) throws java.io.IOException; method public void setCacheBehaviorTombstone(java.io.File, boolean) throws java.io.IOException; method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener); @@ -34891,6 +34894,7 @@ package android.preference { method public int getWidgetLayoutResource(); method public boolean hasKey(); method public boolean isEnabled(); + method public boolean isIconSpaceReserved(); method public boolean isPersistent(); method public boolean isRecycleEnabled(); method public boolean isSelectable(); @@ -34925,6 +34929,7 @@ package android.preference { method public void setFragment(java.lang.String); method public void setIcon(android.graphics.drawable.Drawable); method public void setIcon(int); + method public void setIconSpaceReserved(boolean); method public void setIntent(android.content.Intent); method public void setKey(java.lang.String); method public void setLayoutResource(int); @@ -44531,9 +44536,9 @@ package android.text { public static final class FontConfig.Font implements android.os.Parcelable { method public int describeContents(); method public android.text.FontConfig.Axis[] getAxes(); - method public android.os.ParcelFileDescriptor getFd(); method public java.lang.String getFontName(); method public int getTtcIndex(); + method public android.net.Uri getUri(); method public int getWeight(); method public boolean isItalic(); method public void writeToParcel(android.os.Parcel, int); @@ -52377,6 +52382,7 @@ package android.webkit { method public int getRendererRequestedPriority(); method public deprecated float getScale(); method public android.webkit.WebSettings getSettings(); + method public android.view.textclassifier.TextClassifier getTextClassifier(); method public java.lang.String getTitle(); method public java.lang.String getUrl(); method public android.webkit.WebChromeClient getWebChromeClient(); @@ -52424,6 +52430,7 @@ package android.webkit { method public void setNetworkAvailable(boolean); method public deprecated void setPictureListener(android.webkit.WebView.PictureListener); method public void setRendererPriorityPolicy(int, boolean); + method public void setTextClassifier(android.view.textclassifier.TextClassifier); method public deprecated void setVerticalScrollbarOverlay(boolean); method public void setWebChromeClient(android.webkit.WebChromeClient); method public static void setWebContentsDebuggingEnabled(boolean); @@ -52665,6 +52672,7 @@ package android.webkit { method public abstract float getScale(); method public abstract android.webkit.WebViewProvider.ScrollDelegate getScrollDelegate(); method public abstract android.webkit.WebSettings getSettings(); + method public default android.view.textclassifier.TextClassifier getTextClassifier(); method public abstract java.lang.String getTitle(); method public abstract java.lang.String getTouchIconUrl(); method public abstract java.lang.String getUrl(); @@ -52717,6 +52725,7 @@ package android.webkit { method public abstract void setNetworkAvailable(boolean); method public abstract void setPictureListener(android.webkit.WebView.PictureListener); method public abstract void setRendererPriorityPolicy(int, boolean); + method public default void setTextClassifier(android.view.textclassifier.TextClassifier); method public abstract void setVerticalScrollbarOverlay(boolean); method public abstract void setWebChromeClient(android.webkit.WebChromeClient); method public abstract void setWebViewClient(android.webkit.WebViewClient); diff --git a/api/test-current.txt b/api/test-current.txt index d22f7d388a73..051e2e6eea1e 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -696,6 +696,7 @@ package android { field public static final int hyphenationFrequency = 16843998; // 0x10104de field public static final int icon = 16842754; // 0x1010002 field public static final int iconPreview = 16843337; // 0x1010249 + field public static final int iconSpaceReserved = 16844132; // 0x1010564 field public static final int iconTint = 16844129; // 0x1010561 field public static final int iconTintMode = 16844130; // 0x1010562 field public static final int iconifiedByDefault = 16843514; // 0x10102fa @@ -21003,7 +21004,8 @@ package android.media { method public int getContentType(); method public int getFlags(); method public int getUsage(); - method public static int getVolumeControlStream(android.media.AudioAttributes); + method public static deprecated int getVolumeControlStream(android.media.AudioAttributes); + method public int getVolumeControlStream(); method public void writeToParcel(android.os.Parcel, int); field public static final int CONTENT_TYPE_MOVIE = 3; // 0x3 field public static final int CONTENT_TYPE_MUSIC = 2; // 0x2 @@ -24673,6 +24675,7 @@ package android.media.tv { field public static final java.lang.String COLUMN_REVIEW_RATING_STYLE = "review_rating_style"; field public static final java.lang.String COLUMN_STARTING_PRICE = "starting_price"; field public static final java.lang.String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio"; + field public static final java.lang.String COLUMN_TRANSIENT = "transient"; field public static final java.lang.String COLUMN_TYPE = "type"; field public static final java.lang.String INTERACTION_TYPE_FANS = "INTERACTION_TYPE_FANS"; field public static final java.lang.String INTERACTION_TYPE_FOLLOWERS = "INTERACTION_TYPE_FOLLOWERS"; @@ -24750,6 +24753,7 @@ package android.media.tv { field public static final java.lang.String COLUMN_SEARCHABLE = "searchable"; field public static final java.lang.String COLUMN_SERVICE_ID = "service_id"; field public static final java.lang.String COLUMN_SERVICE_TYPE = "service_type"; + field public static final java.lang.String COLUMN_TRANSIENT = "transient"; field public static final java.lang.String COLUMN_TRANSPORT_STREAM_ID = "transport_stream_id"; field public static final java.lang.String COLUMN_TYPE = "type"; field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number"; @@ -32033,6 +32037,7 @@ package android.os.storage { method public boolean isObbMounted(java.lang.String); method public boolean mountObb(java.lang.String, java.lang.String, android.os.storage.OnObbStateChangeListener); method public android.os.ParcelFileDescriptor openProxyFileDescriptor(int, android.os.ProxyFileDescriptorCallback) throws java.io.IOException; + method public android.os.ParcelFileDescriptor openProxyFileDescriptor(int, android.os.ProxyFileDescriptorCallback, android.os.Handler) throws java.io.IOException; method public void setCacheBehaviorGroup(java.io.File, boolean) throws java.io.IOException; method public void setCacheBehaviorTombstone(java.io.File, boolean) throws java.io.IOException; method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener); @@ -32181,6 +32186,7 @@ package android.preference { method public int getWidgetLayoutResource(); method public boolean hasKey(); method public boolean isEnabled(); + method public boolean isIconSpaceReserved(); method public boolean isPersistent(); method public boolean isRecycleEnabled(); method public boolean isSelectable(); @@ -32215,6 +32221,7 @@ package android.preference { method public void setFragment(java.lang.String); method public void setIcon(android.graphics.drawable.Drawable); method public void setIcon(int); + method public void setIconSpaceReserved(boolean); method public void setIntent(android.content.Intent); method public void setKey(java.lang.String); method public void setLayoutResource(int); @@ -41282,9 +41289,9 @@ package android.text { public static final class FontConfig.Font implements android.os.Parcelable { method public int describeContents(); method public android.text.FontConfig.Axis[] getAxes(); - method public android.os.ParcelFileDescriptor getFd(); method public java.lang.String getFontName(); method public int getTtcIndex(); + method public android.net.Uri getUri(); method public int getWeight(); method public boolean isItalic(); method public void writeToParcel(android.os.Parcel, int); @@ -49207,6 +49214,7 @@ package android.webkit { method public int getRendererRequestedPriority(); method public deprecated float getScale(); method public android.webkit.WebSettings getSettings(); + method public android.view.textclassifier.TextClassifier getTextClassifier(); method public java.lang.String getTitle(); method public java.lang.String getUrl(); method public android.webkit.WebChromeClient getWebChromeClient(); @@ -49253,6 +49261,7 @@ package android.webkit { method public void setNetworkAvailable(boolean); method public deprecated void setPictureListener(android.webkit.WebView.PictureListener); method public void setRendererPriorityPolicy(int, boolean); + method public void setTextClassifier(android.view.textclassifier.TextClassifier); method public deprecated void setVerticalScrollbarOverlay(boolean); method public void setWebChromeClient(android.webkit.WebChromeClient); method public static void setWebContentsDebuggingEnabled(boolean); diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 82229d5b1bc8..b9d1d91fabda 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -629,6 +629,11 @@ interface IActivityManager { */ void setDisablePreviewScreenshots(IBinder token, boolean disable); + /** + * Return the user id of last resumed activity. + */ + int getLastResumedActivityUserId(); + // WARNING: when these transactions are updated, check if they are any callers on the native // side. If so, make sure they are using the correct transaction ids and arguments. // If a transaction which will also be used on the native side is being inserted, add it diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java index ef5bc5cf9075..5fa24612f74f 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -483,7 +483,7 @@ public class RecoverySystem { } final String filenameArg = "--update_package=" + filename + "\n"; - final String localeArg = "--locale=" + Locale.getDefault().toString() + "\n"; + final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() + "\n"; final String securityArg = "--security\n"; String command = filenameArg + localeArg; @@ -531,7 +531,7 @@ public class RecoverySystem { } final String filenameArg = "--update_package=" + filename + "\n"; - final String localeArg = "--locale=" + Locale.getDefault().toString() + "\n"; + final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() + "\n"; final String securityArg = "--security\n"; String command = filenameArg + localeArg; @@ -647,7 +647,7 @@ public class RecoverySystem { reasonArg = "--reason=" + sanitizeArg(reason); } - final String localeArg = "--locale=" + Locale.getDefault().toString(); + final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() ; bootCommand(context, shutdownArg, "--wipe_data", reasonArg, localeArg); } @@ -678,7 +678,7 @@ public class RecoverySystem { reasonArg = "--reason=" + sanitizeArg(reason); } - final String localeArg = "--locale=" + Locale.getDefault().toString(); + final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() ; bootCommand(context, "--wipe_cache", reasonArg, localeArg); } @@ -703,7 +703,7 @@ public class RecoverySystem { final String filename = packageFile.getCanonicalPath(); final String filenameArg = "--wipe_package=" + filename; - final String localeArg = "--locale=" + Locale.getDefault().toString(); + final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() ; bootCommand(context, "--wipe_ab", filenameArg, reasonArg, localeArg); } diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 7e1b5abf8112..e5d73e04bda6 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -59,6 +59,8 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.os.AppFuseMount; import com.android.internal.os.FuseAppLoop; +import com.android.internal.os.FuseAppLoop.UnmountedException; +import com.android.internal.os.FuseUnavailableMountException; import com.android.internal.os.RoSystemProperties; import com.android.internal.os.SomeArgs; import com.android.internal.util.Preconditions; @@ -82,6 +84,7 @@ import java.util.List; import java.util.Objects; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; +import libcore.io.IoUtils; /** * StorageManager is the interface to the systems storage service. The storage @@ -1390,53 +1393,52 @@ public class StorageManager { /** {@hide} */ @VisibleForTesting public @NonNull ParcelFileDescriptor openProxyFileDescriptor( - int mode, ProxyFileDescriptorCallback callback, ThreadFactory factory) + int mode, ProxyFileDescriptorCallback callback, Handler handler, ThreadFactory factory) throws IOException { MetricsLogger.count(mContext, "storage_open_proxy_file_descriptor", 1); // Retry is needed because the mount point mFuseAppLoop is using may be unmounted before // invoking StorageManagerService#openProxyFileDescriptor. In this case, we need to re-mount // the bridge by calling mountProxyFileDescriptorBridge. - int retry = 3; - while (retry-- > 0) { + while (true) { try { synchronized (mFuseAppLoopLock) { + boolean newlyCreated = false; if (mFuseAppLoop == null) { final AppFuseMount mount = mStorageManager.mountProxyFileDescriptorBridge(); if (mount == null) { - Log.e(TAG, "Failed to open proxy file bridge."); - throw new IOException("Failed to open proxy file bridge."); + throw new IOException("Failed to mount proxy bridge"); } - mFuseAppLoop = FuseAppLoop.open(mount.mountPointId, mount.fd, factory); + mFuseAppLoop = new FuseAppLoop(mount.mountPointId, mount.fd, factory); + newlyCreated = true; + } + if (handler == null) { + handler = new Handler(Looper.getMainLooper()); } - try { - final int fileId = mFuseAppLoop.registerCallback(callback); - final ParcelFileDescriptor pfd = - mStorageManager.openProxyFileDescriptor( - mFuseAppLoop.getMountPointId(), fileId, mode); - if (pfd != null) { - return pfd; + final int fileId = mFuseAppLoop.registerCallback(callback, handler); + final ParcelFileDescriptor pfd = mStorageManager.openProxyFileDescriptor( + mFuseAppLoop.getMountPointId(), fileId, mode); + if (pfd == null) { + mFuseAppLoop.unregisterCallback(fileId); + throw new FuseUnavailableMountException( + mFuseAppLoop.getMountPointId()); + } + return pfd; + } catch (FuseUnavailableMountException exception) { + // The bridge is being unmounted. Tried to recreate it unless the bridge was + // just created. + if (newlyCreated) { + throw new IOException(exception); } - // Probably the bridge is being unmounted but mFuseAppLoop has not been - // noticed it yet. - mFuseAppLoop.unregisterCallback(fileId); - } catch (FuseAppLoop.UnmountedException error) { - Log.d(TAG, "mFuseAppLoop has been already unmounted."); mFuseAppLoop = null; continue; } } - try { - Thread.sleep(100); - } catch (InterruptedException e) { - break; - } } catch (RemoteException e) { - e.rethrowFromSystemServer(); + // Cannot recover from remote exception. + throw new IOException(e); } } - - throw new IOException("Failed to mount bridge."); } /** @@ -1448,16 +1450,37 @@ public class StorageManager { * {@link ParcelFileDescriptor#MODE_WRITE_ONLY}, or * {@link ParcelFileDescriptor#MODE_READ_WRITE} * @param callback Callback to process file operation requests issued on returned file - * descriptor. The callback is invoked on a thread managed by the framework. + * descriptor. * @return Seekable ParcelFileDescriptor. * @throws IOException */ public @NonNull ParcelFileDescriptor openProxyFileDescriptor( int mode, ProxyFileDescriptorCallback callback) throws IOException { - return openProxyFileDescriptor(mode, callback, null); + return openProxyFileDescriptor(mode, callback, null, null); } + /** + * Opens seekable ParcelFileDescriptor that routes file operation requests to + * ProxyFileDescriptorCallback. + * + * @param mode The desired access mode, must be one of + * {@link ParcelFileDescriptor#MODE_READ_ONLY}, + * {@link ParcelFileDescriptor#MODE_WRITE_ONLY}, or + * {@link ParcelFileDescriptor#MODE_READ_WRITE} + * @param callback Callback to process file operation requests issued on returned file + * descriptor. + * @param handler Handler that invokes callback methods. + * @return Seekable ParcelFileDescriptor. + * @throws IOException + */ + public @NonNull ParcelFileDescriptor openProxyFileDescriptor( + int mode, ProxyFileDescriptorCallback callback, Handler handler) + throws IOException { + return openProxyFileDescriptor(mode, callback, handler, null); + } + + /** {@hide} */ @VisibleForTesting public int getProxyFileDescriptorMountPointId() { diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java index 4d14277be9c1..d3adce73582e 100644 --- a/core/java/android/preference/Preference.java +++ b/core/java/android/preference/Preference.java @@ -83,6 +83,7 @@ import java.util.Set; * @attr ref android.R.styleable#Preference_shouldDisableView * @attr ref android.R.styleable#Preference_recycleEnabled * @attr ref android.R.styleable#Preference_singleLineTitle + * @attr ref android.R.styleable#Preference_iconSpaceReserved */ public class Preference implements Comparable<Preference> { /** @@ -135,6 +136,7 @@ public class Preference implements Comparable<Preference> { private boolean mParentDependencyMet = true; private boolean mRecycleEnabled = true; private boolean mSingleLineTitle = true; + private boolean mIconSpaceReserved; /** * @see #setShouldDisableView(boolean) @@ -302,7 +304,11 @@ public class Preference implements Comparable<Preference> { case com.android.internal.R.styleable.Preference_singleLineTitle: mSingleLineTitle = a.getBoolean(attr, mSingleLineTitle); break; - } + + case com.android.internal.R.styleable.Preference_iconSpaceReserved: + mIconSpaceReserved = a.getBoolean(attr, mIconSpaceReserved); + break; + } } a.recycle(); } @@ -631,7 +637,11 @@ public class Preference implements Comparable<Preference> { imageView.setImageDrawable(mIcon); } } - imageView.setVisibility(mIcon != null ? View.VISIBLE : View.GONE); + if (mIcon != null) { + imageView.setVisibility(View.VISIBLE); + } else { + imageView.setVisibility(mIconSpaceReserved ? View.INVISIBLE : View.GONE); + } } final View imageFrame = view.findViewById(com.android.internal.R.id.icon_frame); @@ -931,6 +941,25 @@ public class Preference implements Comparable<Preference> { } /** + * Sets whether to reserve the space of this Preference icon view when no icon is provided. + * + * @param iconSpaceReserved set {@code true} if the space for the icon view should be reserved + */ + public void setIconSpaceReserved(boolean iconSpaceReserved) { + mIconSpaceReserved = iconSpaceReserved; + notifyChanged(); + } + + /** + * Gets whether the space this preference icon view is reserved. + * + * @see #setIconSpaceReserved(boolean) + * @return {@code true} if the space of this preference icon view is reserved + */ + public boolean isIconSpaceReserved() { + return mIconSpaceReserved; + } + /** * Returns a unique ID for this Preference. This ID should be unique across all * Preference objects in a hierarchy. * diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java index 70f9bdd9193b..14d3ad790b1d 100644 --- a/core/java/android/text/FontConfig.java +++ b/core/java/android/text/FontConfig.java @@ -22,13 +22,11 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.FontListParser; +import android.net.Uri; import android.os.Parcel; -import android.os.ParcelFileDescriptor; import android.os.Parcelable; -import java.io.IOException; import java.lang.annotation.Retention; -import java.util.Arrays; /** @@ -44,20 +42,6 @@ public final class FontConfig implements Parcelable { } /** - * For duplicating file descriptors. - * - * Note that this copy constructor can not be usable for deep copy. - * @hide - */ - public FontConfig(@NonNull FontConfig config) { - mFamilies = new Family[config.mFamilies.length]; - for (int i = 0; i < config.mFamilies.length; ++i) { - mFamilies[i] = new Family(config.mFamilies[i]); - } - mAliases = Arrays.copyOf(config.mAliases, config.mAliases.length); - } - - /** * Returns the ordered list of families included in the system fonts. */ public @NonNull Family[] getFamilies() { @@ -174,7 +158,7 @@ public final class FontConfig implements Parcelable { private final @NonNull Axis[] mAxes; private final int mWeight; private final boolean mIsItalic; - private @Nullable ParcelFileDescriptor mFd; + private Uri mUri; /** * @hide @@ -186,29 +170,6 @@ public final class FontConfig implements Parcelable { mAxes = axes; mWeight = weight; mIsItalic = isItalic; - mFd = null; - } - - /** - * This is for duplicating FileDescriptors. - * - * Note that this copy ctor doesn't deep copy the members. - * - * @hide - */ - public Font(Font origin) { - mFontName = origin.mFontName; - mTtcIndex = origin.mTtcIndex; - mAxes = origin.mAxes; - mWeight = origin.mWeight; - mIsItalic = origin.mIsItalic; - if (origin.mFd != null) { - try { - mFd = origin.mFd.dup(); - } catch (IOException e) { - e.printStackTrace(); - } - } } /** @@ -247,17 +208,20 @@ public final class FontConfig implements Parcelable { } /** - * Returns a file descriptor to access the specified font. This should be closed after use. + * Returns the content uri associated to this font. + * + * You can reach to the font contents by calling {@link + * android.content.ContentResolver#openInputStream}. */ - public @Nullable ParcelFileDescriptor getFd() { - return mFd; + public @Nullable Uri getUri() { + return mUri; } /** * @hide */ - public void setFd(@NonNull ParcelFileDescriptor fd) { - mFd = fd; + public void setUri(@NonNull Uri uri) { + mUri = uri; } /** @@ -269,11 +233,7 @@ public final class FontConfig implements Parcelable { mAxes = in.createTypedArray(Axis.CREATOR); mWeight = in.readInt(); mIsItalic = in.readInt() == 1; - if (in.readInt() == 1) { /* has FD */ - mFd = ParcelFileDescriptor.CREATOR.createFromParcel(in); - } else { - mFd = null; - } + mUri = in.readTypedObject(Uri.CREATOR); } @Override @@ -283,10 +243,7 @@ public final class FontConfig implements Parcelable { out.writeTypedArray(mAxes, flag); out.writeInt(mWeight); out.writeInt(mIsItalic ? 1 : 0); - out.writeInt(mFd == null ? 0 : 1); - if (mFd != null) { - mFd.writeToParcel(out, flag); - } + out.writeTypedObject(mUri, flag); } @Override @@ -425,22 +382,6 @@ public final class FontConfig implements Parcelable { } /** - * For duplicating file descriptor underlying Font object. - * - * This copy constructor is not for deep copying. - * @hide - */ - public Family(Family origin) { - mName = origin.mName; - mLanguage = origin.mLanguage; - mVariant = origin.mVariant; - mFonts = new Font[origin.mFonts.length]; - for (int i = 0; i < origin.mFonts.length; ++i) { - mFonts[i] = new Font(origin.mFonts[i]); - } - } - - /** * Returns the name given by the system to this font family. */ public @Nullable String getName() { diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index e59073999683..076b33cc9d6a 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -137,7 +137,10 @@ public class SurfaceView extends View { } break; case DRAW_FINISHED_MSG: { mDrawFinished = true; - invalidate(); + if (mAttachedToWindow) { + notifyDrawFinished(); + invalidate(); + } } break; } } @@ -188,9 +191,12 @@ public class SurfaceView extends View { private Translator mTranslator; private boolean mGlobalListenersAdded; + private boolean mAttachedToWindow; private int mSurfaceFlags = SurfaceControl.HIDDEN; + private int mPendingReportDraws; + public SurfaceView(Context context) { this(context, null); } @@ -227,6 +233,7 @@ public class SurfaceView extends View { mViewVisibility = getVisibility() == VISIBLE; mRequestedVisible = mViewVisibility && mWindowVisibility; + mAttachedToWindow = true; if (!mGlobalListenersAdded) { ViewTreeObserver observer = getViewTreeObserver(); observer.addOnScrollChangedListener(mScrollChangedListener); @@ -261,8 +268,17 @@ public class SurfaceView extends View { updateSurface(); } + void notifyDrawFinished() { + ViewRootImpl viewRoot = getViewRootImpl(); + if (viewRoot != null) { + viewRoot.pendingDrawFinished(); + } + mPendingReportDraws--; + } + @Override protected void onDetachedFromWindow() { + mAttachedToWindow = false; if (mGlobalListenersAdded) { ViewTreeObserver observer = getViewTreeObserver(); observer.removeOnScrollChangedListener(mScrollChangedListener); @@ -270,6 +286,10 @@ public class SurfaceView extends View { mGlobalListenersAdded = false; } + while (mPendingReportDraws > 0) { + notifyDrawFinished(); + } + mRequestedVisible = false; updateSurface(); @@ -618,6 +638,9 @@ public class SurfaceView extends View { if (callbacks == null) { callbacks = getSurfaceCallbacks(); } + + mPendingReportDraws++; + viewRoot.drawPending(); SurfaceCallbackHelper sch = new SurfaceCallbackHelper(this::onDrawFinished); sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 3f52a9d59168..e924f77cac85 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -61,6 +61,7 @@ import android.graphics.Shader; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.hardware.display.DisplayManagerGlobal; +import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; @@ -4170,14 +4171,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * When this flag is used with {@link #DRAG_FLAG_GLOBAL}, the drag recipient will be able to * request read access to the content URI(s) contained in the {@link ClipData} object. - * @see android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION + * @see android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION */ public static final int DRAG_FLAG_GLOBAL_URI_READ = Intent.FLAG_GRANT_READ_URI_PERMISSION; /** * When this flag is used with {@link #DRAG_FLAG_GLOBAL}, the drag recipient will be able to * request write access to the content URI(s) contained in the {@link ClipData} object. - * @see android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION + * @see android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION */ public static final int DRAG_FLAG_GLOBAL_URI_WRITE = Intent.FLAG_GRANT_WRITE_URI_PERMISSION; @@ -4185,8 +4186,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * When this flag is used with {@link #DRAG_FLAG_GLOBAL_URI_READ} and/or {@link * #DRAG_FLAG_GLOBAL_URI_WRITE}, the URI permission grant can be persisted across device * reboots until explicitly revoked with - * {@link android.content.Context#revokeUriPermission(Uri,int) Context.revokeUriPermission}. - * @see android.content.Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION + * {@link android.content.Context#revokeUriPermission(Uri, int)} Context.revokeUriPermission}. + * @see android.content.Intent#FLAG_GRANT_PERSISTABLE_URI_PERMISSION */ public static final int DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION = Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION; @@ -4195,7 +4196,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * When this flag is used with {@link #DRAG_FLAG_GLOBAL_URI_READ} and/or {@link * #DRAG_FLAG_GLOBAL_URI_WRITE}, the URI permission grant applies to any URI that is a prefix * match against the original granted URI. - * @see android.content.Intent.FLAG_GRANT_PREFIX_URI_PERMISSION + * @see android.content.Intent#FLAG_GRANT_PREFIX_URI_PERMISSION */ public static final int DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION = Intent.FLAG_GRANT_PREFIX_URI_PERMISSION; @@ -7895,7 +7896,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @param arguments A {@link Bundle} holding any arguments relevant for this request. May be * {@code null} if the service provided no arguments. * - * @see AccessibilityNodeInfo#setExtraAvailableData + * @see AccessibilityNodeInfo#setAvailableExtraData(List) */ public void addExtraDataToAccessibilityNodeInfo( @NonNull AccessibilityNodeInfo info, @NonNull String extraDataKey, @@ -9839,7 +9840,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * Focus gets restored for a view hierarchy when the root of the hierarchy gets added to a * window or serves as a target of cluster navigation. * - * @see #restoreDefaultFocus(int) + * @see #restoreDefaultFocus() * * @return {@code true} if this view is the default-focus view, {@code false} otherwise * @attr ref android.R.styleable#View_focusedByDefault @@ -9859,7 +9860,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @param isFocusedByDefault {@code true} to set this view as the default-focus view, * {@code false} otherwise. * - * @see #restoreDefaultFocus(int) + * @see #restoreDefaultFocus() * * @attr ref android.R.styleable#View_focusedByDefault */ diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index cf52c60fb2fc..6ec4a2bded0d 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -2704,8 +2704,40 @@ public final class ViewRootImpl implements ViewParent, } } - private void onDrawFinished() { + /** + * A count of the number of calls to pendingDrawFinished we + * require to notify the WM drawing is complete. + * + * This starts at 1, for the ViewRootImpl surface itself. + * Subsurfaces may debt the value with drawPending. + */ + int mDrawsNeededToReport = 1; + + /** + * Delay notifying WM of draw finished until + * a balanced call to pendingDrawFinished. + */ + void drawPending() { + mDrawsNeededToReport++; + } + + void pendingDrawFinished() { + if (mDrawsNeededToReport == 0) { + throw new RuntimeException("Unbalanced drawPending/pendingDrawFinished calls"); + } + mDrawsNeededToReport--; + if (mDrawsNeededToReport == 0) { + reportDrawFinished(); + } + } + + private void postDrawFinished() { + mHandler.sendEmptyMessage(MSG_DRAW_FINISHED); + } + + private void reportDrawFinished() { try { + mDrawsNeededToReport = 1; mWindowSession.finishDrawing(mWindow); } catch (RemoteException e) { // Have fun! @@ -2762,15 +2794,12 @@ public final class ViewRootImpl implements ViewParent, } if (mSurfaceHolder != null && mSurface.isValid()) { - SurfaceCallbackHelper sch = new SurfaceCallbackHelper(this::onDrawFinished); + SurfaceCallbackHelper sch = new SurfaceCallbackHelper(this::postDrawFinished); SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks); } else { - try { - mWindowSession.finishDrawing(mWindow); - } catch (RemoteException e) { - } + pendingDrawFinished(); } } } @@ -3576,6 +3605,7 @@ public final class ViewRootImpl implements ViewParent, private final static int MSG_REQUEST_KEYBOARD_SHORTCUTS = 26; private final static int MSG_UPDATE_POINTER_ICON = 27; private final static int MSG_POINTER_CAPTURE_CHANGED = 28; + private final static int MSG_DRAW_FINISHED = 29; final class ViewRootHandler extends Handler { @Override @@ -3627,6 +3657,8 @@ public final class ViewRootImpl implements ViewParent, return "MSG_UPDATE_POINTER_ICON"; case MSG_POINTER_CAPTURE_CHANGED: return "MSG_POINTER_CAPTURE_CHANGED"; + case MSG_DRAW_FINISHED: + return "MSG_DRAW_FINISHED"; } return super.getMessageName(message); } @@ -3902,6 +3934,9 @@ public final class ViewRootImpl implements ViewParent, final boolean hasCapture = msg.arg1 != 0; handlePointerCaptureChanged(hasCapture); } break; + case MSG_DRAW_FINISHED: { + pendingDrawFinished(); + } break; } } } diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index c2b4138b90db..bc491230aa60 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -18,6 +18,7 @@ package android.webkit; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.Widget; import android.content.Context; @@ -58,6 +59,7 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeProvider; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; +import android.view.textclassifier.TextClassifier; import android.widget.AbsoluteLayout; import java.io.BufferedWriter; @@ -2249,6 +2251,23 @@ public class WebView extends AbsoluteLayout public boolean getRendererPriorityWaivedWhenNotVisible() { return mProvider.getRendererPriorityWaivedWhenNotVisible(); } + + /** + * Sets the {@link TextClassifier} for this WebView. + */ + public void setTextClassifier(@Nullable TextClassifier textClassifier) { + mProvider.setTextClassifier(textClassifier); + } + + /** + * Returns the {@link TextClassifier} used by this WebView. + * If no TextClassifier has been set, this WebView uses the default set by the system. + */ + @NonNull + public TextClassifier getTextClassifier() { + return mProvider.getTextClassifier(); + } + //------------------------------------------------------------------------- // Interface for WebView providers //------------------------------------------------------------------------- diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java index 5724a9b24052..aa1ffa25931d 100644 --- a/core/java/android/webkit/WebViewProvider.java +++ b/core/java/android/webkit/WebViewProvider.java @@ -16,6 +16,8 @@ package android.webkit; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.content.res.Configuration; import android.content.Intent; @@ -41,6 +43,7 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeProvider; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; +import android.view.textclassifier.TextClassifier; import android.webkit.WebView.HitTestResult; import android.webkit.WebView.PictureListener; import android.webkit.WebView.VisualStateCallback; @@ -275,6 +278,12 @@ public interface WebViewProvider { public boolean getRendererPriorityWaivedWhenNotVisible(); + @SuppressWarnings("unused") + public default void setTextClassifier(@Nullable TextClassifier textClassifier) {} + + @NonNull + public default TextClassifier getTextClassifier() { return TextClassifier.NO_OP; } + //------------------------------------------------------------------------- // Provider internal methods //------------------------------------------------------------------------- diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 1937187defed..99b91bdf046b 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -6873,9 +6873,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mTransientStateViews.put(position, scrap); } else { // Otherwise, we'll have to remove the view and start over. + clearScrapForRebind(scrap); getSkippedScrap().add(scrap); } } else { + clearScrapForRebind(scrap); if (mViewTypeCount == 1) { mCurrentScrap.add(scrap); } else { @@ -7098,12 +7100,12 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } else if (params.scrappedFromPosition == position) { final View scrap = scrapViews.remove(i); - clearAccessibilityFromScrap(scrap); + clearScrapForRebind(scrap); return scrap; } } final View scrap = scrapViews.remove(size - 1); - clearAccessibilityFromScrap(scrap); + clearScrapForRebind(scrap); return scrap; } else { return null; @@ -7117,7 +7119,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } - private void clearAccessibilityFromScrap(View view) { + private void clearScrapForRebind(View view) { view.clearAccessibilityFocus(); view.setAccessibilityDelegate(null); } diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index 1c0c4ef7cba7..12e35a14e381 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -1639,7 +1639,7 @@ public class ListView extends AbsListView { final View focusChild = getAccessibilityFocusedChild(focusHost); if (focusChild != null) { if (!dataChanged || isDirectChildHeaderOrFooter(focusChild) - || focusChild.hasTransientState() || mAdapterHasStableIds) { + || (focusChild.hasTransientState() && mAdapterHasStableIds)) { // The views won't be changing, so try to maintain // focus on the current host and virtual view. accessibilityFocusLayoutRestoreView = focusHost; diff --git a/core/java/com/android/internal/os/FuseAppLoop.java b/core/java/com/android/internal/os/FuseAppLoop.java index 3603b6df11da..8edd637ea814 100644 --- a/core/java/com/android/internal/os/FuseAppLoop.java +++ b/core/java/com/android/internal/os/FuseAppLoop.java @@ -19,16 +19,16 @@ package com.android.internal.os; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.ProxyFileDescriptorCallback; +import android.os.Handler; import android.os.ParcelFileDescriptor; import android.system.ErrnoException; import android.system.OsConstants; import android.util.Log; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; -import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; - -import java.io.IOException; +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.ThreadFactory; public class FuseAppLoop { @@ -42,14 +42,21 @@ public class FuseAppLoop { return new Thread(r, TAG); } }; + private static final int FUSE_OK = 0; private final Object mLock = new Object(); private final int mMountPointId; private final Thread mThread; + private final Handler mDefaultHandler; + + private static final int CMD_FSYNC = 1; @GuardedBy("mLock") private final SparseArray<CallbackEntry> mCallbackMap = new SparseArray<>(); + @GuardedBy("mLock") + private final BytesMap mBytesMap = new BytesMap(); + /** * Sequential number can be used as file name and inode in AppFuse. * 0 is regarded as an error, 1 is mount point. So we start the number from 2. @@ -57,38 +64,40 @@ public class FuseAppLoop { @GuardedBy("mLock") private int mNextInode = MIN_INODE; - private FuseAppLoop( + @GuardedBy("mLock") + private long mInstance; + + public FuseAppLoop( int mountPointId, @NonNull ParcelFileDescriptor fd, @Nullable ThreadFactory factory) { mMountPointId = mountPointId; - final int rawFd = fd.detachFd(); if (factory == null) { factory = sDefaultThreadFactory; } - mThread = factory.newThread(new Runnable() { - @Override - public void run() { - // rawFd is closed by native_start_loop. Java code does not need to close it. - native_start_loop(rawFd); + mInstance = native_new(fd.detachFd()); + mThread = factory.newThread(() -> { + native_start(mInstance); + synchronized (mLock) { + native_delete(mInstance); + mInstance = 0; + mBytesMap.clear(); } }); + mThread.start(); + mDefaultHandler = null; } - public static @NonNull FuseAppLoop open(int mountPointId, @NonNull ParcelFileDescriptor fd, - @Nullable ThreadFactory factory) { - Preconditions.checkNotNull(fd); - final FuseAppLoop loop = new FuseAppLoop(mountPointId, fd, factory); - loop.mThread.start(); - return loop; - } - - public int registerCallback(@NonNull ProxyFileDescriptorCallback callback) - throws UnmountedException, IOException { - if (mThread.getState() == Thread.State.TERMINATED) { - throw new UnmountedException(); - } + public int registerCallback(@NonNull ProxyFileDescriptorCallback callback, + @NonNull Handler handler) throws FuseUnavailableMountException { synchronized (mLock) { - if (mCallbackMap.size() >= Integer.MAX_VALUE - MIN_INODE) { - throw new IOException("Too many opened files."); + Preconditions.checkNotNull(callback); + Preconditions.checkNotNull(handler); + Preconditions.checkState( + mCallbackMap.size() < Integer.MAX_VALUE - MIN_INODE, "Too many opened files."); + Preconditions.checkArgument( + Thread.currentThread().getId() != handler.getLooper().getThread().getId(), + "Handler must be different from the current thread"); + if (mInstance == 0) { + throw new FuseUnavailableMountException(mMountPointId); } int id; while (true) { @@ -101,118 +110,171 @@ public class FuseAppLoop { break; } } - mCallbackMap.put(id, new CallbackEntry(callback)); + mCallbackMap.put(id, new CallbackEntry(callback, handler)); return id; } } public void unregisterCallback(int id) { - mCallbackMap.remove(id); + synchronized (mLock) { + mCallbackMap.remove(id); + } } public int getMountPointId() { return mMountPointId; } - private CallbackEntry getCallbackEntryOrThrowLocked(long inode) throws ErrnoException { - final CallbackEntry entry = mCallbackMap.get(checkInode(inode)); - if (entry != null) { - return entry; - } else { - throw new ErrnoException("getCallbackEntry", OsConstants.ENOENT); - } - } + // Defined in fuse.h + private static final int FUSE_LOOKUP = 1; + private static final int FUSE_GETATTR = 3; + private static final int FUSE_OPEN = 14; + private static final int FUSE_READ = 15; + private static final int FUSE_WRITE = 16; + private static final int FUSE_RELEASE = 18; + private static final int FUSE_FSYNC = 20; + + // Defined in FuseBuffer.h + private static final int FUSE_MAX_WRITE = 256 * 1024; // Called by JNI. @SuppressWarnings("unused") - private long onGetSize(long inode) { + private void onCommand(int command, long unique, long inode, long offset, int size, + byte[] data) { synchronized(mLock) { try { - return getCallbackEntryOrThrowLocked(inode).callback.onGetSize(); - } catch (ErrnoException exp) { - return getError(exp); + final CallbackEntry entry = getCallbackEntryOrThrowLocked(inode); + entry.postRunnable(() -> { + try { + switch (command) { + case FUSE_LOOKUP: { + final long fileSize = entry.callback.onGetSize(); + synchronized (mLock) { + if (mInstance != 0) { + native_replyLookup(mInstance, unique, inode, fileSize); + } + } + break; + } + case FUSE_GETATTR: { + final long fileSize = entry.callback.onGetSize(); + synchronized (mLock) { + if (mInstance != 0) { + native_replyGetAttr(mInstance, unique, inode, fileSize); + } + } + break; + } + case FUSE_READ: + final int readSize = entry.callback.onRead(offset, size, data); + synchronized (mLock) { + if (mInstance != 0) { + native_replyRead(mInstance, unique, readSize, data); + } + } + break; + case FUSE_WRITE: + final int writeSize = entry.callback.onWrite(offset, size, data); + synchronized (mLock) { + if (mInstance != 0) { + native_replyWrite(mInstance, unique, writeSize); + } + } + break; + case FUSE_FSYNC: + entry.callback.onFsync(); + synchronized (mLock) { + if (mInstance != 0) { + native_replySimple(mInstance, unique, FUSE_OK); + } + } + break; + case FUSE_RELEASE: + entry.callback.onRelease(); + synchronized (mLock) { + if (mInstance != 0) { + native_replySimple(mInstance, unique, FUSE_OK); + } + mBytesMap.stopUsing(entry.getThreadId()); + } + break; + default: + throw new IllegalArgumentException( + "Unknown FUSE command: " + command); + } + } catch (Exception error) { + Log.e(TAG, "", error); + replySimple(unique, getError(error)); + } + }); + } catch (ErrnoException error) { + Log.e(TAG, "", error); + replySimpleLocked(unique, getError(error)); } } } // Called by JNI. @SuppressWarnings("unused") - private int onOpen(long inode) { - synchronized(mLock) { + private byte[] onOpen(long unique, long inode) { + synchronized (mLock) { try { final CallbackEntry entry = getCallbackEntryOrThrowLocked(inode); if (entry.opened) { throw new ErrnoException("onOpen", OsConstants.EMFILE); } - entry.opened = true; - // Use inode as file handle. It's OK because AppFuse does not allow to open the same - // file twice. - return (int) inode; - } catch (ErrnoException exp) { - return getError(exp); + if (mInstance != 0) { + native_replyOpen(mInstance, unique, /* fh */ inode); + entry.opened = true; + return mBytesMap.startUsing(entry.getThreadId()); + } + } catch (ErrnoException error) { + replySimpleLocked(unique, getError(error)); } + return null; } } - // Called by JNI. - @SuppressWarnings("unused") - private int onFsync(long inode) { - synchronized(mLock) { - try { - getCallbackEntryOrThrowLocked(inode).callback.onFsync(); - return 0; - } catch (ErrnoException exp) { - return getError(exp); + private static int getError(@NonNull Exception error) { + if (error instanceof ErrnoException) { + final int errno = ((ErrnoException) error).errno; + if (errno != OsConstants.ENOSYS) { + return -errno; } } + return -OsConstants.EBADF; } - // Called by JNI. - @SuppressWarnings("unused") - private int onRelease(long inode) { - synchronized(mLock) { - try { - getCallbackEntryOrThrowLocked(inode).callback.onRelease(); - return 0; - } catch (ErrnoException exp) { - return getError(exp); - } finally { - mCallbackMap.remove(checkInode(inode)); - } + private CallbackEntry getCallbackEntryOrThrowLocked(long inode) throws ErrnoException { + final CallbackEntry entry = mCallbackMap.get(checkInode(inode)); + if (entry == null) { + throw new ErrnoException("getCallbackEntryOrThrowLocked", OsConstants.ENOENT); } + return entry; } - // Called by JNI. - @SuppressWarnings("unused") - private int onRead(long inode, long offset, int size, byte[] bytes) { - synchronized(mLock) { - try { - return getCallbackEntryOrThrowLocked(inode).callback.onRead(offset, size, bytes); - } catch (ErrnoException exp) { - return getError(exp); - } + private void replySimple(long unique, int result) { + synchronized (mLock) { + replySimpleLocked(unique, result); } } - // Called by JNI. - @SuppressWarnings("unused") - private int onWrite(long inode, long offset, int size, byte[] bytes) { - synchronized(mLock) { - try { - return getCallbackEntryOrThrowLocked(inode).callback.onWrite(offset, size, bytes); - } catch (ErrnoException exp) { - return getError(exp); - } + private void replySimpleLocked(long unique, int result) { + if (mInstance != 0) { + native_replySimple(mInstance, unique, result); } } - private static int getError(@NonNull ErrnoException exp) { - // Should not return ENOSYS because the kernel stops - // dispatching the FUSE action once FUSE implementation returns ENOSYS for the action. - return exp.errno != OsConstants.ENOSYS ? -exp.errno : -OsConstants.EIO; - } + native long native_new(int fd); + native void native_delete(long ptr); + native void native_start(long ptr); - native boolean native_start_loop(int fd); + native void native_replySimple(long ptr, long unique, int result); + native void native_replyOpen(long ptr, long unique, long fh); + native void native_replyLookup(long ptr, long unique, long inode, long size); + native void native_replyGetAttr(long ptr, long unique, long inode, long size); + native void native_replyWrite(long ptr, long unique, int size); + native void native_replyRead(long ptr, long unique, int size, byte[] bytes); private static int checkInode(long inode) { Preconditions.checkArgumentInRange(inode, MIN_INODE, Integer.MAX_VALUE, "checkInode"); @@ -223,10 +285,61 @@ public class FuseAppLoop { private static class CallbackEntry { final ProxyFileDescriptorCallback callback; + final Handler handler; boolean opened; - CallbackEntry(ProxyFileDescriptorCallback callback) { - Preconditions.checkNotNull(callback); - this.callback = callback; + + CallbackEntry(ProxyFileDescriptorCallback callback, Handler handler) { + this.callback = Preconditions.checkNotNull(callback); + this.handler = Preconditions.checkNotNull(handler); + } + + long getThreadId() { + return handler.getLooper().getThread().getId(); + } + + void postRunnable(Runnable runnable) throws ErrnoException { + final boolean result = handler.post(runnable); + if (!result) { + throw new ErrnoException("postRunnable", OsConstants.EBADF); + } + } + } + + /** + * Entry for bytes map. + */ + private static class BytesMapEntry { + int counter = 0; + byte[] bytes = new byte[FUSE_MAX_WRITE]; + } + + /** + * Map between Thread ID and byte buffer. + */ + private static class BytesMap { + final Map<Long, BytesMapEntry> mEntries = new HashMap<>(); + + byte[] startUsing(long threadId) { + BytesMapEntry entry = mEntries.get(threadId); + if (entry == null) { + entry = new BytesMapEntry(); + mEntries.put(threadId, entry); + } + entry.counter++; + return entry.bytes; + } + + void stopUsing(long threadId) { + final BytesMapEntry entry = mEntries.get(threadId); + Preconditions.checkNotNull(entry); + entry.counter--; + if (entry.counter <= 0) { + mEntries.remove(threadId); + } + } + + void clear() { + mEntries.clear(); } } } diff --git a/core/java/com/android/internal/os/FuseUnavailableMountException.java b/core/java/com/android/internal/os/FuseUnavailableMountException.java new file mode 100644 index 000000000000..ca3cfb90ae11 --- /dev/null +++ b/core/java/com/android/internal/os/FuseUnavailableMountException.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.os; + +/** + * Exception occurred when the mount point has already been unavailable. + */ +public class FuseUnavailableMountException extends Exception { + public FuseUnavailableMountException(int mountId) { + super("AppFuse mount point " + mountId + " is unavailable"); + } +} diff --git a/core/jni/com_android_internal_os_FuseAppLoop.cpp b/core/jni/com_android_internal_os_FuseAppLoop.cpp index dd003eb364c5..e125150e8329 100644 --- a/core/jni/com_android_internal_os_FuseAppLoop.cpp +++ b/core/jni/com_android_internal_os_FuseAppLoop.cpp @@ -20,140 +20,214 @@ #include <stdlib.h> #include <sys/stat.h> +#include <map> +#include <memory> + #include <android_runtime/Log.h> #include <android-base/logging.h> #include <android-base/unique_fd.h> #include <jni.h> #include <libappfuse/FuseAppLoop.h> #include <nativehelper/ScopedLocalRef.h> +#include <nativehelper/ScopedPrimitiveArray.h> #include "core_jni_helpers.h" namespace android { - namespace { - constexpr const char* CLASS_NAME = "com/android/internal/os/FuseAppLoop"; jclass gFuseAppLoopClass; -jmethodID gOnGetSizeMethod; +jmethodID gOnCommandMethod; jmethodID gOnOpenMethod; -jmethodID gOnFsyncMethod; -jmethodID gOnReleaseMethod; -jmethodID gOnReadMethod; -jmethodID gOnWriteMethod; class Callback : public fuse::FuseAppLoopCallback { private: - static constexpr size_t kBufferSize = std::max(fuse::kFuseMaxWrite, fuse::kFuseMaxRead); - static_assert(kBufferSize <= INT32_MAX, "kBufferSize should be fit in int32_t."); - + typedef ScopedLocalRef<jbyteArray> LocalBytes; JNIEnv* const mEnv; jobject const mSelf; - ScopedLocalRef<jbyteArray> mJniBuffer; - - template <typename T> - T checkException(T result) const { - if (mEnv->ExceptionCheck()) { - LOGE_EX(mEnv, nullptr); - mEnv->ExceptionClear(); - return -EIO; - } - return result; - } + std::map<uint64_t, std::unique_ptr<LocalBytes>> mBuffers; public: Callback(JNIEnv* env, jobject self) : - mEnv(env), - mSelf(self), - mJniBuffer(env, nullptr) {} + mEnv(env), mSelf(self) {} - bool Init() { - mJniBuffer.reset(mEnv->NewByteArray(kBufferSize)); - return mJniBuffer.get(); + void OnLookup(uint64_t unique, uint64_t inode) override { + mEnv->CallVoidMethod(mSelf, gOnCommandMethod, FUSE_LOOKUP, unique, inode, 0, 0, nullptr); + CHECK(!mEnv->ExceptionCheck()); } - bool IsActive() override { - return true; + void OnGetAttr(uint64_t unique, uint64_t inode) override { + mEnv->CallVoidMethod(mSelf, gOnCommandMethod, FUSE_GETATTR, unique, inode, 0, 0, nullptr); + CHECK(!mEnv->ExceptionCheck()); } - int64_t OnGetSize(uint64_t inode) override { - return checkException(mEnv->CallLongMethod(mSelf, gOnGetSizeMethod, inode)); - } + void OnOpen(uint64_t unique, uint64_t inode) override { + const jbyteArray buffer = static_cast<jbyteArray>(mEnv->CallObjectMethod( + mSelf, gOnOpenMethod, unique, inode)); + CHECK(!mEnv->ExceptionCheck()); + if (buffer == nullptr) { + return; + } - int32_t OnOpen(uint64_t inode) override { - return checkException(mEnv->CallIntMethod(mSelf, gOnOpenMethod, inode)); + mBuffers.insert(std::make_pair(inode, std::unique_ptr<LocalBytes>( + new LocalBytes(mEnv, buffer)))); } - int32_t OnFsync(uint64_t inode) override { - return checkException(mEnv->CallIntMethod(mSelf, gOnFsyncMethod, inode)); + void OnFsync(uint64_t unique, uint64_t inode) override { + mEnv->CallVoidMethod(mSelf, gOnCommandMethod, FUSE_FSYNC, unique, inode, 0, 0, nullptr); + CHECK(!mEnv->ExceptionCheck()); } - int32_t OnRelease(uint64_t inode) override { - return checkException(mEnv->CallIntMethod(mSelf, gOnReleaseMethod, inode)); + void OnRelease(uint64_t unique, uint64_t inode) override { + mBuffers.erase(inode); + mEnv->CallVoidMethod(mSelf, gOnCommandMethod, FUSE_RELEASE, unique, inode, 0, 0, nullptr); + CHECK(!mEnv->ExceptionCheck()); } - int32_t OnRead(uint64_t inode, uint64_t offset, uint32_t size, void* buffer) override { - CHECK_LE(size, static_cast<uint32_t>(kBufferSize)); - const int32_t result = checkException(mEnv->CallIntMethod( - mSelf, gOnReadMethod, inode, offset, size, mJniBuffer.get())); - if (result <= 0) { - return result; - } - if (result > static_cast<int32_t>(size)) { - LOG(ERROR) << "Returned size is too large."; - return -EIO; - } + void OnRead(uint64_t unique, uint64_t inode, uint64_t offset, uint32_t size) override { + CHECK_LE(size, static_cast<uint32_t>(fuse::kFuseMaxRead)); - mEnv->GetByteArrayRegion(mJniBuffer.get(), 0, result, static_cast<jbyte*>(buffer)); - CHECK(!mEnv->ExceptionCheck()); + auto it = mBuffers.find(inode); + CHECK(it != mBuffers.end()); - return checkException(result); + mEnv->CallVoidMethod( + mSelf, gOnCommandMethod, FUSE_READ, unique, inode, offset, size, + it->second->get()); + CHECK(!mEnv->ExceptionCheck()); } - int32_t OnWrite(uint64_t inode, uint64_t offset, uint32_t size, const void* buffer) override { - CHECK_LE(size, static_cast<uint32_t>(kBufferSize)); + void OnWrite(uint64_t unique, uint64_t inode, uint64_t offset, uint32_t size, + const void* buffer) override { + CHECK_LE(size, static_cast<uint32_t>(fuse::kFuseMaxWrite)); + + auto it = mBuffers.find(inode); + CHECK(it != mBuffers.end()); + + jbyteArray const javaBuffer = it->second->get(); - mEnv->SetByteArrayRegion(mJniBuffer.get(), 0, size, static_cast<const jbyte*>(buffer)); + mEnv->SetByteArrayRegion(javaBuffer, 0, size, static_cast<const jbyte*>(buffer)); CHECK(!mEnv->ExceptionCheck()); - return checkException(mEnv->CallIntMethod( - mSelf, gOnWriteMethod, inode, offset, size, mJniBuffer.get())); + mEnv->CallVoidMethod( + mSelf, gOnCommandMethod, FUSE_WRITE, unique, inode, offset, size, javaBuffer); + CHECK(!mEnv->ExceptionCheck()); } }; -jboolean com_android_internal_os_FuseAppLoop_start_loop(JNIEnv* env, jobject self, jint jfd) { - base::unique_fd fd(jfd); +jlong com_android_internal_os_FuseAppLoop_new(JNIEnv* env, jobject self, jint jfd) { + return reinterpret_cast<jlong>(new fuse::FuseAppLoop(base::unique_fd(jfd))); +} + +void com_android_internal_os_FuseAppLoop_delete(JNIEnv* env, jobject self, jlong ptr) { + delete reinterpret_cast<fuse::FuseAppLoop*>(ptr); +} + +void com_android_internal_os_FuseAppLoop_start(JNIEnv* env, jobject self, jlong ptr) { Callback callback(env, self); + reinterpret_cast<fuse::FuseAppLoop*>(ptr)->Start(&callback); +} - if (!callback.Init()) { - LOG(ERROR) << "Failed to init callback"; - return JNI_FALSE; +void com_android_internal_os_FuseAppLoop_replySimple( + JNIEnv* env, jobject self, jlong ptr, jlong unique, jint result) { + if (!reinterpret_cast<fuse::FuseAppLoop*>(ptr)->ReplySimple(unique, result)) { + reinterpret_cast<fuse::FuseAppLoop*>(ptr)->Break(); } +} - return fuse::StartFuseAppLoop(fd.release(), &callback); +void com_android_internal_os_FuseAppLoop_replyOpen( + JNIEnv* env, jobject self, jlong ptr, jlong unique, jlong fh) { + if (!reinterpret_cast<fuse::FuseAppLoop*>(ptr)->ReplyOpen(unique, fh)) { + reinterpret_cast<fuse::FuseAppLoop*>(ptr)->Break(); + } +} + +void com_android_internal_os_FuseAppLoop_replyLookup( + JNIEnv* env, jobject self, jlong ptr, jlong unique, jlong inode, jint size) { + if (!reinterpret_cast<fuse::FuseAppLoop*>(ptr)->ReplyLookup(unique, inode, size)) { + reinterpret_cast<fuse::FuseAppLoop*>(ptr)->Break(); + } +} + +void com_android_internal_os_FuseAppLoop_replyGetAttr( + JNIEnv* env, jobject self, jlong ptr, jlong unique, jlong inode, jint size) { + if (!reinterpret_cast<fuse::FuseAppLoop*>(ptr)->ReplyGetAttr( + unique, inode, size, S_IFREG | 0777)) { + reinterpret_cast<fuse::FuseAppLoop*>(ptr)->Break(); + } +} + +void com_android_internal_os_FuseAppLoop_replyWrite( + JNIEnv* env, jobject self, jlong ptr, jlong unique, jint size) { + if (!reinterpret_cast<fuse::FuseAppLoop*>(ptr)->ReplyWrite(unique, size)) { + reinterpret_cast<fuse::FuseAppLoop*>(ptr)->Break(); + } +} + +void com_android_internal_os_FuseAppLoop_replyRead( + JNIEnv* env, jobject self, jlong ptr, jlong unique, jint size, jbyteArray data) { + ScopedByteArrayRO array(env, data); + CHECK(size >= 0); + CHECK(static_cast<size_t>(size) < array.size()); + if (!reinterpret_cast<fuse::FuseAppLoop*>(ptr)->ReplyRead(unique, size, array.get())) { + reinterpret_cast<fuse::FuseAppLoop*>(ptr)->Break(); + } } const JNINativeMethod methods[] = { { - "native_start_loop", - "(I)Z", - (void *) com_android_internal_os_FuseAppLoop_start_loop - } + "native_new", + "(I)J", + reinterpret_cast<void*>(com_android_internal_os_FuseAppLoop_new) + }, + { + "native_delete", + "(J)V", + reinterpret_cast<void*>(com_android_internal_os_FuseAppLoop_delete) + }, + { + "native_start", + "(J)V", + reinterpret_cast<void*>(com_android_internal_os_FuseAppLoop_start) + }, + { + "native_replySimple", + "(JJI)V", + reinterpret_cast<void*>(com_android_internal_os_FuseAppLoop_replySimple) + }, + { + "native_replyOpen", + "(JJJ)V", + reinterpret_cast<void*>(com_android_internal_os_FuseAppLoop_replyOpen) + }, + { + "native_replyLookup", + "(JJJJ)V", + reinterpret_cast<void*>(com_android_internal_os_FuseAppLoop_replyLookup) + }, + { + "native_replyGetAttr", + "(JJJJ)V", + reinterpret_cast<void*>(com_android_internal_os_FuseAppLoop_replyGetAttr) + }, + { + "native_replyRead", + "(JJI[B)V", + reinterpret_cast<void*>(com_android_internal_os_FuseAppLoop_replyRead) + }, + { + "native_replyWrite", + "(JJI)V", + reinterpret_cast<void*>(com_android_internal_os_FuseAppLoop_replyWrite) + }, }; - } // namespace int register_com_android_internal_os_FuseAppLoop(JNIEnv* env) { gFuseAppLoopClass = MakeGlobalRefOrDie(env, FindClassOrDie(env, CLASS_NAME)); - gOnGetSizeMethod = GetMethodIDOrDie(env, gFuseAppLoopClass, "onGetSize", "(J)J"); - gOnOpenMethod = GetMethodIDOrDie(env, gFuseAppLoopClass, "onOpen", "(J)I"); - gOnFsyncMethod = GetMethodIDOrDie(env, gFuseAppLoopClass, "onFsync", "(J)I"); - gOnReleaseMethod = GetMethodIDOrDie(env, gFuseAppLoopClass, "onRelease", "(J)I"); - gOnReadMethod = GetMethodIDOrDie(env, gFuseAppLoopClass, "onRead", "(JJI[B)I"); - gOnWriteMethod = GetMethodIDOrDie(env, gFuseAppLoopClass, "onWrite", "(JJI[B)I"); + gOnCommandMethod = GetMethodIDOrDie(env, gFuseAppLoopClass, "onCommand", "(IJJJI[B)V"); + gOnOpenMethod = GetMethodIDOrDie(env, gFuseAppLoopClass, "onOpen", "(JJ)[B"); RegisterMethodsOrDie(env, CLASS_NAME, methods, NELEM(methods)); return 0; } - } // namespace android diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 995f2c3b8356..a3b27052bf67 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -7226,6 +7226,10 @@ <!-- Whether to use single line for the preference title text. By default, preference title will be constrained to one line, so the default value of this attribute is true. --> <attr name="singleLineTitle" format="boolean" /> + <!-- Whether the space for the preference icon view will be reserved. By default, preference + icon view visibility will be set to GONE when there is no icon provided, so the default + value of this attribute is false. --> + <attr name="iconSpaceReserved" format="boolean" /> </declare-styleable> <!-- Base attributes available to CheckBoxPreference. --> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 876d44da416b..6e20208cc2d6 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2813,6 +2813,7 @@ <public name="iconTint" /> <public name="iconTintMode" /> <public name="maxAspectRatio"/> + <public name="iconSpaceReserved"/> </public-group> <public-group type="style" first-id="0x010302e0"> diff --git a/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java b/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java index ff98eb763f76..fbba6fffcd8e 100644 --- a/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java +++ b/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java @@ -264,7 +264,7 @@ public class StorageManagerIntegrationTest extends StorageManagerBaseTest { final MyThreadFactory factory = new MyThreadFactory(); int firstMountId; try (final ParcelFileDescriptor fd = mSm.openProxyFileDescriptor( - ParcelFileDescriptor.MODE_READ_ONLY, callback, factory)) { + ParcelFileDescriptor.MODE_READ_ONLY, callback, null, factory)) { assertNotSame(Thread.State.TERMINATED, factory.thread.getState()); firstMountId = mSm.getProxyFileDescriptorMountPointId(); assertNotSame(-1, firstMountId); @@ -276,7 +276,7 @@ public class StorageManagerIntegrationTest extends StorageManagerBaseTest { // StorageManager should mount another bridge on the next open request. try (final ParcelFileDescriptor fd = mSm.openProxyFileDescriptor( - ParcelFileDescriptor.MODE_WRITE_ONLY, callback, factory)) { + ParcelFileDescriptor.MODE_WRITE_ONLY, callback, null, factory)) { assertNotSame(Thread.State.TERMINATED, factory.thread.getState()); assertNotSame(firstMountId, mSm.getProxyFileDescriptorMountPointId()); } diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java index b78df34f5c3e..ff9f11dae4dc 100644 --- a/graphics/java/android/graphics/FontListParser.java +++ b/graphics/java/android/graphics/FontListParser.java @@ -189,9 +189,8 @@ public class FontListParser { skip(parser); } } - String fullFilename = "/system/fonts/" + - FILENAME_WHITESPACE_PATTERN.matcher(filename).replaceAll(""); - return new FontConfig.Font(fullFilename, index, + String sanitizedName = FILENAME_WHITESPACE_PATTERN.matcher(filename).replaceAll(""); + return new FontConfig.Font(sanitizedName, index, axes.toArray(new FontConfig.Axis[axes.size()]), weight, isItalic); } diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index 228d9500180a..8c3a2e8068ca 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -1003,21 +1003,22 @@ public class Typeface { Map<String, ByteBuffer> bufferForPath) { FontFamily fontFamily = new FontFamily(family.getLanguage(), family.getVariant()); for (FontConfig.Font font : family.getFonts()) { - ByteBuffer fontBuffer = bufferForPath.get(font.getFontName()); + String fullPathName = "/system/fonts/" + font.getFontName(); + ByteBuffer fontBuffer = bufferForPath.get(fullPathName); if (fontBuffer == null) { - try (FileInputStream file = new FileInputStream(font.getFontName())) { + try (FileInputStream file = new FileInputStream(fullPathName)) { FileChannel fileChannel = file.getChannel(); long fontSize = fileChannel.size(); fontBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fontSize); - bufferForPath.put(font.getFontName(), fontBuffer); + bufferForPath.put(fullPathName, fontBuffer); } catch (IOException e) { - Log.e(TAG, "Error mapping font file " + font.getFontName()); + Log.e(TAG, "Error mapping font file " + fullPathName); continue; } } if (!fontFamily.addFontFromBuffer(fontBuffer, font.getTtcIndex(), font.getAxes(), font.getWeight(), font.isItalic() ? Builder.ITALIC : Builder.NORMAL)) { - Log.e(TAG, "Error creating font " + font.getFontName() + "#" + font.getTtcIndex()); + Log.e(TAG, "Error creating font " + fullPathName + "#" + font.getTtcIndex()); } } fontFamily.freeze(); diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java index ce58a9c4b91e..77a82ec8cacd 100644 --- a/media/java/android/media/AudioAttributes.java +++ b/media/java/android/media/AudioAttributes.java @@ -912,6 +912,8 @@ public final class AudioAttributes implements Parcelable { return USAGE_UNKNOWN; } } + + // TODO remove, replaced by non-static API getVolumeControlStream() /** * Returns the stream type matching the given attributes for volume control. * Use this method to derive the stream type needed to configure the volume @@ -925,6 +927,7 @@ public final class AudioAttributes implements Parcelable { * the attributes, or {@link AudioManager#USE_DEFAULT_STREAM_TYPE} if there isn't a direct * match. Note that <code>USE_DEFAULT_STREAM_TYPE</code> is not a valid value * for {@link AudioManager#setStreamVolume(int, int, int)}. + * @deprecated use {@link #getVolumeControlStream()} */ public static int getVolumeControlStream(@NonNull AudioAttributes aa) { if (aa == null) { @@ -934,6 +937,24 @@ public final class AudioAttributes implements Parcelable { } /** + * Returns the stream type matching this {@code AudioAttributes} instance for volume control. + * Use this method to derive the stream type needed to configure the volume + * control slider in an {@link android.app.Activity} with + * {@link android.app.Activity#setVolumeControlStream(int)} for playback conducted with these + * attributes. + * <BR>Do not use this method to set the stream type on an audio player object + * (e.g. {@link AudioTrack}, {@link MediaPlayer}) as this is deprecated, + * use {@code AudioAttributes} instead. + * @return a valid stream type for {@code Activity} or stream volume control that matches + * the attributes, or {@link AudioManager#USE_DEFAULT_STREAM_TYPE} if there isn't a direct + * match. Note that {@code USE_DEFAULT_STREAM_TYPE} is not a valid value + * for {@link AudioManager#setStreamVolume(int, int, int)}. + */ + public int getVolumeControlStream() { + return toVolumeStreamType(true /*fromGetVolumeControlStream*/, this); + } + + /** * @hide * Only use to get which stream type should be used for volume control, NOT for audio playback * (all audio playback APIs are supposed to take AudioAttributes as input parameters) diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java index 6808b57c2642..7bf69c06ab5e 100644 --- a/media/java/android/media/tv/TvContract.java +++ b/media/java/android/media/tv/TvContract.java @@ -1321,9 +1321,7 @@ public final class TvContract { * * <p>Type: INTEGER (boolean) * @see Channels#COLUMN_TRANSIENT - * @hide */ - @SystemApi public static final String COLUMN_TRANSIENT = "transient"; /** @@ -2164,10 +2162,9 @@ public final class TvContract { * specified, this value is set to 0 (not transient) by default. * * <p>Type: INTEGER (boolean) - * @see Programs#COLUMN_TRANSIENT - * @hide + * @see PreviewPrograms#COLUMN_TRANSIENT + * @see WatchNextPrograms#COLUMN_TRANSIENT */ - @SystemApi public static final String COLUMN_TRANSIENT = "transient"; /** diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java index 187e35ac842c..f11a9cde9520 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java +++ b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java @@ -113,12 +113,14 @@ public final class RemotePrintDocument { } // Notify we are done. mState = STATE_UPDATED; + mDocumentInfo.updated = true; notifyUpdateCompleted(); } } } else { // We always notify after a write. mState = STATE_UPDATED; + mDocumentInfo.updated = true; notifyUpdateCompleted(); } runPendingCommand(); @@ -229,6 +231,7 @@ public final class RemotePrintDocument { mDocumentInfo, oldAttributes, attributes, preview, mCommandResultCallback); scheduleCommand(command); + mDocumentInfo.updated = false; mState = STATE_UPDATING; // If no layout in progress and we don't have all pages - schedule a write. } else if ((!(mCurrentCommand instanceof LayoutCommand) @@ -249,6 +252,7 @@ public final class RemotePrintDocument { mDocumentInfo.fileProvider, mCommandResultCallback); scheduleCommand(command); + mDocumentInfo.updated = false; mState = STATE_UPDATING; } else { willUpdate = false; @@ -396,7 +400,7 @@ public final class RemotePrintDocument { private void notifyUpdateFailed(CharSequence error) { if (DEBUG) { - Log.i(LOG_TAG, "[CALLING] onUpdateCompleted()"); + Log.i(LOG_TAG, "[CALLING] notifyUpdateFailed()"); } mUpdateCallbacks.onUpdateFailed(error); } diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java index f6df9953bf0f..4cce166db645 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java +++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java @@ -491,8 +491,6 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat setState(STATE_UPDATE_FAILED); - updateOptionsUi(); - mPrintedDocument.kill(message); } @@ -502,7 +500,6 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat && canUpdateDocument() && updateDocument(true)) { ensurePreviewUiShown(); setState(STATE_CONFIGURING); - updateOptionsUi(); } } @@ -579,7 +576,6 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat updatePrintPreviewController(document.changed); setState(STATE_CONFIGURING); - updateOptionsUi(); } break; } } @@ -600,8 +596,6 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat } setState(STATE_UPDATE_FAILED); - - updateOptionsUi(); } @Override @@ -734,7 +728,6 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat updateOptionsUi(); } else { setState(STATE_CREATE_FILE_FAILED); - updateOptionsUi(); // Calling finish here does not invoke lifecycle callbacks but we // update the print job in onPause if finishing, hence post a message. mDestinationSpinner.post(new Runnable() { @@ -958,12 +951,14 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat Log.i(LOG_TAG, "[state]" + state); } mState = state; + updateOptionsUi(); } } else { if (DEBUG) { Log.i(LOG_TAG, "[state]" + state); } mState = state; + updateOptionsUi(); } } @@ -1230,6 +1225,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat final boolean willUpdate = mPrintedDocument.update(mPrintJob.getAttributes(), pages, preview); + updateOptionsUi(); if (willUpdate && !mPrintedDocument.hasLaidOutPages()) { // When the update is done we update the print preview. @@ -1254,7 +1250,6 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat private void cancelPrint() { setState(STATE_PRINT_CANCELED); - updateOptionsUi(); mPrintedDocument.cancel(true); doFinish(); } @@ -1274,7 +1269,6 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat private void confirmPrint() { setState(STATE_PRINT_CONFIRMED); - updateOptionsUi(); addCurrentPrinterToHistory(); setUserPrinted(); @@ -1629,6 +1623,8 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat // Always update the summary. updateSummary(); + mDestinationSpinner.setEnabled(!isFinalState(mState)); + if (mState == STATE_PRINT_CONFIRMED || mState == STATE_PRINT_COMPLETED || mState == STATE_PRINT_CANCELED @@ -1636,9 +1632,6 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat || mState == STATE_CREATE_FILE_FAILED || mState == STATE_PRINTER_UNAVAILABLE || mState == STATE_UPDATE_SLOW) { - if (mState != STATE_PRINTER_UNAVAILABLE) { - mDestinationSpinner.setEnabled(false); - } disableOptionsUi(isFinalState(mState)); return; } @@ -1927,7 +1920,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat mPrintButton.setImageResource(R.drawable.ic_menu_savetopdf); mPrintButton.setContentDescription(getString(R.string.savetopdf_button)); } - if (!mPrintedDocument.getDocumentInfo().laidout + if (!mPrintedDocument.getDocumentInfo().updated ||(mRangeOptionsSpinner.getSelectedItemPosition() == 1 && (TextUtils.isEmpty(mPageRangeEditText.getText()) || hasErrors())) || (mRangeOptionsSpinner.getSelectedItemPosition() == 0 @@ -2048,7 +2041,6 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat updateDocument(false); } ensurePreviewUiShown(); - updateOptionsUi(); } } @@ -2058,7 +2050,6 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat mPrintedDocument.cancel(false); ensureErrorUiShown(getString(R.string.print_error_printer_unavailable), PrintErrorFragment.ACTION_NONE); - updateOptionsUi(); } } @@ -3038,7 +3029,6 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat if (mState == STATE_UPDATE_SLOW) { setState(STATE_UPDATE_SLOW); ensureProgressUiShown(); - updateOptionsUi(); return; } else if (mPosted) { @@ -3080,7 +3070,6 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat mPreviousState = mState; setState(STATE_UPDATE_SLOW); ensureProgressUiShown(); - updateOptionsUi(); } } diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index cc2dc1bb6a7d..f14d0d128e67 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -76,7 +76,7 @@ <!-- Status message of Wi-Fi when it is automatically connected by a network recommendation provider. [CHAR LIMIT=NONE] --> <string name="connected_via_network_scorer">Automatically connected via %1$s</string> <!-- Status message of Wi-Fi when it is automatically connected by a default network recommendation provider. [CHAR LIMIT=NONE] --> - <string name="connected_via_network_scorer_default">Automatically connected via Network Quality Scorer</string> + <string name="connected_via_network_scorer_default">Automatically connected via network rating provider</string> <!-- Status message of Wi-Fi when it is connected by Passpoint configuration. [CHAR LIMIT=NONE] --> <string name="connected_via_passpoint">Connected via %1$s</string> <!-- Status message of Wi-Fi when network has matching passpoint credentials. [CHAR LIMIT=NONE] --> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index bf17e387e321..0c0bdeb5b755 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1869,9 +1869,6 @@ <!-- Title of menu shown over picture-in-picture. Used for accessibility. --> <string name="pip_menu_title">Picture in picture menu</string> - <!-- User visible notification channel name for the PiP BTW notification. [CHAR LIMIT=NONE] --> - <string name="pip_notification_channel_name">Picture-in-picture</string> - <!-- PiP BTW notification title. [CHAR LIMIT=50] --> <string name="pip_notification_title"><xliff:g id="name" example="Google Maps">%s</xliff:g> is in picture-in-picture</string> diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java index ebda2e8a7f57..ec80745f8d26 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java @@ -45,14 +45,12 @@ import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import android.util.Log; -import android.util.Pair; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.WindowManager.LayoutParams; -import android.view.accessibility.AccessibilityManager; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; @@ -61,6 +59,7 @@ import com.android.systemui.Interpolators; import com.android.systemui.R; import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** @@ -128,8 +127,9 @@ public class PipMenuActivity extends Activity { break; case MESSAGE_UPDATE_ACTIONS: { final Bundle data = (Bundle) msg.obj; - setActions(data.getParcelable(EXTRA_STACK_BOUNDS), - ((ParceledListSlice) data.getParcelable(EXTRA_ACTIONS)).getList()); + final ParceledListSlice actions = data.getParcelable(EXTRA_ACTIONS); + setActions(data.getParcelable(EXTRA_STACK_BOUNDS), actions != null + ? actions.getList() : Collections.EMPTY_LIST); break; } case MESSAGE_UPDATE_DISMISS_FRACTION: { @@ -260,6 +260,7 @@ public class PipMenuActivity extends Activity { } notifyMenuVisibility(true); updateExpandButtonFromBounds(stackBounds, movementBounds); + setDecorViewVisibility(true); mMenuContainerAnimator = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA, mMenuContainer.getAlpha(), 1f); mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN); @@ -300,9 +301,7 @@ public class PipMenuActivity extends Activity { if (animationFinishedRunnable != null) { animationFinishedRunnable.run(); } - if (getSystemService(AccessibilityManager.class).isEnabled()) { - finish(); - } + setDecorViewVisibility(false); } }); mMenuContainerAnimator.addUpdateListener(mMenuBgUpdateListener); @@ -411,6 +410,7 @@ public class PipMenuActivity extends Activity { } private void updateDismissFraction(float fraction) { + setDecorViewVisibility(true); int alpha; if (mMenuVisible) { mMenuContainer.setAlpha(1-fraction); @@ -497,4 +497,16 @@ public class PipMenuActivity extends Activity { v.removeCallbacks(mFinishRunnable); v.postDelayed(mFinishRunnable, delay); } + + /** + * Sets the visibility of the root view of the window to disable drawing and touches for the + * activity. This differs from {@link Activity#setVisible(boolean)} in that it does not set + * the internal mVisibleFromClient state. + */ + private void setDecorViewVisibility(boolean visible) { + final View decorView = getWindow().getDecorView(); + if (decorView != null) { + decorView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipNotificationController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipNotificationController.java index bdd6b65026f0..9bf6d6e7610f 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipNotificationController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipNotificationController.java @@ -16,7 +16,6 @@ package com.android.systemui.pip.phone; -import static android.app.NotificationManager.IMPORTANCE_MIN; import static android.app.PendingIntent.FLAG_CANCEL_CURRENT; import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; @@ -24,7 +23,6 @@ import static android.provider.Settings.ACTION_PICTURE_IN_PICTURE_SETTINGS; import android.app.IActivityManager; import android.app.Notification; -import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.ComponentName; @@ -40,6 +38,7 @@ import android.util.Log; import com.android.systemui.R; import com.android.systemui.SystemUI; +import com.android.systemui.util.NotificationChannels; /** * Manages the BTW notification that shows whenever an activity enters or leaves picture-in-picture. @@ -47,8 +46,8 @@ import com.android.systemui.SystemUI; public class PipNotificationController { private static final String TAG = PipNotificationController.class.getSimpleName(); - private static final String CHANNEL_ID = PipNotificationController.class.getName(); - private static final int BTW_NOTIFICATION_ID = 0; + private static final String NOTIFICATION_TAG = PipNotificationController.class.getName(); + private static final int NOTIFICATION_ID = 0; private Context mContext; private IActivityManager mActivityManager; @@ -58,25 +57,25 @@ public class PipNotificationController { mContext = context; mActivityManager = activityManager; mNotificationManager = NotificationManager.from(context); - createNotificationChannel(); } public void onActivityPinned(String packageName) { // Clear any existing notification - mNotificationManager.cancel(CHANNEL_ID, BTW_NOTIFICATION_ID); + mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID); // Build a new notification - final Notification.Builder builder = new Notification.Builder(mContext, CHANNEL_ID) - .setLocalOnly(true) - .setOngoing(true) - .setSmallIcon(R.drawable.pip_notification_icon) - .setColor(mContext.getColor( - com.android.internal.R.color.system_notification_accent_color)); + final Notification.Builder builder = + new Notification.Builder(mContext, NotificationChannels.GENERAL) + .setLocalOnly(true) + .setOngoing(true) + .setSmallIcon(R.drawable.pip_notification_icon) + .setColor(mContext.getColor( + com.android.internal.R.color.system_notification_accent_color)); if (updateNotificationForApp(builder, packageName)) { SystemUI.overrideNotificationAppName(mContext, builder); // Show the new notification - mNotificationManager.notify(CHANNEL_ID, BTW_NOTIFICATION_ID, builder.build()); + mNotificationManager.notify(NOTIFICATION_TAG, NOTIFICATION_ID, builder.build()); } } @@ -85,26 +84,11 @@ public class PipNotificationController { if (topPipActivity != null) { onActivityPinned(topPipActivity.getPackageName()); } else { - mNotificationManager.cancel(CHANNEL_ID, BTW_NOTIFICATION_ID); + mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID); } } /** - * Create the notification channel for the PiP BTW notifications if necessary. - */ - private NotificationChannel createNotificationChannel() { - NotificationChannel channel = mNotificationManager.getNotificationChannel(CHANNEL_ID); - if (channel == null) { - channel = new NotificationChannel(CHANNEL_ID, - mContext.getString(R.string.pip_notification_channel_name), IMPORTANCE_MIN); - channel.enableLights(false); - channel.enableVibration(false); - mNotificationManager.createNotificationChannel(channel); - } - return channel; - } - - /** * Updates the notification builder with app-specific information, returning whether it was * successful. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index 3f7e340eff21..a5d7c57206be 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -417,7 +417,10 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav getHomeButton().setImageDrawable(mHomeDefaultIcon); } - final boolean showImeButton = ((hints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0); + // The Accessibility button always overrides the appearance of the IME switcher + final boolean showImeButton = + !mShowAccessibilityButton && ((hints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) + != 0); getImeSwitchButton().setVisibility(showImeButton ? View.VISIBLE : View.INVISIBLE); getImeSwitchButton().setImageDrawable(mImeIcon); @@ -545,8 +548,9 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav mShowAccessibilityButton = visible; mLongClickableAccessibilityButton = longClickable; if (visible) { - // Accessibility button overrides Menu button. + // Accessibility button overrides Menu and IME switcher buttons. setMenuVisibility(false, true); + getImeSwitchButton().setVisibility(View.INVISIBLE); } getAccessibilityButton().setVisibility(visible ? View.VISIBLE : View.INVISIBLE); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index bb6c8f2e1e73..53ec8c5048ad 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -127,7 +127,6 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, private boolean mVolumeVisible; private boolean mCurrentUserSetup; - private boolean mManagedProfileFocused = false; private boolean mManagedProfileIconVisible = false; private boolean mManagedProfileInQuietMode = false; @@ -439,45 +438,30 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, } } - private void profileChanged(int userId) { - UserInfo user = null; - if (userId == UserHandle.USER_CURRENT) { - try { - user = ActivityManager.getService().getCurrentUser(); - } catch (RemoteException e) { - // Ignore - } - } else { - user = mUserManager.getUserInfo(userId); - } - - mManagedProfileFocused = user != null && user.isManagedProfile(); - if (DEBUG) Log.v(TAG, "profileChanged: mManagedProfileFocused: " + mManagedProfileFocused); - // Actually update the icon later when transition starts. - } - private void updateManagedProfile() { - if (DEBUG) { - Log.v(TAG, "updateManagedProfile: mManagedProfileFocused: " - + mManagedProfileFocused); - } - final boolean showIcon; - if (mManagedProfileFocused && !mKeyguardMonitor.isShowing()) { - showIcon = true; - mIconController.setIcon(mSlotManagedProfile, - R.drawable.stat_sys_managed_profile_status, - mContext.getString(R.string.accessibility_managed_profile)); - } else if (mManagedProfileInQuietMode) { - showIcon = true; - mIconController.setIcon(mSlotManagedProfile, - R.drawable.stat_sys_managed_profile_status_off, - mContext.getString(R.string.accessibility_managed_profile)); - } else { - showIcon = false; - } - if (mManagedProfileIconVisible != showIcon) { - mIconController.setIconVisibility(mSlotManagedProfile, showIcon); - mManagedProfileIconVisible = showIcon; + try { + final boolean showIcon; + final int userId = ActivityManager.getService().getLastResumedActivityUserId(); + if (mUserManager.isManagedProfile(userId) && !mKeyguardMonitor.isShowing()) { + showIcon = true; + mIconController.setIcon(mSlotManagedProfile, + R.drawable.stat_sys_managed_profile_status, + mContext.getString(R.string.accessibility_managed_profile)); + } else if (mManagedProfileInQuietMode) { + showIcon = true; + mIconController.setIcon(mSlotManagedProfile, + R.drawable.stat_sys_managed_profile_status_off, + mContext.getString(R.string.accessibility_managed_profile)); + } else { + showIcon = false; + } + if (mManagedProfileIconVisible != showIcon) { + mIconController.setIconVisibility(mSlotManagedProfile, showIcon); + mManagedProfileIconVisible = showIcon; + } + } catch (RemoteException ex) { + Log.w(TAG, "updateManagedProfile: ", ex); + // ignore } } @@ -556,35 +540,16 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, new SynchronousUserSwitchObserver() { @Override public void onUserSwitching(int newUserId) throws RemoteException { - mHandler.post(new Runnable() { - @Override - public void run() { - mUserInfoController.reloadUserInfo(); - } - }); + mHandler.post(() -> mUserInfoController.reloadUserInfo()); } @Override public void onUserSwitchComplete(int newUserId) throws RemoteException { - mHandler.post(new Runnable() { - @Override - public void run() { - updateAlarm(); - profileChanged(newUserId); - updateQuietState(); - updateManagedProfile(); - updateForegroundInstantApps(); - } - }); - } - - @Override - public void onForegroundProfileSwitch(int newProfileId) { - mHandler.post(new Runnable() { - @Override - public void run() { - profileChanged(newProfileId); - } + mHandler.post(() -> { + updateAlarm(); + updateQuietState(); + updateManagedProfile(); + updateForegroundInstantApps(); }); } }; diff --git a/services/core/java/com/android/server/FontManagerService.java b/services/core/java/com/android/server/FontManagerService.java index 55a945a6fce1..f1726473c591 100644 --- a/services/core/java/com/android/server/FontManagerService.java +++ b/services/core/java/com/android/server/FontManagerService.java @@ -18,6 +18,7 @@ package com.android.server; import android.content.Context; import android.graphics.FontListParser; +import android.net.Uri; import android.os.ParcelFileDescriptor; import android.text.FontConfig; import android.util.Slog; @@ -34,6 +35,7 @@ import java.io.IOException; public class FontManagerService extends IFontManager.Stub { private static final String TAG = "FontManagerService"; private static final String FONTS_CONFIG = "/system/etc/fonts.xml"; + private static final String SYSTEM_FONT_DIR = "/system/fonts/"; @GuardedBy("mLock") private FontConfig mConfig; @@ -63,28 +65,22 @@ public class FontManagerService extends IFontManager.Stub { public FontConfig getSystemFonts() { synchronized (mLock) { if (mConfig != null) { - return new FontConfig(mConfig); + return mConfig; } - FontConfig config = loadFromSystem(); - if (config == null) { + mConfig = loadFromSystem(); + if (mConfig == null) { return null; } - for (FontConfig.Family family : config.getFamilies()) { + for (FontConfig.Family family : mConfig.getFamilies()) { for (FontConfig.Font font : family.getFonts()) { - File fontFile = new File(font.getFontName()); - try { - font.setFd(ParcelFileDescriptor.open( - fontFile, ParcelFileDescriptor.MODE_READ_ONLY)); - } catch (IOException e) { - Slog.e(TAG, "Error opening font file " + font.getFontName(), e); - } + File fontFile = new File(SYSTEM_FONT_DIR, font.getFontName()); + font.setUri(Uri.fromFile(fontFile)); } } - mConfig = config; - return new FontConfig(mConfig); + return mConfig; } } diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 457c5f8e1f65..c68000ab00d4 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -98,6 +98,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IMediaContainerService; import com.android.internal.os.AppFuseMount; import com.android.internal.os.FuseAppLoop; +import com.android.internal.os.FuseUnavailableMountException; import com.android.internal.os.SomeArgs; import com.android.internal.os.Zygote; import com.android.internal.util.ArrayUtils; @@ -3007,32 +3008,36 @@ class StorageManagerService extends IStorageManager.Stub } } - private ParcelFileDescriptor mountAppFuse(int uid, int mountId) - throws NativeDaemonConnectorException { - final NativeDaemonEvent event = StorageManagerService.this.mConnector.execute( - "appfuse", "mount", uid, Process.myPid(), mountId); - if (event.getFileDescriptors() == null || - event.getFileDescriptors().length == 0) { - throw new NativeDaemonConnectorException("Cannot obtain device FD"); + class AppFuseMountScope extends AppFuseBridge.MountScope { + boolean opened = false; + + public AppFuseMountScope(int uid, int pid, int mountId) { + super(uid, pid, mountId); } - return new ParcelFileDescriptor(event.getFileDescriptors()[0]); - } - class AppFuseMountScope extends AppFuseBridge.MountScope { - public AppFuseMountScope(int uid, int pid, int mountId) - throws NativeDaemonConnectorException { - super(uid, pid, mountId, mountAppFuse(uid, mountId)); + @Override + public ParcelFileDescriptor open() throws NativeDaemonConnectorException { + final NativeDaemonEvent event = StorageManagerService.this.mConnector.execute( + "appfuse", "mount", uid, Process.myPid(), mountId); + opened = true; + if (event.getFileDescriptors() == null || + event.getFileDescriptors().length == 0) { + throw new NativeDaemonConnectorException("Cannot obtain device FD"); + } + return new ParcelFileDescriptor(event.getFileDescriptors()[0]); } @Override public void close() throws Exception { - super.close(); - mConnector.execute("appfuse", "unmount", uid, Process.myPid(), mountId); + if (opened) { + mConnector.execute("appfuse", "unmount", uid, Process.myPid(), mountId); + opened = false; + } } } @Override - public AppFuseMount mountProxyFileDescriptorBridge() throws RemoteException { + public @Nullable AppFuseMount mountProxyFileDescriptorBridge() { Slog.v(TAG, "mountProxyFileDescriptorBridge"); final int uid = Binder.getCallingUid(); final int pid = Binder.getCallingPid(); @@ -3049,12 +3054,12 @@ class StorageManagerService extends IStorageManager.Stub final int name = mNextAppFuseName++; try { return new AppFuseMount( - name, - mAppFuseBridge.addBridge(new AppFuseMountScope(uid, pid, name))); - } catch (AppFuseBridge.BridgeException e) { + name, mAppFuseBridge.addBridge(new AppFuseMountScope(uid, pid, name))); + } catch (FuseUnavailableMountException e) { if (newlyCreated) { // If newly created bridge fails, it's a real error. - throw new RemoteException(e.getMessage()); + Slog.e(TAG, "", e); + return null; } // It seems the thread of mAppFuseBridge has already been terminated. mAppFuseBridge = null; @@ -3067,19 +3072,21 @@ class StorageManagerService extends IStorageManager.Stub } @Override - public ParcelFileDescriptor openProxyFileDescriptor(int mountId, int fileId, int mode) - throws RemoteException { - Slog.v(TAG, "mountProxyFileDescriptorBridge"); + public @Nullable ParcelFileDescriptor openProxyFileDescriptor( + int mountId, int fileId, int mode) { + Slog.v(TAG, "mountProxyFileDescriptor"); final int pid = Binder.getCallingPid(); try { synchronized (mAppFuseLock) { if (mAppFuseBridge == null) { - throw new RemoteException("Cannot find mount point"); + Slog.e(TAG, "FuseBridge has not been created"); + return null; } return mAppFuseBridge.openFile(pid, mountId, fileId, mode); } - } catch (FileNotFoundException | SecurityException | InterruptedException error) { - throw new RemoteException(error.getMessage()); + } catch (FuseUnavailableMountException | InterruptedException error) { + Slog.v(TAG, "The mount point has already been invalid", error); + return null; } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index ce9a904fd6af..8cb0eee07b3d 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -3212,18 +3212,18 @@ public class ActivityManagerService extends IActivityManager.Stub } } - mWindowManager.setFocusedApp(r.appToken, true); - - applyUpdateLockStateLocked(r); - applyUpdateVrModeLocked(r); if (mLastResumedActivity != null && r.userId != mLastResumedActivity.userId) { mHandler.removeMessages(FOREGROUND_PROFILE_CHANGED_MSG); mHandler.obtainMessage( FOREGROUND_PROFILE_CHANGED_MSG, r.userId, 0).sendToTarget(); } - mLastResumedActivity = r; + mWindowManager.setFocusedApp(r.appToken, true); + + applyUpdateLockStateLocked(r); + applyUpdateVrModeLocked(r); + EventLogTags.writeAmSetResumedActivity( r == null ? -1 : r.userId, r == null ? "NULL" : r.shortComponentName, @@ -3929,8 +3929,13 @@ public class ActivityManagerService extends IActivityManager.Stub if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) { // Debuggable apps may include a wrapper script with their library directory. String wrapperFileName = app.info.nativeLibraryDir + "/wrap.sh"; - if (new File(wrapperFileName).exists()) { - invokeWith = "/system/bin/logwrapper " + wrapperFileName; + StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); + try { + if (new File(wrapperFileName).exists()) { + invokeWith = "/system/bin/logwrapper " + wrapperFileName; + } + } finally { + StrictMode.setThreadPolicy(oldPolicy); } } @@ -23594,6 +23599,21 @@ public class ActivityManagerService extends IActivityManager.Stub } } + /** + * Return the user id of the last resumed activity. + */ + @Override + public @UserIdInt int getLastResumedActivityUserId() { + enforceCallingPermission( + permission.INTERACT_ACROSS_USERS_FULL, "getLastResumedActivityUserId()"); + synchronized (this) { + if (mLastResumedActivity == null) { + return mUserController.getCurrentUserIdLocked(); + } + return mLastResumedActivity.userId; + } + } + private final class SleepTokenImpl extends SleepToken { private final String mTag; private final long mAcquireTime; diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 93e30ffabd9d..b165d34b147b 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -75,6 +75,9 @@ import static android.net.wifi.WifiManager.EXTRA_CHANGE_REASON; import static android.net.wifi.WifiManager.EXTRA_NETWORK_INFO; import static android.net.wifi.WifiManager.EXTRA_WIFI_CONFIGURATION; import static android.net.wifi.WifiManager.EXTRA_WIFI_INFO; +import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED; +import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEFAULT; +import static android.telephony.CarrierConfigManager.DATA_CYCLE_THRESHOLD_DISABLED; import static android.text.format.DateUtils.DAY_IN_MILLIS; import static com.android.internal.util.ArrayUtils.appendInt; @@ -141,6 +144,7 @@ import android.os.IDeviceIdleController; import android.os.INetworkManagementService; import android.os.Message; import android.os.MessageQueue.IdleHandler; +import android.os.PersistableBundle; import android.os.PowerManager; import android.os.PowerManagerInternal; import android.os.Process; @@ -153,6 +157,7 @@ import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; +import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.text.TextUtils; @@ -174,6 +179,7 @@ import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.notification.SystemNotificationChannels; +import com.android.internal.telephony.PhoneConstants; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.IndentingPrintWriter; @@ -205,6 +211,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Calendar; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -321,6 +328,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private UsageStatsManagerInternal mUsageStats; private final TrustedTime mTime; private final UserManager mUserManager; + private final CarrierConfigManager mCarrierConfigManager; private IConnectivityManager mConnManager; private INotificationManager mNotifManager; @@ -472,6 +480,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { Context.DEVICE_IDLE_CONTROLLER)); mTime = checkNotNull(time, "missing TrustedTime"); mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); + mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class); mIPm = pm; HandlerThread thread = new HandlerThread(TAG); @@ -756,6 +765,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { WifiManager.NETWORK_STATE_CHANGED_ACTION); mContext.registerReceiver(mWifiStateReceiver, wifiStateFilter, null, mHandler); + // listen for carrier config changes to update data cycle information + final IntentFilter carrierConfigFilter = new IntentFilter( + ACTION_CARRIER_CONFIG_CHANGED); + mContext.registerReceiver(mCarrierConfigReceiver, carrierConfigFilter, null, mHandler); + mUsageStats.addAppIdleStateChangeListener(new AppIdleStateChangeListener()); // tell systemReady() that the service has been initialized initCompleteSignal.countDown(); @@ -1292,6 +1306,213 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { }; /** + * Update mobile policies with data cycle information from {@link CarrierConfigManager} + * if necessary. + * + * @param subId that has its associated NetworkPolicy updated if necessary + * @return if any policies were updated + */ + private boolean maybeUpdateMobilePolicyCycleNL(int subId) { + if (LOGV) Slog.v(TAG, "maybeUpdateMobilePolicyCycleNL()"); + final PersistableBundle config = mCarrierConfigManager.getConfigForSubId(subId); + + if (config == null) { + return false; + } + + boolean policyUpdated = false; + final String subscriberId = TelephonyManager.from(mContext).getSubscriberId(subId); + + // find and update the mobile NetworkPolicy for this subscriber id + final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE, + TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true); + for (int i = mNetworkPolicy.size() - 1; i >= 0; i--) { + final NetworkTemplate template = mNetworkPolicy.keyAt(i); + if (template.matches(probeIdent)) { + NetworkPolicy policy = mNetworkPolicy.valueAt(i); + + // only update the policy if the user didn't change any of the defaults. + if (!policy.inferred) { + // TODO: inferred could be split, so that if a user changes their data limit or + // warning, it doesn't prevent their cycle date from being updated. + if (LOGD) Slog.v(TAG, "Didn't update NetworkPolicy because policy.inferred"); + continue; + } + + final int cycleDay = getCycleDayFromCarrierConfig(config, policy.cycleDay); + final long warningBytes = getWarningBytesFromCarrierConfig(config, + policy.warningBytes); + final long limitBytes = getLimitBytesFromCarrierConfig(config, + policy.limitBytes); + + if (policy.cycleDay == cycleDay && + policy.warningBytes == warningBytes && + policy.limitBytes == limitBytes) { + continue; + } + + policyUpdated = true; + policy.cycleDay = cycleDay; + policy.warningBytes = warningBytes; + policy.limitBytes = limitBytes; + + if (LOGD) { + Slog.d(TAG, "Updated NetworkPolicy " + policy + " which matches subscriber " + + NetworkIdentity.scrubSubscriberId(subscriberId) + + " from CarrierConfigManager"); + } + } + } + + return policyUpdated; + } + + /** + * Returns the cycle day that should be used for a mobile NetworkPolicy. + * + * It attempts to get an appropriate cycle day from the passed in CarrierConfig. If it's unable + * to do so, it returns the fallback value. + * + * @param config The CarrierConfig to read the value from. + * @param fallbackCycleDay to return if the CarrierConfig can't be read. + * @return cycleDay to use in the mobile NetworkPolicy. + */ + @VisibleForTesting + public int getCycleDayFromCarrierConfig(@Nullable PersistableBundle config, + int fallbackCycleDay) { + if (config == null) { + return fallbackCycleDay; + } + int cycleDay = + config.getInt(CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT); + if (cycleDay == DATA_CYCLE_USE_PLATFORM_DEFAULT) { + return fallbackCycleDay; + } + // validate cycleDay value + final Calendar cal = Calendar.getInstance(); + if (cycleDay < cal.getMinimum(Calendar.DAY_OF_MONTH) || + cycleDay > cal.getMaximum(Calendar.DAY_OF_MONTH)) { + Slog.e(TAG, "Invalid date in " + + "CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT: " + cycleDay); + return fallbackCycleDay; + } + return cycleDay; + } + + /** + * Returns the warning bytes that should be used for a mobile NetworkPolicy. + * + * It attempts to get an appropriate value from the passed in CarrierConfig. If it's unable + * to do so, it returns the fallback value. + * + * @param config The CarrierConfig to read the value from. + * @param fallbackWarningBytes to return if the CarrierConfig can't be read. + * @return warningBytes to use in the mobile NetworkPolicy. + */ + @VisibleForTesting + public long getWarningBytesFromCarrierConfig(@Nullable PersistableBundle config, + long fallbackWarningBytes) { + if (config == null) { + return fallbackWarningBytes; + } + long warningBytes = + config.getLong(CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG); + + if (warningBytes == DATA_CYCLE_THRESHOLD_DISABLED) { + return WARNING_DISABLED; + } else if (warningBytes == DATA_CYCLE_USE_PLATFORM_DEFAULT) { + return getPlatformDefaultWarningBytes(); + } else if (warningBytes < 0) { + Slog.e(TAG, "Invalid value in " + + "CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG; expected a " + + "non-negative value but got: " + warningBytes); + return fallbackWarningBytes; + } + + return warningBytes; + } + + /** + * Returns the limit bytes that should be used for a mobile NetworkPolicy. + * + * It attempts to get an appropriate value from the passed in CarrierConfig. If it's unable + * to do so, it returns the fallback value. + * + * @param config The CarrierConfig to read the value from. + * @param fallbackLimitBytes to return if the CarrierConfig can't be read. + * @return limitBytes to use in the mobile NetworkPolicy. + */ + @VisibleForTesting + public long getLimitBytesFromCarrierConfig(@Nullable PersistableBundle config, + long fallbackLimitBytes) { + if (config == null) { + return fallbackLimitBytes; + } + long limitBytes = + config.getLong(CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG); + + if (limitBytes == DATA_CYCLE_THRESHOLD_DISABLED) { + return LIMIT_DISABLED; + } else if (limitBytes == DATA_CYCLE_USE_PLATFORM_DEFAULT) { + return getPlatformDefaultLimitBytes(); + } else if (limitBytes < 0) { + Slog.e(TAG, "Invalid value in " + + "CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG; expected a " + + "non-negative value but got: " + limitBytes); + return fallbackLimitBytes; + } + return limitBytes; + } + + /** + * Receiver that watches for {@link CarrierConfigManager} to be changed. + */ + private BroadcastReceiver mCarrierConfigReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + // No need to do a permission check, because the ACTION_CARRIER_CONFIG_CHANGED + // broadcast is protected and can't be spoofed. Runs on a background handler thread. + + if (!intent.hasExtra(PhoneConstants.SUBSCRIPTION_KEY)) { + return; + } + final int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY, -1); + final TelephonyManager tele = TelephonyManager.from(mContext); + final String subscriberId = tele.getSubscriberId(subId); + + maybeRefreshTrustedTime(); + synchronized (mUidRulesFirstLock) { + synchronized (mNetworkPoliciesSecondLock) { + final boolean added = ensureActiveMobilePolicyNL(subId, subscriberId); + if (added) return; + final boolean updated = maybeUpdateMobilePolicyCycleNL(subId); + if (!updated) return; + // update network and notification rules, as the data cycle changed and it's + // possible that we should be triggering warnings/limits now + handleNetworkPoliciesUpdateAL(true); + } + } + } + }; + + /** + * Handles all tasks that need to be run after a new network policy has been set, or an existing + * one has been updated. + * + * @param shouldNormalizePolicies true iff network policies need to be normalized after the + * update. + */ + void handleNetworkPoliciesUpdateAL(boolean shouldNormalizePolicies) { + if (shouldNormalizePolicies) { + normalizePoliciesNL(); + } + updateNetworkEnabledNL(); + updateNetworkRulesNL(); + updateNotificationsNL(); + writePolicyAL(); + } + + /** * Proactively control network data connections when they exceed * {@link NetworkPolicy#limitBytes}. */ @@ -1516,11 +1737,19 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final int[] subIds = sub.getActiveSubscriptionIdList(); for (int subId : subIds) { final String subscriberId = tele.getSubscriberId(subId); - ensureActiveMobilePolicyNL(subscriberId); + ensureActiveMobilePolicyNL(subId, subscriberId); } } - private void ensureActiveMobilePolicyNL(String subscriberId) { + /** + * Once any {@link #mNetworkPolicy} are loaded from disk, ensure that we + * have at least a default mobile policy defined. + * + * @param subId to build a default policy for + * @param subscriberId that we check for an existing policy + * @return true if a mobile network policy was added, or false one already existed. + */ + private boolean ensureActiveMobilePolicyNL(int subId, String subscriberId) { // Poke around to see if we already have a policy final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true); @@ -1531,33 +1760,51 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { Slog.d(TAG, "Found template " + template + " which matches subscriber " + NetworkIdentity.scrubSubscriberId(subscriberId)); } - return; + return false; } } Slog.i(TAG, "No policy for subscriber " + NetworkIdentity.scrubSubscriberId(subscriberId) + "; generating default policy"); + final NetworkPolicy policy = buildDefaultMobilePolicy(subId, subscriberId); + addNetworkPolicyNL(policy); + return true; + } - // Build default mobile policy, and assume usage cycle starts today + private long getPlatformDefaultWarningBytes() { final int dataWarningConfig = mContext.getResources().getInteger( com.android.internal.R.integer.config_networkPolicyDefaultWarning); - final long warningBytes; if (dataWarningConfig == WARNING_DISABLED) { - warningBytes = WARNING_DISABLED; + return WARNING_DISABLED; } else { - warningBytes = dataWarningConfig * MB_IN_BYTES; + return dataWarningConfig * MB_IN_BYTES; } + } + + private long getPlatformDefaultLimitBytes() { + return LIMIT_DISABLED; + } + @VisibleForTesting + public NetworkPolicy buildDefaultMobilePolicy(int subId, String subscriberId) { + PersistableBundle config = mCarrierConfigManager.getConfigForSubId(subId); + + // assume usage cycle starts today final Time time = new Time(); time.setToNow(); - final int cycleDay = time.monthDay; final String cycleTimezone = time.timezone; + final int cycleDay = getCycleDayFromCarrierConfig(config, time.monthDay); + final long warningBytes = getWarningBytesFromCarrierConfig(config, + getPlatformDefaultWarningBytes()); + final long limitBytes = getLimitBytesFromCarrierConfig(config, + getPlatformDefaultLimitBytes()); + final NetworkTemplate template = buildTemplateMobileAll(subscriberId); final NetworkPolicy policy = new NetworkPolicy(template, cycleDay, cycleTimezone, - warningBytes, LIMIT_DISABLED, SNOOZE_NEVER, SNOOZE_NEVER, true, true); - addNetworkPolicyNL(policy); + warningBytes, limitBytes, SNOOZE_NEVER, SNOOZE_NEVER, true, true); + return policy; } private void readPolicyAL() { @@ -2025,10 +2272,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { synchronized (mUidRulesFirstLock) { synchronized (mNetworkPoliciesSecondLock) { normalizePoliciesNL(policies); - updateNetworkEnabledNL(); - updateNetworkRulesNL(); - updateNotificationsNL(); - writePolicyAL(); + handleNetworkPoliciesUpdateAL(false); } } } finally { @@ -2125,11 +2369,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { throw new IllegalArgumentException("unexpected type"); } - normalizePoliciesNL(); - updateNetworkEnabledNL(); - updateNetworkRulesNL(); - updateNotificationsNL(); - writePolicyAL(); + handleNetworkPoliciesUpdateAL(true); } } } @@ -2360,11 +2600,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mNetworkPolicy.valueAt(i).clearSnooze(); } - normalizePoliciesNL(); - updateNetworkEnabledNL(); - updateNetworkRulesNL(); - updateNotificationsNL(); - writePolicyAL(); + handleNetworkPoliciesUpdateAL(true); fout.println("Cleared snooze timestamps"); return; diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index ede5a5e8e337..7468b956af4e 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -90,6 +90,7 @@ import android.media.AudioManagerInternal; import android.media.IRingtonePlayer; import android.net.Uri; import android.os.Binder; +import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.Handler; @@ -3126,8 +3127,9 @@ public class NotificationManagerService extends SystemService { + ", incomingUserId=" + incomingUserId + ", notificationUid=" + notificationUid + ", notification=" + notification; - // STOPSHIP TODO: should throw instead of logging. - // throw new IllegalArgumentException(noChannelStr); + if (Build.IS_DEBUGGABLE) { + throw new IllegalArgumentException(noChannelStr); + } Log.e(TAG, noChannelStr); return; } diff --git a/services/core/java/com/android/server/storage/AppFuseBridge.java b/services/core/java/com/android/server/storage/AppFuseBridge.java index 904d9159e578..6a0b6489f470 100644 --- a/services/core/java/com/android/server/storage/AppFuseBridge.java +++ b/services/core/java/com/android/server/storage/AppFuseBridge.java @@ -21,7 +21,9 @@ import android.system.ErrnoException; import android.system.Os; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; +import com.android.internal.os.FuseUnavailableMountException; import com.android.internal.util.Preconditions; +import com.android.server.NativeDaemonConnectorException; import libcore.io.IoUtils; import java.io.File; import java.io.FileNotFoundException; @@ -54,17 +56,17 @@ public class AppFuseBridge implements Runnable { } public ParcelFileDescriptor addBridge(MountScope mountScope) - throws BridgeException { + throws FuseUnavailableMountException, NativeDaemonConnectorException { try { synchronized (this) { Preconditions.checkArgument(mScopes.indexOfKey(mountScope.mountId) < 0); if (mNativeLoop == 0) { - throw new BridgeException("The thread has already been terminated"); + throw new FuseUnavailableMountException(mountScope.mountId); } final int fd = native_add_bridge( - mNativeLoop, mountScope.mountId, mountScope.deviceFd.detachFd()); + mNativeLoop, mountScope.mountId, mountScope.open().detachFd()); if (fd == -1) { - throw new BridgeException("Failed to invoke native_add_bridge"); + throw new FuseUnavailableMountException(mountScope.mountId); } final ParcelFileDescriptor result = ParcelFileDescriptor.adoptFd(fd); mScopes.put(mountScope.mountId, mountScope); @@ -86,12 +88,12 @@ public class AppFuseBridge implements Runnable { } public ParcelFileDescriptor openFile(int pid, int mountId, int fileId, int mode) - throws FileNotFoundException, SecurityException, InterruptedException { + throws FuseUnavailableMountException, InterruptedException { final MountScope scope; synchronized (this) { scope = mScopes.get(mountId); if (scope == null) { - throw new FileNotFoundException("Cannot find mount point"); + throw new FuseUnavailableMountException(mountId); } } if (scope.pid != pid) { @@ -99,17 +101,14 @@ public class AppFuseBridge implements Runnable { } final boolean result = scope.waitForMount(); if (result == false) { - throw new FileNotFoundException("Mount failed"); + throw new FuseUnavailableMountException(mountId); } try { - if (Os.stat(scope.mountPoint.getPath()).st_ino != 1) { - throw new FileNotFoundException("Could not find bridge mount point."); - } - } catch (ErrnoException e) { - throw new FileNotFoundException( - "Failed to stat mount point: " + scope.mountPoint.getParent()); + return ParcelFileDescriptor.open( + new File(scope.mountPoint, String.valueOf(fileId)), mode); + } catch (FileNotFoundException error) { + throw new FuseUnavailableMountException(mountId); } - return ParcelFileDescriptor.open(new File(scope.mountPoint, String.valueOf(fileId)), mode); } // Used by com_android_server_storage_AppFuse.cpp. @@ -130,20 +129,18 @@ public class AppFuseBridge implements Runnable { } } - public static class MountScope implements AutoCloseable { + public static abstract class MountScope implements AutoCloseable { public final int uid; public final int pid; public final int mountId; - public final ParcelFileDescriptor deviceFd; public final File mountPoint; private final CountDownLatch mMounted = new CountDownLatch(1); private boolean mMountResult = false; - public MountScope(int uid, int pid, int mountId, ParcelFileDescriptor deviceFd) { + public MountScope(int uid, int pid, int mountId) { this.uid = uid; this.pid = pid; this.mountId = mountId; - this.deviceFd = deviceFd; this.mountPoint = new File(String.format(APPFUSE_MOUNT_NAME_TEMPLATE, uid, mountId)); } @@ -161,16 +158,7 @@ public class AppFuseBridge implements Runnable { return mMountResult; } - @Override - public void close() throws Exception { - deviceFd.close(); - } - } - - public static class BridgeException extends Exception { - public BridgeException(String message) { - super(message); - } + public abstract ParcelFileDescriptor open() throws NativeDaemonConnectorException; } private native long native_new(); diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java index 29c6f89e5fa0..dbba72770656 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java @@ -19,6 +19,7 @@ package com.android.server; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.NetworkPolicy.LIMIT_DISABLED; +import static android.net.NetworkPolicy.SNOOZE_NEVER; import static android.net.NetworkPolicy.WARNING_DISABLED; import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND; import static android.net.NetworkPolicyManager.POLICY_NONE; @@ -26,8 +27,15 @@ import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND; import static android.net.NetworkPolicyManager.computeLastCycleBoundary; import static android.net.NetworkPolicyManager.computeNextCycleBoundary; import static android.net.NetworkPolicyManager.uidPoliciesToString; +import static android.net.NetworkTemplate.buildTemplateMobileAll; import static android.net.TrafficStats.KB_IN_BYTES; import static android.net.TrafficStats.MB_IN_BYTES; +import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED; +import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEFAULT; +import static android.telephony.CarrierConfigManager.DATA_CYCLE_THRESHOLD_DISABLED; +import static android.telephony.CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG; +import static android.telephony.CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG; +import static android.telephony.CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT; import static android.text.format.DateUtils.DAY_IN_MILLIS; import static android.text.format.DateUtils.MINUTE_IN_MILLIS; import static android.text.format.Time.TIMEZONE_UTC; @@ -94,18 +102,24 @@ import android.net.NetworkStats; import android.net.NetworkTemplate; import android.os.Binder; import android.os.INetworkManagementService; +import android.os.PersistableBundle; import android.os.PowerManagerInternal; import android.os.PowerSaveState; +import android.os.RemoteException; import android.os.UserHandle; import android.support.test.InstrumentationRegistry; import android.support.test.filters.MediumTest; import android.support.test.runner.AndroidJUnit4; +import android.telephony.CarrierConfigManager; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; import android.text.TextUtils; import android.text.format.Time; import android.util.Log; import android.util.TrustedTime; import com.android.internal.util.IndentingPrintWriter; +import com.android.internal.telephony.PhoneConstants; import com.android.internal.util.test.BroadcastInterceptingContext; import com.android.internal.util.test.BroadcastInterceptingContext.FutureIntent; import com.android.server.net.NetworkPolicyManagerInternal; @@ -143,6 +157,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.Arrays; +import java.util.Calendar; import java.util.LinkedHashSet; import java.util.List; import java.util.concurrent.CountDownLatch; @@ -200,6 +215,9 @@ public class NetworkPolicyManagerServiceTest { private @Mock INotificationManager mNotifManager; private @Mock PackageManager mPackageManager; private @Mock IPackageManager mIpm; + private @Mock SubscriptionManager mSubscriptionManager; + private @Mock CarrierConfigManager mCarrierConfigManager; + private @Mock TelephonyManager mTelephonyManager; private static ActivityManagerInternal mActivityManagerInternal; @@ -214,6 +232,12 @@ public class NetworkPolicyManagerServiceTest { private long mElapsedRealtime; private static final int USER_ID = 0; + private static final int FAKE_SUB_ID = 3737373; + private static final String FAKE_SUBSCRIBER_ID = "FAKE_SUB_ID"; + private static final int DEFAULT_CYCLE_DAY = 1; + private static final int INVALID_CARRIER_CONFIG_VALUE = -9999; + private long mDefaultWarningBytes; // filled in with the actual default before tests are run + private long mDefaultLimitBytes; // filled in with the actual default before tests are run private static final int APP_ID_A = android.os.Process.FIRST_APPLICATION_UID + 4; private static final int APP_ID_B = android.os.Process.FIRST_APPLICATION_UID + 8; @@ -235,6 +259,8 @@ public class NetworkPolicyManagerServiceTest { @BeforeClass public static void registerLocalServices() { + final PowerManagerInternal powerManager = addLocalServiceMock(PowerManagerInternal.class); + when(powerManager.getLowPowerState(anyInt())).thenReturn(mock(PowerSaveState.class)); addLocalServiceMock(DeviceIdleController.LocalService.class); final UsageStatsManagerInternal usageStats = addLocalServiceMock(UsageStatsManagerInternal.class); @@ -255,7 +281,8 @@ public class NetworkPolicyManagerServiceTest { setCurrentTimeMillis(TEST_START); - // intercept various broadcasts, and pretend that uids have packages + // Intercept various broadcasts, and pretend that uids have packages. + // Also return mock service instances for a few critical services. mServiceContext = new BroadcastInterceptingContext(context) { @Override public PackageManager getPackageManager() { @@ -266,6 +293,20 @@ public class NetworkPolicyManagerServiceTest { public void startActivity(Intent intent) { // ignored } + + @Override + public Object getSystemService(String name) { + switch (name) { + case Context.TELEPHONY_SUBSCRIPTION_SERVICE: + return mSubscriptionManager; + case Context.CARRIER_CONFIG_SERVICE: + return mCarrierConfigManager; + case Context.TELEPHONY_SERVICE: + return mTelephonyManager; + default: + return super.getSystemService(name); + } + } }; setNetpolicyXml(context); @@ -321,6 +362,10 @@ public class NetworkPolicyManagerServiceTest { ArgumentCaptor.forClass(INetworkManagementEventObserver.class); verify(mNetworkManager).registerObserver(networkObserver.capture()); mNetworkObserver = networkObserver.getValue(); + + NetworkPolicy defaultPolicy = mService.buildDefaultMobilePolicy(0, ""); + mDefaultWarningBytes = defaultPolicy.warningBytes; + mDefaultLimitBytes = defaultPolicy.limitBytes; } @After @@ -1132,6 +1177,269 @@ public class NetworkPolicyManagerServiceTest { } } + private void assertCycleDayAsExpected(PersistableBundle config, int carrierCycleDay, + boolean expectValid) { + config.putInt(KEY_MONTHLY_DATA_CYCLE_DAY_INT, carrierCycleDay); + int actualCycleDay = mService.getCycleDayFromCarrierConfig(config, + INVALID_CARRIER_CONFIG_VALUE); + if (expectValid) { + assertEquals(carrierCycleDay, actualCycleDay); + } else { + // INVALID_CARRIER_CONFIG_VALUE is returned for invalid values + assertEquals(INVALID_CARRIER_CONFIG_VALUE, actualCycleDay); + } + } + + @Test + public void testGetCycleDayFromCarrierConfig() { + PersistableBundle config = CarrierConfigManager.getDefaultConfig(); + final Calendar cal = Calendar.getInstance(); + int actualCycleDay; + + config.putInt(KEY_MONTHLY_DATA_CYCLE_DAY_INT, DATA_CYCLE_USE_PLATFORM_DEFAULT); + actualCycleDay = mService.getCycleDayFromCarrierConfig(config, DEFAULT_CYCLE_DAY); + assertEquals(DEFAULT_CYCLE_DAY, actualCycleDay); + + // null config returns a default value + actualCycleDay = mService.getCycleDayFromCarrierConfig(null, DEFAULT_CYCLE_DAY); + assertEquals(DEFAULT_CYCLE_DAY, actualCycleDay); + + // Sane, non-default values + assertCycleDayAsExpected(config, 1, true); + assertCycleDayAsExpected(config, cal.getMaximum(Calendar.DAY_OF_MONTH), true); + assertCycleDayAsExpected(config, cal.getMinimum(Calendar.DAY_OF_MONTH), true); + + // Invalid values + assertCycleDayAsExpected(config, 0, false); + assertCycleDayAsExpected(config, DATA_CYCLE_THRESHOLD_DISABLED, false); + assertCycleDayAsExpected(config, cal.getMaximum(Calendar.DAY_OF_MONTH) + 1, false); + assertCycleDayAsExpected(config, cal.getMinimum(Calendar.DAY_OF_MONTH) - 5, false); + } + + private void assertWarningBytesAsExpected(PersistableBundle config, long carrierWarningBytes, + long expected) { + config.putLong(KEY_DATA_WARNING_THRESHOLD_BYTES_LONG, carrierWarningBytes); + long actualWarning = mService.getWarningBytesFromCarrierConfig(config, + INVALID_CARRIER_CONFIG_VALUE); + assertEquals(expected, actualWarning); + } + + @Test + public void testGetWarningBytesFromCarrierConfig() { + PersistableBundle config = CarrierConfigManager.getDefaultConfig(); + long actualWarningBytes; + + assertWarningBytesAsExpected(config, DATA_CYCLE_USE_PLATFORM_DEFAULT, + mDefaultWarningBytes); + assertWarningBytesAsExpected(config, DATA_CYCLE_THRESHOLD_DISABLED, WARNING_DISABLED); + assertWarningBytesAsExpected(config, 0, 0); + // not a valid value + assertWarningBytesAsExpected(config, -1000, INVALID_CARRIER_CONFIG_VALUE); + + // null config returns a default value + actualWarningBytes = mService.getWarningBytesFromCarrierConfig(null, mDefaultWarningBytes); + assertEquals(mDefaultWarningBytes, actualWarningBytes); + } + + private void assertLimitBytesAsExpected(PersistableBundle config, long carrierWarningBytes, + long expected) { + config.putLong(KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG, carrierWarningBytes); + long actualWarning = mService.getLimitBytesFromCarrierConfig(config, + INVALID_CARRIER_CONFIG_VALUE); + assertEquals(expected, actualWarning); + } + + @Test + public void testGetLimitBytesFromCarrierConfig() { + PersistableBundle config = CarrierConfigManager.getDefaultConfig(); + long actualLimitBytes; + + assertLimitBytesAsExpected(config, DATA_CYCLE_USE_PLATFORM_DEFAULT, + mDefaultLimitBytes); + assertLimitBytesAsExpected(config, DATA_CYCLE_THRESHOLD_DISABLED, LIMIT_DISABLED); + assertLimitBytesAsExpected(config, 0, 0); + // not a valid value + assertLimitBytesAsExpected(config, -1000, INVALID_CARRIER_CONFIG_VALUE); + + // null config returns a default value + actualLimitBytes = mService.getWarningBytesFromCarrierConfig(null, mDefaultLimitBytes); + assertEquals(mDefaultLimitBytes, actualLimitBytes); + } + + private PersistableBundle setupUpdateMobilePolicyCycleTests() throws RemoteException { + when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[0]); + when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(new int[]{FAKE_SUB_ID}); + when(mTelephonyManager.getSubscriberId(FAKE_SUB_ID)).thenReturn(FAKE_SUBSCRIBER_ID); + PersistableBundle bundle = CarrierConfigManager.getDefaultConfig(); + when(mCarrierConfigManager.getConfigForSubId(FAKE_SUB_ID)).thenReturn(bundle); + setNetworkPolicies(buildDefaultFakeMobilePolicy()); + return bundle; + } + + @Test + public void testUpdateMobilePolicyCycleWithNullConfig() throws RemoteException { + when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[0]); + when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(new int[]{FAKE_SUB_ID}); + when(mTelephonyManager.getSubscriberId(FAKE_SUB_ID)).thenReturn(FAKE_SUBSCRIBER_ID); + when(mCarrierConfigManager.getConfigForSubId(FAKE_SUB_ID)).thenReturn(null); + setNetworkPolicies(buildDefaultFakeMobilePolicy()); + // smoke test to make sure no errors are raised + mServiceContext.sendBroadcast( + new Intent(ACTION_CARRIER_CONFIG_CHANGED) + .putExtra(PhoneConstants.SUBSCRIPTION_KEY, FAKE_SUB_ID) + ); + assertNetworkPolicyEquals(DEFAULT_CYCLE_DAY, mDefaultWarningBytes, mDefaultLimitBytes, + true); + } + + @Test + public void testUpdateMobilePolicyCycleWithInvalidConfig() throws RemoteException { + PersistableBundle bundle = setupUpdateMobilePolicyCycleTests(); + // Test with an invalid CarrierConfig, there should be no changes or crashes. + bundle.putInt(CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT, -100); + bundle.putLong(CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG, -100); + bundle.putLong(CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG, -100); + mServiceContext.sendBroadcast( + new Intent(ACTION_CARRIER_CONFIG_CHANGED) + .putExtra(PhoneConstants.SUBSCRIPTION_KEY, FAKE_SUB_ID) + ); + + assertNetworkPolicyEquals(DEFAULT_CYCLE_DAY, mDefaultWarningBytes, mDefaultLimitBytes, + true); + } + + @Test + public void testUpdateMobilePolicyCycleWithDefaultConfig() throws RemoteException { + PersistableBundle bundle = setupUpdateMobilePolicyCycleTests(); + // Test that we respect the platform values when told to + bundle.putInt(CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT, + DATA_CYCLE_USE_PLATFORM_DEFAULT); + bundle.putLong(CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG, + DATA_CYCLE_USE_PLATFORM_DEFAULT); + bundle.putLong(CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG, + DATA_CYCLE_USE_PLATFORM_DEFAULT); + mServiceContext.sendBroadcast( + new Intent(ACTION_CARRIER_CONFIG_CHANGED) + .putExtra(PhoneConstants.SUBSCRIPTION_KEY, FAKE_SUB_ID) + ); + + assertNetworkPolicyEquals(DEFAULT_CYCLE_DAY, mDefaultWarningBytes, mDefaultLimitBytes, + true); + } + + @Test + public void testUpdateMobilePolicyCycleWithUserOverrides() throws RemoteException { + PersistableBundle bundle = setupUpdateMobilePolicyCycleTests(); + + // inferred = false implies that a user manually modified this policy. + NetworkPolicy policy = buildDefaultFakeMobilePolicy(); + policy.inferred = false; + setNetworkPolicies(policy); + + bundle.putInt(CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT, 31); + bundle.putLong(CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG, 9999); + bundle.putLong(CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG, + DATA_CYCLE_THRESHOLD_DISABLED); + mServiceContext.sendBroadcast( + new Intent(ACTION_CARRIER_CONFIG_CHANGED) + .putExtra(PhoneConstants.SUBSCRIPTION_KEY, FAKE_SUB_ID) + ); + + // The policy still shouldn't change, because we don't want to overwrite user settings. + assertNetworkPolicyEquals(DEFAULT_CYCLE_DAY, mDefaultWarningBytes, mDefaultLimitBytes, + false); + } + + @Test + public void testUpdateMobilePolicyCycleUpdatesDataCycle() throws RemoteException { + PersistableBundle bundle = setupUpdateMobilePolicyCycleTests(); + + bundle.putInt(CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT, 31); + bundle.putLong(CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG, 9999); + bundle.putLong(CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG, 9999); + mServiceContext.sendBroadcast( + new Intent(ACTION_CARRIER_CONFIG_CHANGED) + .putExtra(PhoneConstants.SUBSCRIPTION_KEY, FAKE_SUB_ID) + ); + + assertNetworkPolicyEquals(31, 9999, 9999, true); + } + + @Test + public void testUpdateMobilePolicyCycleDisableThresholds() throws RemoteException { + PersistableBundle bundle = setupUpdateMobilePolicyCycleTests(); + + bundle.putInt(CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT, 31); + bundle.putLong(CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG, + DATA_CYCLE_THRESHOLD_DISABLED); + bundle.putLong(CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG, + DATA_CYCLE_THRESHOLD_DISABLED); + mServiceContext.sendBroadcast( + new Intent(ACTION_CARRIER_CONFIG_CHANGED) + .putExtra(PhoneConstants.SUBSCRIPTION_KEY, FAKE_SUB_ID) + ); + + assertNetworkPolicyEquals(31, WARNING_DISABLED, LIMIT_DISABLED, true); + } + + @Test + public void testUpdateMobilePolicyCycleRevertsToDefault() throws RemoteException { + PersistableBundle bundle = setupUpdateMobilePolicyCycleTests(); + + bundle.putInt(CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT, 31); + bundle.putLong(CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG, + DATA_CYCLE_THRESHOLD_DISABLED); + bundle.putLong(CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG, + DATA_CYCLE_THRESHOLD_DISABLED); + mServiceContext.sendBroadcast( + new Intent(ACTION_CARRIER_CONFIG_CHANGED) + .putExtra(PhoneConstants.SUBSCRIPTION_KEY, FAKE_SUB_ID) + ); + assertNetworkPolicyEquals(31, WARNING_DISABLED, LIMIT_DISABLED, true); + + // If the user switches carriers to one that doesn't use a CarrierConfig, we should revert + // to the default data limit and warning. The cycle date doesn't need to revert as it's + // arbitrary anyways. + bundle.putInt(CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT, + DATA_CYCLE_USE_PLATFORM_DEFAULT); + bundle.putLong(CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG, + DATA_CYCLE_USE_PLATFORM_DEFAULT); + bundle.putLong(CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG, + DATA_CYCLE_USE_PLATFORM_DEFAULT); + mServiceContext.sendBroadcast( + new Intent(ACTION_CARRIER_CONFIG_CHANGED) + .putExtra(PhoneConstants.SUBSCRIPTION_KEY, FAKE_SUB_ID) + ); + + assertNetworkPolicyEquals(31, mDefaultWarningBytes, mDefaultLimitBytes, + true); + } + + private NetworkPolicy buildDefaultFakeMobilePolicy() { + NetworkPolicy p = mService.buildDefaultMobilePolicy(FAKE_SUB_ID, FAKE_SUBSCRIBER_ID); + // set a deterministic cycle date + p.cycleDay = DEFAULT_CYCLE_DAY; + return p; + } + + private static NetworkPolicy buildFakeMobilePolicy(int cycleDay, long warningBytes, + long limitBytes, boolean inferred){ + final NetworkTemplate template = buildTemplateMobileAll(FAKE_SUBSCRIBER_ID); + return new NetworkPolicy(template, cycleDay, "America/Los_Angeles", warningBytes, + limitBytes, SNOOZE_NEVER, SNOOZE_NEVER, true, inferred); + } + + private void assertNetworkPolicyEquals(int expectedCycleDay, long expectedWarningBytes, + long expectedLimitBytes, boolean expectedInferred) { + NetworkPolicy[] policies = mService.getNetworkPolicies( + mServiceContext.getOpPackageName()); + assertEquals("Unexpected number of network policies", 1, policies.length); + NetworkPolicy actualPolicy = policies[0]; + NetworkPolicy expectedPolicy = buildFakeMobilePolicy(expectedCycleDay, expectedWarningBytes, + expectedLimitBytes, expectedInferred); + assertEquals(expectedPolicy, actualPolicy); + } + private static long parseTime(String time) { final Time result = new Time(); result.parse3339(time); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java index 432fddaad3a4..4f882323486b 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java @@ -648,6 +648,10 @@ public class BridgeContext extends Context { return null; } + if (AUDIO_SERVICE.equals(service)) { + return null; + } + throw new UnsupportedOperationException("Unsupported Service: " + service); } diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java index eceb3658d1d6..4387b0c1dd29 100644 --- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java +++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java @@ -18,11 +18,10 @@ package android.net.wifi.aware; import static org.hamcrest.core.IsEqual.equalTo; import static org.junit.Assert.assertEquals; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Matchers.isNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -140,8 +139,8 @@ public class WifiAwareManagerTest { // (1) connect + success mDut.attach(mockCallback, mMockLooperHandler); - inOrder.verify(mockAwareService).connect(binder.capture(), anyString(), - clientProxyCallback.capture(), (ConfigRequest) isNull(), eq(false)); + inOrder.verify(mockAwareService).connect(binder.capture(), any(), + clientProxyCallback.capture(), isNull(), eq(false)); clientProxyCallback.getValue().onConnectSuccess(clientId); mMockLooper.dispatchAll(); inOrder.verify(mockCallback).onAttached(sessionCaptor.capture()); @@ -150,8 +149,7 @@ public class WifiAwareManagerTest { // (2) publish - should succeed PublishConfig publishConfig = new PublishConfig.Builder().build(); session.publish(publishConfig, mockSessionCallback, mMockLooperHandler); - inOrder.verify(mockAwareService).publish(eq(clientId), eq(publishConfig), - any(IWifiAwareDiscoverySessionCallback.class)); + inOrder.verify(mockAwareService).publish(eq(clientId), eq(publishConfig), any()); // (3) disconnect session.destroy(); @@ -163,8 +161,8 @@ public class WifiAwareManagerTest { // (5) connect mDut.attach(mockCallback, mMockLooperHandler); - inOrder.verify(mockAwareService).connect(binder.capture(), anyString(), - any(IWifiAwareEventCallback.class), (ConfigRequest) isNull(), eq(false)); + inOrder.verify(mockAwareService).connect(binder.capture(), any(), any(), isNull(), + eq(false)); verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService); } @@ -185,16 +183,16 @@ public class WifiAwareManagerTest { // (1) connect + failure mDut.attach(mockCallback, mMockLooperHandler); - inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(), - clientProxyCallback.capture(), (ConfigRequest) isNull(), eq(false)); + inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(), + isNull(), eq(false)); clientProxyCallback.getValue().onConnectFail(reason); mMockLooper.dispatchAll(); inOrder.verify(mockCallback).onAttachFailed(); // (2) connect + success mDut.attach(mockCallback, mMockLooperHandler); - inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(), - clientProxyCallback.capture(), (ConfigRequest) isNull(), eq(false)); + inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(), + isNull(), eq(false)); clientProxyCallback.getValue().onConnectSuccess(clientId); mMockLooper.dispatchAll(); inOrder.verify(mockCallback).onAttached(sessionCaptor.capture()); @@ -203,8 +201,7 @@ public class WifiAwareManagerTest { // (4) subscribe: should succeed SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build(); session.subscribe(subscribeConfig, mockSessionCallback, mMockLooperHandler); - inOrder.verify(mockAwareService).subscribe(eq(clientId), eq(subscribeConfig), - any(IWifiAwareDiscoverySessionCallback.class)); + inOrder.verify(mockAwareService).subscribe(eq(clientId), eq(subscribeConfig), any()); verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService); } @@ -223,19 +220,19 @@ public class WifiAwareManagerTest { // (1) connect + success mDut.attach(mockCallback, mMockLooperHandler); - inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(), - clientProxyCallback.capture(), (ConfigRequest) isNull(), eq(false)); + inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(), + isNull(), eq(false)); clientProxyCallback.getValue().onConnectSuccess(clientId); mMockLooper.dispatchAll(); - inOrder.verify(mockCallback).onAttached(any(WifiAwareSession.class)); + inOrder.verify(mockCallback).onAttached(any()); // (2) connect + success mDut.attach(mockCallback, mMockLooperHandler); - inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(), - clientProxyCallback.capture(), (ConfigRequest) isNull(), eq(false)); + inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(), + isNull(), eq(false)); clientProxyCallback.getValue().onConnectSuccess(clientId + 1); mMockLooper.dispatchAll(); - inOrder.verify(mockCallback).onAttached(any(WifiAwareSession.class)); + inOrder.verify(mockCallback).onAttached(any()); verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService); } @@ -278,8 +275,8 @@ public class WifiAwareManagerTest { // (0) connect + success mDut.attach(mMockLooperHandler, configRequest, mockCallback, null); - inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(), - clientProxyCallback.capture(), eq(configRequest), eq(false)); + inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(), + eq(configRequest), eq(false)); clientProxyCallback.getValue().onConnectSuccess(clientId); mMockLooper.dispatchAll(); inOrder.verify(mockCallback).onAttached(sessionCaptor.capture()); @@ -370,8 +367,8 @@ public class WifiAwareManagerTest { // (1) connect successfully mDut.attach(mMockLooperHandler, configRequest, mockCallback, null); - inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(), - clientProxyCallback.capture(), eq(configRequest), eq(false)); + inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(), + eq(configRequest), eq(false)); clientProxyCallback.getValue().onConnectSuccess(clientId); mMockLooper.dispatchAll(); inOrder.verify(mockCallback).onAttached(sessionCaptor.capture()); @@ -426,8 +423,8 @@ public class WifiAwareManagerTest { // (0) connect + success mDut.attach(mMockLooperHandler, configRequest, mockCallback, null); - inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(), - clientProxyCallback.capture(), eq(configRequest), eq(false)); + inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(), + eq(configRequest), eq(false)); clientProxyCallback.getValue().onConnectSuccess(clientId); mMockLooper.dispatchAll(); inOrder.verify(mockCallback).onAttached(sessionCaptor.capture()); @@ -507,8 +504,8 @@ public class WifiAwareManagerTest { // (1) connect successfully mDut.attach(mMockLooperHandler, configRequest, mockCallback, null); - inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(), - clientProxyCallback.capture(), eq(configRequest), eq(false)); + inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(), + eq(configRequest), eq(false)); clientProxyCallback.getValue().onConnectSuccess(clientId); mMockLooper.dispatchAll(); inOrder.verify(mockCallback).onAttached(sessionCaptor.capture()); @@ -899,8 +896,7 @@ public class WifiAwareManagerTest { final RttManager.RttResult rttResults = new RttManager.RttResult(); rttResults.distance = 10; - when(mockAwareService.startRanging(anyInt(), anyInt(), - any(RttManager.ParcelableRttParams.class))).thenReturn(rangingId); + when(mockAwareService.startRanging(anyInt(), anyInt(), any())).thenReturn(rangingId); InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService, mockPublishSession, mockRttListener); @@ -919,8 +915,8 @@ public class WifiAwareManagerTest { // (1) connect successfully mDut.attach(mMockLooperHandler, configRequest, mockCallback, null); - inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(), - clientProxyCallback.capture(), eq(configRequest), eq(false)); + inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(), + eq(configRequest), eq(false)); clientProxyCallback.getValue().onConnectSuccess(clientId); mMockLooper.dispatchAll(); inOrder.verify(mockCallback).onAttached(sessionCaptor.capture()); @@ -994,8 +990,8 @@ public class WifiAwareManagerTest { // (1) connect successfully mDut.attach(mMockLooperHandler, configRequest, mockCallback, null); - inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(), - clientProxyCallback.capture(), eq(configRequest), eq(false)); + inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(), + eq(configRequest), eq(false)); clientProxyCallback.getValue().onConnectSuccess(clientId); mMockLooper.dispatchAll(); inOrder.verify(mockCallback).onAttached(sessionCaptor.capture()); @@ -1085,8 +1081,8 @@ public class WifiAwareManagerTest { // (1) connect successfully mDut.attach(mMockLooperHandler, configRequest, mockCallback, null); - inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(), - clientProxyCallback.capture(), eq(configRequest), eq(false)); + inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(), + eq(configRequest), eq(false)); clientProxyCallback.getValue().onConnectSuccess(clientId); mMockLooper.dispatchAll(); inOrder.verify(mockCallback).onAttached(sessionCaptor.capture()); |