Merge "TextClassifier: Switch model based on locale" into oc-dev
diff --git a/api/current.txt b/api/current.txt
index 5d94dbd..9b6227f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -696,6 +696,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 c7b377e..515cf97 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -810,6 +810,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 d22f7d3..051e2e6 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -696,6 +696,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 82229d5..b9d1d91 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -629,6 +629,11 @@
*/
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 ef5bc5c..5fa2461 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -483,7 +483,7 @@
}
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 @@
}
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 @@
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 @@
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 @@
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 7e1b5ab..e5d73e0 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.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.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 @@
/** {@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());
}
- // 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.");
+ 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);
+ }
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 @@
* {@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 4d14277..d3adce7 100644
--- a/core/java/android/preference/Preference.java
+++ b/core/java/android/preference/Preference.java
@@ -83,6 +83,7 @@
* @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 @@
private boolean mParentDependencyMet = true;
private boolean mRecycleEnabled = true;
private boolean mSingleLineTitle = true;
+ private boolean mIconSpaceReserved;
/**
* @see #setShouldDisableView(boolean)
@@ -302,7 +304,11 @@
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 @@
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 @@
}
/**
+ * 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 70f9bdd..14d3ad7 100644
--- a/core/java/android/text/FontConfig.java
+++ b/core/java/android/text/FontConfig.java
@@ -22,13 +22,11 @@
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 @@
}
/**
- * 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 @@
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 @@
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 @@
}
/**
- * 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 @@
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 @@
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 @@
}
/**
- * 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 e590739..076b33c 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -137,7 +137,10 @@
} break;
case DRAW_FINISHED_MSG: {
mDrawFinished = true;
- invalidate();
+ if (mAttachedToWindow) {
+ notifyDrawFinished();
+ invalidate();
+ }
} break;
}
}
@@ -188,9 +191,12 @@
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 @@
mViewVisibility = getVisibility() == VISIBLE;
mRequestedVisible = mViewVisibility && mWindowVisibility;
+ mAttachedToWindow = true;
if (!mGlobalListenersAdded) {
ViewTreeObserver observer = getViewTreeObserver();
observer.addOnScrollChangedListener(mScrollChangedListener);
@@ -261,8 +268,17 @@
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 @@
mGlobalListenersAdded = false;
}
+ while (mPendingReportDraws > 0) {
+ notifyDrawFinished();
+ }
+
mRequestedVisible = false;
updateSurface();
@@ -618,6 +638,9 @@
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 3f52a9d..e924f77 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -61,6 +61,7 @@
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 @@
/**
* 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 @@
* 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 @@
* 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 @@
* @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 @@
* 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 @@
* @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 cf52c60..6ec4a2b 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2704,8 +2704,40 @@
}
}
- 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 @@
}
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 @@
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 @@
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 @@
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 c2b4138..bc49123 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -18,6 +18,7 @@
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.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 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 5724a9b..aa1ffa2 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.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 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 1937187..99b91bd 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -6873,9 +6873,11 @@
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 @@
}
} 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 @@
}
}
- 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 1c0c4ef..12e35a1 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -1639,7 +1639,7 @@
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 3603b6d..8edd637 100644
--- a/core/java/com/android/internal/os/FuseAppLoop.java
+++ b/core/java/com/android/internal/os/FuseAppLoop.java
@@ -19,16 +19,16 @@
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 @@
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 @@
@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 @@
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;
+ }
+
+ 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;
+ }
+
+ private void replySimple(long unique, int result) {
+ synchronized (mLock) {
+ replySimpleLocked(unique, result);
+ }
}
- // 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 void replySimpleLocked(long unique, int result) {
+ if (mInstance != 0) {
+ native_replySimple(mInstance, unique, result);
}
}
- // 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);
- }
- }
- }
+ native long native_new(int fd);
+ native void native_delete(long ptr);
+ native void native_start(long ptr);
- // 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 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 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 @@
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 0000000..ca3cfb9
--- /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 dd003eb..e125150 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));
- }
-
- int32_t OnOpen(uint64_t inode) override {
- return checkException(mEnv->CallIntMethod(mSelf, gOnOpenMethod, inode));
- }
-
- int32_t OnFsync(uint64_t inode) override {
- return checkException(mEnv->CallIntMethod(mSelf, gOnFsyncMethod, inode));
- }
-
- int32_t OnRelease(uint64_t inode) override {
- return checkException(mEnv->CallIntMethod(mSelf, gOnReleaseMethod, inode));
- }
-
- 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 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;
}
- mEnv->GetByteArrayRegion(mJniBuffer.get(), 0, result, static_cast<jbyte*>(buffer));
- CHECK(!mEnv->ExceptionCheck());
-
- return checkException(result);
+ mBuffers.insert(std::make_pair(inode, std::unique_ptr<LocalBytes>(
+ new LocalBytes(mEnv, buffer))));
}
- 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 OnFsync(uint64_t unique, uint64_t inode) override {
+ mEnv->CallVoidMethod(mSelf, gOnCommandMethod, FUSE_FSYNC, unique, inode, 0, 0, nullptr);
+ CHECK(!mEnv->ExceptionCheck());
+ }
- mEnv->SetByteArrayRegion(mJniBuffer.get(), 0, size, static_cast<const jbyte*>(buffer));
+ 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());
+ }
+
+ void OnRead(uint64_t unique, uint64_t inode, uint64_t offset, uint32_t size) override {
+ CHECK_LE(size, static_cast<uint32_t>(fuse::kFuseMaxRead));
+
+ auto it = mBuffers.find(inode);
+ CHECK(it != mBuffers.end());
+
+ mEnv->CallVoidMethod(
+ mSelf, gOnCommandMethod, FUSE_READ, unique, inode, offset, size,
+ it->second->get());
+ CHECK(!mEnv->ExceptionCheck());
+ }
+
+ 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(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 995f2c3..a3b2705 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 876d44d..6e20208 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 ff98eb7..fbba6ff 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 @@
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 @@
// 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 b78df34..ff9f11d 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -189,9 +189,8 @@
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 228d950..8c3a2e8 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -1003,21 +1003,22 @@
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 ce58a9c..77a82ec 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -912,6 +912,8 @@
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 @@
* 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 @@
}
/**
+ * 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 6808b57..7bf69c0 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -1321,9 +1321,7 @@
*
* <p>Type: INTEGER (boolean)
* @see Channels#COLUMN_TRANSIENT
- * @hide
*/
- @SystemApi
public static final String COLUMN_TRANSIENT = "transient";
/**
@@ -2164,10 +2162,9 @@
* 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 187e35a..f11a9cd 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 @@
}
// 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 @@
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 @@
mDocumentInfo.fileProvider, mCommandResultCallback);
scheduleCommand(command);
+ mDocumentInfo.updated = false;
mState = STATE_UPDATING;
} else {
willUpdate = false;
@@ -396,7 +400,7 @@
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 f6df995..4cce166 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 @@
setState(STATE_UPDATE_FAILED);
- updateOptionsUi();
-
mPrintedDocument.kill(message);
}
@@ -502,7 +500,6 @@
&& canUpdateDocument() && updateDocument(true)) {
ensurePreviewUiShown();
setState(STATE_CONFIGURING);
- updateOptionsUi();
}
}
@@ -579,7 +576,6 @@
updatePrintPreviewController(document.changed);
setState(STATE_CONFIGURING);
- updateOptionsUi();
} break;
}
}
@@ -600,8 +596,6 @@
}
setState(STATE_UPDATE_FAILED);
-
- updateOptionsUi();
}
@Override
@@ -734,7 +728,6 @@
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 @@
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 @@
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 @@
private void cancelPrint() {
setState(STATE_PRINT_CANCELED);
- updateOptionsUi();
mPrintedDocument.cancel(true);
doFinish();
}
@@ -1274,7 +1269,6 @@
private void confirmPrint() {
setState(STATE_PRINT_CONFIRMED);
- updateOptionsUi();
addCurrentPrinterToHistory();
setUserPrinted();
@@ -1629,6 +1623,8 @@
// 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 @@
|| 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 @@
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 @@
updateDocument(false);
}
ensurePreviewUiShown();
- updateOptionsUi();
}
}
@@ -2058,7 +2050,6 @@
mPrintedDocument.cancel(false);
ensureErrorUiShown(getString(R.string.print_error_printer_unavailable),
PrintErrorFragment.ACTION_NONE);
- updateOptionsUi();
}
}
@@ -3038,7 +3029,6 @@
if (mState == STATE_UPDATE_SLOW) {
setState(STATE_UPDATE_SLOW);
ensureProgressUiShown();
- updateOptionsUi();
return;
} else if (mPosted) {
@@ -3080,7 +3070,6 @@
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 cc2dc1b..f14d0d1 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 bf17e38..0c0bdeb 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 ebda2e8..ec80745 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.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.R;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
@@ -128,8 +127,9 @@
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 @@
}
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 @@
if (animationFinishedRunnable != null) {
animationFinishedRunnable.run();
}
- if (getSystemService(AccessibilityManager.class).isEnabled()) {
- finish();
- }
+ setDecorViewVisibility(false);
}
});
mMenuContainerAnimator.addUpdateListener(mMenuBgUpdateListener);
@@ -411,6 +410,7 @@
}
private void updateDismissFraction(float fraction) {
+ setDecorViewVisibility(true);
int alpha;
if (mMenuVisible) {
mMenuContainer.setAlpha(1-fraction);
@@ -497,4 +497,16 @@
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 bdd6b65..9bf6d6e 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 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 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 @@
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 @@
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 @@
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 3f7e340..a5d7c57 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 @@
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 @@
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 bb6c8f2..53ec8c5 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 @@
private boolean mVolumeVisible;
private boolean mCurrentUserSetup;
- private boolean mManagedProfileFocused = false;
private boolean mManagedProfileIconVisible = false;
private boolean mManagedProfileInQuietMode = false;
@@ -439,45 +438,30 @@
}
}
- 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 @@
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 55a945a..f172647 100644
--- a/services/core/java/com/android/server/FontManagerService.java
+++ b/services/core/java/com/android/server/FontManagerService.java
@@ -18,6 +18,7 @@
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 @@
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 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 457c5f8..c68000a 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.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 @@
}
}
- 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");
- }
- 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));
+ boolean opened = false;
+
+ public AppFuseMountScope(int uid, int pid, int mountId) {
+ super(uid, pid, 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 @@
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 @@
}
@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 ce9a904..8cb0eee 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 @@
}
}
- 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 @@
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 @@
}
}
+ /**
+ * 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 93e30ff..b165d34 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_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.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.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.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.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 @@
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 @@
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 @@
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 @@
};
/**
+ * 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 @@
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 @@
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 @@
synchronized (mUidRulesFirstLock) {
synchronized (mNetworkPoliciesSecondLock) {
normalizePoliciesNL(policies);
- updateNetworkEnabledNL();
- updateNetworkRulesNL();
- updateNotificationsNL();
- writePolicyAL();
+ handleNetworkPoliciesUpdateAL(false);
}
}
} finally {
@@ -2125,11 +2369,7 @@
throw new IllegalArgumentException("unexpected type");
}
- normalizePoliciesNL();
- updateNetworkEnabledNL();
- updateNetworkRulesNL();
- updateNotificationsNL();
- writePolicyAL();
+ handleNetworkPoliciesUpdateAL(true);
}
}
}
@@ -2360,11 +2600,7 @@
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 ede5a5e..7468b95 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.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 @@
+ ", 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 904d915..6a0b648 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.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 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 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 @@
}
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 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 @@
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 29c6f89..dbba727 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 @@
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.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.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.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 @@
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 @@
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 @@
@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 @@
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 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 @@
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 @@
}
}
+ 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 432fdda..4f88232 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 @@
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 eceb365..4387b0c 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 @@
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 @@
// (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 @@
// (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 @@
// (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 @@
// (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 @@
// (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 @@
// (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 @@
// (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 @@
// (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 @@
// (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 @@
// (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 @@
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 @@
// (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 @@
// (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 @@
// (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());