Merge "Change "touch" to "tap"."
diff --git a/api/current.txt b/api/current.txt
index e0b92f4..729f4da 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5686,10 +5686,13 @@
method public android.graphics.drawable.Drawable peekFastDrawable();
method public void sendWallpaperCommand(android.os.IBinder, java.lang.String, int, int, int, android.os.Bundle);
method public void setBitmap(android.graphics.Bitmap) throws java.io.IOException;
- method public void setBitmap(android.graphics.Bitmap, android.graphics.Rect, boolean) throws java.io.IOException;
+ method public int setBitmap(android.graphics.Bitmap, android.graphics.Rect, boolean) throws java.io.IOException;
+ method public int setBitmap(android.graphics.Bitmap, android.graphics.Rect, boolean, int) throws java.io.IOException;
method public void setResource(int) throws java.io.IOException;
+ method public int setResource(int, int) throws java.io.IOException;
method public void setStream(java.io.InputStream) throws java.io.IOException;
- method public void setStream(java.io.InputStream, android.graphics.Rect, boolean) throws java.io.IOException;
+ method public int setStream(java.io.InputStream, android.graphics.Rect, boolean) throws java.io.IOException;
+ method public int setStream(java.io.InputStream, android.graphics.Rect, boolean, int) throws java.io.IOException;
method public void setWallpaperOffsetSteps(float, float);
method public void setWallpaperOffsets(android.os.IBinder, float, float);
method public void suggestDesiredDimensions(int, int);
@@ -5700,6 +5703,8 @@
field public static final java.lang.String COMMAND_SECONDARY_TAP = "android.wallpaper.secondaryTap";
field public static final java.lang.String COMMAND_TAP = "android.wallpaper.tap";
field public static final java.lang.String EXTRA_LIVE_WALLPAPER_COMPONENT = "android.service.wallpaper.extra.LIVE_WALLPAPER_COMPONENT";
+ field public static final int FLAG_SET_LOCK = 2; // 0x2
+ field public static final int FLAG_SET_SYSTEM = 1; // 0x1
field public static final java.lang.String WALLPAPER_PREVIEW_META_DATA = "android.wallpaper.preview";
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 6b84f56..da83f6a 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5818,12 +5818,15 @@
method public android.graphics.drawable.Drawable peekFastDrawable();
method public void sendWallpaperCommand(android.os.IBinder, java.lang.String, int, int, int, android.os.Bundle);
method public void setBitmap(android.graphics.Bitmap) throws java.io.IOException;
- method public void setBitmap(android.graphics.Bitmap, android.graphics.Rect, boolean) throws java.io.IOException;
+ method public int setBitmap(android.graphics.Bitmap, android.graphics.Rect, boolean) throws java.io.IOException;
+ method public int setBitmap(android.graphics.Bitmap, android.graphics.Rect, boolean, int) throws java.io.IOException;
method public void setDisplayOffset(android.os.IBinder, int, int);
method public void setDisplayPadding(android.graphics.Rect);
method public void setResource(int) throws java.io.IOException;
+ method public int setResource(int, int) throws java.io.IOException;
method public void setStream(java.io.InputStream) throws java.io.IOException;
- method public void setStream(java.io.InputStream, android.graphics.Rect, boolean) throws java.io.IOException;
+ method public int setStream(java.io.InputStream, android.graphics.Rect, boolean) throws java.io.IOException;
+ method public int setStream(java.io.InputStream, android.graphics.Rect, boolean, int) throws java.io.IOException;
method public boolean setWallpaperComponent(android.content.ComponentName);
method public void setWallpaperOffsetSteps(float, float);
method public void setWallpaperOffsets(android.os.IBinder, float, float);
@@ -5835,6 +5838,8 @@
field public static final java.lang.String COMMAND_SECONDARY_TAP = "android.wallpaper.secondaryTap";
field public static final java.lang.String COMMAND_TAP = "android.wallpaper.tap";
field public static final java.lang.String EXTRA_LIVE_WALLPAPER_COMPONENT = "android.service.wallpaper.extra.LIVE_WALLPAPER_COMPONENT";
+ field public static final int FLAG_SET_LOCK = 2; // 0x2
+ field public static final int FLAG_SET_SYSTEM = 1; // 0x1
field public static final java.lang.String WALLPAPER_PREVIEW_META_DATA = "android.wallpaper.preview";
}
@@ -46620,12 +46625,18 @@
method public abstract void deleteKey(android.net.Uri, android.webkit.ValueCallback<java.lang.Boolean>);
method public abstract void enableTokenBinding();
method public static android.webkit.TokenBindingService getInstance();
- method public abstract void getKey(android.net.Uri, java.lang.String, android.webkit.ValueCallback<java.security.KeyPair>);
+ method public abstract void getKey(android.net.Uri, java.lang.String[], android.webkit.ValueCallback<android.webkit.TokenBindingService.TokenBindingKey>);
field public static final java.lang.String KEY_ALGORITHM_ECDSAP256 = "ECDSAP256";
field public static final java.lang.String KEY_ALGORITHM_RSA2048_PKCS_1_5 = "RSA2048_PKCS_1.5";
field public static final java.lang.String KEY_ALGORITHM_RSA2048_PSS = "RSA2048PSS";
}
+ public static abstract class TokenBindingService.TokenBindingKey {
+ ctor public TokenBindingService.TokenBindingKey();
+ method public abstract java.lang.String getAlgorithm();
+ method public abstract java.security.KeyPair getKeyPair();
+ }
+
public final class URLUtil {
ctor public URLUtil();
method public static java.lang.String composeSearchUrl(java.lang.String, java.lang.String, java.lang.String);
diff --git a/api/test-current.txt b/api/test-current.txt
index f003875..4193cad 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -5688,10 +5688,13 @@
method public android.graphics.drawable.Drawable peekFastDrawable();
method public void sendWallpaperCommand(android.os.IBinder, java.lang.String, int, int, int, android.os.Bundle);
method public void setBitmap(android.graphics.Bitmap) throws java.io.IOException;
- method public void setBitmap(android.graphics.Bitmap, android.graphics.Rect, boolean) throws java.io.IOException;
+ method public int setBitmap(android.graphics.Bitmap, android.graphics.Rect, boolean) throws java.io.IOException;
+ method public int setBitmap(android.graphics.Bitmap, android.graphics.Rect, boolean, int) throws java.io.IOException;
method public void setResource(int) throws java.io.IOException;
+ method public int setResource(int, int) throws java.io.IOException;
method public void setStream(java.io.InputStream) throws java.io.IOException;
- method public void setStream(java.io.InputStream, android.graphics.Rect, boolean) throws java.io.IOException;
+ method public int setStream(java.io.InputStream, android.graphics.Rect, boolean) throws java.io.IOException;
+ method public int setStream(java.io.InputStream, android.graphics.Rect, boolean, int) throws java.io.IOException;
method public void setWallpaperOffsetSteps(float, float);
method public void setWallpaperOffsets(android.os.IBinder, float, float);
method public void suggestDesiredDimensions(int, int);
@@ -5702,6 +5705,8 @@
field public static final java.lang.String COMMAND_SECONDARY_TAP = "android.wallpaper.secondaryTap";
field public static final java.lang.String COMMAND_TAP = "android.wallpaper.tap";
field public static final java.lang.String EXTRA_LIVE_WALLPAPER_COMPONENT = "android.service.wallpaper.extra.LIVE_WALLPAPER_COMPONENT";
+ field public static final int FLAG_SET_LOCK = 2; // 0x2
+ field public static final int FLAG_SET_SYSTEM = 1; // 0x1
field public static final java.lang.String WALLPAPER_PREVIEW_META_DATA = "android.wallpaper.preview";
}
diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
index ccba250..7a0e7f6 100644
--- a/core/java/android/app/IWallpaperManager.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -28,37 +28,47 @@
/**
* Set the wallpaper.
+ *
+ * If 'extras' is non-null, on successful return it will contain:
+ * EXTRA_SET_WALLPAPER_ID : integer ID that the new wallpaper will have
+ *
+ * 'which' is some combination of:
+ * FLAG_SET_SYSTEM
+ * FLAG_SET_LOCK
*/
- ParcelFileDescriptor setWallpaper(String name, in String callingPackage);
+ ParcelFileDescriptor setWallpaper(String name, in String callingPackage,
+ out Bundle extras, int which);
/**
- * Set the live wallpaper.
+ * Set the live wallpaper. This only affects the system wallpaper.
*/
void setWallpaperComponentChecked(in ComponentName name, in String callingPackage);
/**
- * Set the live wallpaper.
+ * Set the live wallpaper. This only affects the system wallpaper.
*/
void setWallpaperComponent(in ComponentName name);
/**
- * Get the wallpaper.
+ * Get the system wallpaper.
*/
ParcelFileDescriptor getWallpaper(IWallpaperManagerCallback cb,
out Bundle outParams);
/**
- * Get information about a live wallpaper.
+ * If the current system wallpaper is a live wallpaper component, return the
+ * information about that wallpaper. Otherwise, if it is a static image,
+ * simply return null.
*/
WallpaperInfo getWallpaperInfo();
/**
- * Clear the wallpaper.
+ * Clear the system wallpaper.
*/
void clearWallpaper(in String callingPackage);
/**
- * Return whether there is a wallpaper set with the given name.
+ * Return whether the current system wallpaper has the given name.
*/
boolean hasNamedWallpaper(String name);
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 7184337..24a3470 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -545,7 +545,7 @@
* returning the resulting activity or till the timeOut period expires.
* If the timeOut expires before the activity is started, return null.
*
- * @param timeOut Time to wait before the activity is created.
+ * @param timeOut Time to wait in milliseconds before the activity is created.
*
* @return Activity
*/
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 5b5bba4..f103576 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -16,6 +16,7 @@
package android.app;
+import android.annotation.IntDef;
import android.annotation.RawRes;
import android.annotation.SystemApi;
import android.content.ComponentName;
@@ -60,6 +61,8 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.List;
/**
@@ -144,7 +147,33 @@
* and y arguments are the location of the drop.
*/
public static final String COMMAND_DROP = "android.home.drop";
-
+
+ /**
+ * Extra passed back from setWallpaper() giving the new wallpaper's assigned ID.
+ * @hide
+ */
+ public static final String EXTRA_NEW_WALLPAPER_ID = "android.service.wallpaper.extra.ID";
+
+ // flags for which kind of wallpaper to set
+
+ /** @hide */
+ @IntDef(flag = true, value = {
+ FLAG_SET_SYSTEM,
+ FLAG_SET_LOCK
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SetWallpaperFlags {}
+
+ /**
+ * Flag: use the supplied imagery as the general system wallpaper.
+ */
+ public static final int FLAG_SET_SYSTEM = 1 << 0;
+
+ /**
+ * Flag: use the supplied imagery as the lock-screen wallpaper.
+ */
+ public static final int FLAG_SET_LOCK = 1 << 1;
+
private final Context mContext;
/**
@@ -717,20 +746,41 @@
* wallpaper.
*/
public void setResource(@RawRes int resid) throws IOException {
+ setResource(resid, FLAG_SET_SYSTEM);
+ }
+
+ /**
+ * Version of {@link #setResource(int)} that takes an optional Bundle for returning
+ * metadata about the operation to the caller.
+ *
+ * @param resid
+ * @param which Flags indicating which wallpaper(s) to configure with the new imagery.
+ *
+ * @see #FLAG_SET_LOCK
+ * @see #FLAG_SET_SYSTEM
+ *
+ * @return An integer ID assigned to the newly active wallpaper; or zero on failure.
+ *
+ * @throws IOException
+ */
+ public int setResource(@RawRes int resid, @SetWallpaperFlags int which)
+ throws IOException {
if (sGlobals.mService == null) {
Log.w(TAG, "WallpaperService not running");
- return;
+ return 0;
}
+ final Bundle result = new Bundle();
try {
Resources resources = mContext.getResources();
/* Set the wallpaper to the default values */
ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(
- "res:" + resources.getResourceName(resid), mContext.getOpPackageName());
+ "res:" + resources.getResourceName(resid),
+ mContext.getOpPackageName(), result, which);
if (fd != null) {
FileOutputStream fos = null;
try {
fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
- setWallpaper(resources.openRawResource(resid), fos);
+ copyStreamToWallpaperFile(resources.openRawResource(resid), fos);
} finally {
IoUtils.closeQuietly(fos);
}
@@ -738,6 +788,7 @@
} catch (RemoteException e) {
// Ignore
}
+ return result.getInt(EXTRA_NEW_WALLPAPER_ID, 0);
}
/**
@@ -753,7 +804,7 @@
* <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#SET_WALLPAPER}.
*
- * @param bitmap The bitmap to save.
+ * @param bitmap The bitmap to be used as the new system wallpaper.
*
* @throws IOException If an error occurs when attempting to set the wallpaper
* to the provided image.
@@ -775,42 +826,72 @@
* <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#SET_WALLPAPER}.
*
- * @param fullImage A bitmap that will supply the wallpaper imagery.
+ * @param fullImage A bitmap that will supply the wallpaper imagery.
* @param visibleCropHint The rectangular subregion of {@code fullImage} that should be
* displayed as wallpaper. Passing {@code null} for this parameter means that
* the full image should be displayed if possible given the image's and device's
- * aspect ratios, etc.
+ * aspect ratios, etc.
* @param allowBackup {@code true} if the OS is permitted to back up this wallpaper
* image for restore to a future device; {@code false} otherwise.
*
+ * @return An integer ID assigned to the newly active wallpaper; or zero on failure.
+ *
* @throws IOException If an error occurs when attempting to set the wallpaper
* to the provided image.
* @throws IllegalArgumentException If the {@code visibleCropHint} rectangle is
* empty or invalid.
*/
- public void setBitmap(Bitmap fullImage, Rect visibleCropHint, boolean allowBackup)
+ public int setBitmap(Bitmap fullImage, Rect visibleCropHint, boolean allowBackup)
+ throws IOException {
+ return setBitmap(fullImage, visibleCropHint, allowBackup, FLAG_SET_SYSTEM);
+ }
+
+ /**
+ /**
+ * Version of {@link #setBitmap(Bitmap, Rect, boolean)} that allows the caller
+ * to specify which of the supported wallpaper categories to set.
+ *
+ * @param fullImage A bitmap that will supply the wallpaper imagery.
+ * @param visibleCropHint The rectangular subregion of {@code fullImage} that should be
+ * displayed as wallpaper. Passing {@code null} for this parameter means that
+ * the full image should be displayed if possible given the image's and device's
+ * aspect ratios, etc.
+ * @param allowBackup {@code true} if the OS is permitted to back up this wallpaper
+ * image for restore to a future device; {@code false} otherwise.
+ * @param which Flags indicating which wallpaper(s) to configure with the new imagery.
+ *
+ * @see #FLAG_SET_LOCK_WALLPAPER
+ * @see #FLAG_SET_SYSTEM_WALLPAPER
+ *
+ * @return An integer ID assigned to the newly active wallpaper; or zero on failure.
+ *
+ * @throws IOException
+ */
+ public int setBitmap(Bitmap fullImage, Rect visibleCropHint,
+ boolean allowBackup, @SetWallpaperFlags int which)
throws IOException {
validateRect(visibleCropHint);
if (sGlobals.mService == null) {
Log.w(TAG, "WallpaperService not running");
- return;
+ return 0;
}
+ final Bundle result = new Bundle();
try {
ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null,
- mContext.getOpPackageName());
- if (fd == null) {
- return;
- }
- FileOutputStream fos = null;
- try {
- fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
- fullImage.compress(Bitmap.CompressFormat.PNG, 90, fos);
- } finally {
- IoUtils.closeQuietly(fos);
+ mContext.getOpPackageName(), result, which);
+ if (fd != null) {
+ FileOutputStream fos = null;
+ try {
+ fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
+ fullImage.compress(Bitmap.CompressFormat.PNG, 90, fos);
+ } finally {
+ IoUtils.closeQuietly(fos);
+ }
}
} catch (RemoteException e) {
// Ignore
}
+ return result.getInt(EXTRA_NEW_WALLPAPER_ID, 0);
}
private final void validateRect(Rect rect) {
@@ -843,7 +924,7 @@
setStream(bitmapData, null, true);
}
- private void setWallpaper(InputStream data, FileOutputStream fos)
+ private void copyStreamToWallpaperFile(InputStream data, FileOutputStream fos)
throws IOException {
byte[] buffer = new byte[32768];
int amt;
@@ -877,29 +958,55 @@
* @throws IllegalArgumentException If the {@code visibleCropHint} rectangle is
* empty or invalid.
*/
- public void setStream(InputStream bitmapData, Rect visibleCropHint, boolean allowBackup)
+ public int setStream(InputStream bitmapData, Rect visibleCropHint, boolean allowBackup)
throws IOException {
+ return setStream(bitmapData, visibleCropHint, allowBackup, FLAG_SET_SYSTEM);
+ }
+
+ /**
+ * Version of {@link #setStream(InputStream, Rect, boolean)} that allows the caller
+ * to specify which of the supported wallpaper categories to set.
+ *
+ * @param bitmapData A stream containing the raw data to install as a wallpaper.
+ * @param visibleCropHint The rectangular subregion of the streamed image that should be
+ * displayed as wallpaper. Passing {@code null} for this parameter means that
+ * the full image should be displayed if possible given the image's and device's
+ * aspect ratios, etc.
+ * @param allowBackup {@code true} if the OS is permitted to back up this wallpaper
+ * image for restore to a future device; {@code false} otherwise.
+ * @param which Flags indicating which wallpaper(s) to configure with the new imagery.
+ *
+ * @see #FLAG_SET_LOCK_WALLPAPER
+ * @see #FLAG_SET_SYSTEM_WALLPAPER
+ *
+ * @throws IOException
+ */
+ public int setStream(InputStream bitmapData, Rect visibleCropHint,
+ boolean allowBackup, @SetWallpaperFlags int which)
+ throws IOException {
validateRect(visibleCropHint);
if (sGlobals.mService == null) {
Log.w(TAG, "WallpaperService not running");
- return;
+ return 0;
}
+ final Bundle result = new Bundle();
try {
ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null,
- mContext.getOpPackageName());
- if (fd == null) {
- return;
- }
- FileOutputStream fos = null;
- try {
- fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
- setWallpaper(bitmapData, fos);
- } finally {
- IoUtils.closeQuietly(fos);
+ mContext.getOpPackageName(), result, which);
+ if (fd != null) {
+ FileOutputStream fos = null;
+ try {
+ fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
+ copyStreamToWallpaperFile(bitmapData, fos);
+ } finally {
+ IoUtils.closeQuietly(fos);
+ }
}
} catch (RemoteException e) {
// Ignore
}
+
+ return result.getInt(EXTRA_NEW_WALLPAPER_ID, 0);
}
/**
diff --git a/core/java/android/webkit/TokenBindingService.java b/core/java/android/webkit/TokenBindingService.java
index f11ce51..f7caac7 100644
--- a/core/java/android/webkit/TokenBindingService.java
+++ b/core/java/android/webkit/TokenBindingService.java
@@ -38,6 +38,21 @@
public static final String KEY_ALGORITHM_ECDSAP256 = "ECDSAP256";
/**
+ * Provides the KeyPair information.
+ */
+ public static abstract class TokenBindingKey {
+ /**
+ * The public, private key pair.
+ */
+ public abstract KeyPair getKeyPair();
+
+ /**
+ * The algorithm that is used to generate the key pair.
+ */
+ public abstract String getAlgorithm();
+ }
+
+ /**
* Returns the default TokenBinding service instance. At present there is
* only one token binding service instance for all WebView instances,
* however this restriction may be relaxed in the future.
@@ -59,16 +74,25 @@
/**
* Retrieves the key pair for a given origin from the internal
* TokenBinding key store asynchronously.
- * Will create a key pair if one does not exist.
+ *
+ * The user can provide a list of acceptable algorithms for the retrieved
+ * key pair. If a key pair exists and it is in the list of algorithms, then
+ * the key is returned. If it is not in the list, no key is returned.
+ *
+ * If no key pair exists, WebView chooses an algorithm from the list, in
+ * the order given, to generate a key.
+ *
+ * The user can pass a null if any algorithm is acceptable.
*
* @param origin The origin for the server.
- * @param algorithm The algorithm for generating the token binding key.
+ * @param algorithm The list of algorithms. Can be null. An
+ * IllegalArgumentException is thrown if array is empty.
* @param callback The callback that will be called when key is available.
* Cannot be null.
*/
public abstract void getKey(Uri origin,
- String algorithm,
- ValueCallback<KeyPair> callback);
+ String[] algorithm,
+ ValueCallback<TokenBindingKey> callback);
/**
* Deletes specified key (for use when associated cookie is cleared).
*
diff --git a/core/java/com/android/internal/os/KernelCpuSpeedReader.java b/core/java/com/android/internal/os/KernelCpuSpeedReader.java
index 5b776ac..3f6ebb9 100644
--- a/core/java/com/android/internal/os/KernelCpuSpeedReader.java
+++ b/core/java/com/android/internal/os/KernelCpuSpeedReader.java
@@ -16,8 +16,11 @@
package com.android.internal.os;
import android.text.TextUtils;
+import android.system.OsConstants;
import android.util.Slog;
+import libcore.io.Libcore;
+
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
@@ -29,7 +32,7 @@
*
* freq time
*
- * where time is measured in 1/100 seconds.
+ * where time is measured in jiffies.
*/
public class KernelCpuSpeedReader {
private static final String TAG = "KernelCpuSpeedReader";
@@ -38,6 +41,9 @@
private final long[] mLastSpeedTimes;
private final long[] mDeltaSpeedTimes;
+ // How long a CPU jiffy is in milliseconds.
+ private final long mJiffyMillis;
+
/**
* @param cpuNumber The cpu (cpu0, cpu1, etc) whose state to read.
*/
@@ -46,6 +52,8 @@
cpuNumber);
mLastSpeedTimes = new long[numSpeedSteps];
mDeltaSpeedTimes = new long[numSpeedSteps];
+ long jiffyHz = Libcore.os.sysconf(OsConstants._SC_CLK_TCK);
+ mJiffyMillis = 1000/jiffyHz;
}
/**
@@ -62,8 +70,7 @@
splitter.setString(line);
Long.parseLong(splitter.next());
- // The proc file reports time in 1/100 sec, so convert to milliseconds.
- long time = Long.parseLong(splitter.next()) * 10;
+ long time = Long.parseLong(splitter.next()) * mJiffyMillis;
if (time < mLastSpeedTimes[speedIndex]) {
// The stats reset when the cpu hotplugged. That means that the time
// we read is offset from 0, so the time is the delta.
diff --git a/core/java/com/android/internal/os/ProcessCpuTracker.java b/core/java/com/android/internal/os/ProcessCpuTracker.java
index bf97f1f..d831902 100644
--- a/core/java/com/android/internal/os/ProcessCpuTracker.java
+++ b/core/java/com/android/internal/os/ProcessCpuTracker.java
@@ -67,10 +67,10 @@
static final int PROCESS_STAT_UTIME = 2;
static final int PROCESS_STAT_STIME = 3;
- /** Stores user time and system time in 100ths of a second. */
+ /** Stores user time and system time in jiffies. */
private final long[] mProcessStatsData = new long[4];
- /** Stores user time and system time in 100ths of a second. Used for
+ /** Stores user time and system time in jiffies. Used for
* public API to retrieve CPU use for a process. Must lock while in use. */
private final long[] mSinglePidStatsData = new long[4];
diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h
index 88fecce..d8801b8 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -1128,7 +1128,6 @@
// configuration. (eg. Hant, Latn, etc.). Interpreted in conjunction with
// the locale field.
char localeScript[4];
- bool localeScriptWasProvided;
// A single BCP-47 variant subtag. Will vary in length between 4 and 8
// chars. Interpreted in conjunction with the locale field.
@@ -1152,6 +1151,10 @@
uint32_t screenConfig2;
};
+ // If true, it means that the script of the locale was explicitly provided.
+ // If false, it means that the script was automatically computed.
+ bool localeScriptWasProvided;
+
void copyFromDeviceNoSwap(const ResTable_config& o);
void copyFromDtoH(const ResTable_config& o);
diff --git a/packages/DocumentsUI/res/layout/dialog_create_dir.xml b/packages/DocumentsUI/res/layout/dialog_file_name.xml
similarity index 100%
rename from packages/DocumentsUI/res/layout/dialog_create_dir.xml
rename to packages/DocumentsUI/res/layout/dialog_file_name.xml
diff --git a/packages/DocumentsUI/res/menu/mode_directory.xml b/packages/DocumentsUI/res/menu/mode_directory.xml
index 4ff396f..6f9bfb5 100644
--- a/packages/DocumentsUI/res/menu/mode_directory.xml
+++ b/packages/DocumentsUI/res/menu/mode_directory.xml
@@ -48,4 +48,9 @@
android:title="@string/menu_move"
android:showAsAction="never"
android:visible="false" />
+ <item
+ android:id="@+id/menu_rename"
+ android:title="@string/menu_rename"
+ android:showAsAction="never"
+ android:visible="false" />
</menu>
diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml
index 30c1238..8ac228e 100644
--- a/packages/DocumentsUI/res/values/strings.xml
+++ b/packages/DocumentsUI/res/values/strings.xml
@@ -192,4 +192,8 @@
</plurals>
<!-- Toast shown when a user tries to paste files into an unsupported location. -->
<string name="clipboard_files_cannot_paste">Cannot paste the selected files in this location.</string>
+ <!-- Menu item that renames the selected document [CHAR LIMIT=24] -->
+ <string name="menu_rename">Rename</string>
+ <!-- Toast shown when renaming document failed with an error [CHAR LIMIT=48] -->
+ <string name="rename_error">Failed to rename document</string>
</resources>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index 0fb8daf..f43b81c 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -437,7 +437,7 @@
DirectoryFragment.get(getFragmentManager()).onUserModeChanged();
}
- void setPending(boolean pending) {
+ public void setPending(boolean pending) {
final SaveFragment save = SaveFragment.get(getFragmentManager());
if (save != null) {
save.setPending(pending);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
index f3c3f2f..df036b9 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
@@ -63,7 +63,7 @@
final AlertDialog.Builder builder = new AlertDialog.Builder(context);
final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
- final View view = dialogInflater.inflate(R.layout.dialog_create_dir, null, false);
+ final View view = dialogInflater.inflate(R.layout.dialog_file_name, null, false);
final EditText editText = (EditText) view.findViewById(android.R.id.text1);
builder.setTitle(R.string.menu_create_dir);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index bfd40bb..809db31 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -93,6 +93,7 @@
import com.android.documentsui.RecentLoader;
import com.android.documentsui.RecentsProvider;
import com.android.documentsui.RecentsProvider.StateColumns;
+import com.android.documentsui.dirlist.RenameDocumentFragment;
import com.android.documentsui.RootsCache;
import com.android.documentsui.Shared;
import com.android.documentsui.Snackbars;
@@ -559,6 +560,7 @@
private Selection mSelected = new Selection();
private ActionMode mActionMode;
private int mNoDeleteCount = 0;
+ private int mNoRenameCount = -1;
private Menu mMenu;
@Override
@@ -582,6 +584,9 @@
if ((docFlags & Document.FLAG_SUPPORTS_DELETE) == 0) {
mNoDeleteCount += selected ? 1 : -1;
}
+ if ((docFlags & Document.FLAG_SUPPORTS_RENAME) != 0) {
+ mNoRenameCount += selected ? 1 : -1;
+ }
}
@Override
@@ -620,6 +625,7 @@
mSelectionManager.clearSelection();
mSelected.clear();
mNoDeleteCount = 0;
+ mNoRenameCount = -1;
}
@Override
@@ -637,10 +643,19 @@
return true;
}
+ boolean canRenameSelection() {
+ return mNoRenameCount == 0 && mSelectionManager.getSelection().size() == 1;
+ }
+
+ boolean canDeleteSelection() {
+ return mNoDeleteCount == 0;
+ }
+
private void updateActionMenu() {
checkNotNull(mMenu);
+
// Delegate update logic to our owning action, since specialized logic is desired.
- mTuner.updateActionMenu(mMenu, mType, mNoDeleteCount == 0);
+ mTuner.updateActionMenu(mMenu, mType, canDeleteSelection(), canRenameSelection());
Menus.disableHiddenItems(mMenu);
}
@@ -680,6 +695,7 @@
case R.id.menu_copy_to_clipboard:
if (!selection.isEmpty()) {
copySelectionToClipboard(selection);
+ mode.finish();
}
return true;
@@ -687,6 +703,11 @@
selectAllFiles();
return true;
+ case R.id.menu_rename:
+ renameDocuments(selection);
+ mode.finish();
+ return true;
+
default:
if (DEBUG) Log.d(TAG, "Unhandled menu item selected: " + item);
return false;
@@ -834,6 +855,19 @@
}.execute(selected);
}
+ private void renameDocuments(Selection selected) {
+ // Batch renaming not supported
+ // Rename option is only available in menu when 1 document selected
+ checkArgument(selected.size() == 1);
+
+ new GetDocumentsTask() {
+ @Override
+ void onDocumentsReady(List<DocumentInfo> docs) {
+ RenameDocumentFragment.show(getFragmentManager(), docs.get(0));
+ }
+ }.execute(selected);
+ }
+
@Override
public void initDocumentHolder(DocumentHolder holder) {
holder.addEventListener(mItemEventListener);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
index ef6d2c9..a295ab2 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
@@ -58,7 +58,8 @@
}
- public abstract void updateActionMenu(Menu menu, int dirType, boolean canDelete);
+ public abstract void updateActionMenu(Menu menu, int dirType, boolean canDelete,
+ boolean canRename);
// Subtly different from isDocumentEnabled. The reason may be illuminated as follows.
// A folder is enabled such that it may be double clicked, even in settings
@@ -129,7 +130,8 @@
}
@Override
- public void updateActionMenu(Menu menu, int dirType, boolean canDelete) {
+ public void updateActionMenu(Menu menu, int dirType, boolean canDelete,
+ boolean canRename) {
boolean copyEnabled = dirType != DirectoryFragment.TYPE_RECENT_OPEN;
boolean moveEnabled =
@@ -141,6 +143,7 @@
final MenuItem delete = menu.findItem(R.id.menu_delete);
final MenuItem copyTo = menu.findItem(R.id.menu_copy_to);
final MenuItem moveTo = menu.findItem(R.id.menu_move_to);
+ final MenuItem rename = menu.findItem(R.id.menu_rename);
open.setVisible(true);
share.setVisible(false);
@@ -149,6 +152,7 @@
copyTo.setEnabled(copyEnabled);
moveTo.setVisible(moveEnabled);
moveTo.setEnabled(moveEnabled);
+ rename.setVisible(false);
}
}
@@ -162,7 +166,8 @@
}
@Override
- public void updateActionMenu(Menu menu, int dirType, boolean canDelete) {
+ public void updateActionMenu(Menu menu, int dirType, boolean canDelete,
+ boolean canRename) {
checkArgument(dirType != DirectoryFragment.TYPE_RECENT_OPEN);
boolean moveEnabled =
@@ -174,6 +179,7 @@
final MenuItem delete = menu.findItem(R.id.menu_delete);
final MenuItem copyTo = menu.findItem(R.id.menu_copy_to);
final MenuItem moveTo = menu.findItem(R.id.menu_move_to);
+ final MenuItem rename = menu.findItem(R.id.menu_rename);
open.setVisible(false);
delete.setVisible(canDelete);
@@ -181,6 +187,7 @@
copyTo.setEnabled(true);
moveTo.setVisible(moveEnabled);
moveTo.setEnabled(moveEnabled);
+ rename.setVisible(false);
}
}
@@ -194,12 +201,17 @@
}
@Override
- public void updateActionMenu(Menu menu, int dirType, boolean canDelete) {
+ public void updateActionMenu(Menu menu, int dirType, boolean canDelete,
+ boolean canRename) {
MenuItem copy = menu.findItem(R.id.menu_copy_to_clipboard);
MenuItem paste = menu.findItem(R.id.menu_paste_from_clipboard);
copy.setEnabled(dirType != DirectoryFragment.TYPE_RECENT_OPEN);
+ MenuItem rename = menu.findItem(R.id.menu_rename);
+ rename.setVisible(true);
+ rename.setEnabled(canRename);
+
menu.findItem(R.id.menu_share).setVisible(true);
menu.findItem(R.id.menu_delete).setVisible(canDelete);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/RenameDocumentFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/RenameDocumentFragment.java
new file mode 100644
index 0000000..71708c1
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/RenameDocumentFragment.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2016 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.documentsui.dirlist;
+
+import static com.android.documentsui.Shared.TAG;
+import static com.android.internal.util.Preconditions.checkArgument;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.FragmentManager;
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.provider.DocumentsContract;
+import android.support.annotation.Nullable;
+import android.support.design.widget.Snackbar;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.TextView.OnEditorActionListener;
+
+import com.android.documentsui.BaseActivity;
+import com.android.documentsui.DocumentsApplication;
+import com.android.documentsui.model.DocumentInfo;
+import com.android.documentsui.R;
+import com.android.documentsui.Snackbars;
+/**
+ * Dialog to rename file or directory.
+ */
+class RenameDocumentFragment extends DialogFragment {
+ private static final String TAG_RENAME_DOCUMENT = "rename_document";
+ private DocumentInfo mDocument;
+
+ public static void show(FragmentManager fm, DocumentInfo document) {
+ final RenameDocumentFragment dialog = new RenameDocumentFragment();
+ dialog.mDocument = document;
+ dialog.show(fm, TAG_RENAME_DOCUMENT);
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ Context context = getActivity();
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
+ View view = dialogInflater.inflate(R.layout.dialog_file_name, null, false);
+
+ final EditText editText = (EditText) view.findViewById(android.R.id.text1);
+ editText.setText(mDocument.displayName);
+
+ builder.setTitle(R.string.menu_rename);
+ builder.setView(view);
+
+ builder.setPositiveButton(
+ android.R.string.ok,
+ new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ renameDocuments(editText.getText().toString());
+ }
+ });
+
+ builder.setNegativeButton(android.R.string.cancel, null);
+
+ final AlertDialog dialog = builder.create();
+
+ editText.setOnEditorActionListener(
+ new OnEditorActionListener() {
+ @Override
+ public boolean onEditorAction(
+ TextView view, int actionId, @Nullable KeyEvent event) {
+ if (event != null
+ && event.getKeyCode() == KeyEvent.KEYCODE_ENTER
+ && event.hasNoModifiers()) {
+ renameDocuments(editText.getText().toString());
+ dialog.dismiss();
+ return true;
+ }
+ return false;
+ }
+ });
+
+ return dialog;
+ }
+
+ private void renameDocuments(String newDisplayName) {
+ BaseActivity activity = (BaseActivity) getActivity();
+
+ new RenameDocumentsTask(activity, newDisplayName).execute(mDocument);
+ }
+
+ private class RenameDocumentsTask extends AsyncTask<DocumentInfo, Void, DocumentInfo> {
+ private final BaseActivity mActivity;
+ private final String mNewDisplayName;
+
+ public RenameDocumentsTask(BaseActivity activity, String newDisplayName) {
+ mActivity = activity;
+ mNewDisplayName = newDisplayName;
+ }
+
+ @Override
+ protected void onPreExecute() {
+ mActivity.setPending(true);
+ }
+
+ @Override
+ protected DocumentInfo doInBackground(DocumentInfo... document) {
+ checkArgument(document.length == 1);
+ final ContentResolver resolver = mActivity.getContentResolver();
+ ContentProviderClient client = null;
+
+ try {
+ client = DocumentsApplication.acquireUnstableProviderOrThrow(
+ resolver, document[0].derivedUri.getAuthority());
+ Uri newUri = DocumentsContract.renameDocument(
+ client, document[0].derivedUri, mNewDisplayName);
+ return DocumentInfo.fromUri(resolver, newUri);
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to rename file", e);
+ return null;
+ } finally {
+ ContentProviderClient.releaseQuietly(client);
+ }
+ }
+
+ @Override
+ protected void onPostExecute(DocumentInfo result) {
+ if (result == null) {
+ Snackbars.makeSnackbar(mActivity, R.string.rename_error, Snackbar.LENGTH_SHORT)
+ .show();
+ }
+
+ mActivity.setPending(false);
+ }
+ }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
index 83df18c..4b5499a 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
@@ -239,6 +239,10 @@
return (flags & Document.FLAG_SUPPORTS_DELETE) != 0;
}
+ public boolean isRenameSupported() {
+ return (flags & Document.FLAG_SUPPORTS_RENAME) != 0;
+ }
+
public boolean isGridTitlesHidden() {
return (flags & Document.FLAG_DIR_HIDE_GRID_TITLES) != 0;
}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
index a47d350..cc48786 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
@@ -660,7 +660,7 @@
this.documentId = getDocumentIdForFile(file);
this.mimeType = Document.MIME_TYPE_DIR;
this.streamTypes = new ArrayList<String>();
- this.flags = Document.FLAG_DIR_SUPPORTS_CREATE;
+ this.flags = Document.FLAG_DIR_SUPPORTS_CREATE | Document.FLAG_SUPPORTS_RENAME;
this.parentId = null;
this.rootInfo = rootInfo;
}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
index 409f6a7..c7d17dc 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -18,6 +18,7 @@
import android.app.Activity;
import android.app.AlertDialog;
import android.app.admin.DevicePolicyManager;
+import android.auditing.SecurityLog;
import android.content.Context;
import android.os.UserHandle;
import android.util.AttributeSet;
@@ -423,6 +424,11 @@
}
public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) {
+ if (SecurityLog.isLoggingEnabled()) {
+ SecurityLog.writeEvent(SecurityLog.TAG_DEVICE_UNLOCK_ATTEMPT,
+ (success ? 1 : 0),
+ mCurrentSecuritySelection.name());
+ }
KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
if (success) {
monitor.clearFailedUnlockAttempts();
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 9a00b4b..704de97 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -24,6 +24,7 @@
import android.app.SearchManager;
import android.app.StatusBarManager;
import android.app.trust.TrustManager;
+import android.auditing.SecurityLog;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -1352,6 +1353,11 @@
* @see #KEYGUARD_DONE
*/
private void handleKeyguardDone(boolean authenticated) {
+ if (SecurityLog.isLoggingEnabled()
+ && mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())) {
+ SecurityLog.writeEvent(SecurityLog.TAG_DEVICE_UNLOCK_ATTEMPT,
+ (authenticated ? 1 : 0), "Unknown");
+ }
if (DEBUG) Log.d(TAG, "handleKeyguardDone");
synchronized (this) {
resetKeyguardDonePendingLocked();
@@ -1463,6 +1469,10 @@
* @see #SHOW
*/
private void handleShow(Bundle options) {
+ if (SecurityLog.isLoggingEnabled()
+ && mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())) {
+ SecurityLog.writeEvent(SecurityLog.TAG_DEVICE_LOCKED, "");
+ }
synchronized (KeyguardViewMediator.this) {
if (!mSystemReady) {
if (DEBUG) Log.d(TAG, "ignoring handleShow because system is not ready.");
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index adb11a4..8c2090e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -20899,7 +20899,9 @@
}
public boolean isUserStopped(int userId) {
- return mUserController.getStartedUserStateLocked(userId) == null;
+ synchronized (this) {
+ return mUserController.getStartedUserStateLocked(userId) == null;
+ }
}
ActivityInfo getActivityInfoForUser(ActivityInfo aInfo, int userId) {
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index a3c26cb..05702af 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -77,8 +77,8 @@
}
/**
- * Loads the persistent recentTasks for {@code userId} into {@link #mRecentTasks} from
- * persistent storage. Does nothing if they are already loaded.
+ * Loads the persistent recentTasks for {@code userId} into this list from persistent storage.
+ * Does nothing if they are already loaded.
*
* @param userId the user Id
*/
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index f360dc2..f5da52e 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -297,7 +297,6 @@
checkType(guest.service);
if (registerServiceImpl(guest) != null) {
onServiceAdded(guest);
- onServiceAdded(guest);
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index b1fe68c..2ee74db 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -223,6 +223,8 @@
private WorkerHandler mHandler;
private final HandlerThread mRankingThread = new HandlerThread("ranker",
Process.THREAD_PRIORITY_BACKGROUND);
+ private final HandlerThread mAssistantThread = new HandlerThread("assistant",
+ Process.THREAD_PRIORITY_BACKGROUND);
private Light mNotificationLight;
Light mAttentionLight;
@@ -295,6 +297,7 @@
private static final int MY_UID = Process.myUid();
private static final int MY_PID = Process.myPid();
private RankingHandler mRankingHandler;
+ private Handler mAssistantHandler;
private static class Archive {
final int mBufferSize;
@@ -878,6 +881,7 @@
mHandler = new WorkerHandler();
mRankingThread.start();
+ mAssistantThread.start();
String[] extractorNames;
try {
extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
@@ -886,6 +890,7 @@
}
mUsageStats = new NotificationUsageStats(getContext());
mRankingHandler = new RankingHandlerWorker(mRankingThread.getLooper());
+ mAssistantHandler = new Handler(mAssistantThread.getLooper());
mRankingHelper = new RankingHelper(getContext(),
mRankingHandler,
mUsageStats,
@@ -1957,7 +1962,7 @@
@Override
public void setImportanceFromAssistant(INotificationListener token, String key,
- int importance, CharSequence explanation) {
+ int importance, CharSequence explanation) throws RemoteException {
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mNotificationList) {
@@ -2249,115 +2254,145 @@
+ " id=" + id + " notification=" + notification);
}
- mHandler.post(new Runnable() {
- @Override
- public void run() {
+ // Sanitize inputs
+ notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
+ Notification.PRIORITY_MAX);
- synchronized (mNotificationList) {
-
- // Sanitize inputs
- notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
- Notification.PRIORITY_MAX);
-
- // setup local book-keeping
- final StatusBarNotification n = new StatusBarNotification(
- pkg, opPkg, id, tag, callingUid, callingPid, 0, notification,
- user);
- NotificationRecord r = new NotificationRecord(getContext(), n);
- NotificationRecord old = mNotificationsByKey.get(n.getKey());
- if (old != null) {
- // Retain ranking information from previous record
- r.copyRankingInformation(old);
- }
-
- // Handle grouped notifications and bail out early if we
- // can to avoid extracting signals.
- handleGroupedNotificationLocked(r, old, callingUid, callingPid);
- boolean ignoreNotification =
- removeUnusedGroupedNotificationLocked(r, old, callingUid, callingPid);
-
- // This conditional is a dirty hack to limit the logging done on
- // behalf of the download manager without affecting other apps.
- if (!pkg.equals("com.android.providers.downloads")
- || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
- int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
- if (ignoreNotification) {
- enqueueStatus = EVENTLOG_ENQUEUE_STATUS_IGNORED;
- } else if (old != null) {
- enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
- }
- EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
- pkg, id, tag, userId, notification.toString(),
- enqueueStatus);
- }
-
- if (ignoreNotification) {
- return;
- }
-
- mRankingHelper.extractSignals(r);
- savePolicyFile();
-
- // blocked apps/topics
- if (r.getImportance() == NotificationListenerService.Ranking.IMPORTANCE_NONE
- || !noteNotificationOp(pkg, callingUid)) {
- if (!isSystemNotification) {
- Slog.e(TAG, "Suppressing notification from package " + pkg
- + " by user request.");
- mUsageStats.registerBlocked(r);
- return;
- }
- }
-
- int index = indexOfNotificationLocked(n.getKey());
- if (index < 0) {
- mNotificationList.add(r);
- mUsageStats.registerPostedByApp(r);
- } else {
- old = mNotificationList.get(index);
- mNotificationList.set(index, r);
- mUsageStats.registerUpdatedByApp(r, old);
- // Make sure we don't lose the foreground service state.
- notification.flags |=
- old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
- r.isUpdate = true;
- }
-
- mNotificationsByKey.put(n.getKey(), r);
-
- // Ensure if this is a foreground service that the proper additional
- // flags are set.
- if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
- notification.flags |= Notification.FLAG_ONGOING_EVENT
- | Notification.FLAG_NO_CLEAR;
- }
-
- applyZenModeLocked(r);
- mRankingHelper.sort(mNotificationList);
-
- if (notification.getSmallIcon() != null) {
- StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
- mListeners.notifyPostedLocked(n, oldSbn);
- } else {
- Slog.e(TAG, "Not posting notification without small icon: " + notification);
- if (old != null && !old.isCanceled) {
- mListeners.notifyRemovedLocked(n);
- }
- // ATTENTION: in a future release we will bail out here
- // so that we do not play sounds, show lights, etc. for invalid
- // notifications
- Slog.e(TAG, "WARNING: In a future release this will crash the app: "
- + n.getPackageName());
- }
-
- buzzBeepBlinkLocked(r);
- }
- }
- });
+ // setup local book-keeping
+ final StatusBarNotification n = new StatusBarNotification(
+ pkg, opPkg, id, tag, callingUid, callingPid, 0, notification,
+ user);
+ final NotificationRecord r = new NotificationRecord(getContext(), n);
+ mHandler.post(new EnqueueNotificationRunnable(userId, r));
idOut[0] = id;
}
+ private class EnqueueNotificationRunnable implements Runnable {
+ private final NotificationRecord r;
+ private final int userId;
+
+ EnqueueNotificationRunnable(int userId, NotificationRecord r) {
+ this.userId = userId;
+ this.r = r;
+ };
+
+ @Override
+ public void run() {
+
+ synchronized (mNotificationList) {
+ final StatusBarNotification n = r.sbn;
+ Slog.d(TAG, "EnqueueNotificationRunnable.run for: " + n.getKey());
+ NotificationRecord old = mNotificationsByKey.get(n.getKey());
+ if (old != null) {
+ // Retain ranking information from previous record
+ r.copyRankingInformation(old);
+ }
+
+ final int callingUid = n.getUid();
+ final int callingPid = n.getInitialPid();
+ final Notification notification = n.getNotification();
+ final String pkg = n.getPackageName();
+ final int id = n.getId();
+ final String tag = n.getTag();
+ final boolean isSystemNotification = isUidSystem(callingUid) ||
+ ("android".equals(pkg));
+
+ // Handle grouped notifications and bail out early if we
+ // can to avoid extracting signals.
+ handleGroupedNotificationLocked(r, old, callingUid, callingPid);
+ boolean ignoreNotification =
+ removeUnusedGroupedNotificationLocked(r, old, callingUid, callingPid);
+ Slog.d(TAG, "ignoreNotification is " + ignoreNotification);
+
+ // This conditional is a dirty hack to limit the logging done on
+ // behalf of the download manager without affecting other apps.
+ if (!pkg.equals("com.android.providers.downloads")
+ || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
+ int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
+ if (ignoreNotification) {
+ enqueueStatus = EVENTLOG_ENQUEUE_STATUS_IGNORED;
+ } else if (old != null) {
+ enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
+ }
+ EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
+ pkg, id, tag, userId, notification.toString(),
+ enqueueStatus);
+ }
+
+ if (ignoreNotification) {
+ return;
+ }
+
+ mRankingHelper.extractSignals(r);
+
+ // why is this here?
+ savePolicyFile();
+
+ // blocked apps/topics
+ if (r.getImportance() == NotificationListenerService.Ranking.IMPORTANCE_NONE
+ || !noteNotificationOp(pkg, callingUid)) {
+ if (!isSystemNotification) {
+ Slog.e(TAG, "Suppressing notification from package " + pkg
+ + " by user request.");
+ mUsageStats.registerBlocked(r);
+ return;
+ }
+ }
+
+ // tell the assistant about the notification
+ if (mAssistant.isEnabled()) {
+ mAssistant.onNotificationEnqueued(r);
+ // TODO delay the code below here for 100ms or until there is an answer
+ }
+
+
+ int index = indexOfNotificationLocked(n.getKey());
+ if (index < 0) {
+ mNotificationList.add(r);
+ mUsageStats.registerPostedByApp(r);
+ } else {
+ old = mNotificationList.get(index);
+ mNotificationList.set(index, r);
+ mUsageStats.registerUpdatedByApp(r, old);
+ // Make sure we don't lose the foreground service state.
+ notification.flags |=
+ old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
+ r.isUpdate = true;
+ }
+
+ mNotificationsByKey.put(n.getKey(), r);
+
+ // Ensure if this is a foreground service that the proper additional
+ // flags are set.
+ if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
+ notification.flags |= Notification.FLAG_ONGOING_EVENT
+ | Notification.FLAG_NO_CLEAR;
+ }
+
+ applyZenModeLocked(r);
+ mRankingHelper.sort(mNotificationList);
+
+ if (notification.getSmallIcon() != null) {
+ StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
+ mListeners.notifyPostedLocked(n, oldSbn);
+ } else {
+ Slog.e(TAG, "Not posting notification without small icon: " + notification);
+ if (old != null && !old.isCanceled) {
+ mListeners.notifyRemovedLocked(n);
+ }
+ // ATTENTION: in a future release we will bail out here
+ // so that we do not play sounds, show lights, etc. for invalid
+ // notifications
+ Slog.e(TAG, "WARNING: In a future release this will crash the app: "
+ + n.getPackageName());
+ }
+
+ buzzBeepBlinkLocked(r);
+ }
+ }
+ }
+
/**
* Ensures that grouped notification receive their special treatment.
*
@@ -3391,6 +3426,30 @@
return true;
}
+ private class TrimCache {
+ StatusBarNotification heavy;
+ StatusBarNotification sbnClone;
+ StatusBarNotification sbnCloneLight;
+
+ TrimCache(StatusBarNotification sbn) {
+ heavy = sbn;
+ }
+
+ StatusBarNotification ForListener(ManagedServiceInfo info) {
+ if (mListeners.getOnNotificationPostedTrim(info) == TRIM_LIGHT) {
+ if (sbnCloneLight == null) {
+ sbnCloneLight = heavy.cloneLight();
+ }
+ return sbnCloneLight;
+ } else {
+ if (sbnClone == null) {
+ sbnClone = heavy.clone();
+ }
+ return sbnClone;
+ }
+ }
+ }
+
public class NotificationAssistant extends ManagedServices {
public NotificationAssistant() {
@@ -3428,6 +3487,46 @@
protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
mListeners.unregisterService(removed.service, removed.userid);
}
+
+ public void onNotificationEnqueued(final NotificationRecord r) {
+ final StatusBarNotification sbn = r.sbn;
+ TrimCache trimCache = new TrimCache(sbn);
+
+ // mServices is the list inside ManagedServices of all the assistants,
+ // There should be only one, but it's a list, so while we enforce
+ // singularity elsewhere, we keep it general here, to avoid surprises.
+ for (final ManagedServiceInfo info : NotificationAssistant.this.mServices) {
+ boolean sbnVisible = isVisibleToListener(sbn, info);
+ if (!sbnVisible) {
+ continue;
+ }
+
+ final int importance = r.getImportance();
+ final boolean fromUser = r.isImportanceFromUser();
+ final StatusBarNotification sbnToPost = trimCache.ForListener(info);
+ mAssistantHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ notifyEnqueued(info, sbnToPost, importance, fromUser);
+ }
+ });
+ }
+ }
+
+ private void notifyEnqueued(final ManagedServiceInfo info,
+ final StatusBarNotification sbn, int importance, boolean fromUser) {
+ final INotificationListener assistant = (INotificationListener) info.service;
+ StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
+ try {
+ assistant.onNotificationEnqueued(sbnHolder, importance, fromUser);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex);
+ }
+ }
+
+ public boolean isEnabled() {
+ return !mServices.isEmpty();
+ }
}
public class NotificationListeners extends ManagedServices {
@@ -3476,7 +3575,6 @@
}
}
-
@Override
protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
if (mListenersDisablingEffects.remove(removed)) {
@@ -3508,8 +3606,7 @@
*/
public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
// Lazily initialized snapshots of the notification.
- StatusBarNotification sbnClone = null;
- StatusBarNotification sbnCloneLight = null;
+ TrimCache trimCache = new TrimCache(sbn);
for (final ManagedServiceInfo info : mServices) {
boolean sbnVisible = isVisibleToListener(sbn, info);
@@ -3532,16 +3629,7 @@
continue;
}
- final int trim = mListeners.getOnNotificationPostedTrim(info);
-
- if (trim == TRIM_LIGHT && sbnCloneLight == null) {
- sbnCloneLight = sbn.cloneLight();
- } else if (trim == TRIM_FULL && sbnClone == null) {
- sbnClone = sbn.clone();
- }
- final StatusBarNotification sbnToPost =
- (trim == TRIM_FULL) ? sbnClone : sbnCloneLight;
-
+ final StatusBarNotification sbnToPost = trimCache.ForListener(info);
mHandler.post(new Runnable() {
@Override
public void run() {
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 0be2edd..490e890 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -15,6 +15,7 @@
*/
package com.android.server.notification;
+import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_DEFAULT;
import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH;
import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_LOW;
@@ -88,8 +89,8 @@
private int mAuthoritativeRank;
private String mGlobalSortKey;
private int mPackageVisibility;
- private int mTopicImportance = NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
- private int mImportance = NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
+ private int mTopicImportance = IMPORTANCE_UNSPECIFIED;
+ private int mImportance = IMPORTANCE_UNSPECIFIED;
private CharSequence mImportanceExplanation = null;
private int mSuppressedVisualEffects = 0;
@@ -510,4 +511,8 @@
public String getGroupKey() {
return sbn.getGroupKey();
}
+
+ public boolean isImportanceFromUser() {
+ return mImportance == mTopicImportance;
+ }
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 87d0700..39983dd 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -92,6 +92,8 @@
import com.android.internal.R;
import com.android.server.EventLogTags;
+import libcore.io.IoUtils;
+
public class WallpaperManagerService extends IWallpaperManager.Stub {
static final String TAG = "WallpaperManagerService";
static final boolean DEBUG = false;
@@ -170,6 +172,12 @@
WallpaperData mLastWallpaper;
/**
+ * ID of the current wallpaper, changed every time anything sets a wallpaper.
+ * This is used for external detection of wallpaper update activity.
+ */
+ int mWallpaperId;
+
+ /**
* Name of the component used to display bitmap wallpapers from either the gallery or
* built-in wallpapers.
*/
@@ -205,6 +213,11 @@
*/
ComponentName nextWallpaperComponent;
+ /**
+ * The ID of this wallpaper
+ */
+ int wallpaperId;
+
WallpaperConnection connection;
long lastDiedTime;
boolean wallpaperUpdating;
@@ -227,6 +240,13 @@
}
}
+ int makeWallpaperIdLocked() {
+ do {
+ ++mWallpaperId;
+ } while (mWallpaperId == 0);
+ return mWallpaperId;
+ }
+
class WallpaperConnection extends IWallpaperConnection.Stub
implements ServiceConnection {
final WallpaperInfo mInfo;
@@ -333,7 +353,7 @@
public ParcelFileDescriptor setWallpaper(String name) {
synchronized (mLock) {
if (mWallpaper.connection == this) {
- return updateWallpaperBitmapLocked(name, mWallpaper);
+ return updateWallpaperBitmapLocked(name, mWallpaper, null);
}
return null;
}
@@ -848,18 +868,26 @@
}
}
- public ParcelFileDescriptor setWallpaper(String name, String callingPackage) {
+ @Override
+ public ParcelFileDescriptor setWallpaper(String name, String callingPackage, Bundle extras,
+ int which) {
checkPermission(android.Manifest.permission.SET_WALLPAPER);
+
+ if (which == 0) {
+ return null;
+ }
+
if (!isWallpaperSupported(callingPackage)) {
return null;
}
+
synchronized (mLock) {
if (DEBUG) Slog.v(TAG, "setWallpaper");
int userId = UserHandle.getCallingUserId();
WallpaperData wallpaper = getWallpaperSafeLocked(userId);
final long ident = Binder.clearCallingIdentity();
try {
- ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name, wallpaper);
+ ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name, wallpaper, extras);
if (pfd != null) {
wallpaper.imageWallpaperPending = true;
}
@@ -870,7 +898,8 @@
}
}
- ParcelFileDescriptor updateWallpaperBitmapLocked(String name, WallpaperData wallpaper) {
+ ParcelFileDescriptor updateWallpaperBitmapLocked(String name, WallpaperData wallpaper,
+ Bundle extras) {
if (name == null) name = "";
try {
File dir = getWallpaperDir(wallpaper.userId);
@@ -888,6 +917,14 @@
return null;
}
wallpaper.name = name;
+ wallpaper.wallpaperId = makeWallpaperIdLocked();
+ if (extras != null) {
+ extras.putInt(WallpaperManager.EXTRA_NEW_WALLPAPER_ID, wallpaper.wallpaperId);
+ }
+ if (DEBUG) {
+ Slog.v(TAG, "updateWallpaperBitmapLocked() : id=" + wallpaper.wallpaperId
+ + " name=" + name);
+ }
return fd;
} catch (FileNotFoundException e) {
Slog.w(TAG, "Error setting wallpaper", e);
@@ -1156,6 +1193,7 @@
out.startDocument(null, true);
out.startTag(null, "wp");
+ out.attribute(null, "id", Integer.toString(wallpaper.wallpaperId));
out.attribute(null, "width", Integer.toString(wallpaper.width));
out.attribute(null, "height", Integer.toString(wallpaper.height));
if (wallpaper.padding.left != 0) {
@@ -1184,13 +1222,7 @@
stream.close();
journal.commit();
} catch (IOException e) {
- try {
- if (stream != null) {
- stream.close();
- }
- } catch (IOException ex) {
- // Ignore
- }
+ IoUtils.closeQuietly(stream);
journal.rollback();
}
}
@@ -1259,6 +1291,16 @@
if (type == XmlPullParser.START_TAG) {
String tag = parser.getName();
if ("wp".equals(tag)) {
+ final String idString = parser.getAttributeValue(null, "id");
+ if (idString != null) {
+ final int id = wallpaper.wallpaperId = Integer.parseInt(idString);
+ if (id > mWallpaperId) {
+ mWallpaperId = id;
+ }
+ } else {
+ wallpaper.wallpaperId = makeWallpaperIdLocked();
+ }
+
wallpaper.width = Integer.parseInt(parser.getAttributeValue(null, "width"));
wallpaper.height = Integer.parseInt(parser
.getAttributeValue(null, "height"));
@@ -1301,19 +1343,21 @@
} catch (IndexOutOfBoundsException e) {
Slog.w(TAG, "failed parsing " + file + " " + e);
}
- try {
- if (stream != null) {
- stream.close();
- }
- } catch (IOException e) {
- // Ignore
- }
+ IoUtils.closeQuietly(stream);
if (!success) {
wallpaper.width = -1;
wallpaper.height = -1;
wallpaper.padding.set(0, 0, 0, 0);
wallpaper.name = "";
+ } else {
+ if (wallpaper.wallpaperId <= 0) {
+ wallpaper.wallpaperId = makeWallpaperIdLocked();
+ if (DEBUG) {
+ Slog.w(TAG, "Didn't set wallpaper id in loadSettingsLocked(" + userId
+ + "); now " + wallpaper.wallpaperId);
+ }
+ }
}
// We always want to have some reasonable width hint.
@@ -1346,6 +1390,7 @@
synchronized (mLock) {
loadSettingsLocked(0);
wallpaper = mWallpaperMap.get(0);
+ wallpaper.wallpaperId = makeWallpaperIdLocked(); // always bump id at restore
if (wallpaper.nextWallpaperComponent != null
&& !wallpaper.nextWallpaperComponent.equals(mImageWallpaper)) {
if (!bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, false, false,
@@ -1366,7 +1411,8 @@
if (DEBUG) Slog.v(TAG, "settingsRestored: attempting to restore named resource");
success = restoreNamedResourceLocked(wallpaper);
}
- if (DEBUG) Slog.v(TAG, "settingsRestored: success=" + success);
+ if (DEBUG) Slog.v(TAG, "settingsRestored: success=" + success
+ + " id=" + wallpaper.wallpaperId);
if (success) {
bindWallpaperComponentLocked(wallpaper.nextWallpaperComponent, false, false,
wallpaper, null);
@@ -1442,16 +1488,10 @@
} catch (IOException e) {
Slog.e(TAG, "IOException while restoring wallpaper ", e);
} finally {
- if (res != null) {
- try {
- res.close();
- } catch (IOException ex) {}
- }
+ IoUtils.closeQuietly(res);
if (fos != null) {
FileUtils.sync(fos);
- try {
- fos.close();
- } catch (IOException ex) {}
+ IoUtils.closeQuietly(fos);
}
}
}
@@ -1474,7 +1514,8 @@
pw.println("Current Wallpaper Service state:");
for (int i = 0; i < mWallpaperMap.size(); i++) {
WallpaperData wallpaper = mWallpaperMap.valueAt(i);
- pw.println(" User " + wallpaper.userId + ":");
+ pw.print(" User "); pw.print(wallpaper.userId);
+ pw.print(": id="); pw.println(wallpaper.wallpaperId);
pw.print(" mWidth=");
pw.print(wallpaper.width);
pw.print(" mHeight=");
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 4253cd4..f2949bf4 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -558,6 +558,7 @@
}
mRealTimeSnapshot = actualRealtime;
mSystemTimeSnapshot = actualSystemTime;
+ postCheckIdleStates(UserHandle.USER_ALL);
}
return actualSystemTime;
}
@@ -602,7 +603,7 @@
|| event.mEventType == Event.SYSTEM_INTERACTION
|| event.mEventType == Event.USER_INTERACTION)) {
if (previouslyIdle) {
- // Slog.d(TAG, "Informing listeners of out-of-idle " + event.mPackage);
+ //Slog.d(TAG, "Informing listeners of out-of-idle " + event.mPackage);
mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
/* idle = */ 0, event.mPackage));
notifyBatteryStats(event.mPackage, userId, false);
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index 364a55b..f3a9ea3 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -56,6 +56,7 @@
Maybe<std::string> generateProguardRulesPath;
bool noAutoVersion = false;
bool staticLib = false;
+ bool generateNonFinalIds = false;
bool verbose = false;
bool outputToDirectory = false;
bool autoAddOverlay = false;
@@ -835,7 +836,7 @@
JavaClassGeneratorOptions options;
options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
- if (mOptions.staticLib) {
+ if (mOptions.staticLib || mOptions.generateNonFinalIds) {
options.useFinal = false;
}
@@ -933,6 +934,9 @@
.optionalFlag("--version-name", "Version name to inject into the AndroidManifest.xml "
"if none is present", &versionName)
.optionalSwitch("--static-lib", "Generate a static Android library", &options.staticLib)
+ .optionalSwitch("--non-final-ids", "Generates R.java without the final modifier.\n"
+ "This is implied when --static-lib is specified.",
+ &options.generateNonFinalIds)
.optionalFlag("--private-symbols", "Package name to use when generating R.java for "
"private symbols.\n"
"If not specified, public and private symbols will use the application's "