Merge "Revert "Precompute cursor indexes in DocumentsUI and improve perf by 2.7%."" into nyc-dev
diff --git a/Android.mk b/Android.mk
index 1cde699..7d7aa48 100644
--- a/Android.mk
+++ b/Android.mk
@@ -598,6 +598,7 @@
frameworks/base/core/java/android/net/Uri.aidl \
frameworks/base/core/java/android/net/NetworkRequest.aidl \
frameworks/base/core/java/android/net/LinkAddress.aidl \
+ frameworks/base/core/java/android/util/MemoryIntArray.aidl \
frameworks/base/core/java/android/view/Display.aidl \
frameworks/base/core/java/android/view/InputDevice.aidl \
frameworks/base/core/java/android/view/InputEvent.aidl \
diff --git a/api/current.txt b/api/current.txt
index 9834c8c..f267789 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -513,6 +513,7 @@
field public static final int elevation = 16843840; // 0x1010440
field public static final int ellipsize = 16842923; // 0x10100ab
field public static final int ems = 16843096; // 0x1010158
+ field public static final int enableVrMode = 16844072; // 0x1010528
field public static final int enabled = 16842766; // 0x101000e
field public static final int end = 16843996; // 0x10104dc
field public static final int endColor = 16843166; // 0x101019e
@@ -32491,7 +32492,6 @@
field public static final java.lang.String RADIO_WIFI = "wifi";
field public static final java.lang.String SHOW_PROCESSES = "show_processes";
field public static final java.lang.String STAY_ON_WHILE_PLUGGED_IN = "stay_on_while_plugged_in";
- field public static final java.lang.String SYS_PROP_SETTING_VERSION = "sys.settings_global_version";
field public static final java.lang.String TRANSITION_ANIMATION_SCALE = "transition_animation_scale";
field public static final java.lang.String USB_MASS_STORAGE_ENABLED = "usb_mass_storage_enabled";
field public static final java.lang.String USE_GOOGLE_MAIL = "use_google_mail";
@@ -32551,7 +32551,6 @@
field public static final deprecated java.lang.String DEVICE_PROVISIONED = "device_provisioned";
field public static final java.lang.String ENABLED_ACCESSIBILITY_SERVICES = "enabled_accessibility_services";
field public static final java.lang.String ENABLED_INPUT_METHODS = "enabled_input_methods";
- field public static final java.lang.String ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES = "enabled_notification_policy_access_packages";
field public static final deprecated java.lang.String HTTP_PROXY = "http_proxy";
field public static final java.lang.String INPUT_METHOD_SELECTOR_VISIBILITY = "input_method_selector_visibility";
field public static final java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
@@ -32572,7 +32571,6 @@
field public static final java.lang.String SELECTED_INPUT_METHOD_SUBTYPE = "selected_input_method_subtype";
field public static final java.lang.String SETTINGS_CLASSNAME = "settings_classname";
field public static final java.lang.String SKIP_FIRST_USE_HINTS = "skip_first_use_hints";
- field public static final java.lang.String SYS_PROP_SETTING_VERSION = "sys.settings_secure_version";
field public static final java.lang.String TOUCH_EXPLORATION_ENABLED = "touch_exploration_enabled";
field public static final deprecated java.lang.String TTS_DEFAULT_COUNTRY = "tts_default_country";
field public static final deprecated java.lang.String TTS_DEFAULT_LANG = "tts_default_lang";
@@ -32687,7 +32685,6 @@
field public static final deprecated java.lang.String SHOW_WEB_SUGGESTIONS = "show_web_suggestions";
field public static final java.lang.String SOUND_EFFECTS_ENABLED = "sound_effects_enabled";
field public static final deprecated java.lang.String STAY_ON_WHILE_PLUGGED_IN = "stay_on_while_plugged_in";
- field public static final java.lang.String SYS_PROP_SETTING_VERSION = "sys.settings_system_version";
field public static final java.lang.String TEXT_AUTO_CAPS = "auto_caps";
field public static final java.lang.String TEXT_AUTO_PUNCTUATE = "auto_punctuate";
field public static final java.lang.String TEXT_AUTO_REPLACE = "auto_replace";
diff --git a/api/system-current.txt b/api/system-current.txt
index 2755bc8..5a62d98 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -617,6 +617,7 @@
field public static final int elevation = 16843840; // 0x1010440
field public static final int ellipsize = 16842923; // 0x10100ab
field public static final int ems = 16843096; // 0x1010158
+ field public static final int enableVrMode = 16844072; // 0x1010528
field public static final int enabled = 16842766; // 0x101000e
field public static final int end = 16843996; // 0x10104dc
field public static final int endColor = 16843166; // 0x101019e
@@ -35212,7 +35213,6 @@
field public static final java.lang.String RADIO_WIFI = "wifi";
field public static final java.lang.String SHOW_PROCESSES = "show_processes";
field public static final java.lang.String STAY_ON_WHILE_PLUGGED_IN = "stay_on_while_plugged_in";
- field public static final java.lang.String SYS_PROP_SETTING_VERSION = "sys.settings_global_version";
field public static final java.lang.String THEATER_MODE_ON = "theater_mode_on";
field public static final java.lang.String TRANSITION_ANIMATION_SCALE = "transition_animation_scale";
field public static final java.lang.String USB_MASS_STORAGE_ENABLED = "usb_mass_storage_enabled";
@@ -35274,7 +35274,6 @@
field public static final deprecated java.lang.String DEVICE_PROVISIONED = "device_provisioned";
field public static final java.lang.String ENABLED_ACCESSIBILITY_SERVICES = "enabled_accessibility_services";
field public static final java.lang.String ENABLED_INPUT_METHODS = "enabled_input_methods";
- field public static final java.lang.String ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES = "enabled_notification_policy_access_packages";
field public static final deprecated java.lang.String HTTP_PROXY = "http_proxy";
field public static final java.lang.String INPUT_METHOD_SELECTOR_VISIBILITY = "input_method_selector_visibility";
field public static final java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
@@ -35295,7 +35294,6 @@
field public static final java.lang.String SELECTED_INPUT_METHOD_SUBTYPE = "selected_input_method_subtype";
field public static final java.lang.String SETTINGS_CLASSNAME = "settings_classname";
field public static final java.lang.String SKIP_FIRST_USE_HINTS = "skip_first_use_hints";
- field public static final java.lang.String SYS_PROP_SETTING_VERSION = "sys.settings_secure_version";
field public static final java.lang.String TOUCH_EXPLORATION_ENABLED = "touch_exploration_enabled";
field public static final deprecated java.lang.String TTS_DEFAULT_COUNTRY = "tts_default_country";
field public static final deprecated java.lang.String TTS_DEFAULT_LANG = "tts_default_lang";
@@ -35410,7 +35408,6 @@
field public static final deprecated java.lang.String SHOW_WEB_SUGGESTIONS = "show_web_suggestions";
field public static final java.lang.String SOUND_EFFECTS_ENABLED = "sound_effects_enabled";
field public static final deprecated java.lang.String STAY_ON_WHILE_PLUGGED_IN = "stay_on_while_plugged_in";
- field public static final java.lang.String SYS_PROP_SETTING_VERSION = "sys.settings_system_version";
field public static final java.lang.String TEXT_AUTO_CAPS = "auto_caps";
field public static final java.lang.String TEXT_AUTO_PUNCTUATE = "auto_punctuate";
field public static final java.lang.String TEXT_AUTO_REPLACE = "auto_replace";
diff --git a/api/test-current.txt b/api/test-current.txt
index 5dc6d51..25e58e2 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -513,6 +513,7 @@
field public static final int elevation = 16843840; // 0x1010440
field public static final int ellipsize = 16842923; // 0x10100ab
field public static final int ems = 16843096; // 0x1010158
+ field public static final int enableVrMode = 16844072; // 0x1010528
field public static final int enabled = 16842766; // 0x101000e
field public static final int end = 16843996; // 0x10104dc
field public static final int endColor = 16843166; // 0x101019e
@@ -32563,7 +32564,6 @@
field public static final java.lang.String RADIO_WIFI = "wifi";
field public static final java.lang.String SHOW_PROCESSES = "show_processes";
field public static final java.lang.String STAY_ON_WHILE_PLUGGED_IN = "stay_on_while_plugged_in";
- field public static final java.lang.String SYS_PROP_SETTING_VERSION = "sys.settings_global_version";
field public static final java.lang.String TRANSITION_ANIMATION_SCALE = "transition_animation_scale";
field public static final java.lang.String USB_MASS_STORAGE_ENABLED = "usb_mass_storage_enabled";
field public static final java.lang.String USE_GOOGLE_MAIL = "use_google_mail";
@@ -32644,7 +32644,6 @@
field public static final java.lang.String SELECTED_INPUT_METHOD_SUBTYPE = "selected_input_method_subtype";
field public static final java.lang.String SETTINGS_CLASSNAME = "settings_classname";
field public static final java.lang.String SKIP_FIRST_USE_HINTS = "skip_first_use_hints";
- field public static final java.lang.String SYS_PROP_SETTING_VERSION = "sys.settings_secure_version";
field public static final java.lang.String TOUCH_EXPLORATION_ENABLED = "touch_exploration_enabled";
field public static final deprecated java.lang.String TTS_DEFAULT_COUNTRY = "tts_default_country";
field public static final deprecated java.lang.String TTS_DEFAULT_LANG = "tts_default_lang";
@@ -32760,7 +32759,6 @@
field public static final deprecated java.lang.String SHOW_WEB_SUGGESTIONS = "show_web_suggestions";
field public static final java.lang.String SOUND_EFFECTS_ENABLED = "sound_effects_enabled";
field public static final deprecated java.lang.String STAY_ON_WHILE_PLUGGED_IN = "stay_on_while_plugged_in";
- field public static final java.lang.String SYS_PROP_SETTING_VERSION = "sys.settings_system_version";
field public static final java.lang.String TEXT_AUTO_CAPS = "auto_caps";
field public static final java.lang.String TEXT_AUTO_PUNCTUATE = "auto_punctuate";
field public static final java.lang.String TEXT_AUTO_REPLACE = "auto_replace";
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index d44a1df..a45d432 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -464,6 +464,9 @@
case "-g":
sessionParams.installFlags |= PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS;
break;
+ case "--dont-kill":
+ sessionParams.installFlags |= PackageManager.INSTALL_DONT_KILL_APP;
+ break;
case "--originating-uri":
sessionParams.originatingUri = Uri.parse(nextOptionData());
break;
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 2be55317..3050ac8 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -510,11 +510,15 @@
}
// Setup jit profile support.
+ //
// It is ok to call this multiple times if the application gets updated with new splits.
// The runtime only keeps track of unique code paths and can handle re-registration of
// the same code path. There's no need to pass `addedPaths` since any new code paths
// are already in `mApplicationInfo`.
- if (needToSetupJitProfiles) {
+ //
+ // It is NOT ok to call this function from the system_server (for any of the packages it
+ // loads code from) so we explicitly disallow it there.
+ if (needToSetupJitProfiles && !ActivityThread.isSystem()) {
// Temporarily disable logging of disk reads/writes on the Looper thread
// as this is early and necessary. Write is only needed to create the
// profile file if it's not already there.
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 0f4cbbb..167befc 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -191,6 +191,13 @@
public int resizeMode;
/**
+ * Name of the VrListenerService component to run for this activity.
+ * @see android.R.attr#enableVrMode
+ * @hide
+ */
+ public String requestedVrComponent;
+
+ /**
* Bit in {@link #flags} indicating whether this activity is able to
* run in multiple processes. If
* true, the system may instantiate it in the some process as the
@@ -794,6 +801,7 @@
lockTaskLaunchMode = orig.lockTaskLaunchMode;
windowLayout = orig.windowLayout;
resizeMode = orig.resizeMode;
+ requestedVrComponent = orig.requestedVrComponent;
}
/**
@@ -896,6 +904,9 @@
+ windowLayout.heightFraction + ", " + windowLayout.gravity);
}
pw.println(prefix + "resizeMode=" + resizeModeToString(resizeMode));
+ if (requestedVrComponent != null) {
+ pw.println(prefix + "requestedVrComponent=" + requestedVrComponent);
+ }
super.dumpBack(pw, prefix, flags);
}
@@ -939,6 +950,7 @@
dest.writeInt(0);
}
dest.writeInt(resizeMode);
+ dest.writeString(requestedVrComponent);
}
public static final Parcelable.Creator<ActivityInfo> CREATOR
@@ -972,6 +984,7 @@
windowLayout = new WindowLayout(source);
}
resizeMode = source.readInt();
+ requestedVrComponent = source.readString();
}
/**
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index f42a340..6502022 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3532,6 +3532,9 @@
a.info.encryptionAware = a.info.directBootAware = sa.getBoolean(
R.styleable.AndroidManifestActivity_directBootAware,
false);
+
+ a.info.requestedVrComponent =
+ sa.getString(R.styleable.AndroidManifestActivity_enableVrMode);
} else {
a.info.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
a.info.configChanges = 0;
@@ -5096,6 +5099,19 @@
return latestUse;
}
+ public long getLatestForegroundPackageUseTimeInMills() {
+ int[] foregroundReasons = {
+ PackageManager.NOTIFY_PACKAGE_USE_ACTIVITY,
+ PackageManager.NOTIFY_PACKAGE_USE_FOREGROUND_SERVICE
+ };
+
+ long latestUse = 0L;
+ for (int reason : foregroundReasons) {
+ latestUse = Math.max(latestUse, mLastPackageUsageTimeInMills[reason]);
+ }
+ return latestUse;
+ }
+
public String toString() {
return "Package{"
+ Integer.toHexString(System.identityHashCode(this))
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 9070ad9..faf5c64 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -81,13 +81,6 @@
public class ConnectivityManager {
private static final String TAG = "ConnectivityManager";
- private static final SparseArray<String> sMagicDecoderRing = MessageUtils.findMessageNames(
- new Class[]{ConnectivityManager.class}, new String[]{"CALLBACK_"});
-
- private static final String whatToString(int what) {
- return sMagicDecoderRing.get(what, Integer.toString(what));
- }
-
/**
* A change in network connectivity has occurred. A default connection has either
* been established or lost. The NetworkInfo for the affected network is
@@ -3360,4 +3353,32 @@
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * A holder class for debug info (mapping CALLBACK values to field names). This is stored
+ * in a holder for two reasons:
+ * 1) The reflection necessary to establish the map can't be run at compile-time. Thus, this
+ * code will make the enclosing class not compile-time initializeable, deferring its
+ * initialization to zygote startup. This leads to dirty (but shared) memory.
+ * As this is debug info, use a holder that isn't initialized by default. This way the map
+ * will be created on demand, while ConnectivityManager can be compile-time initialized.
+ * 2) Static initialization is still preferred for its strong thread safety guarantees without
+ * requiring a lock.
+ */
+ private static class NoPreloadHolder {
+ public static final SparseArray<String> sMagicDecoderRing = MessageUtils.findMessageNames(
+ new Class[]{ConnectivityManager.class}, new String[]{"CALLBACK_"});
+ }
+
+ static {
+ // When debug is enabled, aggressively initialize the holder by touching the field (which
+ // will guarantee static initialization).
+ if (CallbackHandler.DBG) {
+ Object dummy = NoPreloadHolder.sMagicDecoderRing;
+ }
+ }
+
+ private static final String whatToString(int what) {
+ return NoPreloadHolder.sMagicDecoderRing.get(what, Integer.toString(what));
+ }
}
diff --git a/core/java/android/net/ConnectivityMetricsEvent.java b/core/java/android/net/ConnectivityMetricsEvent.java
index 5153ba9..71ba65f 100644
--- a/core/java/android/net/ConnectivityMetricsEvent.java
+++ b/core/java/android/net/ConnectivityMetricsEvent.java
@@ -77,8 +77,8 @@
}
public String toString() {
- return String.format("ConnectivityMetricsEvent(%d, %d, %d)", timestamp,
- componentTag, eventTag);
+ return String.format("ConnectivityMetricsEvent(%d, %d, %d): %s", timestamp,
+ componentTag, eventTag, data);
}
/** {@hide} */
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d80d4be..4ca1b97 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -16,6 +16,7 @@
package android.provider;
+import android.annotation.NonNull;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
@@ -50,7 +51,6 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.Build.VERSION_CODES;
import android.speech.tts.TextToSpeech;
@@ -61,9 +61,12 @@
import android.util.LocaleList;
import android.util.Log;
+import android.util.MemoryIntArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
import com.android.internal.widget.ILockSettings;
+import java.io.IOException;
import java.net.URISyntaxException;
import java.text.SimpleDateFormat;
import java.util.HashMap;
@@ -1282,6 +1285,29 @@
public static final String CALL_METHOD_GET_GLOBAL = "GET_global";
/**
+ * @hide - Specifies that the caller of the fast-path call()-based flow tracks
+ * the settings generation in order to cache values locally. If this key is
+ * mapped to a <code>null</code> string extra in the request bundle, the response
+ * bundle will contain the same key mapped to a parcelable extra which would be
+ * an {@link android.util.MemoryIntArray}. The response will also contain an
+ * integer mapped to the {@link #CALL_METHOD_GENERATION_INDEX_KEY} which is the
+ * index in the array clients should use to lookup the generation. For efficiency
+ * the caller should request the generation tracking memory array only if it
+ * doesn't already have it.
+ *
+ * @see #CALL_METHOD_GENERATION_INDEX_KEY
+ */
+ public static final String CALL_METHOD_TRACK_GENERATION_KEY = "_track_generation";
+
+ /**
+ * @hide Key with the location in the {@link android.util.MemoryIntArray} where
+ * to look up the generation id of the backing table.
+ *
+ * @see #CALL_METHOD_TRACK_GENERATION_KEY
+ */
+ public static final String CALL_METHOD_GENERATION_INDEX_KEY = "_generation_index";
+
+ /**
* @hide - User handle argument extra to the fast-path call()-based requests
*/
public static final String CALL_METHOD_USER_KEY = "_user";
@@ -1424,9 +1450,42 @@
}
}
+ private static final class GenerationTracker {
+ private final MemoryIntArray mArray;
+ private final int mIndex;
+ private int mCurrentGeneration;
+
+ public GenerationTracker(@NonNull MemoryIntArray array, int index) {
+ mArray = array;
+ mIndex = index;
+ mCurrentGeneration = readCurrentGeneration();
+ }
+
+ public boolean isGenerationChanged() {
+ final int currentGeneration = readCurrentGeneration();
+ if (currentGeneration >= 0) {
+ if (currentGeneration == mCurrentGeneration) {
+ return false;
+ }
+ mCurrentGeneration = currentGeneration;
+ }
+ return true;
+ }
+
+ private int readCurrentGeneration() {
+ try {
+ return mArray.get(mIndex);
+ } catch (IOException e) {
+ Log.e(TAG, "Error getting current generation", e);
+ }
+ return -1;
+ }
+ }
+
// Thread-safe.
private static class NameValueCache {
- private final String mVersionSystemProperty;
+ private static final boolean DEBUG = false;
+
private final Uri mUri;
private static final String[] SELECT_VALUE =
@@ -1435,7 +1494,6 @@
// Must synchronize on 'this' to access mValues and mValuesVersion.
private final HashMap<String, String> mValues = new HashMap<String, String>();
- private long mValuesVersion = 0;
// Initially null; set lazily and held forever. Synchronized on 'this'.
private IContentProvider mContentProvider = null;
@@ -1445,9 +1503,10 @@
private final String mCallGetCommand;
private final String mCallSetCommand;
- public NameValueCache(String versionSystemProperty, Uri uri,
- String getCommand, String setCommand) {
- mVersionSystemProperty = versionSystemProperty;
+ @GuardedBy("this")
+ private GenerationTracker mGenerationTracker;
+
+ public NameValueCache(Uri uri, String getCommand, String setCommand) {
mUri = uri;
mCallGetCommand = getCommand;
mCallSetCommand = setCommand;
@@ -1482,22 +1541,18 @@
public String getStringForUser(ContentResolver cr, String name, final int userHandle) {
final boolean isSelf = (userHandle == UserHandle.myUserId());
if (isSelf) {
- long newValuesVersion = SystemProperties.getLong(mVersionSystemProperty, 0);
-
- // Our own user's settings data uses a client-side cache
synchronized (this) {
- if (mValuesVersion != newValuesVersion) {
- if (LOCAL_LOGV || false) {
- Log.v(TAG, "invalidate [" + mUri.getLastPathSegment() + "]: current "
- + newValuesVersion + " != cached " + mValuesVersion);
+ if (mGenerationTracker != null) {
+ if (mGenerationTracker.isGenerationChanged()) {
+ if (DEBUG) {
+ Log.i(TAG, "Generation changed for type:"
+ + mUri.getPath() + " in package:"
+ + cr.getPackageName() +" and user:" + userHandle);
+ }
+ mValues.clear();
+ } else if (mValues.containsKey(name)) {
+ return mValues.get(name);
}
-
- mValues.clear();
- mValuesVersion = newValuesVersion;
- }
-
- if (mValues.containsKey(name)) {
- return mValues.get(name); // Could be null, that's OK -- negative caching
}
}
} else {
@@ -1518,12 +1573,42 @@
args = new Bundle();
args.putInt(CALL_METHOD_USER_KEY, userHandle);
}
+ boolean needsGenerationTracker = false;
+ synchronized (this) {
+ if (isSelf && mGenerationTracker == null) {
+ needsGenerationTracker = true;
+ if (args == null) {
+ args = new Bundle();
+ }
+ args.putString(CALL_METHOD_TRACK_GENERATION_KEY, null);
+ if (DEBUG) {
+ Log.i(TAG, "Requested generation tracker for type: "+ mUri.getPath()
+ + " in package:" + cr.getPackageName() +" and user:"
+ + userHandle);
+ }
+ }
+ }
Bundle b = cp.call(cr.getPackageName(), mCallGetCommand, name, args);
if (b != null) {
- String value = b.getPairValue();
+ String value = b.getString(Settings.NameValueTable.VALUE);
// Don't update our cache for reads of other users' data
if (isSelf) {
synchronized (this) {
+ if (needsGenerationTracker) {
+ MemoryIntArray array = b.getParcelable(
+ CALL_METHOD_TRACK_GENERATION_KEY);
+ final int index = b.getInt(
+ CALL_METHOD_GENERATION_INDEX_KEY, -1);
+ if (array != null && index >= 0) {
+ if (DEBUG) {
+ Log.i(TAG, "Received generation tracker for type:"
+ + mUri.getPath() + " in package:"
+ + cr.getPackageName() + " and user:"
+ + userHandle + " with index:" + index);
+ }
+ mGenerationTracker = new GenerationTracker(array, index);
+ }
+ }
mValues.put(name, value);
}
} else {
@@ -1592,8 +1677,6 @@
* functions for accessing individual settings entries.
*/
public static final class System extends NameValueTable {
- public static final String SYS_PROP_SETTING_VERSION = "sys.settings_system_version";
-
private static final float DEFAULT_FONT_SCALE = 1.0f;
/** @hide */
@@ -1608,7 +1691,6 @@
Uri.parse("content://" + AUTHORITY + "/system");
private static final NameValueCache sNameValueCache = new NameValueCache(
- SYS_PROP_SETTING_VERSION,
CONTENT_URI,
CALL_METHOD_GET_SYSTEM,
CALL_METHOD_PUT_SYSTEM);
@@ -3913,8 +3995,6 @@
* APIs for those values, not modified directly by applications.
*/
public static final class Secure extends NameValueTable {
- public static final String SYS_PROP_SETTING_VERSION = "sys.settings_secure_version";
-
/**
* The content:// style URL for this table
*/
@@ -3923,7 +4003,6 @@
// Populated lazily, guarded by class object:
private static final NameValueCache sNameValueCache = new NameValueCache(
- SYS_PROP_SETTING_VERSION,
CONTENT_URI,
CALL_METHOD_GET_SECURE,
CALL_METHOD_PUT_SECURE);
@@ -5867,7 +5946,10 @@
/**
* Names of the packages that the current user has explicitly allowed to
* manage notification policy configuration, separated by ':'.
+ *
+ * @hide
*/
+ @TestApi
public static final String ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES =
"enabled_notification_policy_access_packages";
@@ -6357,8 +6439,6 @@
* explicitly modify through the system UI or specialized APIs for those values.
*/
public static final class Global extends NameValueTable {
- public static final String SYS_PROP_SETTING_VERSION = "sys.settings_global_version";
-
/**
* The content:// style URL for global secure settings items. Not public.
*/
@@ -8409,7 +8489,6 @@
// Populated lazily, guarded by class object:
private static NameValueCache sNameValueCache = new NameValueCache(
- SYS_PROP_SETTING_VERSION,
CONTENT_URI,
CALL_METHOD_GET_GLOBAL,
CALL_METHOD_PUT_GLOBAL);
diff --git a/core/java/android/util/MemoryIntArray.aidl b/core/java/android/util/MemoryIntArray.aidl
new file mode 100644
index 0000000..3b15535
--- /dev/null
+++ b/core/java/android/util/MemoryIntArray.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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 android.util;
+
+parcelable MemoryIntArray;
diff --git a/core/java/android/util/MemoryIntArray.java b/core/java/android/util/MemoryIntArray.java
new file mode 100644
index 0000000..c3bd963
--- /dev/null
+++ b/core/java/android/util/MemoryIntArray.java
@@ -0,0 +1,258 @@
+/*
+ * 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 android.util;
+
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
+import android.os.Process;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.UUID;
+
+/**
+ * This class is an array of integers that is backed by shared memory.
+ * It is useful for efficiently sharing state between processes. The
+ * write and read operations are guaranteed to not result in read/
+ * write memory tear, i.e. they are atomic. However, multiple read/
+ * write operations are <strong>not</strong> synchronized between
+ * each other.
+ * <p>
+ * The data structure is designed to have one owner process that can
+ * read/write. There may be multiple client processes that can only read or
+ * read/write depending how the data structure was configured when
+ * instantiated. The owner process is the process that created the array.
+ * The shared memory is pinned (not reclaimed by the system) until the
+ * owning process dies or the data structure is closed. This class
+ * is <strong>not</strong> thread safe. You should not interact with
+ * an instance of this class once it is closed.
+ * </p>
+ *
+ * @hide
+ */
+public final class MemoryIntArray implements Parcelable, Closeable {
+ private static final int MAX_SIZE = 1024;
+
+ private final int mOwnerPid;
+ private final boolean mClientWritable;
+ private final long mMemoryAddr;
+ private ParcelFileDescriptor mFd;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param size The size of the array in terms of integer slots. Cannot be
+ * more than {@link #getMaxSize()}.
+ * @param clientWritable Whether other processes can write to the array.
+ * @throws IOException If an error occurs while accessing the shared memory.
+ */
+ public MemoryIntArray(int size, boolean clientWritable) throws IOException {
+ if (size > MAX_SIZE) {
+ throw new IllegalArgumentException("Max size is " + MAX_SIZE);
+ }
+ mOwnerPid = Process.myPid();
+ mClientWritable = clientWritable;
+ final String name = UUID.randomUUID().toString();
+ mFd = ParcelFileDescriptor.fromFd(nativeCreate(name, size));
+ mMemoryAddr = nativeOpen(mFd.getFd(), true, clientWritable);
+ }
+
+ private MemoryIntArray(Parcel parcel) throws IOException {
+ mOwnerPid = parcel.readInt();
+ mClientWritable = (parcel.readInt() == 1);
+ mFd = parcel.readParcelable(null);
+ if (mFd == null) {
+ throw new IOException("No backing file descriptor");
+ }
+ final long memoryAddress = parcel.readLong();
+ if (isOwner()) {
+ mMemoryAddr = memoryAddress;
+ } else {
+ mMemoryAddr = nativeOpen(mFd.getFd(), false, mClientWritable);
+ }
+ }
+
+ /**
+ * @return Gets whether this array is mutable.
+ */
+ public boolean isWritable() {
+ enforceNotClosed();
+ return isOwner() || mClientWritable;
+ }
+
+ /**
+ * Gets the value at a given index.
+ *
+ * @param index The index.
+ * @return The value at this index.
+ * @throws IOException If an error occurs while accessing the shared memory.
+ */
+ public int get(int index) throws IOException {
+ enforceNotClosed();
+ enforceValidIndex(index);
+ return nativeGet(mFd.getFd(), mMemoryAddr, index, isOwner());
+ }
+
+ /**
+ * Sets the value at a given index. This method can be called only if
+ * {@link #isWritable()} returns true which means your process is the
+ * owner.
+ *
+ * @param index The index.
+ * @param value The value to set.
+ * @throws IOException If an error occurs while accessing the shared memory.
+ */
+ public void set(int index, int value) throws IOException {
+ enforceNotClosed();
+ enforceWritable();
+ enforceValidIndex(index);
+ nativeSet(mFd.getFd(), mMemoryAddr, index, value, isOwner());
+ }
+
+ /**
+ * Gets the array size.
+ *
+ * @throws IOException If an error occurs while accessing the shared memory.
+ */
+ public int size() throws IOException {
+ enforceNotClosed();
+ return nativeSize(mFd.getFd());
+ }
+
+ /**
+ * Closes the array releasing resources.
+ *
+ * @throws IOException If an error occurs while accessing the shared memory.
+ */
+ @Override
+ public void close() throws IOException {
+ if (!isClosed()) {
+ nativeClose(mFd.getFd(), mMemoryAddr, isOwner());
+ mFd = null;
+ }
+ }
+
+ /**
+ * @return Whether this array is closed and shouldn't be used.
+ */
+ public boolean isClosed() {
+ return mFd == null;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ close();
+ super.finalize();
+ }
+
+ @Override
+ public int describeContents() {
+ return CONTENTS_FILE_DESCRIPTOR;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(mOwnerPid);
+ parcel.writeInt(mClientWritable ? 1 : 0);
+ parcel.writeParcelable(mFd, 0);
+ parcel.writeLong(mMemoryAddr);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ MemoryIntArray other = (MemoryIntArray) obj;
+ if (mFd == null) {
+ if (other.mFd != null) {
+ return false;
+ }
+ } else if (mFd.getFd() != other.mFd.getFd()) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return mFd != null ? mFd.hashCode() : 1;
+ }
+
+ private boolean isOwner() {
+ return mOwnerPid == Process.myPid();
+ }
+
+ private void enforceNotClosed() {
+ if (isClosed()) {
+ throw new IllegalStateException("cannot interact with a closed instance");
+ }
+ }
+
+ private void enforceValidIndex(int index) throws IOException {
+ final int size = size();
+ if (index < 0 || index > size - 1) {
+ throw new IndexOutOfBoundsException(
+ index + " not between 0 and " + (size - 1));
+ }
+ }
+
+ private void enforceWritable() {
+ if (!isWritable()) {
+ throw new UnsupportedOperationException("array is not writable");
+ }
+ }
+
+ private native int nativeCreate(String name, int size);
+ private native long nativeOpen(int fd, boolean owner, boolean writable);
+ private native void nativeClose(int fd, long memoryAddr, boolean owner);
+ private native int nativeGet(int fd, long memoryAddr, int index, boolean owner);
+ private native void nativeSet(int fd, long memoryAddr, int index, int value, boolean owner);
+ private native int nativeSize(int fd);
+ private native static int nativeGetMemoryPageSize();
+
+ /**
+ * @return The max array size.
+ */
+ public static int getMaxSize() {
+ return MAX_SIZE;
+ }
+
+ public static final Parcelable.Creator<MemoryIntArray> CREATOR =
+ new Parcelable.Creator<MemoryIntArray>() {
+ @Override
+ public MemoryIntArray createFromParcel(Parcel parcel) {
+ try {
+ return new MemoryIntArray(parcel);
+ } catch (IOException ioe) {
+ throw new RuntimeException(ioe);
+ }
+ }
+
+ @Override
+ public MemoryIntArray[] newArray(int size) {
+ return new MemoryIntArray[size];
+ }
+ };
+}
diff --git a/core/java/android/view/DisplayListCanvas.java b/core/java/android/view/DisplayListCanvas.java
index 415c291..cb7c5a2 100644
--- a/core/java/android/view/DisplayListCanvas.java
+++ b/core/java/android/view/DisplayListCanvas.java
@@ -37,6 +37,8 @@
// view hierarchy because display lists are generated recursively.
private static final int POOL_LIMIT = 25;
+ private static final int MAX_BITMAP_SIZE = 100 * 1024 * 1024; // 100 MB
+
private static final SynchronizedPool<DisplayListCanvas> sPool =
new SynchronizedPool<DisplayListCanvas>(POOL_LIMIT);
@@ -249,4 +251,14 @@
private static native void nDrawRoundRect(long renderer, long propLeft, long propTop,
long propRight, long propBottom, long propRx, long propRy, long propPaint);
+
+ @Override
+ protected void throwIfCannotDraw(Bitmap bitmap) {
+ super.throwIfCannotDraw(bitmap);
+ int bitmapSize = bitmap.getByteCount();
+ if (bitmapSize > MAX_BITMAP_SIZE) {
+ throw new RuntimeException(
+ "Canvas: trying to draw too large(" + bitmapSize + "bytes) bitmap.");
+ }
+ }
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index eb895ff..5b757d3 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3071,6 +3071,13 @@
/**
* @hide
*
+ * Whether the TV's picture-in-picture is visible or not.
+ */
+ public static final int TV_PICTURE_IN_PICTURE_VISIBLE = 0x00010000;
+
+ /**
+ * @hide
+ *
* Makes navigation bar transparent (but not the status bar).
*/
public static final int NAVIGATION_BAR_TRANSPARENT = 0x00008000;
@@ -5743,9 +5750,9 @@
* view-relative coordinate.
*
* @param x the X coordinate in pixels relative to the view to which the
- * menu should be anchored
+ * menu should be anchored, or {@link Float#NaN} to disable anchoring
* @param y the Y coordinate in pixels relative to the view to which the
- * menu should be anchored
+ * menu should be anchored, or {@link Float#NaN} to disable anchoring
* @return {@code true} if the context menu was shown, {@code false}
* otherwise
*/
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index 06afef2..849c8b9 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -199,9 +199,11 @@
* @param originalView the source view where the context menu was first
* invoked
* @param x the X coordinate in pixels relative to the original view to
- * which the menu should be anchored
+ * which the menu should be anchored, or {@link Float#NaN} to
+ * disable anchoring
* @param y the Y coordinate in pixels relative to the original view to
- * which the menu should be anchored
+ * which the menu should be anchored, or {@link Float#NaN} to
+ * disable anchoring
* @return {@code true} if the context menu was shown, {@code false}
* otherwise
*/
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index e3abb5d..e598113 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -24,6 +24,7 @@
import android.annotation.Nullable;
import android.annotation.StyleRes;
import android.annotation.SystemApi;
+import android.app.ActivityManagerNative;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -1256,6 +1257,18 @@
}
/**
+ * Puts the activity in picture-in-picture mode.
+ * @see android.R.attr#supportsPictureInPicture
+ * @hide
+ */
+ protected void enterPictureInPictureMode() {
+ try {
+ ActivityManagerNative.getDefault().enterPictureInPictureMode(mAppToken);
+ } catch (IllegalArgumentException|RemoteException e) {
+ }
+ }
+
+ /**
* Convenience for
* {@link #setContentView(View, android.view.ViewGroup.LayoutParams)}
* to set the screen content from a layout resource. The resource will be
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index f13cbae..5db0f16 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -216,7 +216,7 @@
}
if (chosen.versionCode > toUse.versionCode) {
throw new MissingWebViewPackageException("Failed to verify WebView provider, "
- + "version code mismatch, expected: " + chosen.versionCode
+ + "version code is lower than expected: " + chosen.versionCode
+ " actual: " + toUse.versionCode);
}
if (getWebViewLibrary(toUse.applicationInfo) == null) {
diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java
index df506ca..21595d15 100644
--- a/core/java/android/widget/CheckedTextView.java
+++ b/core/java/android/widget/CheckedTextView.java
@@ -185,11 +185,12 @@
d.setCallback(this);
d.setVisible(getVisibility() == VISIBLE, false);
d.setState(CHECKED_STATE_SET);
- setMinHeight(d.getIntrinsicHeight());
+ // Record the intrinsic dimensions when in "checked" state.
+ setMinHeight(d.getIntrinsicHeight());
mCheckMarkWidth = d.getIntrinsicWidth();
+
d.setState(getDrawableState());
- applyCheckMarkTint();
} else {
mCheckMarkWidth = 0;
}
@@ -197,6 +198,8 @@
mCheckMarkDrawable = d;
mCheckMarkResource = resId;
+ applyCheckMarkTint();
+
// Do padding resolution. This will call internalSetPadding() and do a
// requestLayout() if needed.
resolvePadding();
diff --git a/core/java/com/android/internal/app/WindowDecorActionBar.java b/core/java/com/android/internal/app/WindowDecorActionBar.java
index c6bf1b4..a09dbe5 100644
--- a/core/java/com/android/internal/app/WindowDecorActionBar.java
+++ b/core/java/com/android/internal/app/WindowDecorActionBar.java
@@ -505,6 +505,9 @@
mContextView.killMode();
ActionModeImpl mode = new ActionModeImpl(mContextView.getContext(), callback);
if (mode.dispatchOnCreate()) {
+ // This needs to be set before invalidate() so that it calls
+ // onPrepareActionMode()
+ mActionMode = mode;
mode.invalidate();
mContextView.initForMode(mode);
animateToMode(true);
@@ -518,7 +521,6 @@
}
}
mContextView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
- mActionMode = mode;
return mode;
}
return null;
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 3aa7719..645ffda 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -733,16 +733,16 @@
@Override
public boolean showContextMenuForChild(View originalView) {
- return showContextMenuForChildInternal(originalView, 0, 0, false);
+ return showContextMenuForChildInternal(originalView, Float.NaN, Float.NaN);
}
@Override
public boolean showContextMenuForChild(View originalView, float x, float y) {
- return showContextMenuForChildInternal(originalView, x, y, true);
+ return showContextMenuForChildInternal(originalView, x, y);
}
private boolean showContextMenuForChildInternal(View originalView,
- float x, float y, boolean isPopup) {
+ float x, float y) {
// Only allow one context menu at a time.
if (mWindow.mContextMenuHelper != null) {
mWindow.mContextMenuHelper.dismiss();
@@ -759,6 +759,7 @@
}
final MenuHelper helper;
+ final boolean isPopup = !Float.isNaN(x) && !Float.isNaN(y);
if (isPopup) {
helper = mWindow.mContextMenu.showPopup(getContext(), originalView, x, y);
} else {
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 151c530..1619843 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -2002,6 +2002,13 @@
}
return true;
}
+
+ case KeyEvent.KEYCODE_WINDOW: {
+ if (!event.isCanceled()) {
+ enterPictureInPictureMode();
+ }
+ return true;
+ }
}
return false;
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 5cbe1ce..7706ff7 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -105,14 +105,9 @@
void onCameraLaunchGestureDetected(int source);
/**
- * Request picture-in-picture.
- *
- * <p>
- * This is called when an user presses picture-in-picture key or equivalent.
- * TV device may start picture-in-picture from foreground activity if there's none.
- * Picture-in-picture overlay menu will be shown instead otherwise.
+ * Shows the TV's picture-in-picture menu if an activity is in picture-in-picture mode.
*/
- void requestTvPictureInPicture();
+ void showTvPictureInPictureMenu();
void addQsTile(in ComponentName tile);
void remQsTile(in ComponentName tile);
diff --git a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
index 72ed9d0..07f3801 100644
--- a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
+++ b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
@@ -349,21 +349,18 @@
final LayoutInflater inflater = LayoutInflater.from(mContext);
final MenuAdapter adapter = new MenuAdapter(menu, inflater, mOverflowOnly);
- // Apply "force show icon" setting. There are 4 cases:
- // (1) This is the top level menu. Only add spacing for icons if forced.
+ // Apply "force show icon" setting. There are 3 cases:
+ // (1) This is the top level menu and icon spacing is forced. Add spacing.
// (2) This is a submenu. Add spacing if any of the visible menu items has an icon.
- // (3) This is a top level menu that is not an overflow menu. Add spacing if any of the
- // visible menu items has an icon.
- // (4) This is an overflow menu or a top level menu that doesn't have "force" set.
- // Don't allow spacing.
+ // (3) This is the top level menu and icon spacing isn't forced. Do not add spacing.
if (!isShowing() && mForceShowIcon) {
// Case 1
adapter.setForceShowIcon(true);
- } else if (isShowing() || !isShowing() && !mForceShowIcon && !mOverflowOnly) {
- // Case 2 or 3
+ } else if (isShowing()) {
+ // Case 2
adapter.setForceShowIcon(MenuPopup.shouldPreserveIconSpacing(menu));
}
- // Case 4: Else, don't allow spacing for icons.
+ // Case 3: Else, don't allow spacing for icons (default behavior; do nothing).
final int menuWidth = measureIndividualMenuWidth(adapter, null, mContext, mMenuMaxWidth);
final MenuPopupWindow popupWindow = createPopupWindow();
diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
index 1f1e594..324f923 100644
--- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -23,10 +23,13 @@
import android.annotation.Nullable;
import android.annotation.StyleRes;
import android.content.Context;
+import android.graphics.Point;
import android.graphics.Rect;
import android.util.DisplayMetrics;
+import android.view.Display;
import android.view.Gravity;
import android.view.View;
+import android.view.WindowManager;
import android.widget.PopupWindow.OnDismissListener;
/**
@@ -104,6 +107,9 @@
*/
public void setForceShowIcon(boolean forceShowIcon) {
mForceShowIcon = forceShowIcon;
+ if (mPopup != null) {
+ mPopup.setForceShowIcon(forceShowIcon);
+ }
}
/**
@@ -206,8 +212,16 @@
*/
@NonNull
private MenuPopup createPopup() {
- final boolean enableCascadingSubmenus = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_enableCascadingSubmenus);
+ final WindowManager windowManager = (WindowManager) mContext.getSystemService(
+ Context.WINDOW_SERVICE);
+ final Display display = windowManager.getDefaultDisplay();
+ final Point displaySize = new Point();
+ display.getRealSize(displaySize);
+
+ final int smallestWidth = Math.min(displaySize.x, displaySize.y);
+ final int minSmallestWidthCascading = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.cascading_menus_min_smallest_width);
+ final boolean enableCascadingSubmenus = smallestWidth >= minSmallestWidthCascading;
final MenuPopup popup;
if (enableCascadingSubmenus) {
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 8a512a6..06d9b29 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -95,6 +95,7 @@
android_util_AssetManager.cpp \
android_util_Binder.cpp \
android_util_EventLog.cpp \
+ android_util_MemoryIntArray.cpp \
android_util_Log.cpp \
android_util_PathParser.cpp \
android_util_Process.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index d3dca5d9..a6a4f6b 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -113,6 +113,7 @@
extern int register_android_content_AssetManager(JNIEnv* env);
extern int register_android_util_EventLog(JNIEnv* env);
extern int register_android_util_Log(JNIEnv* env);
+extern int register_android_util_MemoryIntArray(JNIEnv* env);
extern int register_android_util_PathParser(JNIEnv* env);
extern int register_android_content_StringBlock(JNIEnv* env);
extern int register_android_content_XmlBlock(JNIEnv* env);
@@ -1256,6 +1257,7 @@
REG_JNI(register_android_os_SystemClock),
REG_JNI(register_android_util_EventLog),
REG_JNI(register_android_util_Log),
+ REG_JNI(register_android_util_MemoryIntArray),
REG_JNI(register_android_util_PathParser),
REG_JNI(register_android_app_admin_SecurityLog),
REG_JNI(register_android_content_AssetManager),
diff --git a/core/jni/android_util_MemoryIntArray.cpp b/core/jni/android_util_MemoryIntArray.cpp
new file mode 100644
index 0000000..dbe8ed3
--- /dev/null
+++ b/core/jni/android_util_MemoryIntArray.cpp
@@ -0,0 +1,219 @@
+/*
+ * 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.
+ */
+
+
+#include "core_jni_helpers.h"
+#include <cutils/ashmem.h>
+#include <sys/mman.h>
+
+namespace android {
+
+static jint android_util_MemoryIntArray_create(JNIEnv* env, jobject clazz, jstring name,
+ jint size)
+{
+ if (name == NULL) {
+ jniThrowException(env, "java/io/IOException", "bad name");
+ return -1;
+ }
+
+ if (size <= 0) {
+ jniThrowException(env, "java/io/IOException", "bad size");
+ return -1;
+ }
+
+ const char* nameStr = env->GetStringUTFChars(name, NULL);
+ const int ashmemSize = sizeof(std::atomic_int) * size;
+ int fd = ashmem_create_region(nameStr, ashmemSize);
+ env->ReleaseStringUTFChars(name, nameStr);
+
+ if (fd < 0) {
+ jniThrowException(env, "java/io/IOException", "ashmem creation failed");
+ return -1;
+ }
+
+ if (ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) {
+ jniThrowException(env, "java/io/IOException", "ashmem was purged");
+ return -1;
+ }
+
+ int setProtResult = ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE);
+ if (setProtResult < 0) {
+ jniThrowException(env, "java/io/IOException", "cannot set ashmem prot mode");
+ return -1;
+ }
+
+ return fd;
+}
+
+static jlong android_util_MemoryIntArray_open(JNIEnv* env, jobject clazz, jint fd,
+ jboolean owner, jboolean writable)
+{
+ if (fd < 0) {
+ jniThrowException(env, "java/io/IOException", "bad file descriptor");
+ return -1;
+ }
+
+ int ashmemSize = ashmem_get_size_region(fd);
+ if (ashmemSize <= 0) {
+ jniThrowException(env, "java/io/IOException", "bad ashmem size");
+ return -1;
+ }
+
+ int protMode = (owner || writable) ? (PROT_READ | PROT_WRITE) : PROT_READ;
+ void* ashmemAddr = mmap(NULL, ashmemSize, protMode, MAP_SHARED, fd, 0);
+ if (ashmemAddr == MAP_FAILED) {
+ jniThrowException(env, "java/io/IOException", "cannot mmap ashmem");
+ return -1;
+ }
+
+ if (owner) {
+ int size = ashmemSize / sizeof(std::atomic_int);
+ new (ashmemAddr) std::atomic_int[size];
+ }
+
+ if (owner && !writable) {
+ int setProtResult = ashmem_set_prot_region(fd, PROT_READ);
+ if (setProtResult < 0) {
+ jniThrowException(env, "java/io/IOException", "cannot set ashmem prot mode");
+ return -1;
+ }
+ }
+
+ return reinterpret_cast<jlong>(ashmemAddr);
+}
+
+static void android_util_MemoryIntArray_close(JNIEnv* env, jobject clazz, jint fd,
+ jlong ashmemAddr, jboolean owner)
+{
+ if (fd < 0) {
+ jniThrowException(env, "java/io/IOException", "bad file descriptor");
+ return;
+ }
+
+ int ashmemSize = ashmem_get_size_region(fd);
+ if (ashmemSize <= 0) {
+ jniThrowException(env, "java/io/IOException", "bad ashmem size");
+ return;
+ }
+
+ int unmapResult = munmap(reinterpret_cast<void *>(ashmemAddr), ashmemSize);
+ if (unmapResult < 0) {
+ jniThrowException(env, "java/io/IOException", "munmap failed");
+ return;
+ }
+
+ // We don't deallocate the atomic ints we created with placement new in the ashmem
+ // region as the kernel we reclaim all pages when the ashmem region is destroyed.
+ if (owner && (ashmem_unpin_region(fd, 0, 0) != ASHMEM_IS_UNPINNED)) {
+ jniThrowException(env, "java/io/IOException", "ashmem unpinning failed");
+ return;
+ }
+
+ close(fd);
+}
+
+static jint android_util_MemoryIntArray_get(JNIEnv* env, jobject clazz,
+ jint fd, jlong address, jint index, jboolean owner)
+{
+ if (fd < 0) {
+ jniThrowException(env, "java/io/IOException", "bad file descriptor");
+ return -1;
+ }
+
+ bool unpin = false;
+
+ if (!owner) {
+ if (ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) {
+ jniThrowException(env, "java/io/IOException", "ashmem region was purged");
+ return -1;
+ }
+ unpin = true;
+ }
+
+ std::atomic_int* value = reinterpret_cast<std::atomic_int*>(address) + index;
+ const int result = value->load(std::memory_order_relaxed);
+
+ if (unpin) {
+ ashmem_unpin_region(fd, 0, 0);
+ }
+
+ return result;
+}
+
+static void android_util_MemoryIntArray_set(JNIEnv* env, jobject clazz,
+ jint fd, jlong address, jint index, jint newValue, jboolean owner)
+{
+ if (fd < 0) {
+ jniThrowException(env, "java/io/IOException", "bad file descriptor");
+ return;
+ }
+
+ bool unpin = false;
+
+ if (!owner) {
+ if (ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) {
+ jniThrowException(env, "java/io/IOException", "ashmem region was purged");
+ return;
+ }
+ unpin = true;
+ }
+
+ std::atomic_int* value = reinterpret_cast<std::atomic_int*>(address) + index;
+ value->store(newValue, std::memory_order_relaxed);
+
+ if (unpin) {
+ ashmem_unpin_region(fd, 0, 0);
+ }
+}
+
+static jint android_util_MemoryIntArray_size(JNIEnv* env, jobject clazz, jint fd) {
+ if (fd < 0) {
+ jniThrowException(env, "java/io/IOException", "bad file descriptor");
+ return -1;
+ }
+
+ // Use ASHMEM_GET_SIZE to find out if the fd refers to an ashmem region.
+ // ASHMEM_GET_SIZE should succeed for all ashmem regions, and the kernel
+ // should return ENOTTY for all other valid file descriptors
+ int ashmemSize = ashmem_get_size_region(fd);
+ if (ashmemSize < 0) {
+ if (errno == ENOTTY) {
+ // ENOTTY means that the ioctl does not apply to this object,
+ // i.e., it is not an ashmem region.
+ return -1;
+ }
+ // Some other error, throw exception
+ jniThrowIOException(env, errno);
+ return -1;
+ }
+ return ashmemSize / sizeof(std::atomic_int);
+}
+
+static const JNINativeMethod methods[] = {
+ {"nativeCreate", "(Ljava/lang/String;I)I", (void*)android_util_MemoryIntArray_create},
+ {"nativeOpen", "(IZZ)J", (void*)android_util_MemoryIntArray_open},
+ {"nativeClose", "(IJZ)V", (void*)android_util_MemoryIntArray_close},
+ {"nativeGet", "(IJIZ)I", (void*)android_util_MemoryIntArray_get},
+ {"nativeSet", "(IJIIZ)V", (void*) android_util_MemoryIntArray_set},
+ {"nativeSize", "(I)I", (void*) android_util_MemoryIntArray_size},
+};
+
+int register_android_util_MemoryIntArray(JNIEnv* env)
+{
+ return RegisterMethodsOrDie(env, "android/util/MemoryIntArray", methods, NELEM(methods));
+}
+
+}
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index d3e18ed..a6db0f4 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -562,6 +562,16 @@
bounds.top -= info.windowInsetTop;
bounds.bottom -= info.windowInsetTop;
+ if (CC_LIKELY(transform.isPureTranslate())) {
+ // snap/round the computed bounds, so they match the rounding behavior
+ // of the clear done in SurfaceView#draw().
+ bounds.snapToPixelBoundaries();
+ } else {
+ // Conservatively round out so the punched hole (in the ZOrderOnTop = true case)
+ // doesn't extend beyond the other window
+ bounds.roundOut();
+ }
+
auto functor = std::bind(
std::mem_fn(&SurfaceViewPositionUpdater::doUpdatePosition), this,
(jlong) info.canvasContext.getFrameNumber(),
diff --git a/core/res/res/values-sw720dp/config.xml b/core/res/res/values-sw720dp/config.xml
index 1f5791a..9792835 100644
--- a/core/res/res/values-sw720dp/config.xml
+++ b/core/res/res/values-sw720dp/config.xml
@@ -19,7 +19,4 @@
used for picking activities to handle an intent. -->
<integer name="config_maxResolverActivityColumns">4</integer>
- <!-- Enable cascading submenus. -->
- <bool name="config_enableCascadingSubmenus">true</bool>
-
</resources>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index e040bea..cd7df87 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -552,6 +552,15 @@
should not be interrupted with other activities or notifications. -->
<attr name="immersive" format="boolean" />
+ <!-- Flag declaring that this activity will be run in VR mode, and specifying
+ the component of the VrListenerService that should be bound while this
+ Activity is visible if installed and enabled on this device. This is
+ equivalent to calling {@link android.app.Activity#setVrModeEnabled} with the
+ the given component name within this Activity. Declaring this will prevent
+ the system from leaving VR mode during an Activity transtion one VR activity
+ to another. -->
+ <attr name="enableVrMode" format="string" />
+
<!-- Specify the order in which content providers hosted by a process
are instantiated when that process is created. Not needed unless
you have providers with dependencies between each other, to make
@@ -1884,6 +1893,7 @@
For example, {@link android.R.attr#supportsPictureInPicture} activities are placed
in a task/stack that isn't focusable. This flag allows them to be focusable.-->
<attr name="alwaysFocusable" format="boolean" />
+ <attr name="enableVrMode" />
</declare-styleable>
<!-- The <code>activity-alias</code> tag declares a new
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 789a417..e73fa9a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2380,10 +2380,6 @@
must match the value of config_cameraLaunchGestureSensorType in OEM's HAL -->
<string translatable="false" name="config_cameraLaunchGestureSensorStringType"></string>
- <!-- Whether to open UI submenus side by side with the top menu (as opposed to
- replacing the top menu). -->
- <bool name="config_enableCascadingSubmenus">false</bool>
-
<!-- Allow the gesture to double tap the power button twice to start the camera while the device
is non-interactive. -->
<bool name="config_cameraDoubleTapPowerGestureEnabled">true</bool>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 5446d4c..8ce922e 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -468,4 +468,7 @@
<!-- The default minimal size of a resizable task, in both dimensions. -->
<dimen name="default_minimal_size_resizable_task">220dp</dimen>
+
+ <!-- Minimum "smallest width" of the display for cascading menus to be enabled. -->
+ <dimen name="cascading_menus_min_smallest_width">720dp</dimen>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 80b3a64..11df8e5 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2714,6 +2714,7 @@
<public type="attr" name="contentInsetStartWithNavigation" />
<public type="attr" name="contentInsetEndWithActions" />
<public type="attr" name="numberPickerStyle" />
+ <public type="attr" name="enableVrMode" />
<public type="style" name="Theme.Material.Light.DialogWhenLarge.DarkActionBar" />
<public type="style" name="Widget.Material.SeekBar.Discrete" />
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 22b6b75..4d86a92 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2256,7 +2256,8 @@
<java-symbol type="string" name="prohibit_manual_network_selection_in_gobal_mode" />
<java-symbol type="id" name="profile_button" />
- <java-symbol type="bool" name="config_enableCascadingSubmenus" />
+ <!-- Cascading submenus -->
+ <java-symbol type="dimen" name="cascading_menus_min_smallest_width" />
<!-- From SignalStrength -->
<java-symbol type="integer" name="config_LTE_RSRP_threshold_type" />
diff --git a/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java b/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java
new file mode 100644
index 0000000..6b31916
--- /dev/null
+++ b/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java
@@ -0,0 +1,192 @@
+/*
+ * 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 android.util;
+
+import android.os.Parcel;
+import junit.framework.TestCase;
+import libcore.io.IoUtils;
+
+public class MemoryIntArrayTest extends TestCase {
+
+ public void testSize() throws Exception {
+ MemoryIntArray array = null;
+ try {
+ array = new MemoryIntArray(3, false);
+ assertEquals("size must be three", 3, array.size());
+ } finally {
+ IoUtils.closeQuietly(array);
+ }
+ }
+
+ public void testGetSet() throws Exception {
+ MemoryIntArray array = null;
+ try {
+ array = new MemoryIntArray(3, false);
+
+ array.set(0, 1);
+ array.set(1, 2);
+ array.set(2, 3);
+
+ assertEquals("First element should be 1", 1, array.get(0));
+ assertEquals("First element should be 2", 2, array.get(1));
+ assertEquals("First element should be 3", 3, array.get(2));
+ } finally {
+ IoUtils.closeQuietly(array);
+ }
+ }
+
+ public void testWritable() throws Exception {
+ MemoryIntArray array = null;
+ try {
+ array = new MemoryIntArray(3, true);
+ assertTrue("Must be mutable", array.isWritable());
+ } finally {
+ IoUtils.closeQuietly(array);
+ }
+ }
+
+ public void testClose() throws Exception {
+ MemoryIntArray array = null;
+ try {
+ array = new MemoryIntArray(3, false);
+ array.close();
+ assertTrue("Must be closed", array.isClosed());
+ } finally {
+ if (array != null && !array.isClosed()) {
+ IoUtils.closeQuietly(array);
+ }
+ }
+ }
+
+ public void testMarshalledGetSet() throws Exception {
+ MemoryIntArray firstArray = null;
+ MemoryIntArray secondArray = null;
+ try {
+ firstArray = new MemoryIntArray(3, false);
+
+ firstArray.set(0, 1);
+ firstArray.set(1, 2);
+ firstArray.set(2, 3);
+
+ Parcel parcel = Parcel.obtain();
+ parcel.writeParcelable(firstArray, 0);
+ parcel.setDataPosition(0);
+ secondArray = parcel.readParcelable(null);
+ parcel.recycle();
+
+ assertNotNull("Should marshall file descriptor", secondArray);
+
+ assertEquals("First element should be 1", 1, secondArray.get(0));
+ assertEquals("First element should be 2", 2, secondArray.get(1));
+ assertEquals("First element should be 3", 3, secondArray.get(2));
+ } finally {
+ IoUtils.closeQuietly(firstArray);
+ IoUtils.closeQuietly(secondArray);
+ }
+ }
+
+ public void testInteractOnceClosed() throws Exception {
+ MemoryIntArray array = null;
+ try {
+ array = new MemoryIntArray(3, false);
+ array.close();
+
+ array.close();
+
+ try {
+ array.size();
+ fail("Cannot interact with a closed instance");
+ } catch (IllegalStateException e) {
+ /* expected */
+ }
+
+ try {
+ array.get(0);
+ fail("Cannot interact with a closed instance");
+ } catch (IllegalStateException e) {
+ /* expected */
+ }
+
+ try {
+ array.set(0, 1);
+ fail("Cannot interact with a closed instance");
+ } catch (IllegalStateException e) {
+ /* expected */
+ }
+
+ try {
+ array.isWritable();
+ fail("Cannot interact with a closed instance");
+ } catch (IllegalStateException e) {
+ /* expected */
+ }
+ } finally {
+ if (array != null && !array.isClosed()) {
+ IoUtils.closeQuietly(array);
+ }
+ }
+ }
+
+ public void testInteractPutOfBounds() throws Exception {
+ MemoryIntArray array = null;
+ try {
+ array = new MemoryIntArray(3, false);
+
+ try {
+ array.get(-1);
+ fail("Cannot interact out of array bounds");
+ } catch (IndexOutOfBoundsException e) {
+ /* expected */
+ }
+
+ try {
+ array.get(3);
+ fail("Cannot interact out of array bounds");
+ } catch (IndexOutOfBoundsException e) {
+ /* expected */
+ }
+
+ try {
+ array.set(-1, 0);
+ fail("Cannot interact out of array bounds");
+ } catch (IndexOutOfBoundsException e) {
+ /* expected */
+ }
+
+ try {
+ array.set(3, 0);
+ fail("Cannot interact out of array bounds");
+ } catch (IndexOutOfBoundsException e) {
+ /* expected */
+ }
+ } finally {
+ IoUtils.closeQuietly(array);
+ }
+ }
+
+ public void testOverMaxSize() throws Exception {
+ MemoryIntArray array = null;
+ try {
+ array = new MemoryIntArray(MemoryIntArray.getMaxSize() + 1, false);
+ fail("Cannot use over max size");
+ } catch (IllegalArgumentException e) {
+ /* expected */
+ } finally {
+ IoUtils.closeQuietly(array);
+ }
+ }
+}
diff --git a/docs/html/develop/index.jd b/docs/html/develop/index.jd
index 460d056..49235ae 100644
--- a/docs/html/develop/index.jd
+++ b/docs/html/develop/index.jd
@@ -9,7 +9,7 @@
excludeFromSuggestions=true
@jd:body
-<section class="dac-expand dac-hero dac-section-light">
+<section class="dac-expand dac-hero dac-section-light" style="background:#FFE57F">
<div class="wrap">
<div class="cols dac-hero-content">
<div class="col-1of2 col-push-1of2 dac-hero-figure">
@@ -19,30 +19,37 @@
frameborder="0" allowfullscreen=""
style="float: right;"></iframe>
-->
- <img class="dac-hero-image" src="{@docRoot}images/develop/hero_image_studio5_2x.png"
- srcset="/images/develop/hero_image_studio5.png 1x, /images/develop/hero_image_studio5_2x.png 2x" />
+ <img class="dac-hero-image" src="{@docRoot}images/tools/studio/studio-feature-instant-run_2x.png" />
</div>
<div class="col-1of2 col-pull-1of2">
- <h1 class="dac-hero-title">Get Started with Android Studio</h1>
+ <h1 class="dac-hero-title">Android Studio 2.0,<br>now available!</h1>
<p class="dac-hero-description">
- Everything you need to build incredible app experiences on phones and tablets, Wear, TV, and Auto. </p>
+ The latest version of Android Studio is the biggest update yet.
+ It includes new features like <strong>Instant Run</strong>, which
+ dramatically speeds up your edit, build, and run cycles, keeping
+ you "in the flow."</p>
</p>
- <a class="dac-hero-cta" href="{@docRoot}sdk/index.html">
- <span class="dac-sprite dac-auto-chevron"></span>
- Set up Android Studio
- </a><br>
- <a class="dac-hero-cta" href="{@docRoot}training/index.html">
- <span class="dac-sprite dac-auto-chevron"></span>
- Build your first app
- </a><br>
- <a class="dac-hero-cta" href="{@docRoot}guide/index.html">
- <span class="dac-sprite dac-auto-chevron"></span>
- Learn about Android
- </a><br>
- <a class="dac-hero-cta" href="{@docRoot}samples/index.html">
- <span class="dac-sprite dac-auto-chevron"></span>
- Sample projects
- </a><br>
+ <div class="cols">
+ <div class="col-1of2">
+ <a class="dac-hero-cta" href="{@docRoot}sdk/index.html">
+ <span class="dac-sprite dac-auto-chevron"></span>
+ Get Android Studio
+ </a><br>
+ <a class="dac-hero-cta" href="{@docRoot}training/index.html">
+ <span class="dac-sprite dac-auto-chevron"></span>
+ Build your first app
+ </a>
+ </div>
+ <div class="col-1of2">
+ <a class="dac-hero-cta" href="{@docRoot}guide/index.html">
+ <span class="dac-sprite dac-auto-chevron"></span>
+ Learn about Android
+ </a><br>
+ <a class="dac-hero-cta" href="{@docRoot}samples/index.html">
+ <span class="dac-sprite dac-auto-chevron"></span>
+ See sample projects
+ </a>
+ </div>
</div>
</div>
<!--<div class="dac-section dac-small">
diff --git a/docs/html/images/tools/ai-appindexingtest.png b/docs/html/images/tools/ai-appindexingtest.png
new file mode 100644
index 0000000..385bc21
--- /dev/null
+++ b/docs/html/images/tools/ai-appindexingtest.png
Binary files differ
diff --git a/docs/html/images/tools/as-cleanrerun.png b/docs/html/images/tools/as-cleanrerun.png
new file mode 100644
index 0000000..7d57b0f
--- /dev/null
+++ b/docs/html/images/tools/as-cleanrerun.png
Binary files differ
diff --git a/docs/html/images/tools/as-launchavdm.png b/docs/html/images/tools/as-launchavdm.png
index bf15981..f2c7bf3 100644
--- a/docs/html/images/tools/as-launchavdm.png
+++ b/docs/html/images/tools/as-launchavdm.png
Binary files differ
diff --git a/docs/html/images/tools/as-ok.png b/docs/html/images/tools/as-ok.png
new file mode 100644
index 0000000..244a77f
--- /dev/null
+++ b/docs/html/images/tools/as-ok.png
Binary files differ
diff --git a/docs/html/images/tools/as-restart.png b/docs/html/images/tools/as-restart.png
index 12d2923..39122c9 100644
--- a/docs/html/images/tools/as-restart.png
+++ b/docs/html/images/tools/as-restart.png
Binary files differ
diff --git a/docs/html/images/tools/as-stop.png b/docs/html/images/tools/as-stop.png
new file mode 100644
index 0000000..ed1424b
--- /dev/null
+++ b/docs/html/images/tools/as-stop.png
Binary files differ
diff --git a/docs/html/images/tools/as-wrench.png b/docs/html/images/tools/as-wrench.png
new file mode 100644
index 0000000..5886487
--- /dev/null
+++ b/docs/html/images/tools/as-wrench.png
Binary files differ
diff --git a/docs/html/images/tools/instant-run/as-irdebug.png b/docs/html/images/tools/instant-run/as-irdebug.png
new file mode 100644
index 0000000..0754afa
--- /dev/null
+++ b/docs/html/images/tools/instant-run/as-irdebug.png
Binary files differ
diff --git a/docs/html/images/tools/instant-run/as-irrun.png b/docs/html/images/tools/instant-run/as-irrun.png
new file mode 100644
index 0000000..8b5fafa
--- /dev/null
+++ b/docs/html/images/tools/instant-run/as-irrun.png
Binary files differ
diff --git a/docs/html/images/tools/instant-run/update-project-dialog.png b/docs/html/images/tools/instant-run/update-project-dialog.png
new file mode 100644
index 0000000..e106c57
--- /dev/null
+++ b/docs/html/images/tools/instant-run/update-project-dialog.png
Binary files differ
diff --git a/docs/html/images/training/ctl-exec-log.png b/docs/html/images/training/ctl-exec-log.png
new file mode 100644
index 0000000..96bbb81
--- /dev/null
+++ b/docs/html/images/training/ctl-exec-log.png
Binary files differ
diff --git a/docs/html/images/training/ctl-test-results.png b/docs/html/images/training/ctl-test-results.png
new file mode 100644
index 0000000..bb2fc93
--- /dev/null
+++ b/docs/html/images/training/ctl-test-results.png
Binary files differ
diff --git a/docs/html/sdk/index.jd b/docs/html/sdk/index.jd
index 0250c1e..923a6d7 100644
--- a/docs/html/sdk/index.jd
+++ b/docs/html/sdk/index.jd
@@ -503,7 +503,7 @@
<li>2 GB of available disk space minimum (500 MB for IDE + 1.5 GB for
Android SDK and emulator system image). 4 GB Recommended.</li>
<li>1280 x 800 minimum screen resolution</li>
-<li>Java Development Kit (JDK) 8</li>
+<li>Java Development Kit (JDK) 6</li>
</ul>
</div>
diff --git a/docs/html/tools/building/building-studio.jd b/docs/html/tools/building/building-studio.jd
index 2e3615e..43e3129 100644
--- a/docs/html/tools/building/building-studio.jd
+++ b/docs/html/tools/building/building-studio.jd
@@ -7,10 +7,34 @@
<div id="qv">
<h2>In this document</h2>
<ol>
- <li><a href="#run-configuration">Changing the run configuration</a> </li>
- <li><a href="#changing-variant">Changing build variants</a></li>
- <li><a href="#gradle-console">Monitoring the build process</a></li>
- <li><a href="#generating-apks">Generating APKs</a></li>
+ <li>
+ <a href="#run-configuration">Changing the run configuration</a>
+ </li>
+
+ <li>
+ <a href="#changing-variant">Changing build variants</a>
+ </li>
+
+ <li>
+ <a href="#gradle-console">Monitoring the build process</a>
+ </li>
+
+ <li>
+ <a href="#generating-apks">Generating APKs</a>
+ </li>
+
+ <li>
+ <a href="#instant-run">About Instant Run</a>
+ <ol>
+ <li>
+ <a href="#set-up-ir">Configuring and optimizing your project for Instant Run</a>
+ </li>
+
+ <li>
+ <a href="#ir-limitations">Limitations of Instant Run</a>
+ </li>
+ </ol>
+ </li>
</ol>
<h2>See also</h2>
@@ -31,7 +55,9 @@
<p>
By default, Android Studio sets up new projects to deploy to the Emulator or
- a physical device with just a few clicks.
+ a physical device with just a few clicks. With Instant Run, you can push
+ changes to methods and existing app resources to a running app without
+ building a new APK, so code changes are visible almost instantly.
</p>
<p>
@@ -138,8 +164,8 @@
</h3>
<p>
- You can view details about the build process by clicking <em>Gradle
- Console</em> <img src="{@docRoot}images/tools/as-gradlebutton.png" alt=""
+ You can view details about the build process by clicking <strong>Gradle
+ Console</strong> <img src="{@docRoot}images/tools/as-gradlebutton.png" alt=""
style="vertical-align:bottom;margin:0;">. The console displays each
task that Gradle executes in order to build your app, as shown in figure 1.
</p>
@@ -160,7 +186,40 @@
<p>
If an error occurs during the build process, the <em>Messages</em> window
- will appear to describe the issue.
+ appears to describe the issue. Gradle may recommend some command-line
+ options to help you resolve the issue, such as <code>--stacktrace</code> or
+ <code>--debug</code>. To use command-line options with your build process:
+</p>
+
+<ol>
+ <li>Open the <strong>Settings</strong> or <strong>Preferences</strong>
+ dialog:
+ <ul>
+ <li>On Windows or Linux, select <strong>File</strong> >
+ <strong>Settings</strong> from the main menu.
+ </li>
+
+ <li>On Mac OSX, select <strong>Android Studio</strong> >
+ <strong>Preferences</strong> from the main menu.
+ </li>
+ </ul>
+ </li>
+
+ <li>Navigate to <strong>Build, Execution, Deployment</strong> >
+ <strong>Compiler</strong>.
+ </li>
+
+ <li>In the text field next to <em>Command-line Options</em>, enter your
+ command-line options.
+ </li>
+
+ <li>Click <strong>OK</strong> to save and exit.
+ </li>
+</ol>
+
+<p>
+ Gradle will apply these command-line options the next time you try building
+ your app.
</p>
<h3 id="generating-apks">
@@ -184,4 +243,502 @@
<p class="img-caption">
<strong>Figure 2.</strong> Click the link to locate the generated APK
files.
-</p>
\ No newline at end of file
+</p>
+
+<h2 id="instant-run">About Instant Run</h3>
+
+<p>
+ Introduced in Android Studio 2.0, Instant Run is a behavior for the
+ <strong>Run</strong> <img src="{@docRoot}images/tools/as-run.png" alt=""
+ style="vertical-align:bottom;margin:0;"> and <strong>Debug</strong> <img src=
+ "{@docRoot}images/tools/as-debugbutton.png" alt="" style=
+ "vertical-align:bottom;margin:0;"> commands that significantly reduces the
+ time between updates to your app. Instant Run pushes updates to your app
+ without building a new APK, so changes are visible much more quickly.
+</p>
+
+<p>
+ Instant Run is supported only when you deploy the debug build variant, use
+ Android Plugin for Gradle version 2.0.0 or higher, and set
+ <code>minSdkVersion</code> to 15 or higher in your app's module-level
+ <code>build.gradle</code> file. For the best performance, set
+ <code>minSdkVersion</code> to 21 or higher.
+</p>
+
+<p>
+ After deploying an app, a small, yellow thunderbolt icon appears within the
+ <strong>Run</strong> <img src=
+ "{@docRoot}images/tools/instant-run/as-irrun.png" alt="" style=
+ "vertical-align:bottom;margin:0;"> button (or <strong>Debug</strong>
+ <img src="{@docRoot}images/tools/instant-run/as-irdebug.png" alt="" style=
+ "vertical-align:bottom;margin:0;"> button), indicating that Instant Run is
+ ready to push updates the next time you click the button. Instead of building
+ a new APK, it pushes just those new changes and, in some cases, the app
+ doesn't even need to restart but immediately shows the effect of those code
+ changes.
+</p>
+
+<p>
+ Instant Run pushes updated code and resources to your connected device or
+ emulator by performing a <em>hot swap</em>, <em>warm swap</em>, or <em>cold
+ swap</em>. It automatically determines the type of swap to perform based on
+ the type of change you made. The following table describes how Instant Run
+ behaves when you push certain code changes to a target device.
+</p>
+
+<table id="ir-table">
+ <col width="40%">
+ <tr>
+ <th scope="col">
+ Code Change
+ </th>
+ <th scope="col">
+ Instant Run Behavior
+ </th>
+ </tr>
+
+ <tr id="hot-swap">
+ <td>
+ <ul>
+ <li>Change implementation code of an existing method
+ </li>
+ </ul>
+ </td>
+ <td>
+ <p>
+ Supported with <strong>hot swap</strong>: This is the
+ fastest type of swap and makes changes visible much more quickly. Your
+ application keeps running and a stub method with the new implementation is used
+ the next time the method is called.
+ </p>
+
+ <p>
+ Hot swaps do not re-initialize objects in your running app. You may need to
+ restart the current activity, or <a href="#rerun">restart the app</a>, before
+ you see certain updates. By default, Android Studio automatically restarts the
+ current activity after performing a hot swap. If you do not want this behavior,
+ you can <a href="#activity-restart">disable automatic activity restarts</a>.
+ </p>
+ </td>
+ </tr>
+
+ <tr id="warm-swap">
+ <td>
+ <ul>
+ <li>Change or remove an existing resource
+ </li>
+ </ul>
+ </td>
+ <td>
+ Supported with <strong>warm swap</strong>: This swap
+ is still very fast, but Instant Run must restart the current activity when it
+ pushes the changed resources to your app. Your app keeps running, but a small
+ flicker may appear on the screen as the activity restarts—this is normal.
+ </td>
+ </tr>
+
+ <tr id="cold-swap">
+ <td>
+ Structural code changes, such as:
+ <ul>
+ <li>Add, remove, or change:
+ <ul>
+ <li>an annotation
+ </li>
+
+ <li>an instance field
+ </li>
+
+ <li>a static field
+ </li>
+
+ <li>a static method signature
+ </li>
+
+ <li>an instance method signature
+ </li>
+ </ul>
+ </li>
+
+ <li>Change which parent class the current class inherits from
+ </li>
+
+ <li>Change the list of implemented interfaces
+ </li>
+
+ <li>Change a class's static initializer
+ </li>
+
+ <li>Reorder layout elements that use dynamic resource IDs
+ </li>
+ </ul>
+ </td>
+ <td>
+ <p>
+ Supported with <strong>cold swap</strong> (API level 21 or higher): This swap
+ is a bit slower because, although a new APK is not required, Instant Run must
+ restart the whole app when it pushes structural code changes.
+ </p>
+
+ <p>
+ For target devices running API level 20 or lower, Android Studio
+ deploys a full APK.
+ </p>
+ </td>
+ </tr>
+
+ <tr>
+ <td>
+ <ul>
+ <li>Change the app manifest
+ </li>
+
+ <li>Change resources reference by the app manifest
+ </li>
+
+ <li>Change an Android widget UI element (requires a <a href="#rerun">
+ Clean and Rerun</a>)
+ </li>
+ </ul>
+ </td>
+ <td>
+ <p>
+ When making changes to the app's manifest or resources referenced by the
+ manifest, Android Studio automatically <strong>deploys a new build</strong>
+ in order to apply these changes. This is because certain information about
+ the app, such as its name, app icon resources, and intent filters, are
+ determined from the manifest when the APK is installed on the device.
+ </p>
+
+ <p>
+ If your build process automatically updates any part of the app manifest,
+ such as automatically iterating <code>versionCode</code> or
+ <code>versionName</code>, you will not be able to benefit from the full
+ performance of Instant Run. We recommend that you disable automatic updates
+ to any part in the app manifest in your debug build variants.
+ </p>
+ </td>
+ </tr>
+</table>
+
+<p class="note">
+ <strong>Note:</strong> If you need to restart your app after a crash, do not
+ launch it from your target device. Restarting your app from your target
+ device does not apply any of your code changes since the last cold swap or
+ <em>incremental build</em>. To launch your app with all your recent changes,
+ click <strong>Run</strong> <img src="{@docRoot}images/tools/as-run.png" alt=
+ "" style="vertical-align:bottom;margin:0;"> (or <strong>Debug</strong>
+ <img src="{@docRoot}images/tools/as-debugbutton.png" alt="" style=
+ "vertical-align:bottom;margin:0;">) from Android Studio.
+</p>
+
+<h4 id="rerun">
+ Using Rerun
+</h4>
+
+<p>
+ When pushing code changes that affect certain initializers, such as changes
+ to an app's {@link android.app.Application#onCreate onCreate()} method, you
+ need to restart your app for the changes to take effect. To perform an
+ <em>incremental build</em> and restart the app, click <strong>Rerun</strong>
+ <img src="{@docRoot}images/tools/as-restart.png" alt="" style=
+ "vertical-align:bottom;margin:0;">.
+</p>
+
+<p>
+ If you need to deploy a <em>clean build</em>, select <strong>Run</strong>
+ > <strong>Clean and Rerun 'app'</strong> <img src=
+ "{@docRoot}images/tools/as-cleanrerun.png" alt="" style=
+ "vertical-align:bottom;margin:0;"> from the main menu, or hold down the
+ <strong>Shift</strong> key while clicking <strong>Rerun</strong> <img src=
+ "{@docRoot}images/tools/as-restart.png" alt="" style=
+ "vertical-align:bottom;margin:0;">. This action stops the running app,
+ performs a full clean build, and deploys the new APK to your target device.
+</p>
+
+<h4 id="activity-restart">
+ Disabling automatic activity restart
+</h4>
+
+<p>
+ When performing a hot swap, your app keeps running but Android Studio
+ automatically restarts the current activity. To disable this default setting:
+</p>
+
+<ol>
+ <li>Open the <strong>Settings</strong> or <strong>Preferences</strong>
+ dialog:
+ <ul>
+ <li>On Windows or Linux, select <strong>File</strong> >
+ <strong>Settings</strong> from the main menu.
+ </li>
+
+ <li>On Mac OSX, select <strong>Android Studio</strong> >
+ <strong>Preferences</strong> from the main menu.
+ </li>
+ </ul>
+ </li>
+
+ <li>Navigate to <strong>Build, Execution, Deployment</strong> >
+ <strong>Instant Run</strong>.
+ </li>
+
+ <li>Uncheck the box next to <strong>Restart activity on code
+ changes</strong>.
+ </li>
+</ol>
+
+<p>
+ If automatic activity restart is disabled, you can manually restart the current
+ activity from the menu bar by selecting <strong>Run</strong> > <strong>Restart
+ Activity</strong>.
+</p>
+
+<h3 id="set-up-ir">
+ Configuring and optimizing your project for Instant Run
+</h3>
+
+<p>
+ Android Studio enables Instant Run by default for projects built using
+ Android Plugin for Gradle 2.0.0 and higher.
+</p>
+
+<p>
+ To update an existing project with the latest version of the plugin:
+</p>
+
+<ol>
+ <li>Open the <strong>Settings</strong> or <strong>Preferences</strong>
+ dialog.
+ </li>
+
+ <li>
+ <p>
+ Navigate to <strong>Build, Execution, Deployment</strong> >
+ <strong>Instant Run</strong> and click <strong>Update Project</strong>,
+ as shown in figure 3.
+ </p>
+
+ <p>
+ If the option to update the project does not appear, it’s already
+ up-to-date with the latest Android Plugin for Gradle.
+ </p>
+
+ <img src="{@docRoot}images/tools/instant-run/update-project-dialog.png"
+ alt="" height="51">
+
+ <p class="img-caption">
+ <strong>Figure 3.</strong> Updating the Android Plugin for Gradle for an
+ existing project.
+ </p>
+ </li>
+</ol>
+
+<p>
+ You also need to <a href="#changing-variant">change the build variant</a> to
+ a debug version of your app to start using Instant Run.
+</p>
+
+<h4 id="configure-dexoptions">
+ Improve build times by configuring DEX resources
+</h4>
+
+<p>
+ When you deploy a clean build, Android Studio instruments your app to allow
+ Instant Run to push code and resource updates. Although updating the running
+ app happens much more quickly, the first build may take longer to complete.
+ You can improve the build process by configuring a few DEX options, such as
+ <code>maxProcessCount</code> and <code>javaMaxHeapSize</code>.
+</p>
+
+<dl>
+ <dt>
+ <code>maxProcessCount</code>
+ </dt>
+
+ <dd>
+ Sets the maximum number of DEX processes that can be started
+ concurrently. If the Gradle daemon is already running, you need to
+ stop the process before initializing it with a new maximum process count.
+ You can terminate the Gradle daemon by calling one of the following from
+ the <em>Terminal</em> window:
+ <ul>
+ <li>On Windows, call <code>gradlew --stop</code>
+ </li>
+
+ <li>On Linux/Mac OSX, call <code>./gradlew --stop</code>
+ </li>
+ </ul>
+ </dd>
+
+ <dt>
+ <code>javaMaxHeapSize</code>
+ </dt>
+
+ <dd>
+ Sets the maximum memory allocation pool size for the dex operation. When
+ passing a value, you can append the letter 'k' to indicate kilobytes, 'm'
+ to indicate megabytes, or 'g' to indicate gigabytes.
+ </dd>
+</dl>
+
+<p>
+ The following example sets <code>maxProcessCount</code> to 4 and
+ <code>javaMaxHeapSize</code> to "2g" in the module-level
+ <code>build.gradle</code> file:
+</p>
+
+<pre>
+android {
+ ...
+ dexOptions {
+ maxProcessCount 8
+ javaMaxHeapSize "2g"
+ }
+}
+</pre>
+
+<p>
+ You should experiment with these settings by incrementing their values and
+ observing the effect on your build times. You could experience a negative
+ impact to performance if you allocate too many resources to the DEX'ing process.
+</p>
+
+<h4 id="windows-defender">
+ Excluding your project from Windows Defender
+</h4>
+
+<p>
+ On Windows systems, Windows Defender may cause slowdowns while using Instant
+ Run. If you are using Windows Defender, you should <a class="external-link"
+ href=
+ "http://answers.microsoft.com/en-us/protect/wiki/protect_defender-protect_scanning/how-to-exclude-a-filefolder-from-windows-defender/f32ee18f-a012-4f02-8611-0737570e8eee">
+ exclude your Android Studio project folder from Windows Defender malware
+ scans</a>.
+</p>
+
+<h4 id="crashlytics">
+ Disabling Crashlytics for your debug build variant
+</h4>
+
+<p>
+ Using Crashlytics is known to cause slower build times. To improve build
+ performance while developing your app, you can <a class="external-link" href=
+ "https://docs.fabric.io/android/crashlytics/build-tools.html#disabling-crashlytics-for-debug-builds">
+ disable Crashlytics for your debug build variant</a>.
+</p>
+
+<h3 id="ir-limitations">
+ Limitations of Instant Run
+</h3>
+
+<p>
+ Instant Run is designed to speed up the build and deploy process in most
+ situations. However, there are some aspects to using Instant Run that might
+ affect its behavior and compatibility with your app. If you experience any
+ other issues while using Instant Run, please <a class="external-link" href=
+ "http://tools.android.com/filing-bugs">file a bug</a>.
+</p>
+
+<h4 id="multiple-devices">
+ Deploying to multiple devices
+</h4>
+
+<p>
+ Instant Run uses different techniques to perform hot, warm, and cold swaps
+ that are specific to the API level of the target device. For this reason,
+ while deploying an app to multiple devices at once, Android Studio
+ temporarily turns off Instant Run.
+</p>
+
+<h4 id="ir-multidex">
+ Multidexing your app
+</h4>
+
+<p>
+ If your project is configured for <a href=
+ "{@docRoot}tools/building/multidex.html#mdex-pre-l">Legacy Multidex</a>—that
+ is, when <code>build.gradle</code> is configured with <code>multiDexEnabled
+ true</code> and <code>minSdkVersion 20</code> or lower—and you deploy to
+ target devices running Android 4.4 (API level 20) or lower, Android Studio
+ disables Instant Run.
+</p>
+
+<p>
+ If <code>minSdkVersion</code> is set to 21 or higher, Instant Run
+ automatically configures your app for multidex. Because Instant Run only
+ works with the debug version of your app, you may need to <a href=
+ "{@docRoot}tools/building/multidex.html#mdex-gradle">configure your app for
+ multidex</a> when deploying your release build variant.
+</p>
+
+<h4 id="instrumented-tests">
+ Running instrumented tests and performance profilers
+</h4>
+
+<p>
+ Instrumented tests load both the debug APK and a test APK into the same
+ process on a test device, allowing control methods to override the normal
+ lifecycle of the app and perform tests. While running or debugging
+ instrumented tests, Android Studio does not inject the additional methods
+ required for Instant Run and turns the feature off.
+</p>
+
+<p>
+ While profiling an app, you should disable Instant Run. There is a small
+ performance impact when using Instant Run and a slightly larger impact when
+ overriding methods with a hot swap. This performance impact could interfere
+ with information provided by performance profiling tools. Additionally, the
+ stub methods generated with each hot swap can complicate stack traces.
+</p>
+
+<h4 id="plugins">
+ Using third-party plugins
+</h4>
+
+<p>
+ Android Studio temporarily disables the Java Code Coverage Library (JaCoCo)
+ and ProGuard while using Instant Run. Because Instant Run only works with
+ debug builds, this does not affect your release build.
+</p>
+
+<p>
+ Certain third-party plugins that perform bytecode enhancement may cause
+ issues with how Instant Run instruments your app. If you experience these
+ issues, but want to continue using Instant Run, you should disable those
+ plugins for your debug build variant. You can also help improve compatibility
+ with third-party plugins by <a class="external-link" href=
+ "http://tools.android.com/filing-bugs">filing a bug</a>.
+</p>
+
+<h4 id="multi-process-apps">
+ Pushing changes to multi-process apps
+</h4>
+
+<p>
+ Instant Run only instruments your app's main process in order to perform hot
+ swaps and warm swaps. When pushing code changes to other app processes, such
+ as changes to a method implementation or an existing resource, Instant Run
+ performs a <a href="#cold-swap">cold swap</a>.
+</p>
+
+<h4 id="disable-ir">
+ Disabling Instant Run
+</h4>
+
+<p>
+ To disable Instant Run:
+</p>
+
+<ol>
+ <li>Open the <strong>Settings</strong> or <strong>Preferences</strong>
+ dialog.
+ </li>
+
+ <li>Navigate to <strong>Build, Execution, Deployment</strong> >
+ <strong>Instant Run</strong>.
+ </li>
+
+ <li>Uncheck the box next to <strong>Enable Instant Run</strong>.
+ </li>
+</ol>
\ No newline at end of file
diff --git a/docs/html/tools/building/multidex.jd b/docs/html/tools/building/multidex.jd
index e441a7c..7d05fb9 100644
--- a/docs/html/tools/building/multidex.jd
+++ b/docs/html/tools/building/multidex.jd
@@ -1,4 +1,4 @@
-page.title=Building Apps with Over 65K Methods
+page.title=Building Apps with Over 64K Methods
page.tags="65536","references","max","65k","dex","64k","multidex","multi-dex","methods"</p>
@jd:body
@@ -8,14 +8,14 @@
<h2>In this document</h2>
<ol>
<li><a href="#about">
- About the 65K Reference Limit</a>
+ About the 64K Reference Limit</a>
<ol>
<li><a href="#mdex-pre-l">Multidex support prior to Android 5.0</a></li>
<li><a href="#mdex-on-l">Multidex support for Android 5.0 and higher</a></li>
</ol>
</li>
<li><a href="#avoid">
- Avoiding the 65K Limit</a></li>
+ Avoiding the 64K Limit</a></li>
<li><a href="#mdex-gradle">
Configuring Your App for Multidex with Gradle</a>
<ol>
@@ -82,45 +82,71 @@
</p>
-<h2 id="about">About the 65K Reference Limit</h2>
+<h2 id="about">About the 64K Reference Limit</h2>
<p>
- Android application (APK) files contain executable bytecode files in the form of <a href=
- "https://source.android.com/devices/tech/dalvik/">Dalvik</a> Executable (DEX) files, which
- contain the compiled code used to run your app. The Dalvik Executable specification limits the
- total number of methods that can be referenced within a single DEX file to 65,536, including
- Android framework methods, library methods, and methods in your own code. Getting past this limit
- requires that you configure your app build process to generate more than one DEX file, known as a
- <em>multidex</em> configuration.
+ Android application (APK) files contain executable bytecode files in the form
+ of <a href="https://source.android.com/devices/tech/dalvik/">Dalvik</a>
+ Executable (DEX) files, which contain the compiled code used to run your app.
+ The Dalvik Executable specification limits the total number of methods that
+ can be referenced within a single DEX file to 65,536—including Android
+ framework methods, library methods, and methods in your own code. In the
+ context of computer science, the term <a class="external-link" href=
+ "https://en.wikipedia.org/wiki/Kilo-"><em>Kilo, K</em></a>, denotes 1024 (or
+ 2^10). Because 65,536 is equal to 64 X 1024, this limit is referred to as the
+ '64K reference limit'.
</p>
+<p>
+ Getting past this limit requires that you configure your app build process to
+ generate more than one DEX file, known as a <em>multidex</em> configuration.
+</p>
<h3 id="mdex-pre-l">Multidex support prior to Android 5.0</h3>
<p>
- Versions of the platform prior to Android 5.0 use the Dalvik runtime for executing app code. By
- default, Dalvik limits apps to a single classes.dex bytecode file per APK. In order to get around
- this limitation, you can use the <a href="{@docRoot}tools/support-library/features.html#multidex">
- multidex support library</a>, which becomes part of the primary DEX file of your app and then
+ Versions of the platform prior to Android 5.0 (API level 21) use the Dalvik
+ runtime for executing app code. By default, Dalvik limits apps to a single
+ classes.dex bytecode file per APK. In order to get around this limitation,
+ you can use the <a href=
+ "{@docRoot}tools/support-library/features.html#multidex">multidex support
+ library</a>, which becomes part of the primary DEX file of your app and then
manages access to the additional DEX files and the code they contain.
</p>
+<p class="note">
+ <strong>Note:</strong> If your project is configured for multidex with
+ <code>minSdkVersion 20</code> or lower, and you deploy to target devices
+ running Android 4.4 (API level 20) or lower, Android Studio disables <a href=
+ "{@docRoot}tools/building/building-studio.html#instant-run">Instant Run</a>.
+</p>
<h3 id="mdex-on-l">Multidex support for Android 5.0 and higher</h3>
<p>
- Android 5.0 and higher uses a runtime called ART which natively supports loading multiple dex
- files from application APK files. ART performs pre-compilation at application install time which
- scans for classes(..N).dex files and compiles them into a single .oat file for execution by the
- Android device. For more information on the Android 5.0 runtime, see <a href=
- "https://source.android.com/devices/tech/dalvik/art.html">Introducing ART</a>.
+ Android 5.0 (API level 21) and higher uses a runtime called ART which
+ natively supports loading multiple dex files from application APK files. ART
+ performs pre-compilation at application install time which scans for
+ classes(..N).dex files and compiles them into a single .oat file for
+ execution by the Android device. For more information on the Android 5.0
+ runtime, see <a href=
+ "https://source.android.com/devices/tech/dalvik/art.html">Introducing
+ ART</a>.
</p>
+<p class="note">
+ <strong>Note:</strong> While using <a href=
+ "{@docRoot}tools/building/building-studio.html#instant-run">Instant Run</a>,
+ Android Studio automatically configures your app for multidex when your app's
+ <code>minSdkVersion</code> is set to 21 or higher. Because Instant Run only
+ works with the debug version of your app, you still need to configure your
+ release build for multidex to avoid the 64K limit.
+</p>
-<h2 id="avoid">Avoiding the 65K Limit</h2>
+<h2 id="avoid">Avoiding the 64K Limit</h2>
<p>
- Before configuring your app to enable use of 65K or more method references, you should take steps
+ Before configuring your app to enable use of 64K or more method references, you should take steps
to reduce the total number of references called by your app code, including methods defined by
your app code or included libraries. The following strategies can help you avoid hitting the dex
reference limit:
@@ -173,8 +199,9 @@
</ul>
<p>
- Modify your app Gradle build file configuration to include the support library and enable
- multidex output, as shown in the following Gradle build file snippet:
+ Modify the module-level <code>build.gradle</code> file configuration to
+ include the support library and enable multidex output, as shown in the
+ following code snippet:
</p>
<pre>
@@ -199,13 +226,6 @@
}
</pre>
-<p class="note">
- <strong>Note:</strong> You can specify the <code>multiDexEnabled</code> setting in the
- <code>defaultConfig,</code> <code>buildType</code>, or <code>productFlavor</code> sections of
- your Gradle build file.
-</p>
-
-
<p>
In your manifest add the {@link android.support.multidex.MultiDexApplication} class from the
multidex support library to the application element.
@@ -444,7 +464,7 @@
dependencies {
androidTestCompile('com.android.support:multidex-instrumentation:1.0.1') {
exclude group: 'com.android.support', module: 'multidex'
- }
+ }
}
</pre>
</p>
diff --git a/docs/html/tools/help/app-link-indexing.jd b/docs/html/tools/help/app-link-indexing.jd
index 611373a..5b76059 100644
--- a/docs/html/tools/help/app-link-indexing.jd
+++ b/docs/html/tools/help/app-link-indexing.jd
@@ -1,4 +1,4 @@
-page.title=Deep Link and App Indexing API Support in Android Studio
+page.title=Supporting URLs and App Indexing in Android Studio
parent.title=Tools
parent.link=index.html
page.tags=app indexing
@@ -9,11 +9,13 @@
<h2>In this document</h2>
<ol>
<li><a href="#workflow">Typical Workflow</a></li>
- <li><a href="#intent">Adding an Intent Filter for Deep Linking and Google Search</a></li>
+ <li><a href="#intent">Adding an Intent Filter for URL Support and Google Search</a></li>
<li><a href="#indexing">Adding App Indexing API Skeleton Code to an Activity</a></li>
- <li><a href="#testintent">Testing a Deep Link</a></li>
+ <li><a href="#testintent">Testing a URL</a></li>
<li><a href="#testindexing">Viewing App Indexing API Messages in the logcat Monitor</a></li>
<li><a href="#lint">Configuring Lint</a></li>
+ <li><a href="#appindexingtest">Performing a Google App Indexing Test</a></li>
+
</ol>
<h2>See also</h2>
@@ -48,15 +50,16 @@
</div>
</div>
-<p>Android Studio helps you add deep links, app indexing, and search functionality to your apps.
+<p>Android Studio helps you add support for URLs, app indexing, and search
+functionality to your apps.
These features can help to drive more traffic to your
app, discover which app content is used most, make it easier for users to find content in an
installed app, and attract new users.</p>
<h2 id="workflow">Typical Workflow</h2>
-<p>To use Android Studio to add deep link, app indexing, and search features to your app, follow
- these basic steps:</p>
+<p>To use Android Studio to add support for URL, app indexing, and search
+features to your app:</p>
<ol>
<li>Add intent filters and code to handle incoming intents.</li>
@@ -64,23 +67,25 @@
<li>Add App Indexing API code.</li>
</ol>
-<p>Intent filters and the App Indexing API are ways to implement deep links and app indexing, but
+<p>Intent filters and the App Indexing API are ways to implement URL support
+and app indexing, but
there are other possible implementations as well. See
<a href="https://developers.google.com/app-indexing/reference/deeplinks"
class="external-link">Alternate Android Indexing Methods</a>
for more information.</p>
-<h3 id="aboutintent">Intent filters for deep links</h3>
+<h3 id="aboutintent">Intent filters for URLs</h3>
<p>Android Studio can create a basic intent filter in your manifest that you can customize to
- define deep link URLs for your app. You can then write Java code in an activity to handle the
+ define URLs for your app. You can then write Java code in an activity to handle the
intent. This implementation lets users directly open the specified app activity by
- clicking a deep link. Users can see the deep links in google.com in a browser, in the
+ clicking a URL. Users can see the URLs in google.com in a browser, in the
Google Search app, and in Google Now on Tap. </p>
-<h3 id="aboutassociation">Website association with deep links</h3>
+<h3 id="aboutassociation">Website association with URLs</h3>
-<p>After setting up deep links for your app, you can associate your website with your app by using
+<p>After setting up URL support for your app, you can associate your website
+with your app by using
the Google Search Console and Google Play Developer Console. Afterward, Google indexes your app
for URLs defined in
your intent filters and begins to include them in search results. In addition, you can optionally
@@ -91,11 +96,11 @@
<p>As an alternative to associating your app with a website,
for Android 6.0 (API level 23) and higher, you can add
<a href="{@docRoot}training/app-links/index.html"
- >default handlers and verification for deep links</a>
+ >default handlers and verification for URLs</a>
instead.</p>
-<p>Chrome displaying google.com serves search results with deep links that are accessible to both
- signed-in users and those who aren't. Google Search app users must be signed in to see deep links
+<p>Chrome displaying google.com serves search results with URLs that are accessible to both
+ signed-in users and those who aren't. Google Search app users must be signed in to see URLs
in their search results. </p>
<h3 id="aboutapi">App Indexing API code in activities</h3>
@@ -108,24 +113,27 @@
class="external-link">Googlebot</a>
can’t get content from your app.</p>
-<h3 id="abouttest">Deep link and App Indexing API testing</h3>
+<h3 id="abouttest">URL support and App Indexing API testing</h3>
<p>Android Studio helps you test your code with the following features:</p>
<ul>
-<li>Deep link testing helps you verify that a specified deep link can launch an app. </li>
+<li>URL support testing helps you verify that a specified URL can launch an app.
+</li>
<li>The logcat Monitor helps you test App Indexing API calls in an activity. </li>
-<li>The Android Lint tool has warnings for certain issues involving deep links and the App Indexing
+<li>The Android Lint tool has warnings for certain issues involving URL support
+and the App Indexing
API. These warnings and errors appear in the Code Editor and in Lint inspection results.</li>
+ <li>A Google App Indexing test checks whether Google can index a URL by
+ either crawling your app page or using the App Indexing API.</li>
</ul>
-<p>The details for implementing deep links and app indexing are described next.
+<p>The details for implementing URL support and app indexing are described next.
-<h2 id="intent">Adding an Intent Filter for Deep Linking and Google Search</h2>
+<h2 id="intent">Adding an Intent Filter for URL Support and Google Search</h2>
-<p>To use Android Studio features to add an intent filter defining a deep link, follow these
- steps:</p>
+<p>To use Android Studio features to add an intent filter defining a URL:</p>
<ol>
<li>In the <a href="{@docRoot}sdk/installing/create-project.html#ProjectView">Android view</a>
@@ -138,11 +146,11 @@
style="vertical-align:sub;margin:0;height:17px" alt="Lightbulb icon" /> appears. Click
<img src="{@docRoot}images/tools/ai-ilightbulb.png"
style="vertical-align:sub;margin:0;height:17px" alt="Lightbulb icon" />
- and select <strong>Create Deep Link</strong>.</li>
+ and select <strong>Create URL</strong>.</li>
<li>Right-click in an <code><activity></code> element and select <strong>Generate</strong>
- > <strong>Deep Link</strong>.</li>
+ > <strong>URL</strong>.</li>
<li>Place your cursor in an activity, and then select <strong>Code</strong> >
- <strong>Generate</strong> > <strong>Deep Link</strong>.</li>
+ <strong>Generate</strong> > <strong>URL</strong>.</li>
</ul>
<p>The Code Editor adds skeleton code using the
@@ -154,7 +162,7 @@
<p>The Code Editor adds an intent filter similar to the following:</p>
<pre>
<!-- ATTENTION: This intent was auto-generated. Follow instructions at
- https://g.co/AppIndexing/AndroidStudio to publish your Android app deep links. -->
+ https://g.co/AppIndexing/AndroidStudio to publish your URLs. -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
@@ -177,19 +185,20 @@
<p>We recommend that you define a <code><data></code> element that supports URLs that you’ll
add in the future. In the previous sample code, for example, Google will index any URLs starting
- with <code>www.example.com/gizmos</code>. Also, remember to
- include a deep link for your app home screen so it’s included in search results. </p>
+ with <code>http://www.example.com/gizmos</code>. Also, remember to
+ include a URL for your app home screen so it’s included in search results. </p>
-<p>Deep link URLs can be the same as the URLs of the comparable pages on your website.</p>
+<p>The URLs you specify in your intent filters can be the same as the URLs of
+the comparable pages on your website.</p>
<li>In the corresponding activity,
<a href="{@docRoot}training/app-indexing/deep-linking.html#handling-intents">add Java code</a>
to read data from the intent filter and direct the app to respond accordingly. </li>
-<li><a href="#testintent">Test your deep link</a>.</li>
+<li><a href="#testintent">Test your URL</a>.</li>
</ol>
-<p>To support Google Search for your deep links, follow these steps:</p>
+<p>To support Google Search for your URLs:</p>
<ol>
<li><a href="https://developers.google.com/app-indexing/android/app#declare-a-website-association"
class="external-link">Define an association</a>
@@ -198,7 +207,7 @@
<a href="{@docRoot}training/app-links/index.html">link default handling and verification</a>.</p>
<li>Optionally
<a href="https://developers.google.com/app-indexing/android/app#create-the-noindexxml-file"
- class="external-link">exclude app URLs</a>
+ class="external-link">exclude certain URLs</a>
from the Google index.</li>
<li>Optionally <a href="#indexing">add App Indexing API code</a> to support additional search
features.</li>
@@ -207,27 +216,28 @@
<p>To test and debug your links, you can use the following Android Studio features:</p>
<ul>
-<li><a href="#testintent">Launch your deep link</a> in Android Studio to test that it works.</li>
+<li><a href="#testintent">Launch your URL</a> in Android Studio to test that it works.</li>
<li><a href="#lint">Enable the following Android Lint categories</a>:</li>
<ul>
-<li><strong>Missing Support for Google App Indexing</strong></li>
-<li><strong>Incorrect Usage of App Link for Google App Indexing</strong></li>
+<li><strong>Missing support for Google App Indexing</strong></li>
+<li><strong>URL not supported by app for Google App Indexing</strong></li>
</ul>
+<li><a href="#appindexingtest">Perform a Google App Indexing Test</a>.</li>
</ul>
<p>In addition, you can
<a href="https://developers.google.com/app-indexing/android/test#preview-your-apk-on-search-console"
class="external-link">preview your APK in the Google Search Console</a>
- to test your deep links, whether the app is associated with a website or not. </p>
+ to test your URLs, whether the app is associated with a website or not. </p>
<h2 id="indexing">Adding App Indexing API Skeleton Code to an Activity</h2>
-<p>After adding deep links, you can add App Indexing API code to an activity to support additional
- search features. </p>
+<p>After adding URL support to your app, you can add App Indexing API code to
+an activity to support additional search features. </p>
-<p>To add App Indexing API code to an activity, follow these steps:</p>
+<p>To add App Indexing API code to an activity:</p>
<ol>
<li>In <a href="{@docRoot}sdk/installing/create-project.html#ProjectView">Android view</a>
in the <em>Project</em> window, double-click the activity Java file to open it in the
@@ -297,9 +307,9 @@
// this app activity's content,
// make sure this auto-generated web page URL is correct.
// Otherwise, set the URL to null.
- Uri.parse("http://host/path"),
- // TODO: Make sure this auto-generated app deep link URI is correct.
- Uri.parse("android-app://com.example/http/host/path")
+ Uri.parse("http://www.example.com/gizmos"),
+ // TODO: Make sure this auto-generated app URL is correct.
+ Uri.parse("android-app://com.example/http/www.example.com/gizmos")
);
AppIndex.AppIndexApi.start(client, viewAction);
}
@@ -317,9 +327,9 @@
// this app activity's content,
// make sure this auto-generated web page URL is correct.
// Otherwise, set the URL to null.
- Uri.parse("http://host/path"),
- // TODO: Make sure this auto-generated app deep link URI is correct.
- Uri.parse("android-app://com.example/http/host/path")
+ Uri.parse("http://www.example.com/gizmos"),
+ // TODO: Make sure this auto-generated app URL is correct.
+ Uri.parse("android-app://com.example/http/www.example.com/gizmos")
);
AppIndex.AppIndexApi.end(client, viewAction);
client.disconnect();
@@ -363,7 +373,9 @@
<ul>
<li><a href="#testindexing">Examine logcat Monitor Messages</a>.</li>
<li><a href="#lint">Enable the following Android Lint category</a>:
- <strong>Missing Support for Google App Indexing API</strong></li>
+ <strong>Missing support for Google App Indexing API</strong></li>
+
+<li><a href="#appindexingtest">Perform a Google App Indexing Test</a>.</li>
</ul>
<p>In addition, you can
@@ -371,12 +383,12 @@
class="external-link">preview your APK in the Google Search Console</a>.</p>
-<h2 id="testintent">Testing a Deep Link</h2>
+<h2 id="testintent">Testing a URL</h2>
-<p>When you run your app from Android Studio, you can specify a deep link to launch so you can
+<p>When you run your app from Android Studio, you can specify a URL to launch so you can
test it.</p>
-<p>To launch a deep link from Android Studio, follow these steps:</p>
+<p>To launch a URL from Android Studio:</p>
<ol>
<li>In Android Studio, open your project in
<a href="{@docRoot}sdk/installing/create-project.html#ProjectView">Android view</a>.</li>
@@ -385,14 +397,14 @@
<li>In the <em>Run/Debug Configurations</em> dialog, beneath <strong>Android Application,</strong>
select the module you want to test.</li>
<li>Select the <strong>General</strong> tab. </li>
-<li>In the <strong>Launch</strong> field, select <strong>Deep Link</strong>. </li>
-<li>In the <strong>Deep Link</strong> field, click <strong>…</strong> to select from a list of
- defined deep links.</li>
+<li>In the <strong>Launch</strong> field, select <strong>URL</strong>. </li>
+<li>In the <strong>URL</strong> field, click <strong>…</strong> to select from a list of
+ defined URLs.</li>
<p>Or type the URL you want to test, for example, <code>http://example.com/gizmos</code>. </p>
<li>Click <strong>OK</strong>.</li>
<li>Select <strong>Run</strong> > <strong>Run app</strong> or <strong>Debug app</strong>.</li>
-<li>If the <em>Device Chooser</em> dialog appears, select a connected device or an
+<li>If the <em>Select Deployment Target</em> dialog appears, select a connected device or an
emulator, and click <strong>OK</strong>.</li>
<p>If the link is successful, the app launches in the device or emulator, and displays the app at
@@ -411,12 +423,12 @@
<p>The logcat Monitor can display app indexing log messages to determine if your App Indexing API
code is pushing the correct data to the cloud. For example, you can check the app title and the
- URL. The logcat Monitor is part of Android Monitor in Android Studio. </p>
+ URL. The logcat Monitor is part of Android Monitor in Android Studio.</p>
-<p>Follow these steps:</p>
+<p>To view App Indexing API messages in the logcat Monitor:</p>
<ol>
-<li>Run your app in Android Studio so it <a href="#testintent">launches a deep link</a>.</li>
-<li><a href="{@docRoot}tools/help/android-monitor.html#displaying">Display Android Monitor</a>
+<li>Run your app in Android Studio so it <a href="#testintent">launches a URL</a>.</li>
+<li><a href="{@docRoot}tools/help/am-logcat.html#running">Display Android Monitor</a>
and click the <strong>logcat</strong> tab.</li>
<li><a href="{@docRoot}tools/help/am-logcat.html#level">Set the log level</a> to
<strong>Verbose</strong>.</li>
@@ -445,10 +457,10 @@
<h2 id="lint">Configuring Lint</h2>
-<p>You can use the Android Studio built-in Lint tool to check whether you have valid deep links
+<p>You can use the Android Studio built-in Lint tool to check whether you have valid URLs
defined in the manifest and have implemented the App Indexing API correctly in activities.</p>
-<p>You can view deep link and app indexing warnings and errors in two ways: </p>
+<p>You can view URL and app indexing warnings and errors in two ways: </p>
<ul>
<li>As pop-up text in the Code Editor. When Lint finds a problem, it highlights the problematic
code in yellow, or underlines the code in red for more serious issues.</li>
@@ -458,7 +470,7 @@
-<p>To set default Lint checks for deep links and the App Indexing API, follow these steps:</p>
+<p>To set default Lint checks for URLs and the App Indexing API:</p>
<ol>
<li>In Android Studio, open your project in
<a href="{@docRoot}sdk/installing/create-project.html#ProjectView">Android view</a>.
@@ -472,25 +484,24 @@
respectively. </li>
<li>Expand the <strong>Android Lint</strong> category and change the Lint settings as needed:</li>
<ul>
-<li><strong>Missing Support for Google App Indexing</strong> - Reports a warning if the app hasn’t
- implemented deep links, which are used by Google Search. This warning setting is enabled by
+<li><strong>Missing support for Google App Indexing</strong> - Reports a warning if the app hasn’t
+ implemented URLs, which are used by Google Search. This warning setting is enabled by
default.</li>
-<li><strong>Missing Support for Google App Indexing API</strong> - Reports if an app hasn’t
+<li><strong>Missing support for Google App Indexing API</strong> - Reports if an app hasn’t
implemented the App Indexing API at all. This warning setting is disabled by default.</li>
-<li><strong>Incorrect Usage of App Link for Google App Indexing</strong> - Reports deep link
+<li><strong>URL not supported by app for Google App Indexing</strong> - Reports URL
errors in manifest code. This error setting is enabled by default.</li>
</ul>
<p>For example, the following Lint warning appears for the first setting:</p>
-<p><img src="{@docRoot}images/tools/ai-lint.png" /></p>
+<p><img src="{@docRoot}images/tools/ai-lint.png" alt="Lint warning" /></p>
<li>Click <strong>OK</strong>.</li>
</ol>
-<p>To produce a list of Lint checks displayed in the <em>Inspection Results</em> window,
- follow these steps:</p>
+<p>To produce a list of Lint checks displayed in the <em>Inspection Results</em> window:</p>
<ol>
<li>In Android Studio, open your project in
<a href="{@docRoot}sdk/installing/create-project.html#ProjectView">Android view</a>
@@ -504,7 +515,7 @@
dialog, optionally click <strong>Manage</strong> to define a new profile, specify the Lint
settings you want, and then click <strong>OK</strong>.</li>
<p>In the <em>Inspections</em> dialog, you can search for the string "app indexing"
-to find the deep link and App Indexing API Lint checks. Note that changing Lint settings for a
+to find the URL and App Indexing API Lint checks. Note that changing Lint settings for a
profile in the <em>Inspections</em> dialog doesn’t change the default settings, as described in
the previous procedure. It does change the settings for profiles displayed in the
<em>Inspections</em> dialog, however.</p>
@@ -512,3 +523,87 @@
<p>The results appear in the <em>Inspection Results</em> window.</p>
</ol>
+
+
+<h2 id="appindexingtest">Performing a Google App Indexing Test</h2>
+
+<p>You can use a Google App Indexing Test to check whether Google can index
+a URL by either crawling your app page or using the App Indexing API.
+Google can index the URL if your app supports at least one of the following:
+</p>
+<ul>
+<li>Googlebot can open and crawl the page identified by the URL.</li>
+<li>Your app sends the correct data by using the App Indexing API.</li>
+</ul>
+
+<p>To perform a Google App Indexing Test: </p>
+<ol>
+<li>Add <a href="#intent">URL</a> and
+<a href="#indexing">App Indexing API</a> support to your app.
+</li>
+<li>While your project is open in Android Studio, select <strong>Tools</strong>
+> <strong>Android</strong> > <strong>Google App Indexing Test</strong>.
+</li>
+<li>In the <em>Google App Indexing Test</em> dialog, select a
+<strong>Module</strong>, <strong>URL</strong>, and <strong>Language</strong>.
+</li>
+<li>Log in if you see a message asking you to log into a Google Cloud Platform
+account.</li>
+<li>In the <em>Google App Indexing Test</em> dialog, click <strong>OK</strong>.
+</li>
+
+<p>Android Studio builds the APK and starts the test. The test can take a few
+minutes to complete. The results appear in a new tab in the Code Editor.</p>
+<p><img src="{@docRoot}images/tools/ai-appindexingtest.png"
+alt="Google App Indexing Test results" /></p>
+
+<p>If the app preview on the right shows the screen that corresponds to the URL
+you're testing, then Googlebot can find the URL.</p>
+
+<li>Correct any issues the test identifies, and repeat the test as often as
+needed.</li>
+</li>
+</ol>
+
+<p>The following table lists common errors and warnings you might encounter.</p>
+
+<table>
+ <tr>
+ <th scope="col">Warning or Error</th>
+ <th scope="col">Description</th>
+ </tr>
+
+ <tr>
+ <td>Error: Google cannot index this page.</td>
+ <td>Your app can't be crawled by Googlebot or using the App Indexing API,
+ so Google isn't able to index this app.</td>
+ </tr>
+ <tr>
+ <td>Warning: The App URL you sent by using the App Indexing API doesn't
+ match the URL opened.</td>
+ <td>When calling the App Indexing API, the URL specified in the app must
+ match the opened URL.</td>
+ </tr>
+ <tr>
+ <td>Warning: Google cannot index this page using the App Indexing API
+ because the title is empty.</td>
+ <td>When calling the App Indexing API, the title shouldn't be empty.</td>
+ </tr>
+ <tr>
+ <td>Warning: Google can index this page using Googlebot crawling but
+ identified blocked resources.</td>
+ <td>The app references other resources, and some of them are blocked or
+ temporarily unavailable. If these resources aren't critical, it might not
+ matter. Check the preview on the right to see whether the content
+ displays correctly. To fix this issue, make sure the resources aren't
+ blocked by <a href="https://support.google.com/webmasters/answer/6062608"
+ class="external-link"><code>robots.txt</code></a>.</td>
+ </tr>
+ <tr>
+ <td>Warning: Google cannot index this page using the App Indexing API.</td>
+ <td>Your app isn’t using the App Indexing API. We recommended adding App
+ Indexing API support to your app.</td>
+ </tr>
+
+</table>
+
diff --git a/docs/html/tools/performance/index.jd b/docs/html/tools/performance/index.jd
index fa5af86..299b05e 100644
--- a/docs/html/tools/performance/index.jd
+++ b/docs/html/tools/performance/index.jd
@@ -22,6 +22,17 @@
visualize the rendering, compute, memory, and battery performance of your
app.</p>
+<p class="note">
+ <strong>Note:</strong> While profiling an app, you should <a href=
+ "{@docRoot}tools/building/building-studio.html#disable-ir">disable Instant
+ Run</a>. There is a small performance impact when <a href=
+ "{@docRoot}tools/building/building-studio.html#instant-run">using Instant
+ Run</a> and a slightly larger impact when updating methods. This
+ performance impact could interfere with information provided by performance
+ profiling tools. Additionally, the stub methods generated while using the
+ feature can complicate stack traces.
+</p>
+
<h2 id="rendering-tools">Rendering Analysis Tools</h2>
<p>Visualize the rendering behavior and performance of your app.</p>
diff --git a/docs/html/tools/revisions/gradle-plugin.jd b/docs/html/tools/revisions/gradle-plugin.jd
index 540bbcd..6a35cab 100644
--- a/docs/html/tools/revisions/gradle-plugin.jd
+++ b/docs/html/tools/revisions/gradle-plugin.jd
@@ -3,42 +3,249 @@
@jd:body
<div id="qv-wrapper">
-<div id="qv">
+ <div id="qv">
+ <h2>
+ In this document
+ </h2>
- <h2>See also</h2>
- <ol>
- <li><a href="{@docRoot}sdk/installing/studio-build.html">Build System Overview</a></li>
- <li><a href="{@docRoot}tools/building/plugin-for-gradle.html">Android Plugin for Gradle</a></li>
- </ol>
+ <ol>
+ <li>
+ <a href="#updating-plugin">Updating the Android Plugin for Gradle
+ Version</a>
+ </li>
-</div>
+ <li>
+ <a href="#updating-gradle">Updating the Gradle Version</a>
+ </li>
+
+ <li>
+ <a href="#revisions">Revisions</a>
+ </li>
+ </ol>
+
+ <h2>
+ See also
+ </h2>
+
+ <ol>
+ <li>
+ <a href="{@docRoot}sdk/installing/studio-build.html">Build System
+ Overview</a>
+ </li>
+
+ <li>
+ <a href="{@docRoot}tools/building/plugin-for-gradle.html">Android
+ Plugin for Gradle</a>
+ </li>
+ </ol>
+ </div>
</div>
+<p>
+ The Android build system uses the Android Plugin for Gradle to support
+ building Android applications with the <a href=
+ "http://www.gradle.org/">Gradle</a> build toolkit. The plugin runs
+ independent of Android Studio so the plugin and the Gradle build system can
+ be updated independently of Android Studio.
+</p>
-<p>The Android build system uses the Android Plugin for Gradle to support building Android
-applications with the <a href="http://www.gradle.org/">Gradle</a> build toolkit. The plugin runs
-independent of Android Studio so the plugin and the Gradle build system can be updated
-independently of Android Studio.</p>
+<h2 id="updating-plugin">
+ Updating the Android Plugin for Gradle
+</h2>
-<p class="note"><strong>Note:</strong> When you update Android Studio or open a project in a
-previous version of Android Studio, Android Studio prompts you to automatically update the plugin
-and Gradle to the latest available versions. You can choose to accept these updates based
-on your project's build requirements. </p>
+<p>
+ When you update Android Studio, you may receive a prompt to automatically
+ update the Android Plugin for Gradle to the latest available version. You
+ can choose to accept the update or manually specify a version based on
+ your project's build requirements.
+</p>
+<p>
+ You can specify the <a href=
+ "{@docRoot}tools/building/plugin-for-gradle.html">Android Plugin for
+ Gradle</a> version in either the <strong>File</strong> > <strong>Project
+ Structure</strong> > <strong>Project</strong> menu in Android Studio, or
+ the top-level <code>build.gradle</code> file. The plugin version applies to
+ all modules built in that Android Studio project. The following example sets
+ the Android Plugin for Gradle to version 2.0.0 from the
+ <code>build.gradle</code> file:
+</p>
-<h2 id="revisions">Revisions</h2>
+<pre>
+buildscript {
+ ...
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.0.0'
+ }
+}
+</pre>
+<p class="caution">
+ <strong>Caution:</strong> You should not use dynamic dependencies in version
+ numbers, such as <code>'com.android.tools.build:gradle:2.+'</code>. Using
+ this feature can cause unexpected version updates and difficulty resolving
+ version differences.
+</p>
-<p>The sections below provide notes about successive releases of
-the Android Plugin for Gradle, as denoted by revision number. To determine what revision of the
-plugin you are using, check the version declaration in the project-level
-<strong>build.gradle</strong> file. </p>
+<p>
+ If the specified plugin version has not been downloaded, Gradle downloads it
+ the next time you build your project or click <strong>Tools</strong> >
+ <strong>Android</strong> > <strong>Sync Project with Gradle Files</strong>
+ from the Android Studio main menu.
+</p>
-<p>For a summary of known issues in Android Plugin for Gradle, see <a
-href="http://tools.android.com/knownissues">http://tools.android.com/knownissues</a>.</p>
+<h2 id="updating-gradle">
+ Updating Gradle
+</h2>
+
+<p>
+ When you update Android Studio, you may receive a prompt to automatically
+ update Gradle to the latest available version. You can choose to accept the
+ update or manually specify a version based on your project's build
+ requirements.
+</p>
+
+<p>
+ You can specify the Gradle version in either the <strong>File</strong> >
+ <strong>Project Structure</strong> > <strong>Project</strong> menu in
+ Android Studio, or by editing the Gradle distribution reference in the
+ <code>gradle/wrapper/gradle-wrapper.properties</code> file. The following
+ example sets the Gradle version to 2.10 in the
+ <code>gradle-wrapper.properties</code> file.
+</p>
+
+<pre>
+...
+distributionUrl = https\://services.gradle.org/distributions/gradle-2.10-all.zip
+...
+</pre>
+
+<h2 id="revisions">
+ Revisions
+</h2>
+
+<p>
+ For a summary of known issues in Android Plugin for Gradle, visit the
+ <a class="external-link" href="http://tools.android.com/knownissues">Android
+ Tools Project Site</a>.
+</p>
<div class="toggle-content opened">
<p><a href="#" onclick="return toggleContent(this)">
<img src="{@docRoot}assets/images/styles/disclosure_up.png" class="toggle-content-img"
+ alt=""/>Android Plugin for Gradle, Revision 2.0.0</a> <em>(March 2016)</em>
+ </p>
+
+ <div class="toggle-content-toggleme">
+ <dl>
+ <dt>Dependencies:</dt>
+
+ <dd>
+ <ul>
+ <li>Gradle 2.10 or higher.
+ </li>
+
+ <li>
+ <a href="{@docRoot}tools/revisions/build-tools.html">Build Tools 21.1.1</a>
+ or higher.
+ </li>
+ </ul>
+ </dd>
+
+ <dt>New:</dt>
+ <dd>
+ <ul>
+ <li>Enables <a href=
+ "{@docRoot}tools/building/building-studio.html#instant-run">Instant Run</a> by
+ supporting bytecode injection, and pushing code and resource updates to a
+ running app on the emulator or a physical device.
+ </li>
+
+ <li>Added support for incremental builds, even when the app isn’t running.
+ Full build times are improved by pushing incremental changes through the
+ <a href="{@docRoot}tools/help/adb.html">Android Debug Bridge</a> to the
+ connected device.
+ </li>
+
+ <li>Added <code>maxProcessCount</code> to control how many slave dex processes can
+ be spawned concurrently. The following code, in the module-level
+ <code>build.gradle</code> file, sets the maximum number of concurrent processes
+ to 4:
+<pre>
+android {
+ ...
+ dexOptions {
+ maxProcessCount = 4
+ }
+}
+</pre>
+ </li>
+
+ <li>Added an experimental code shrinker to support pre-dexing and reduce re-dexing
+ of dependencies, which are not supported with Proguard. This improves the build
+ speed of your debug build variant. Because the experimental shrinker does not
+ support optimization and obfuscation, you should enable Proguard for your
+ release builds. To enable the experimental shrinker for your debug builds, add
+ the following to your module-level <code>build.gradle</code> file:
+
+<pre>
+android {
+ ...
+ buildTypes {
+ debug {
+ minifyEnabed true
+ useProguard false
+ }
+ release {
+ minifyEnabed true
+ useProguard true // this is a default setting
+ }
+ }
+}
+</pre>
+ </li>
+
+ <li>Added logging support and improved performance for the resource shrinker.
+ The resource shrinker now logs all of its operations into a <code>resources.txt</code>
+ file located in the same folder as the Proguard log files.
+ </li>
+ </ul>
+ </dd>
+
+ <dt>Changed behavior:</dt>
+ <dd>
+ <ul>
+ <li>When <code>minSdkVersion</code> is set to 18 or higher, APK signing uses
+ SHA256.
+ </li>
+
+ <li>DSA and ECDSA keys can now sign APK packages.
+
+ <p class="note">
+ <strong>Note:</strong> The <a href=
+ "{@docRoot}training/articles/keystore.html">Android keystore</a> provider no
+ longer supports <a href=
+ "{@docRoot}about/versions/marshmallow/android-6.0-changes.html#behavior-keystore">
+ DSA keys on Android 6.0</a> (API level 23) and higher.
+ </p>
+
+ </li>
+ </ul>
+ </dd>
+
+ <dt>Fixed issues:</dt>
+ <dd>
+ <ul>
+ <li>Fixed an issue that caused duplicate AAR dependencies in both
+ the test and main build configurations.
+ </li>
+ </ul>
+ </dd>
+ </div>
+</div>
+
+<div class="toggle-content closed">
+ <p><a href="#" onclick="return toggleContent(this)">
+ <img src="{@docRoot}assets/images/styles/disclosure_down.png" class="toggle-content-img"
alt=""/>Android Plugin for Gradle, Revision 1.5.0</a> <em>(November 2015)</em>
</p>
@@ -546,9 +753,6 @@
</div>
</div>
-
-
-
<div class="toggle-content closed">
<p><a href="#" onclick="return toggleContent(this)">
<img src="{@docRoot}assets/images/styles/disclosure_down.png" class="toggle-content-img"
@@ -577,47 +781,4 @@
</ul>
</dd>
</div>
-</div>
-
-
-
-
-<h2>Updating the Android Plugin for Gradle Version</h2>
-<p>The Android Plugin for Gradle version is specified in the
-<strong>File > Project Structure > Project</strong> menu and the project-level
-<code>build.gradle</code> file. The plugin version applies to all modules built in that
-Android Studio project. This example sets the Android Plugin for Gradle to version 1.1.0 from the <code>build.gradle</code> file:
-<pre>
-...
- dependencies {
- classpath 'com.android.tools.build:gradle:1.1.0'
- }
-...
-</pre>
-
-
-<p class="caution"><strong>Caution:</strong> You should not use dynamic dependencies (+) in
-version numbers. Using this feature can cause unexpected version updates and difficulty
-resolving version differences. </p>
-
-<p>If you're building with Gradle but not using Android Studio, the build process downloads the
-latest Android Plugin for Gradle when it runs. </p>
-
-
-
-<h2>Updating the Gradle Version </h2>
-
-<p>Android Studio requires Gradle version 2.2.1 or later. To view and
-update the Gradle version, edit the Gradle distribution reference in the
-<code>gradle/wrapper/gradle-wrapper.properties</code> file. This example sets the
-Gradle version to 2.2.1.</p>
-
-<pre>
-...
-distributionUrl=http\://services.gradle.org/distributions/gradle-2.2.1-all.zip
-...
-</pre>
-
-<p>For more details about the supported Android Plugin for Gradle properties and syntax, click
-the link to the
-<a href="{@docRoot}tools/building/plugin-for-gradle.html">Plugin Language Reference</a>.</p>
\ No newline at end of file
+</div>
\ No newline at end of file
diff --git a/docs/html/tools/tools_toc.cs b/docs/html/tools/tools_toc.cs
index 3ce0d11..f737770 100644
--- a/docs/html/tools/tools_toc.cs
+++ b/docs/html/tools/tools_toc.cs
@@ -97,7 +97,7 @@
<li><a href="<?cs var:toroot ?>tools/debugging/annotations.html">
<span class="en">Improving Code Inspection with Annotations</span></a></li>
<li><a href="<?cs var:toroot ?>tools/help/app-link-indexing.html">
- <span class="en">Deep Link and App Indexing API Support</span></a></li>
+ <span class="en">Supporting URLs and App Indexing in Android Studio</span></a></li>
</ul>
</li>
@@ -251,7 +251,7 @@
<li><a href="<?cs var:toroot ?>tools/building/manifest-merge.html">
<span class="en">Manifest Merging</span></a></li>
<li><a href="<?cs var:toroot ?>tools/building/multidex.html">
- <span class="en">Apps Over 65K Methods</span></a></li>
+ <span class="en">Apps Over 64K Methods</span></a></li>
</ul>
</li><!-- end of build system -->
diff --git a/docs/html/training/app-indexing/index.jd b/docs/html/training/app-indexing/index.jd
index a1a47e9..60f40b4 100644
--- a/docs/html/training/app-indexing/index.jd
+++ b/docs/html/training/app-indexing/index.jd
@@ -22,7 +22,7 @@
target="_blank">App Indexing for Google Search</a></li>
<li><a href="{@docRoot}guide/components/intents-filters.html">Intents and Intent
Filters</a></li>
-<li><a href="{@docRoot}tools/help/app-link-indexing.html">Deep Link and App Indexing API Support in Android Studio</a></li>
+<li><a href="{@docRoot}tools/help/app-link-indexing.html">Supporting URLs and App Indexing in Android Studio</a></li>
</ul>
</div>
</div>
diff --git a/docs/html/training/app-links/index.jd b/docs/html/training/app-links/index.jd
index 27b6480..80309ee 100644
--- a/docs/html/training/app-links/index.jd
+++ b/docs/html/training/app-links/index.jd
@@ -16,7 +16,7 @@
</ol>
<h2>See also</h2>
<ol>
- <li><a href="{@docRoot}tools/help/app-link-indexing.html">Deep Link and App Indexing API Support in Android Studio</a></li>
+ <li><a href="{@docRoot}tools/help/app-link-indexing.html">Supporting URLs and App Indexing in Android Studio</a></li>
</ol>
</div>
</div>
diff --git a/docs/html/training/testing/start/index.jd b/docs/html/training/testing/start/index.jd
index a4b4aea..74617b0 100644
--- a/docs/html/training/testing/start/index.jd
+++ b/docs/html/training/testing/start/index.jd
@@ -6,69 +6,153 @@
@jd:body
<div id="tb-wrapper">
-<div id="tb">
+ <div id="tb">
-<!-- Required platform, tools, add-ons, devices, knowledge, etc. -->
-<h2>Dependencies and prerequisites</h2>
-<ul>
- <li><a href="{@docRoot}tools/studio/index.html">Android Studio (latest version)</a>.</li>
-</ul>
+ <h2>
+ Dependencies and prerequisites
+ </h2>
-<h2>This lesson teaches you to</h2>
-<ol>
-<li><a href="#setup">Set Up Your Testing Environment</a></li>
-<li><a href="#build">Build and Run Your Tests</a></li>
-</ol>
+ <ul>
+ <li>
+ <a href="{@docRoot}tools/studio/index.html">Android Studio 2.0</a>, or
+ later.
+ </li>
-<h2>You Should Also Read</h2>
-<ul>
-<li><a href="{@docRoot}tools/testing/testing_android.html">Testing Concepts</a></li>
-<li><a href="https://github.com/googlesamples/android-testing"
- class="external-link">Android Testing Samples</a></li>
-<li><a href="{@docRoot}about/dashboards/index.html">Android Dashboards</a></li>
-</ul>
+ <li>The Android Support Repository (available from the <a href=
+ "{@docRoot}tools/help/sdk-manager.html">SDK Manager</a>)
+ </li>
+ </ul>
-</div>
+ <h2>
+ This lesson teaches you to
+ </h2>
+
+ <ol>
+ <li>
+ <a href="#config-local-tests">Configure Your Project for Local Unit
+ Tests</a>
+ </li>
+
+ <li>
+ <a href="#config-instrumented-tests">Configure Your Project for
+ Instrumented Tests</a>
+ </li>
+
+ <li>
+ <a href="#build">Build and Run Your Tests</a>
+ <ol>
+ <li>
+ <a href="#run-local-tests">Run Local Unit Tests</a>
+ </li>
+
+ <li>
+ <a href="#run-instrumented-tests">Run Instrumented Tests</a>
+ </li>
+
+ <li>
+ <a href="#run-ctl">Run Instrumented Tests with Cloud Test Lab</a>
+ </li>
+ </ol>
+ </li>
+ </ol>
+
+ <h2>
+ See also
+ </h2>
+
+ <ul>
+ <li>
+ <a href="{@docRoot}tools/testing/testing_android.html">Testing
+ Concepts</a>
+ </li>
+
+ <li>
+ <a href="https://github.com/googlesamples/android-testing" class=
+ "external-link">Android Testing Samples</a>
+ </li>
+
+ <li>
+ <a href="https://developers.google.com/cloud-test-lab/">Cloud Test
+ Lab</a>
+ </li>
+ </ul>
+ </div>
</div>
-<p>You should be writing and running tests as part of your Android app development cycle.
-Well-written tests can help you catch bugs early in development and give you confidence in your
-code.</p>
+<p>
+ Writing and running tests are important parts of the Android app development
+ cycle. Well-written tests can help you catch bugs early in development and
+ give you confidence in your code. Using Android Studio, you can run local
+ unit tests or instrumented tests on a variety of physical or virtual Android
+ devices. You can then analyze the results and make changes to your code
+ without leaving the development environment.
+</p>
-<p>To verify specific behavior in your app, and to check for consistency across different Android
-devices, you can write a <a href="//en.wikipedia.org/wiki/Test_case"
-class="external-link">test case</a>. This lesson teaches you how to build a test case using the
-JUnit 4 framework and the testing APIs and tools provided by Google, and how to run your
-tests.</p>
+<p>
+ <em>Local unit tests</em> are tests that run on your local machine, without
+ needing access to the Android framework or an Android device. To learn how to
+ develop local units tests, see <a href=
+ "{@docRoot}training/testing/unit-testing/local-unit-tests.html">Building
+ Local Unit Tests</a>.
+</p>
-<h2 id="setup">Set Up Your Testing Environment</h2>
+<p>
+ <em>Instrumented tests</em> are tests that run on an Android device or
+ emulator. These tests have access to {@link android.app.Instrumentation}
+ information, such as the {@link android.content.Context} for the app under
+ test. Instrumented tests can be used for unit, user interface (UI), or app
+ component integration testing. To learn how to develop instrumented tests for
+ your specific needs, see these additional topics:
+</p>
-<p>Before you start writing and running your tests, you must set up your test
-development environment. Android Studio provides an integrated development environment for you to
-create, build, and run Android app test cases from a graphical user interface (GUI).</p>
-
-<p>You must first download the prerequisite Android development tools before proceeding:
<ul>
-<li><a href="{@docRoot}sdk/index.html">Android Studio</a> (latest version).</li>
-<li>The latest Android Support Repository using the
- <a href="{@docRoot}tools/help/sdk-manager.html">SDK Manager</a>. </li>
+ <li>
+ <a href=
+ "{@docRoot}training/testing/unit-testing/instrumented-unit-tests.html">Building
+ Instrumented Unit Tests</a> - Build more complex unit tests that have
+ Android dependencies which cannot be easily filled by using mock objects.
+ </li>
+
+ <li>
+ <a href="{@docRoot}training/testing/ui-testing/index.html">Automating User
+ Interface Tests</a> - Create tests to verify that the user interface
+ behaves correctly for user interactions within a single app or for
+ interactions across multiple apps.
+ </li>
+
+ <li>
+ <a href="{@docRoot}training/testing/integration-testing/index.html">Testing
+ App Component Integrations</a> - Verify the behavior of components that
+ users do not directly interact with, such as a <a href=
+ "{@docRoot}guide/components/services.html">Service</a> or a <a href=
+ "guide/topics/providers/content-providers.html">Content Provider</a>.
+ </li>
</ul>
-<p>Based on the type of test you want to create, configure the test code source location and the
- project dependencies in Android Studio as described in the following sections.</p>
+<p>
+ This lesson teaches you how to build and run your tests using using Android
+ Studio. If you are not using Android Studio, you can learn how to
+ <a href="{@docRoot}tools/testing/testing_otheride.html">run your tests from
+ the command-line</a>.
+</p>
-<h3 id="config-local-tests">Configure Your Project for Local Unit Tests</h3>
-<p><em>Local unit tests</em> are tests that run on your local machine, without needing access to the
-Android framework or an Android device. To learn how to develop local units tests, see
-<a href="{@docRoot}training/testing/unit-testing/local-unit-tests.html">
-Building Local Unit Tests</a>.</p>
-<p>In your Android Studio project, you must store the source files for local unit tests under a
-specific source directory ({@code src/test/java}). This feature improves your project organization
-by letting you group your unit tests together into a single source set.</p>
-<p>As with production code, you can create local unit tests for a
-<a href="http://developer.android.com/tools/building/configuring-gradle.html#workBuildVariants"
-class="external-link">specific flavor or build type</a>. Keep your unit tests in a test
-source tree location that corresponds to your production source tree, such as:</p>
+<h3 id="config-local-tests">
+ Configure Your Project for Local Unit Tests
+</h3>
+
+<p>
+ In your Android Studio project, you must store the source files for local
+ unit tests under a specific source directory ({@code src/test/java}). This
+ improves project organization by grouping your unit tests together into a
+ single source set.
+</p>
+
+<p>
+ As with production code, you can create local unit tests for a <a href=
+ "{@docRoot}tools/building/configuring-gradle.html#workBuildVariants">specific
+ flavor or build type</a>. Keep your unit tests in a test source tree location
+ that corresponds to your production source tree, such as:
+</p>
<table>
<tr>
@@ -89,15 +173,21 @@
</tr>
</table>
-<p>You'll need to configure the testing dependencies for your project to use the
- standard APIs provided by the JUnit 4 framework. To simplify your local unit test development,
- we recommend that you include the <a href="https://github.com/mockito/mockito"
- class="external-link">Mockito</a> library if your test needs to interact with Android
- dependencies. To learn more about using mock objects in your local unit tests, see
-<a href="{@docRoot}training/testing/unit-testing/local-unit-tests.html#mocking-dependencies">
- Mocking Android dependencies</a>.</p>
-<p>In the {@code build.gradle} file of your Android app module, specify your dependencies like
-this:</p>
+<p>
+ You'll need to configure the testing dependencies for your project to use the
+ standard APIs provided by the JUnit 4 framework. If your test needs to
+ interact with Android dependencies, include the <a href=
+ "https://github.com/mockito/mockito" class="external-link">Mockito</a>
+ library to simplify your local unit tests. To learn more about using mock
+ objects in your local unit tests, see <a href=
+ "{@docRoot}training/testing/unit-testing/local-unit-tests.html#mocking-dependencies">
+ Mocking Android dependencies</a>.
+</p>
+
+<p>
+ In your app's top-level {@code build.gradle} file, you need to specify these
+ libraries as dependencies:
+</p>
<pre>
dependencies {
@@ -108,46 +198,43 @@
}
</pre>
-<h3 id="config-instrumented-tests">Configure Your Project for Instrumented Tests</h3>
-<p><em>Instrumented tests</em> are tests that run on an Android device or emulator. These tests
-have access to {@link android.app.Instrumentation} information, such as the
-{@link android.content.Context} for the app under test. Instrumented tests can be used for unit,
-user interface (UI), or app component integration testing. To learn how to develop instrumented
-tests for your specific needs, see these additional topics:
-<ul>
-<li><a href="{@docRoot}training/testing/unit-testing/instrumented-unit-tests.html">
- Building Instrumented Unit Tests</a> - Build more complex unit tests that have Android
- dependencies which cannot be easily filled by using mock objects.</li>
-<li><a href="{@docRoot}training/testing/ui-testing/index.html">
- Automating User Interface Tests</a> - Create tests to verify that the user interface behaves
- correctly for user interactions within a single app or for interactions across multiple apps.</li>
-<li><a href="{@docRoot}training/testing/integration-testing/index.html">
- Testing App Component Integrations</a> - Verify the behavior of components that users do not
-directly interact with, such as a <a href="{@docRoot}guide/components/services.html">Service</a> or
-a <a href="guide/topics/providers/content-providers.html">Content Provider</a>.</li>
-</ul>
-</p>
+<h3 id="config-instrumented-tests">
+ Configure Your Project for Instrumented Tests
+</h3>
+
<p>
-In your Android Studio project, you must place the source code for your instrumentated tests under
-a specific directory (<code>src/androidTest/java</code>).
+ In your Android Studio project, you must place the source code for your
+ instrumentated tests under a specific directory
+ (<code>src/androidTest/java</code>).
</p>
+
<p>
-Download the Android Testing Support Library, which provides APIs that allow you to quickly build and
-run instrumented test code for your apps. The Testing Support Library includes a JUnit 4 test runner
-(<a href="{@docRoot}tools/testing-support-library/index.html#AndroidJUnitRunner">AndroidJUnitRunner
-</a>) and APIs for functional UI tests
-(<a href="{@docRoot}tools/testing-support-library/index.html#Espresso">Espresso</a> and
-<a href="{@docRoot}tools/testing-support-library/index.html#UIAutomator">UI Automator</a>). To
-learn how to install the library, see
-<a href="{@docRoot}tools/testing-support-library/index.html#setup">Testing Support Library Setup</a>.
+ <a href="{@docRoot}tools/testing-support-library/index.html#setup">Download
+ the Android Testing Support Library Setup</a>, which provides APIs that allow
+ you to quickly build and run instrumented test code for your apps. The
+ Testing Support Library includes a JUnit 4 test runner (<a href=
+ "{@docRoot}tools/testing-support-library/index.html#AndroidJUnitRunner">AndroidJUnitRunner</a>
+ ) and APIs for functional UI tests (<a href=
+ "{@docRoot}tools/testing-support-library/index.html#Espresso">Espresso</a>
+ and <a href=
+ "{@docRoot}tools/testing-support-library/index.html#UIAutomator">UI
+ Automator</a>).
</p>
-<p>You'll need to configure the Android testing dependencies for your project to use the test runner
-and the rules APIs provided by the Testing Support Library. To simplify your test development,
-we also recommend that you include the <a href="https://github.com/hamcrest"
-class="external-link">Hamcrest</a> library, which lets you create more flexible assertions using the
-Hamcrest matcher APIs.</p>
-<p>In the {@code build.gradle} file of your Android app module, specify your dependencies like
-this:</p>
+
+<p>
+ You'll need to configure the Android testing dependencies for your project to
+ use the test runner and the rules APIs provided by the Testing Support
+ Library. To simplify your test development, we also recommend that you
+ include the <a href="https://github.com/hamcrest" class=
+ "external-link">Hamcrest</a> library, which lets you create more flexible
+ assertions using the Hamcrest matcher APIs.
+</p>
+
+<p>
+ In your app's top-level {@code build.gradle} file, you need to specify these
+ libraries as dependencies:
+</p>
+
<pre>
dependencies {
androidTestCompile 'com.android.support:support-annotations:23.0.1'
@@ -162,59 +249,13 @@
}
</pre>
-<h2 id="build">Build and Run Your Tests</h2>
-
-<p>You can run build and run your tests in a similar way to how you run your Android apps --
- graphically in Android Studio or from the command-line using the
-<a href="{@docRoot}tools/building/plugin-for-gradle.html">
-Android Plugin for Gradle</a>.</p>
-
-<h3 id="run-local-tests">Run Local Unit Tests</h3>
<p>
-The Android Plugin for Gradle compiles the local unit test code located in the default directory
-({@code src/test/java}), builds a test app, and executes it locally
-using the default test runner class.
+ To use JUnit 4 test classes, make sure to specify <a href=
+ "{@docRoot}reference/android/support/test/runner/AndroidJUnitRunner.html">{@code
+ AndroidJUnitRunner}</a> as the default test instrumentation runner in your
+ project by including the following setting in your app's module-level {@code build.gradle}
+ file:
</p>
-<p>
-To run local unit tests in your Gradle project from Android Studio:
-</p>
-<ol>
-<li>In the <strong>Project</strong> window, right click on the project and synchronize your project.
-</li>
-<li>Open the <strong>Build Variants</strong> window by clicking the left-hand tab, then change the
-test artifact to <em>Unit Tests</em>.
-</li>
-<li>In the <strong>Project</strong> window, drill down to your unit test class or method,
-then right-click and run it. To run all tests in the unit test directory, select the directory then
-right-click and press <strong>Run tests</strong>.
-</li>
-</ol>
-
-<p>Android Studio displays the results of the unit test execution in the <strong>Run</strong>
-window.</p>
-
-<p>To run local unit tests in your Gradle project from the command-line, call the {@code test} task
-command.</p>
-
-<pre>
-./gradlew test
-</pre>
-
-<p>If there are failing tests, the command will display links to HTML reports (one per build
-variant). You can find the generated HTML test result reports in the
-{@code <path_to_your_project>/app/build/reports/tests/} directory, and the corresponding XML
-files in the {@code <path_to_your_project>/app/build/test-results/} directory.</p>
-
-<h3 id="run-instrumented-tests">Run Instrumented Tests</h3>
-<p>
-The Android Plugin for Gradle compiles the instrumented test code located in the default directory
-({@code src/androidTest/java}), builds a test APK and production APK, installs both APKs on the
-connected device or emulator, and executes the tests.</p>
-
-<p>Make sure to specify
-<a href="{@docRoot}reference/android/support/test/runner/AndroidJUnitRunner.html">
-{@code AndroidJUnitRunner}</a> as the default test instrumentation runner in your project. To do
-this, add the following setting in your {@code build.gradle} file:</p>
<pre>
android {
@@ -224,29 +265,257 @@
}
</pre>
-<p>To run your instrumented tests in Android Studio:</p>
+<h2 id="build">
+ Build and Run Your Tests
+</h2>
+
+<p>
+ Android Studio provides all the tools you need to build, run, and analyze
+ your tests within the development environment. You can also run instrumented
+ tests on multiple device configurations, simultaneously, using <a href=
+ "https://developers.google.com/cloud-test-lab/">Cloud Test Lab</a>
+ integration.
+</p>
+
+<p class="note">
+ <strong>Note:</strong> While running or debugging instrumented tests,
+ Android Studio does not inject the additional methods required for <a href=
+ "{@docRoot}tools/building/building-studio.html#instant-run">Instant Run</a>
+ and turns the feature off.
+</p>
+
+<h3 id="run-local-tests">
+ Run Local Unit Tests
+</h3>
+
+<p>
+ To run your local unit tests:
+</p>
+
<ol>
-<li>Open the <strong>Build Variants</strong> window by clicking the left-hand tab, then set the
-test artifact to <em>Android Instrumentation Tests</em>.
-</li>
-<li>In the <strong>Project</strong> window, drill down to your instrumented test class or method,
- then right-click and run it using the Android Test configuration. To run all tests in the
-instrumented test directory, select the directory then right-click and press
-<strong>Run tests</strong>.
-</li>
+ <li>In the <em>Project</em> window, right click on the project and
+ synchronize your project.
+ </li>
+
+ <li>Open the <em>Build Variants</em> window by clicking the left-hand tab,
+ then change the test artifact to <em>Unit Tests</em>.
+ </li>
+
+ <li>In the <em>Project</em> window, navigate to your unit test class or
+ method, then right-click it and select <strong>Run</strong> <img src=
+ "{@docRoot}images/tools/as-run.png" alt="" style=
+ "vertical-align:bottom;margin:0;">.
+ <ul>
+ <li>To run all tests in the unit test directory, right-click on the
+ directory and select <strong>Run tests</strong> <img src=
+ "{@docRoot}images/tools/as-run.png" alt="" style=
+ "vertical-align:bottom;margin:0;">.
+ </li>
+ </ul>
+ </li>
</ol>
-<p>Android Studio displays the results of the instrumented test execution in the
-<strong>Run</strong> window.</p>
+<p>
+ The Android Plugin for Gradle compiles the local unit test code located in
+ the default directory ({@code src/test/java}), builds a test app, and
+ executes it locally using the default test runner class. Android Studio then
+ displays the results in the <em>Run</em> window.
+</p>
-<p>To run your instrumented tests from the command-line via Gradle, call the
- {@code connectedAndroidTest} (or {@code cAT}) task:</p>
+<h3 id="run-instrumented-tests">
+ Run Instrumented Tests
+</h3>
-<pre>
-./gradlew cAT
-</pre>
+<p>
+ To run your instrumented tests:
+</p>
-<p>You can find the generated HTML test result reports in the
-{@code <path_to_your_project>/app/build/outputs/reports/androidTests/connected/} directory,
-and the corresponding XML files in the
-{@code <path_to_your_project>/app/build/outputs/androidTest-results/connected/} directory.</p>
\ No newline at end of file
+<ol>
+ <li>Open the <em>Build Variants</em> window by clicking the left-hand tab,
+ then set the test artifact to <em>Android Instrumentation Tests</em>.
+ </li>
+
+ <li>In the <em>Project</em> window, navigate to your instrumented test class
+ or method, then right-click and run it using the Android Test configuration.
+ To run all tests in the instrumented test directory, right-click the
+ directory and select <strong>Run tests</strong> <img src=
+ "{@docRoot}images/tools/as-run.png" alt="" style=
+ "vertical-align:bottom;margin:0;">.
+ </li>
+</ol>
+
+<p>
+ The <a href="{@docRoot}tools/building/plugin-for-gradle.html">Android Plugin
+ for Gradle</a> compiles the instrumented test code located in the default
+ directory ({@code src/androidTest/java}), builds a test APK and production
+ APK, installs both APKs on the connected device or emulator, and runs the
+ tests. Android Studio then displays the results of the instrumented test execution in the
+ <em>Run</em> window.
+</p>
+
+<h3 id="run-ctl">Run Instrumented Tests with Cloud Test Lab</h3>
+
+<p>
+ Using <a href="https://developers.google.com/cloud-test-lab/">Cloud Test
+ Lab</a>, you can simultaneously test your app on many popular Android
+ devices, across multiple languages, screen orientations, and versions of the
+ Android platform. These tests run on actual physical devices in remote Google
+ data centers. You can also <a href=
+ "https://developers.google.com/cloud-test-lab/test-screenshots">configure
+ your instrumented tests to take screenshots</a> while Cloud Test Lab runs its
+ tests. You can <a href=
+ "https://developers.google.com/cloud-test-lab/command-line">deploy tests to
+ Cloud Test Lab from the command line</a>, or from Android Studio's integrated
+ testing tools.
+</p>
+
+<p>
+ Android Studio allows you to connect to your Google Cloud Platform account,
+ configure your tests, deploy them to Cloud Test Lab, and analyze the results
+ all within the development environment. Cloud Test Lab in Android Studio
+ supports the following Android test frameworks: <a href=
+ "{@docRoot}training/testing/ui-testing/espresso-testing.html">Espresso</a>,
+ <a href="{@docRoot}tools/testing-support-library/index.html#UIAutomator">UI
+ Automator 2.0</a>, or <a class="external-link" href=
+ "https://github.com/robotiumtech/robotium">Robotium</a>. Test results provide
+ test logs and include the details of any app failures.
+</p>
+
+<p>
+ Before you can start using Cloud Test Lab, you need to:
+</p>
+
+<ol>
+ <li>
+ <a href="https://console.developers.google.com/freetrial">Create a
+ Google Cloud Platform account</a> to use with active billing.
+ </li>
+
+ <li>
+ <a href="https://support.google.com/cloud/answer/6251787">Create a Google
+ Cloud project</a> for your app.
+ </li>
+
+ <li>
+ <a href="https://support.google.com/cloud/answer/6288653">Set up an active
+ billing account</a> and associate it with the project you just created.
+ </li>
+</ol>
+
+<h4 id="configure-matrix">
+Configure a test matrix and run a test
+</h4>
+
+<p>
+ Android Studio provides integrated tools that allow you to configure how you
+ want to deploy your tests to Cloud Test Lab. After you have created a Google
+ Cloud project with active billing, you can create a test configuration and
+ run your tests:
+</p>
+
+<ol>
+ <li>Click <strong>Run</strong> > <strong>Edit Configurations</strong> from
+ the main menu.
+ </li>
+
+ <li>Click <strong>Add New Configuration (+)</strong> and select
+ <strong>Android Tests</strong>.
+ </li>
+
+ <li>In the Android Test configuration dialog:
+ <ol type="a">
+ <li>Enter or select the details of your test, such as the test name, module
+ type, test type, and test class.
+ </li>
+
+ <li>From the <em>Target</em> drop-down menu under <em>Deployment Target
+ Options</em>, select <strong>Cloud Test Lab Device Matrix</strong>.
+ </li>
+
+ <li>If you are not logged in, click <strong>Connect to Google Cloud
+ Platform</strong> and allow Android Studio access to your account.
+ </li>
+
+ <li>Next to <em>Cloud Project</em>, click the <img src=
+ "{@docRoot}images/tools/as-wrench.png" alt="wrench and nut" style=
+ "vertical-align:bottom;margin:0;"> button and select your Google Cloud
+ Platform project from the list.
+ </li>
+ </ol>
+ </li>
+
+ <li>Create and configure a test matrix:
+ <ol type="a">
+ <li>Next to the <em>Matrix Configuration</em> drop-down list, click <strong>
+ Open Dialog</strong> <img src="{@docRoot}images/tools/as-launchavdm.png"
+ alt="ellipses button" style="vertical-align:bottom;margin:0;">.
+ </li>
+
+ <li>Click <strong>Add New Configuration (+)</strong>.
+ </li>
+
+ <li>In the <strong>Name</strong> field, enter a name for your new
+ configuration.
+ </li>
+
+ <li>Select the device(s), Android version(s), locale(s) and screen
+ orientation(s) that you want to test your app with. Cloud Test Lab will test
+ your app against every combination of your selections when generating test
+ results.
+ </li>
+
+ <li>Click <strong>OK</strong> to save your configuration.
+ </li>
+ </ol>
+ </li>
+
+ <li>Click <strong>OK</strong> in the <em>Run/Debug Configurations</em> dialog
+ to exit.
+ </li>
+
+ <li>Run your tests by clicking <strong>Run</strong> <img src=
+ "{@docRoot}images/tools/as-run.png" alt="" style=
+ "vertical-align:bottom;margin:0;">.
+ </li>
+</ol>
+
+<h4 id="ctl-results">
+ Analyzing test results
+</h4>
+
+<p>
+ When Cloud Test Lab completes running your tests, the <em>Run</em> window will
+ open to show the results, as shown in figure 1. You may need to click
+ <strong>Show Passed</strong> <img src="{@docRoot}images/tools/as-ok.png" alt=
+ "" style="vertical-align:bottom;margin:0;"> to see all your executed tests.
+</p>
+
+<p>
+ <img src="{@docRoot}images/training/ctl-test-results.png" alt="">
+</p>
+
+<p class="img-caption">
+ <strong>Figure 1.</strong> Viewing the results of instrumented tests using
+ Cloud Test Lab.
+</p>
+
+<p>
+ You can also analyze your tests on the web by following the link displayed at
+ the beginning of the test execution log in the <em>Run</em> window, as shown
+ in figure 2.
+</p>
+
+<p>
+ <img src="{@docRoot}images/training/ctl-exec-log.png" alt="">
+</p>
+
+<p class="img-caption">
+ <strong>Figure 2.</strong> Click the link to view detailed test results on
+ the web.
+</p>
+
+<p>
+ To learn more about interpreting web results, see <a href=
+ "https://developers.google.com/cloud-test-lab/analyzing-results">Analyzing
+ Cloud Test Lab Web Results</a>.
+</p>
\ No newline at end of file
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index af99f79..c65880b 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -1266,7 +1266,7 @@
/**
* @hide
*/
- protected static void throwIfCannotDraw(Bitmap bitmap) {
+ protected void throwIfCannotDraw(Bitmap bitmap) {
if (bitmap.isRecycled()) {
throw new RuntimeException("Canvas: trying to use a recycled bitmap " + bitmap);
}
diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp
index 5fb8425..8251ee6 100644
--- a/libs/hwui/BakedOpDispatcher.cpp
+++ b/libs/hwui/BakedOpDispatcher.cpp
@@ -344,7 +344,8 @@
const SkPaint& paint, int vertexBufferRenderFlags) {
if (CC_LIKELY(vertexBuffer.getVertexCount())) {
bool shadowInterp = vertexBufferRenderFlags & VertexBufferRenderFlags::ShadowInterp;
- const int transformFlags = TransformFlags::OffsetByFudgeFactor;
+ const int transformFlags = vertexBufferRenderFlags & VertexBufferRenderFlags::Offset
+ ? TransformFlags::OffsetByFudgeFactor : 0;
Glop glop;
GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
.setRoundRectClipState(state.roundRectClipState)
diff --git a/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp b/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp
index 5471486..781f88c 100644
--- a/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp
+++ b/libs/hwui/tests/unit/BakedOpDispatcherTests.cpp
@@ -109,3 +109,37 @@
EXPECT_FLOAT_EQ(128 / 255.0f, glop.fill.color.a) << "Rect quad should use op alpha";
});
}
+
+static int getGlopTransformFlags(renderthread::RenderThread& renderThread, RecordedOp* op) {
+ int result = 0;
+ testUnmergedGlopDispatch(renderThread, op, [&result] (const Glop& glop) {
+ result = glop.transform.transformFlags;
+ });
+ return result;
+}
+
+RENDERTHREAD_TEST(BakedOpDispatcher, offsetFlags) {
+ Rect bounds(10, 15, 20, 25);
+ SkPaint paint;
+ SkPaint aaPaint;
+ aaPaint.setAntiAlias(true);
+
+ RoundRectOp roundRectOp(bounds, Matrix4::identity(), nullptr, &paint, 0, 270);
+ EXPECT_EQ(TransformFlags::None, getGlopTransformFlags(renderThread, &roundRectOp))
+ << "Expect no offset for round rect op.";
+
+ const float points[4] = {0.5, 0.5, 1.0, 1.0};
+ PointsOp antiAliasedPointsOp(bounds, Matrix4::identity(), nullptr, &aaPaint, points, 4);
+ EXPECT_EQ(TransformFlags::None, getGlopTransformFlags(renderThread, &antiAliasedPointsOp))
+ << "Expect no offset for AA points.";
+ PointsOp pointsOp(bounds, Matrix4::identity(), nullptr, &paint, points, 4);
+ EXPECT_EQ(TransformFlags::OffsetByFudgeFactor, getGlopTransformFlags(renderThread, &pointsOp))
+ << "Expect an offset for non-AA points.";
+
+ LinesOp antiAliasedLinesOp(bounds, Matrix4::identity(), nullptr, &aaPaint, points, 4);
+ EXPECT_EQ(TransformFlags::None, getGlopTransformFlags(renderThread, &antiAliasedLinesOp))
+ << "Expect no offset for AA lines.";
+ LinesOp linesOp(bounds, Matrix4::identity(), nullptr, &paint, points, 4);
+ EXPECT_EQ(TransformFlags::OffsetByFudgeFactor, getGlopTransformFlags(renderThread, &linesOp))
+ << "Expect an offset for non-AA lines.";
+}
\ No newline at end of file
diff --git a/packages/DocumentsUI/res/values-night/colors.xml b/packages/DocumentsUI/res/values-night/colors.xml
deleted file mode 100644
index f77017d..0000000
--- a/packages/DocumentsUI/res/values-night/colors.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 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.
--->
-
-<resources>
- <color name="directory_background">#ff111111</color>
- <color name="item_doc_grid_protect_background">#88ffffff</color>
-</resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
new file mode 100644
index 0000000..48533fa
--- /dev/null
+++ b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
@@ -0,0 +1,160 @@
+/*
+ * 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.providers.settings;
+
+import android.os.Bundle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.util.MemoryIntArray;
+import android.util.Slog;
+import android.util.SparseIntArray;
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.IOException;
+
+/**
+ * This class tracks changes for global/secure/system tables on a
+ * per user basis and updates a shared memory region which client
+ * processes can read to determine if their local caches are stale,
+ */
+final class GenerationRegistry {
+ private static final String LOG_TAG = "GenerationTracker";
+
+ private static final boolean DEBUG = false;
+
+ private final Object mLock;
+
+ @GuardedBy("mLock")
+ private final SparseIntArray mKeyToIndexMap = new SparseIntArray();
+
+ @GuardedBy("mLock")
+ private final MemoryIntArray mImpl;
+
+ public GenerationRegistry(Object lock) {
+ mLock = lock;
+ // One for the global table, two for system and secure tables for a
+ // managed profile (managed profile is not included in the max user
+ // count), ten for partially deleted users if users are quickly removed,
+ // and twice max user count for system and secure.
+ final int size = 1 + 2 + 10 + 2 * UserManager.getMaxSupportedUsers();
+ MemoryIntArray impl = null;
+ try {
+ impl = new MemoryIntArray(size, false);
+ } catch (IOException e) {
+ Slog.e(LOG_TAG, "Error creating generation tracker", e);
+ }
+ mImpl = impl;
+ }
+
+ public void incrementGeneration(int key) {
+ synchronized (mLock) {
+ if (mImpl != null) {
+ try {
+ final int index = getKeyIndexLocked(key);
+ if (index >= 0) {
+ final int generation = mImpl.get(index) + 1;
+ mImpl.set(index, generation);
+ }
+ } catch (IOException e) {
+ Slog.e(LOG_TAG, "Error updating generation id", e);
+ }
+ }
+ }
+ }
+
+ public void addGenerationData(Bundle bundle, int key) {
+ synchronized (mLock) {
+ if (mImpl != null) {
+ final int index = getKeyIndexLocked(key);
+ if (index >= 0) {
+ bundle.putParcelable(Settings.CALL_METHOD_TRACK_GENERATION_KEY, mImpl);
+ bundle.putInt(Settings.CALL_METHOD_GENERATION_INDEX_KEY, index);
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Exported index:" + index + " for key:"
+ + SettingsProvider.keyToString(key));
+ }
+ }
+ }
+ }
+ }
+
+ private int getKeyIndexLocked(int key) {
+ int index = mKeyToIndexMap.get(key, -1);
+ if (index < 0) {
+ index = findNextEmptyIndex();
+ if (index >= 0) {
+ try {
+ mImpl.set(index, 1);
+ mKeyToIndexMap.append(key, index);
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Allocated index:" + index + " for key:"
+ + SettingsProvider.keyToString(key));
+ }
+ } catch (IOException e) {
+ Slog.e(LOG_TAG, "Cannot write to generation memory array", e);
+ }
+ } else {
+ Slog.e(LOG_TAG, "Could not allocate generation index");
+ }
+ }
+ return index;
+ }
+
+ public void onUserRemoved(int userId) {
+ synchronized (mLock) {
+ if (mImpl != null && mKeyToIndexMap.size() > 0) {
+ final int secureKey = SettingsProvider.makeKey(
+ SettingsProvider.SETTINGS_TYPE_SECURE, userId);
+ resetSlotForKeyLocked(secureKey);
+
+ final int systemKey = SettingsProvider.makeKey(
+ SettingsProvider.SETTINGS_TYPE_SYSTEM, userId);
+ resetSlotForKeyLocked(systemKey);
+ }
+ }
+ }
+
+ private void resetSlotForKeyLocked(int key) {
+ final int index = mKeyToIndexMap.get(key, -1);
+ if (index >= 0) {
+ mKeyToIndexMap.delete(key);
+ try {
+ mImpl.set(index, 0);
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Freed index:" + index + " for key:"
+ + SettingsProvider.keyToString(key));
+ }
+ } catch (IOException e) {
+ Slog.e(LOG_TAG, "Cannot write to generation memory array", e);
+ }
+ }
+ }
+
+ private int findNextEmptyIndex() {
+ try {
+ final int size = mImpl.size();
+ for (int i = 0; i < size; i++) {
+ if (mImpl.get(i) == 0) {
+ return i;
+ }
+ }
+ } catch (IOException e) {
+ Slog.e(LOG_TAG, "Error reading generation memory array", e);
+ }
+ return -1;
+ }
+}
\ No newline at end of file
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 8dc247a..75fb413 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -42,6 +42,7 @@
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
+import android.os.Debug;
import android.os.DropBoxManager;
import android.os.Environment;
import android.os.Handler;
@@ -51,9 +52,9 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.SELinux;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.UserManagerInternal;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArraySet;
@@ -64,6 +65,7 @@
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
import com.android.providers.settings.SettingsState.Setting;
+import com.android.server.LocalServices;
import com.android.server.SystemConfig;
import java.io.File;
@@ -146,8 +148,15 @@
Settings.NameValueTable.VALUE
};
- private static final Bundle NULL_SETTING = Bundle.forPair(Settings.NameValueTable.VALUE, null);
+ public static final int SETTINGS_TYPE_GLOBAL = 0;
+ public static final int SETTINGS_TYPE_SYSTEM = 1;
+ public static final int SETTINGS_TYPE_SECURE = 2;
+ public static final int SETTINGS_TYPE_MASK = 0xF0000000;
+ public static final int SETTINGS_TYPE_SHIFT = 28;
+
+ private static final Bundle NULL_SETTING_BUNDLE = Bundle.forPair(
+ Settings.NameValueTable.VALUE, null);
// Per user secure settings that moved to the for all users global settings.
static final Set<String> sSecureMovedToGlobalSettings = new ArraySet<>();
@@ -196,6 +205,40 @@
// We have to call in the package manager with no lock held,
private volatile IPackageManager mPackageManager;
+ public static int makeKey(int type, int userId) {
+ return (type << SETTINGS_TYPE_SHIFT) | userId;
+ }
+
+ public static int getTypeFromKey(int key) {
+ return key >>> SETTINGS_TYPE_SHIFT;
+ }
+
+ public static int getUserIdFromKey(int key) {
+ return key & ~SETTINGS_TYPE_MASK;
+ }
+
+ public static String settingTypeToString(int type) {
+ switch (type) {
+ case SETTINGS_TYPE_GLOBAL: {
+ return "SETTINGS_GLOBAL";
+ }
+ case SETTINGS_TYPE_SECURE: {
+ return "SETTINGS_SECURE";
+ }
+ case SETTINGS_TYPE_SYSTEM: {
+ return "SETTINGS_SYSTEM";
+ }
+ default: {
+ return "UNKNOWN";
+ }
+ }
+ }
+
+ public static String keyToString(int key) {
+ return "Key[user=" + getUserIdFromKey(key) + ";type="
+ + settingTypeToString(getTypeFromKey(key)) + "]";
+ }
+
@Override
public boolean onCreate() {
synchronized (mLock) {
@@ -204,6 +247,7 @@
mSettingsRegistry = new SettingsRegistry();
}
registerBroadcastReceivers();
+ startWatchingUserRestrictionChanges();
return true;
}
@@ -213,28 +257,28 @@
switch (method) {
case Settings.CALL_METHOD_GET_GLOBAL: {
Setting setting = getGlobalSetting(name);
- return packageValueForCallResult(setting);
+ return packageValueForCallResult(setting, isTrackingGeneration(args));
}
case Settings.CALL_METHOD_GET_SECURE: {
Setting setting = getSecureSetting(name, requestingUserId);
- return packageValueForCallResult(setting);
+ return packageValueForCallResult(setting, isTrackingGeneration(args));
}
case Settings.CALL_METHOD_GET_SYSTEM: {
Setting setting = getSystemSetting(name, requestingUserId);
- return packageValueForCallResult(setting);
+ return packageValueForCallResult(setting, isTrackingGeneration(args));
}
case Settings.CALL_METHOD_PUT_GLOBAL: {
String value = getSettingValue(args);
- insertGlobalSetting(name, value, requestingUserId);
+ insertGlobalSetting(name, value, requestingUserId, false);
break;
}
case Settings.CALL_METHOD_PUT_SECURE: {
String value = getSettingValue(args);
- insertSecureSetting(name, value, requestingUserId);
+ insertSecureSetting(name, value, requestingUserId, false);
break;
}
@@ -335,13 +379,13 @@
switch (table) {
case TABLE_GLOBAL: {
- if (insertGlobalSetting(name, value, UserHandle.getCallingUserId())) {
+ if (insertGlobalSetting(name, value, UserHandle.getCallingUserId(), false)) {
return Uri.withAppendedPath(Settings.Global.CONTENT_URI, name);
}
} break;
case TABLE_SECURE: {
- if (insertSecureSetting(name, value, UserHandle.getCallingUserId())) {
+ if (insertSecureSetting(name, value, UserHandle.getCallingUserId(), false)) {
return Uri.withAppendedPath(Settings.Secure.CONTENT_URI, name);
}
} break;
@@ -398,12 +442,12 @@
switch (args.table) {
case TABLE_GLOBAL: {
final int userId = UserHandle.getCallingUserId();
- return deleteGlobalSetting(args.name, userId) ? 1 : 0;
+ return deleteGlobalSetting(args.name, userId, false) ? 1 : 0;
}
case TABLE_SECURE: {
final int userId = UserHandle.getCallingUserId();
- return deleteSecureSetting(args.name, userId) ? 1 : 0;
+ return deleteSecureSetting(args.name, userId, false) ? 1 : 0;
}
case TABLE_SYSTEM: {
@@ -439,12 +483,12 @@
switch (args.table) {
case TABLE_GLOBAL: {
final int userId = UserHandle.getCallingUserId();
- return updateGlobalSetting(args.name, value, userId) ? 1 : 0;
+ return updateGlobalSetting(args.name, value, userId, false) ? 1 : 0;
}
case TABLE_SECURE: {
final int userId = UserHandle.getCallingUserId();
- return updateSecureSetting(args.name, value, userId) ? 1 : 0;
+ return updateSecureSetting(args.name, value, userId, false) ? 1 : 0;
}
case TABLE_SYSTEM: {
@@ -557,11 +601,15 @@
switch (intent.getAction()) {
case Intent.ACTION_USER_REMOVED: {
- mSettingsRegistry.removeUserStateLocked(userId, true);
+ synchronized (mLock) {
+ mSettingsRegistry.removeUserStateLocked(userId, true);
+ }
} break;
case Intent.ACTION_USER_STOPPED: {
- mSettingsRegistry.removeUserStateLocked(userId, false);
+ synchronized (mLock) {
+ mSettingsRegistry.removeUserStateLocked(userId, false);
+ }
} break;
}
}
@@ -582,6 +630,92 @@
UserHandle.ALL, true);
}
+ private void startWatchingUserRestrictionChanges() {
+ // TODO: The current design of settings looking different based on user restrictions
+ // should be reworked to keep them separate and system code should check the setting
+ // first followed by checking the user restriction before performing an operation.
+ UserManagerInternal userManager = LocalServices.getService(UserManagerInternal.class);
+ userManager.addUserRestrictionsListener((int userId, Bundle newRestrictions,
+ Bundle prevRestrictions) -> {
+ // We are changing the settings affected by restrictions to their current
+ // value with a forced update to ensure that all cross profile dependencies
+ // are taken into account. Also make sure the settings update to.. the same
+ // value passes the security checks, so clear binder calling id.
+ if (newRestrictions.containsKey(UserManager.DISALLOW_SHARE_LOCATION)
+ != prevRestrictions.containsKey(UserManager.DISALLOW_SHARE_LOCATION)) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ Setting setting = getSecureSetting(
+ Settings.Secure.LOCATION_PROVIDERS_ALLOWED, userId);
+ updateSecureSetting(Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+ setting != null ? setting.getValue() : null, userId, true);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ if (newRestrictions.containsKey(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES)
+ != prevRestrictions.containsKey(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES)) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ Setting setting = getGlobalSetting(Settings.Global.INSTALL_NON_MARKET_APPS);
+ updateGlobalSetting(Settings.Global.INSTALL_NON_MARKET_APPS,
+ setting != null ? setting.getValue() : null, userId, true);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ if (newRestrictions.containsKey(UserManager.DISALLOW_DEBUGGING_FEATURES)
+ != prevRestrictions.containsKey(UserManager.DISALLOW_DEBUGGING_FEATURES)) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ Setting setting = getGlobalSetting(Settings.Global.ADB_ENABLED);
+ updateGlobalSetting(Settings.Global.ADB_ENABLED,
+ setting != null ? setting.getValue() : null, userId, true);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ if (newRestrictions.containsKey(UserManager.ENSURE_VERIFY_APPS)
+ != prevRestrictions.containsKey(UserManager.ENSURE_VERIFY_APPS)) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ Setting enable = getGlobalSetting(
+ Settings.Global.PACKAGE_VERIFIER_ENABLE);
+ updateGlobalSetting(Settings.Global.PACKAGE_VERIFIER_ENABLE,
+ enable != null ? enable.getValue() : null, userId, true);
+ Setting include = getGlobalSetting(
+ Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB);
+ updateGlobalSetting(Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB,
+ include != null ? include.getValue() : null, userId, true);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ if (newRestrictions.containsKey(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)
+ != prevRestrictions.containsKey(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ Setting setting = getGlobalSetting(
+ Settings.Global.PREFERRED_NETWORK_MODE);
+ updateGlobalSetting(Settings.Global.PREFERRED_NETWORK_MODE,
+ setting != null ? setting.getValue() : null, userId, true);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ });
+ }
+
private Cursor getAllGlobalSettings(String[] projection) {
if (DEBUG) {
Slog.v(LOG_TAG, "getAllGlobalSettings()");
@@ -590,7 +724,7 @@
synchronized (mLock) {
// Get the settings.
SettingsState settingsState = mSettingsRegistry.getSettingsLocked(
- SettingsRegistry.SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM);
+ SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM);
List<String> names = settingsState.getSettingNamesLocked();
@@ -617,34 +751,39 @@
// Get the value.
synchronized (mLock) {
- return mSettingsRegistry.getSettingLocked(SettingsRegistry.SETTINGS_TYPE_GLOBAL,
+ return mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_GLOBAL,
UserHandle.USER_SYSTEM, name);
}
}
- private boolean updateGlobalSetting(String name, String value, int requestingUserId) {
+ private boolean updateGlobalSetting(String name, String value, int requestingUserId,
+ boolean forceNotify) {
if (DEBUG) {
Slog.v(LOG_TAG, "updateGlobalSetting(" + name + ", " + value + ")");
}
- return mutateGlobalSetting(name, value, requestingUserId, MUTATION_OPERATION_UPDATE);
+ return mutateGlobalSetting(name, value, requestingUserId, MUTATION_OPERATION_UPDATE,
+ forceNotify);
}
- private boolean insertGlobalSetting(String name, String value, int requestingUserId) {
+ private boolean insertGlobalSetting(String name, String value, int requestingUserId,
+ boolean forceNotify) {
if (DEBUG) {
Slog.v(LOG_TAG, "insertGlobalSetting(" + name + ", " + value + ")");
}
- return mutateGlobalSetting(name, value, requestingUserId, MUTATION_OPERATION_INSERT);
+ return mutateGlobalSetting(name, value, requestingUserId, MUTATION_OPERATION_INSERT,
+ forceNotify);
}
- private boolean deleteGlobalSetting(String name, int requestingUserId) {
+ private boolean deleteGlobalSetting(String name, int requestingUserId, boolean forceNotify) {
if (DEBUG) {
Slog.v(LOG_TAG, "deleteGlobalSettingLocked(" + name + ")");
}
- return mutateGlobalSetting(name, null, requestingUserId, MUTATION_OPERATION_DELETE);
+ return mutateGlobalSetting(name, null, requestingUserId, MUTATION_OPERATION_DELETE,
+ forceNotify);
}
private boolean mutateGlobalSetting(String name, String value, int requestingUserId,
- int operation) {
+ int operation, boolean forceNotify) {
// Make sure the caller can change the settings - treated as secure.
enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
@@ -662,20 +801,19 @@
switch (operation) {
case MUTATION_OPERATION_INSERT: {
return mSettingsRegistry
- .insertSettingLocked(SettingsRegistry.SETTINGS_TYPE_GLOBAL,
- UserHandle.USER_SYSTEM, name, value, getCallingPackage());
+ .insertSettingLocked(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM,
+ name, value, getCallingPackage(), forceNotify);
}
case MUTATION_OPERATION_DELETE: {
- return mSettingsRegistry.deleteSettingLocked(
- SettingsRegistry.SETTINGS_TYPE_GLOBAL,
- UserHandle.USER_SYSTEM, name);
+ return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_GLOBAL,
+ UserHandle.USER_SYSTEM, name, forceNotify);
}
case MUTATION_OPERATION_UPDATE: {
return mSettingsRegistry
- .updateSettingLocked(SettingsRegistry.SETTINGS_TYPE_GLOBAL,
- UserHandle.USER_SYSTEM, name, value, getCallingPackage());
+ .updateSettingLocked(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM,
+ name, value, getCallingPackage(), forceNotify);
}
}
}
@@ -693,7 +831,7 @@
synchronized (mLock) {
List<String> names = mSettingsRegistry.getSettingsNamesLocked(
- SettingsRegistry.SETTINGS_TYPE_SECURE, callingUserId);
+ SETTINGS_TYPE_SECURE, callingUserId);
final int nameCount = names.size();
@@ -712,7 +850,7 @@
}
Setting setting = mSettingsRegistry.getSettingLocked(
- SettingsRegistry.SETTINGS_TYPE_SECURE, owningUserId, name);
+ SETTINGS_TYPE_SECURE, owningUserId, name);
appendSettingToCursor(result, setting);
}
@@ -738,39 +876,44 @@
// Get the value.
synchronized (mLock) {
- return mSettingsRegistry.getSettingLocked(SettingsRegistry.SETTINGS_TYPE_SECURE,
+ return mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_SECURE,
owningUserId, name);
}
}
- private boolean insertSecureSetting(String name, String value, int requestingUserId) {
+ private boolean insertSecureSetting(String name, String value, int requestingUserId,
+ boolean forceNotify) {
if (DEBUG) {
Slog.v(LOG_TAG, "insertSecureSetting(" + name + ", " + value + ", "
+ requestingUserId + ")");
}
- return mutateSecureSetting(name, value, requestingUserId, MUTATION_OPERATION_INSERT);
+ return mutateSecureSetting(name, value, requestingUserId, MUTATION_OPERATION_INSERT,
+ forceNotify);
}
- private boolean deleteSecureSetting(String name, int requestingUserId) {
+ private boolean deleteSecureSetting(String name, int requestingUserId, boolean forceNotify) {
if (DEBUG) {
Slog.v(LOG_TAG, "deleteSecureSetting(" + name + ", " + requestingUserId + ")");
}
- return mutateSecureSetting(name, null, requestingUserId, MUTATION_OPERATION_DELETE);
+ return mutateSecureSetting(name, null, requestingUserId, MUTATION_OPERATION_DELETE,
+ forceNotify);
}
- private boolean updateSecureSetting(String name, String value, int requestingUserId) {
+ private boolean updateSecureSetting(String name, String value, int requestingUserId,
+ boolean forceNotify) {
if (DEBUG) {
Slog.v(LOG_TAG, "updateSecureSetting(" + name + ", " + value + ", "
+ requestingUserId + ")");
}
- return mutateSecureSetting(name, value, requestingUserId, MUTATION_OPERATION_UPDATE);
+ return mutateSecureSetting(name, value, requestingUserId, MUTATION_OPERATION_UPDATE,
+ forceNotify);
}
private boolean mutateSecureSetting(String name, String value, int requestingUserId,
- int operation) {
+ int operation, boolean forceNotify) {
// Make sure the caller can change the settings.
enforceWritePermission(Manifest.permission.WRITE_SECURE_SETTINGS);
@@ -793,28 +936,25 @@
// Special cases for location providers (sigh).
if (Settings.Secure.LOCATION_PROVIDERS_ALLOWED.equals(name)) {
- return updateLocationProvidersAllowedLocked(value, owningUserId);
+ return updateLocationProvidersAllowedLocked(value, owningUserId, forceNotify);
}
// Mutate the value.
synchronized (mLock) {
switch (operation) {
case MUTATION_OPERATION_INSERT: {
- return mSettingsRegistry
- .insertSettingLocked(SettingsRegistry.SETTINGS_TYPE_SECURE,
- owningUserId, name, value, getCallingPackage());
+ return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SECURE,
+ owningUserId, name, value, getCallingPackage(), forceNotify);
}
case MUTATION_OPERATION_DELETE: {
- return mSettingsRegistry.deleteSettingLocked(
- SettingsRegistry.SETTINGS_TYPE_SECURE,
- owningUserId, name);
+ return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_SECURE,
+ owningUserId, name, forceNotify);
}
case MUTATION_OPERATION_UPDATE: {
- return mSettingsRegistry
- .updateSettingLocked(SettingsRegistry.SETTINGS_TYPE_SECURE,
- owningUserId, name, value, getCallingPackage());
+ return mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_SECURE,
+ owningUserId, name, value, getCallingPackage(), forceNotify);
}
}
}
@@ -832,7 +972,7 @@
synchronized (mLock) {
List<String> names = mSettingsRegistry.getSettingsNamesLocked(
- SettingsRegistry.SETTINGS_TYPE_SYSTEM, callingUserId);
+ SETTINGS_TYPE_SYSTEM, callingUserId);
final int nameCount = names.size();
@@ -847,7 +987,7 @@
name);
Setting setting = mSettingsRegistry.getSettingLocked(
- SettingsRegistry.SETTINGS_TYPE_SYSTEM, owningUserId, name);
+ SETTINGS_TYPE_SYSTEM, owningUserId, name);
appendSettingToCursor(result, setting);
}
@@ -868,8 +1008,7 @@
// Get the value.
synchronized (mLock) {
- return mSettingsRegistry.getSettingLocked(SettingsRegistry.SETTINGS_TYPE_SYSTEM,
- owningUserId, name);
+ return mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_SYSTEM, owningUserId, name);
}
}
@@ -944,22 +1083,19 @@
switch (operation) {
case MUTATION_OPERATION_INSERT: {
validateSystemSettingValue(name, value);
- return mSettingsRegistry
- .insertSettingLocked(SettingsRegistry.SETTINGS_TYPE_SYSTEM,
- owningUserId, name, value, getCallingPackage());
+ return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SYSTEM,
+ owningUserId, name, value, getCallingPackage(), false);
}
case MUTATION_OPERATION_DELETE: {
- return mSettingsRegistry.deleteSettingLocked(
- SettingsRegistry.SETTINGS_TYPE_SYSTEM,
- owningUserId, name);
+ return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_SYSTEM,
+ owningUserId, name, false);
}
case MUTATION_OPERATION_UPDATE: {
validateSystemSettingValue(name, value);
- return mSettingsRegistry
- .updateSettingLocked(SettingsRegistry.SETTINGS_TYPE_SYSTEM,
- owningUserId, name, value, getCallingPackage());
+ return mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_SYSTEM,
+ owningUserId, name, value, getCallingPackage(), false);
}
}
@@ -1003,8 +1139,8 @@
* Checks whether changing a setting to a value is prohibited by the corresponding user
* restriction.
*
- * <p>See also {@link com.android.server.pm.UserRestrictionsUtils#applyUserRestrictionLR},
- * which should be in sync with this method.
+ * <p>See also {@link com.android.server.pm.UserRestrictionsUtils#applyUserRestriction(
+ * Context, int, String, boolean)}, which should be in sync with this method.
*
* @return true if the change is prohibited, false if the change is allowed.
*/
@@ -1177,13 +1313,19 @@
*
* @returns whether the enabled location providers changed.
*/
- private boolean updateLocationProvidersAllowedLocked(String value, int owningUserId) {
+ private boolean updateLocationProvidersAllowedLocked(String value, int owningUserId,
+ boolean forceNotify) {
if (TextUtils.isEmpty(value)) {
return false;
}
final char prefix = value.charAt(0);
if (prefix != '+' && prefix != '-') {
+ if (forceNotify) {
+ final int key = makeKey(SETTINGS_TYPE_SECURE, owningUserId);
+ mSettingsRegistry.notifyForSettingsChange(key,
+ Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
+ }
return false;
}
@@ -1232,12 +1374,17 @@
}
} else {
// nothing changed, so no need to update the database
+ if (forceNotify) {
+ final int key = makeKey(SETTINGS_TYPE_SECURE, owningUserId);
+ mSettingsRegistry.notifyForSettingsChange(key,
+ Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
+ }
return false;
}
- return mSettingsRegistry.insertSettingLocked(SettingsRegistry.SETTINGS_TYPE_SECURE,
+ return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SECURE,
owningUserId, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, newProviders,
- getCallingPackage());
+ getCallingPackage(), forceNotify);
}
private static void warnOrThrowForUndesiredSecureSettingsMutationForTargetSdk(
@@ -1270,11 +1417,19 @@
"get/set setting for user", null);
}
- private static Bundle packageValueForCallResult(Setting setting) {
- if (setting == null) {
- return NULL_SETTING;
+ private Bundle packageValueForCallResult(Setting setting,
+ boolean trackingGeneration) {
+ if (!trackingGeneration) {
+ if (setting.isNull()) {
+ return NULL_SETTING_BUNDLE;
+ }
+ return Bundle.forPair(Settings.NameValueTable.VALUE, setting.getValue());
}
- return Bundle.forPair(Settings.NameValueTable.VALUE, setting.getValue());
+ Bundle result = new Bundle();
+ result.putString(Settings.NameValueTable.VALUE,
+ !setting.isNull() ? setting.getValue() : null);
+ mSettingsRegistry.mGenerationRegistry.addGenerationData(result, setting.getkey());
+ return result;
}
private static int getRequestingUserId(Bundle args) {
@@ -1283,6 +1438,10 @@
: callingUserId;
}
+ private boolean isTrackingGeneration(Bundle args) {
+ return args != null && args.containsKey(Settings.CALL_METHOD_TRACK_GENERATION_KEY);
+ }
+
private static String getSettingValue(Bundle args) {
return (args != null) ? args.getString(Settings.NameValueTable.VALUE) : null;
}
@@ -1448,26 +1607,22 @@
final class SettingsRegistry {
private static final String DROPBOX_TAG_USERLOG = "restricted_profile_ssaid";
- private static final int SETTINGS_TYPE_GLOBAL = 0;
- private static final int SETTINGS_TYPE_SYSTEM = 1;
- private static final int SETTINGS_TYPE_SECURE = 2;
-
- private static final int SETTINGS_TYPE_MASK = 0xF0000000;
- private static final int SETTINGS_TYPE_SHIFT = 28;
-
private static final String SETTINGS_FILE_GLOBAL = "settings_global.xml";
private static final String SETTINGS_FILE_SYSTEM = "settings_system.xml";
private static final String SETTINGS_FILE_SECURE = "settings_secure.xml";
private final SparseArray<SettingsState> mSettingsStates = new SparseArray<>();
- private final BackupManager mBackupManager;
+ private GenerationRegistry mGenerationRegistry;
private final Handler mHandler;
+ private final BackupManager mBackupManager;
+
public SettingsRegistry() {
- mBackupManager = new BackupManager(getContext());
mHandler = new MyHandler(getContext().getMainLooper());
+ mGenerationRegistry = new GenerationRegistry(mLock);
+ mBackupManager = new BackupManager(getContext());
migrateAllLegacySettingsIfNeeded();
}
@@ -1554,28 +1709,31 @@
});
}
}
+
+ // Nuke generation tracking data
+ mGenerationRegistry.onUserRemoved(userId);
}
public boolean insertSettingLocked(int type, int userId, String name, String value,
- String packageName) {
+ String packageName, boolean forceNotify) {
final int key = makeKey(type, userId);
SettingsState settingsState = peekSettingsStateLocked(key);
final boolean success = settingsState.insertSettingLocked(name, value, packageName);
- if (success) {
+ if (forceNotify || success) {
notifyForSettingsChange(key, name);
}
return success;
}
- public boolean deleteSettingLocked(int type, int userId, String name) {
+ public boolean deleteSettingLocked(int type, int userId, String name, boolean forceNotify) {
final int key = makeKey(type, userId);
SettingsState settingsState = peekSettingsStateLocked(key);
final boolean success = settingsState.deleteSettingLocked(name);
- if (success) {
+ if (forceNotify || success) {
notifyForSettingsChange(key, name);
}
return success;
@@ -1589,13 +1747,13 @@
}
public boolean updateSettingLocked(int type, int userId, String name, String value,
- String packageName) {
+ String packageName, boolean forceNotify) {
final int key = makeKey(type, userId);
SettingsState settingsState = peekSettingsStateLocked(key);
final boolean success = settingsState.updateSettingLocked(name, value, packageName);
- if (success) {
+ if (forceNotify || success) {
notifyForSettingsChange(key, name);
}
@@ -1786,38 +1944,6 @@
}
private void notifyForSettingsChange(int key, String name) {
- // Update the system property *first*, so if someone is listening for
- // a notification and then using the contract class to get their data,
- // the system property will be updated and they'll get the new data.
-
- boolean backedUpDataChanged = false;
- String property = null;
- if (isGlobalSettingsKey(key)) {
- property = Settings.Global.SYS_PROP_SETTING_VERSION;
- backedUpDataChanged = true;
- } else if (isSecureSettingsKey(key)) {
- property = Settings.Secure.SYS_PROP_SETTING_VERSION;
- backedUpDataChanged = true;
- } else if (isSystemSettingsKey(key)) {
- property = Settings.System.SYS_PROP_SETTING_VERSION;
- backedUpDataChanged = true;
- }
-
- if (property != null) {
- final long version = SystemProperties.getLong(property, 0) + 1;
- SystemProperties.set(property, Long.toString(version));
- if (DEBUG) {
- Slog.v(LOG_TAG, "System property " + property + "=" + version);
- }
- }
-
- // Inform the backup manager about a data change
- if (backedUpDataChanged) {
- mHandler.obtainMessage(MyHandler.MSG_NOTIFY_DATA_CHANGED).sendToTarget();
- }
-
- // Now send the notification through the content framework.
-
final int userId = getUserIdFromKey(key);
Uri uri = getNotificationUriFor(key, name);
@@ -1825,13 +1951,19 @@
userId, 0, uri).sendToTarget();
if (isSecureSettingsKey(key)) {
- maybeNotifyProfiles(userId, uri, name, sSecureCloneToManagedSettings);
+ maybeNotifyProfiles(getTypeFromKey(key), userId, uri, name,
+ sSecureCloneToManagedSettings);
} else if (isSystemSettingsKey(key)) {
- maybeNotifyProfiles(userId, uri, name, sSystemCloneToManagedSettings);
+ maybeNotifyProfiles(getTypeFromKey(key), userId, uri, name,
+ sSystemCloneToManagedSettings);
}
+
+ mGenerationRegistry.incrementGeneration(key);
+
+ mHandler.obtainMessage(MyHandler.MSG_NOTIFY_DATA_CHANGED).sendToTarget();
}
- private void maybeNotifyProfiles(int userId, Uri uri, String name,
+ private void maybeNotifyProfiles(int type, int userId, Uri uri, String name,
Set<String> keysCloned) {
if (keysCloned.contains(name)) {
for (int profileId : mUserManager.getProfileIdsWithDisabled(userId)) {
@@ -1839,23 +1971,15 @@
if (profileId != userId) {
mHandler.obtainMessage(MyHandler.MSG_NOTIFY_URI_CHANGED,
profileId, 0, uri).sendToTarget();
+ final int key = makeKey(type, profileId);
+ mGenerationRegistry.incrementGeneration(key);
+
+ mHandler.obtainMessage(MyHandler.MSG_NOTIFY_DATA_CHANGED).sendToTarget();
}
}
}
}
- private int makeKey(int type, int userId) {
- return (type << SETTINGS_TYPE_SHIFT) | userId;
- }
-
- private int getTypeFromKey(int key) {
- return key >> SETTINGS_TYPE_SHIFT;
- }
-
- private int getUserIdFromKey(int key) {
- return key & ~SETTINGS_TYPE_MASK;
- }
-
private boolean isGlobalSettingsKey(int key) {
return getTypeFromKey(key) == SETTINGS_TYPE_GLOBAL;
}
@@ -1953,7 +2077,7 @@
public void upgradeIfNeededLocked() {
// The version of all settings for a user is the same (all users have secure).
SettingsState secureSettings = getSettingsLocked(
- SettingsRegistry.SETTINGS_TYPE_SECURE, mUserId);
+ SETTINGS_TYPE_SECURE, mUserId);
// Try an update from the current state.
final int oldVersion = secureSettings.getVersionLocked();
@@ -1987,7 +2111,7 @@
// Set the global settings version if owner.
if (mUserId == UserHandle.USER_SYSTEM) {
SettingsState globalSettings = getSettingsLocked(
- SettingsRegistry.SETTINGS_TYPE_GLOBAL, mUserId);
+ SETTINGS_TYPE_GLOBAL, mUserId);
globalSettings.setVersionLocked(newVersion);
}
@@ -1996,7 +2120,7 @@
// Set the system settings version.
SettingsState systemSettings = getSettingsLocked(
- SettingsRegistry.SETTINGS_TYPE_SYSTEM, mUserId);
+ SETTINGS_TYPE_SYSTEM, mUserId);
systemSettings.setVersionLocked(newVersion);
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index dde9709..6dfd0e6 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -16,6 +16,7 @@
package com.android.providers.settings;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
@@ -109,6 +110,14 @@
@GuardedBy("mLock")
private final File mStatePersistFile;
+ private final Setting mNullSetting = new Setting(null, null, null) {
+ @Override
+ public boolean isNull() {
+ return true;
+ }
+ };
+
+ @GuardedBy("mLock")
public final int mKey;
@GuardedBy("mLock")
@@ -198,9 +207,13 @@
// The settings provider must hold its lock when calling here.
public Setting getSettingLocked(String name) {
if (TextUtils.isEmpty(name)) {
- return null;
+ return mNullSetting;
}
- return mSettings.get(name);
+ Setting setting = mSettings.get(name);
+ if (setting != null) {
+ return new Setting(setting);
+ }
+ return mNullSetting;
}
// The settings provider must hold its lock when calling here.
@@ -549,12 +562,19 @@
}
}
- public final class Setting {
+ class Setting {
private String name;
private String value;
private String packageName;
private String id;
+ public Setting(Setting other) {
+ name = other.name;
+ value = other.value;
+ packageName = other.packageName;
+ id = other.id;
+ }
+
public Setting(String name, String value, String packageName) {
init(name, value, packageName, String.valueOf(mNextId++));
}
@@ -575,6 +595,10 @@
return name;
}
+ public int getkey() {
+ return mKey;
+ }
+
public String getValue() {
return value;
}
@@ -587,6 +611,10 @@
return id;
}
+ public boolean isNull() {
+ return false;
+ }
+
public boolean update(String value, String packageName) {
if (Objects.equal(value, this.value)) {
return false;
diff --git a/packages/Shell/src/com/android/shell/BugreportWarningActivity.java b/packages/Shell/src/com/android/shell/BugreportWarningActivity.java
index da33a65..2426ba0 100644
--- a/packages/Shell/src/com/android/shell/BugreportWarningActivity.java
+++ b/packages/Shell/src/com/android/shell/BugreportWarningActivity.java
@@ -25,6 +25,7 @@
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
+import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.widget.CheckBox;
@@ -59,7 +60,15 @@
ap.mNegativeButtonListener = this;
mConfirmRepeat = (CheckBox) ap.mView.findViewById(android.R.id.checkbox);
- mConfirmRepeat.setChecked(getWarningState(this, STATE_UNKNOWN) != STATE_SHOW);
+
+ final int state = getWarningState(this, STATE_UNKNOWN);
+ final boolean checked;
+ if (Build.TYPE.equals("user")) {
+ checked = state == STATE_HIDE; // Only checks if specifically set to.
+ } else {
+ checked = state != STATE_SHOW; // Checks by default.
+ }
+ mConfirmRepeat.setChecked(checked);
setupAlert();
}
diff --git a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
index 07c1546..44e956a 100644
--- a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
+++ b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
@@ -63,6 +63,7 @@
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.SystemProperties;
import android.service.notification.StatusBarNotification;
@@ -517,7 +518,14 @@
mUiBot.getVisibleObject(mContext.getString(R.string.bugreport_confirm_dont_repeat));
final boolean firstTime = propertyState == null || propertyState == STATE_UNKNOWN;
if (firstTime) {
- assertTrue("Checkbox should be checked by default", dontShowAgain.isChecked());
+ if (Build.TYPE.equals("user")) {
+ assertFalse("Checkbox should NOT be checked by default on user builds",
+ dontShowAgain.isChecked());
+ mUiBot.click(dontShowAgain, "dont-show-again");
+ } else {
+ assertTrue("Checkbox should be checked by default on build type " + Build.TYPE,
+ dontShowAgain.isChecked());
+ }
} else {
assertFalse("Checkbox should not be checked", dontShowAgain.isChecked());
mUiBot.click(dontShowAgain, "dont-show-again");
diff --git a/packages/SystemUI/res/layout/notification_guts.xml b/packages/SystemUI/res/layout/notification_guts.xml
index f60a3b6..4d72cc8 100644
--- a/packages/SystemUI/res/layout/notification_guts.xml
+++ b/packages/SystemUI/res/layout/notification_guts.xml
@@ -69,7 +69,7 @@
<RadioButton
android:id="@+id/silent_importance"
android:layout_width="wrap_content"
- android:layout_height="48dp"
+ android:layout_height="40dp"
android:paddingStart="32dp"
android:text="@string/show_silently"
style="@style/TextAppearance.NotificationGuts.Radio"
@@ -77,7 +77,7 @@
<RadioButton
android:id="@+id/block_importance"
android:layout_width="wrap_content"
- android:layout_height="48dp"
+ android:layout_height="40dp"
android:paddingStart="32dp"
android:text="@string/block"
style="@style/TextAppearance.NotificationGuts.Radio"
@@ -85,7 +85,7 @@
<RadioButton
android:id="@+id/reset_importance"
android:layout_width="wrap_content"
- android:layout_height="48dp"
+ android:layout_height="40dp"
android:paddingStart="32dp"
style="@style/TextAppearance.NotificationGuts.Radio"
android:buttonTint="@color/notification_guts_buttons" />
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 1bc2ebc..7b23c80 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -68,7 +68,7 @@
private static final int MSG_START_ASSIST = 23 << MSG_SHIFT;
private static final int MSG_CAMERA_LAUNCH_GESTURE = 24 << MSG_SHIFT;
private static final int MSG_TOGGLE_KEYBOARD_SHORTCUTS = 25 << MSG_SHIFT;
- private static final int MSG_REQUEST_TV_PICTURE_IN_PICTURE = 26 << MSG_SHIFT;
+ private static final int MSG_SHOW_TV_PICTURE_IN_PICTURE_MENU = 26 << MSG_SHIFT;
private static final int MSG_ADD_QS_TILE = 27 << MSG_SHIFT;
private static final int MSG_REMOVE_QS_TILE = 28 << MSG_SHIFT;
private static final int MSG_CLICK_QS_TILE = 29 << MSG_SHIFT;
@@ -124,7 +124,7 @@
void showAssistDisclosure();
void startAssist(Bundle args);
void onCameraLaunchGestureDetected(int source);
- void requestTvPictureInPicture();
+ void showTvPictureInPictureMenu();
void addQsTile(ComponentName tile);
void remQsTile(ComponentName tile);
@@ -274,10 +274,10 @@
}
@Override
- public void requestTvPictureInPicture() {
+ public void showTvPictureInPictureMenu() {
synchronized (mLock) {
- mHandler.removeMessages(MSG_REQUEST_TV_PICTURE_IN_PICTURE);
- mHandler.obtainMessage(MSG_REQUEST_TV_PICTURE_IN_PICTURE).sendToTarget();
+ mHandler.removeMessages(MSG_SHOW_TV_PICTURE_IN_PICTURE_MENU);
+ mHandler.obtainMessage(MSG_SHOW_TV_PICTURE_IN_PICTURE_MENU).sendToTarget();
}
}
@@ -488,8 +488,8 @@
case MSG_CAMERA_LAUNCH_GESTURE:
mCallbacks.onCameraLaunchGestureDetected(msg.arg1);
break;
- case MSG_REQUEST_TV_PICTURE_IN_PICTURE:
- mCallbacks.requestTvPictureInPicture();
+ case MSG_SHOW_TV_PICTURE_IN_PICTURE_MENU:
+ mCallbacks.showTvPictureInPictureMenu();
break;
case MSG_ADD_QS_TILE:
mCallbacks.addQsTile((ComponentName) msg.obj);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index caa1585..beba4f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -1123,6 +1123,9 @@
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
updateMaxHeights();
+ if (mSettingsIconRow != null) {
+ mSettingsIconRow.updateVerticalLocation();
+ }
}
private void updateMaxHeights() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index 057b020..4650a1f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -299,12 +299,13 @@
private void applyAuto() {
mSeekBar.setEnabled(!mAuto);
- final ColorStateList tint = mAuto ? mInactiveSliderTint : mActiveSliderTint;
+ final ColorStateList sliderTint = mAuto ? mInactiveSliderTint : mActiveSliderTint;
+ final ColorStateList starTint = mAuto ? mActiveSliderTint : mInactiveSliderTint;
Drawable icon = mAutoButton.getDrawable().mutate();
- icon.setTintList(tint);
+ icon.setTintList(starTint);
mAutoButton.setImageDrawable(icon);
- mSeekBar.setProgressTintList(tint);
- mSeekBar.setThumbTintList(tint);
+ mSeekBar.setProgressTintList(sliderTint);
+ mSeekBar.setThumbTintList(sliderTint);
if (mAuto) {
mSeekBar.setProgress(mNotificationImportance);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java
index 060d8f4..4a1c451 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java
@@ -244,7 +244,6 @@
}
public void setIconLocation(boolean onLeft) {
- updateVerticalLocation();
if ((mIconPlaced && onLeft == mOnLeft) || mSnapping || mParent == null
|| mGearIcon.getWidth() == 0) {
// Do nothing
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index c4b7932..7c70e22 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -883,6 +883,10 @@
new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
+ if (!mInstantExpanding) {
+ getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ return;
+ }
if (mStatusBar.getStatusBarWindow().getHeight()
!= mStatusBar.getStatusBarHeight()) {
getViewTreeObserver().removeOnGlobalLayoutListener(this);
@@ -907,6 +911,10 @@
if (mExpanding) {
notifyExpandingFinished();
}
+ if (mInstantExpanding) {
+ mInstantExpanding = false;
+ notifyBarPanelExpansionChanged();
+ }
}
private void abortAnimations() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 436dc9d..23e3561 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -4503,7 +4503,7 @@
}
@Override
- public void requestTvPictureInPicture() {
+ public void showTvPictureInPictureMenu() {
// no-op.
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index f9202c4..27726af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -189,8 +189,8 @@
}
@Override
- public void requestTvPictureInPicture() {
- PipManager.getInstance().requestTvPictureInPicture();
+ public void showTvPictureInPictureMenu() {
+ PipManager.getInstance().showTvPictureInPictureMenu();
}
@Override
@@ -224,8 +224,22 @@
putComponent(TvStatusBar.class, this);
}
+ /**
+ * Updates the visibility of the picture-in-picture.
+ */
+ public void updatePipVisibility(boolean visible) {
+ if (visible) {
+ mSystemUiVisibility |= View.TV_PICTURE_IN_PICTURE_VISIBLE;
+ } else {
+ mSystemUiVisibility &= ~View.TV_PICTURE_IN_PICTURE_VISIBLE;
+ }
+ notifyUiVisibilityChanged(mSystemUiVisibility);
+ }
+
+ /**
+ * Updates the visibility of the Recents
+ */
public void updateRecentsVisibility(boolean visible) {
- // Update the recents visibility flag
if (visible) {
mSystemUiVisibility |= View.RECENT_APPS_VISIBLE;
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
index d7efca7..9842634 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
@@ -41,8 +41,10 @@
import com.android.systemui.Prefs;
import com.android.systemui.R;
-import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.SystemUIApplication;
import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.statusbar.tv.TvStatusBar;
import java.util.ArrayList;
import java.util.List;
@@ -225,26 +227,14 @@
}
/**
- * Request PIP.
- * It could either start PIP if there's none, and show PIP menu otherwise.
+ * Shows the picture-in-picture menu if an activity is in picture-in-picture mode.
*/
- public void requestTvPictureInPicture() {
- if (DEBUG) Log.d(TAG, "requestTvPictureInPicture()");
- if (!isPipShown()) {
- startPip();
- } else if (mState == STATE_PIP_OVERLAY) {
+ public void showTvPictureInPictureMenu() {
+ if (mState == STATE_PIP_OVERLAY) {
resizePinnedStack(STATE_PIP_MENU);
}
}
- private void startPip() {
- try {
- mActivityManager.moveTopActivityToPinnedStack(FULLSCREEN_WORKSPACE_STACK_ID, mPipBounds);
- } catch (RemoteException|IllegalArgumentException e) {
- Log.e(TAG, "moveTopActivityToPinnedStack failed", e);
- }
- }
-
/**
* Closes PIP (PIPed activity and PIP system UI).
*/
@@ -268,6 +258,7 @@
mListeners.get(i).onPipActivityClosed();
}
mHandler.removeCallbacks(mClosePipRunnable);
+ updatePipVisibility(false);
}
/**
@@ -622,6 +613,7 @@
for (int i = mListeners.size() - 1; i >= 0; i--) {
mListeners.get(i).onPipEntered();
}
+ updatePipVisibility(true);
}
@Override
@@ -706,4 +698,11 @@
public PipRecentsOverlayManager getPipRecentsOverlayManager() {
return mPipRecentsOverlayManager;
}
+
+ private void updatePipVisibility(boolean visible) {
+ TvStatusBar statusBar = ((SystemUIApplication) mContext).getComponent(TvStatusBar.class);
+ if (statusBar != null) {
+ statusBar.updatePipVisibility(visible);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 7458898..9fc17a2 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -72,6 +72,7 @@
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
@@ -2047,6 +2048,65 @@
}
}
+ private void closeSocketsForFirewallChain(int chain, String chainName) {
+ // UID ranges to close sockets on.
+ UidRange[] ranges;
+ // UID ranges whose sockets we won't touch.
+ int[] exemptUids;
+
+ SparseIntArray rules = getUidFirewallRules(chain);
+ int numUids = 0;
+
+ if (getFirewallType(chain) == FIREWALL_TYPE_WHITELIST) {
+ // Close all sockets on all non-system UIDs...
+ ranges = new UidRange[] {
+ // TODO: is there a better way of finding all existing users? If so, we could
+ // specify their ranges here.
+ new UidRange(Process.FIRST_APPLICATION_UID, Integer.MAX_VALUE),
+ };
+ // ... except for the UIDs that have allow rules.
+ exemptUids = new int[rules.size()];
+ for (int i = 0; i < exemptUids.length; i++) {
+ if (rules.valueAt(i) == NetworkPolicyManager.FIREWALL_RULE_ALLOW) {
+ exemptUids[numUids] = rules.keyAt(i);
+ numUids++;
+ }
+ }
+ // Normally, whitelist chains only contain deny rules, so numUids == exemptUids.length.
+ // But the code does not guarantee this in any way, and at least in one case - if we add
+ // a UID rule to the firewall, and then disable the firewall - the chains can contain
+ // the wrong type of rule. In this case, don't close connections that we shouldn't.
+ //
+ // TODO: tighten up this code by ensuring we never set the wrong type of rule, and
+ // fix setFirewallEnabled to grab mQuotaLock and clear rules.
+ if (numUids != exemptUids.length) {
+ exemptUids = Arrays.copyOf(exemptUids, numUids);
+ }
+ } else {
+ // Close sockets for every UID that has a deny rule...
+ ranges = new UidRange[rules.size()];
+ for (int i = 0; i < ranges.length; i++) {
+ if (rules.valueAt(i) == NetworkPolicyManager.FIREWALL_RULE_DENY) {
+ int uid = rules.keyAt(i);
+ ranges[numUids] = new UidRange(uid, uid);
+ numUids++;
+ }
+ }
+ // As above; usually numUids == ranges.length, but not always.
+ if (numUids != ranges.length) {
+ ranges = Arrays.copyOf(ranges, numUids);
+ }
+ // ... with no exceptions.
+ exemptUids = new int[0];
+ }
+
+ try {
+ mNetdService.socketDestroy(ranges, exemptUids);
+ } catch(RemoteException | ServiceSpecificException e) {
+ Slog.e(TAG, "Error closing sockets after enabling chain " + chainName + ": " + e);
+ }
+ }
+
@Override
public void setFirewallChainEnabled(int chain, boolean enable) {
enforceSystemUid();
@@ -2059,25 +2119,35 @@
mFirewallChainStates.put(chain, enable);
final String operation = enable ? "enable_chain" : "disable_chain";
+ String chainName;
+ switch(chain) {
+ case FIREWALL_CHAIN_STANDBY:
+ chainName = FIREWALL_CHAIN_NAME_STANDBY;
+ break;
+ case FIREWALL_CHAIN_DOZABLE:
+ chainName = FIREWALL_CHAIN_NAME_DOZABLE;
+ break;
+ case FIREWALL_CHAIN_POWERSAVE:
+ chainName = FIREWALL_CHAIN_NAME_POWERSAVE;
+ break;
+ default:
+ throw new IllegalArgumentException("Bad child chain: " + chain);
+ }
+
try {
- String chainName;
- switch(chain) {
- case FIREWALL_CHAIN_STANDBY:
- chainName = FIREWALL_CHAIN_NAME_STANDBY;
- break;
- case FIREWALL_CHAIN_DOZABLE:
- chainName = FIREWALL_CHAIN_NAME_DOZABLE;
- break;
- case FIREWALL_CHAIN_POWERSAVE:
- chainName = FIREWALL_CHAIN_NAME_POWERSAVE;
- break;
- default:
- throw new IllegalArgumentException("Bad child chain: " + chain);
- }
mConnector.execute("firewall", operation, chainName);
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
+
+ // Close any sockets that were opened by the affected UIDs. This has to be done after
+ // disabling network connectivity, in case they react to the socket close by reopening
+ // the connection and race with the iptables commands that enable the firewall. All
+ // whitelist and blacklist chains allow RSTs through.
+ if (enable) {
+ if (DBG) Slog.d(TAG, "Closing sockets after enabling chain " + chainName);
+ closeSocketsForFirewallChain(chain, chainName);
+ }
}
}
@@ -2376,7 +2446,7 @@
}
private void dumpUidFirewallRule(PrintWriter pw, String name, SparseIntArray rules) {
- pw.print("UID firewall");
+ pw.print("UID firewall ");
pw.print(name);
pw.print(" rule: [");
final int size = rules.size();
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index a7f4314..a41a8ef 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -721,6 +721,9 @@
}
immersive = (aInfo.flags & ActivityInfo.FLAG_IMMERSIVE) != 0;
+
+ requestedVrComponent = (aInfo.requestedVrComponent == null) ?
+ null : ComponentName.unflattenFromString(aInfo.requestedVrComponent);
} else {
realActivity = null;
taskAffinity = null;
@@ -732,6 +735,7 @@
noDisplay = false;
mActivityType = APPLICATION_ACTIVITY_TYPE;
immersive = false;
+ requestedVrComponent = null;
}
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index f281683..66bc51c 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1097,7 +1097,21 @@
// Don't debug things in the system process
if (!aInfo.processName.equals("system")) {
if ((startFlags & ActivityManager.START_FLAG_DEBUG) != 0) {
- mService.setDebugApp(aInfo.processName, true, false);
+ final ProcessRecord app = mService.getProcessRecordLocked(
+ aInfo.processName, aInfo.applicationInfo.uid, true);
+ // If the process already started, we can't wait for debugger and shouldn't
+ // modify the debug settings.
+ // For non-persistent debug, normally we set the debug app here, and restores
+ // to the original at attachApplication time. However, if the app process
+ // already exists, we won't get another attachApplication, and the debug setting
+ // never gets restored. Furthermore, if we get two such calls back-to-back,
+ // both mOrigDebugApp and mDebugApp will become the same app, and it won't be
+ // cleared even if we get attachApplication after the app process is killed.
+ if (app == null || app.thread == null) {
+ mService.setDebugApp(aInfo.processName, true, false);
+ } else {
+ Slog.w(TAG, "Ignoring waitForDebugger because process already exists");
+ }
}
if ((startFlags & ActivityManager.START_FLAG_NATIVE_DEBUGGING) != 0) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index a6dfab0..0874fa7 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -3940,33 +3940,40 @@
}
}
+ private int getAbsoluteVolumeIndex(int index) {
+ /* Special handling for Bluetooth Absolute Volume scenario
+ * If we send full audio gain, some accessories are too loud even at its lowest
+ * volume. We are not able to enumerate all such accessories, so here is the
+ * workaround from phone side.
+ * Pre-scale volume at lowest volume steps 1 2 and 3.
+ * For volume step 0, set audio gain to 0 as some accessories won't mute on their end.
+ */
+ if (index == 0) {
+ // 0% for volume 0
+ index = 0;
+ } else if (index == 1) {
+ // 50% for volume 1
+ index = (int)(mIndexMax * 0.5) /10;
+ } else if (index == 2) {
+ // 70% for volume 2
+ index = (int)(mIndexMax * 0.70) /10;
+ } else if (index == 3) {
+ // 85% for volume 3
+ index = (int)(mIndexMax * 0.85) /10;
+ } else {
+ // otherwise, full gain
+ index = (mIndexMax + 5)/10;
+ }
+ return index;
+ }
+
// must be called while synchronized VolumeStreamState.class
public void applyDeviceVolume_syncVSS(int device) {
int index;
if (mIsMuted) {
index = 0;
} else if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 && mAvrcpAbsVolSupported) {
- /* Special handling for Bluetooth Absolute Volume scenario
- * If we send full audio gain, some accessories are too loud even at its lowest
- * volume. We are not able to enumerate all such accessories, so here is the
- * workaround from phone side.
- * For the lowest volume steps 1 and 2, restrict audio gain to 50% and 75%.
- * For volume step 0, set audio gain to 0 as some accessories won't mute on their end.
- */
- int i = (getIndex(device) + 5)/10;
- if (i == 0) {
- // 0% for volume 0
- index = 0;
- } else if (i == 1) {
- // 50% for volume 1
- index = (int)(mIndexMax * 0.5) /10;
- } else if (i == 2) {
- // 75% for volume 2
- index = (int)(mIndexMax * 0.75) /10;
- } else {
- // otherwise, full gain
- index = (mIndexMax + 5)/10;
- }
+ index = getAbsoluteVolumeIndex((getIndex(device) + 5)/10);
} else if ((device & mFullVolumeDevices) != 0) {
index = (mIndexMax + 5)/10;
} else {
@@ -3984,9 +3991,10 @@
if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
if (mIsMuted) {
index = 0;
- } else if (((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 &&
- mAvrcpAbsVolSupported)
- || ((device & mFullVolumeDevices) != 0)) {
+ } else if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 &&
+ mAvrcpAbsVolSupported) {
+ index = getAbsoluteVolumeIndex((getIndex(device) + 5)/10);
+ } else if ((device & mFullVolumeDevices) != 0) {
index = (mIndexMax + 5)/10;
} else {
index = (mIndexMap.valueAt(i) + 5)/10;
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 8af0af0..fcf2162 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -405,6 +405,7 @@
private final SyncHandler mSyncHandler;
private volatile boolean mBootCompleted = false;
+ private volatile boolean mJobServiceReady = false;
private ConnectivityManager getConnectivityManager() {
synchronized (this) {
@@ -1300,7 +1301,7 @@
}
getJobScheduler().scheduleAsPackage(b.build(), syncOperation.owningPackage,
- syncOperation.target.userId, "sync");
+ syncOperation.target.userId, syncOperation.wakeLockName());
}
/**
@@ -2177,11 +2178,7 @@
static final int MESSAGE_SCHEDULE_SYNC = 12;
static final int MESSAGE_UPDATE_PERIODIC_SYNC = 13;
static final int MESSAGE_REMOVE_PERIODIC_SYNC = 14;
- /**
- * Posted delayed in order to expire syncs that are long-running.
- * obj: {@link com.android.server.content.SyncManager.ActiveSyncContext}
- */
- private static final int MESSAGE_SYNC_EXPIRED = 7;
+
/**
* Posted periodically to monitor network process for long-running syncs.
* obj: {@link com.android.server.content.SyncManager.ActiveSyncContext}
@@ -2209,7 +2206,7 @@
}
void checkIfDeviceReady() {
- if (mProvisioned && mBootCompleted) {
+ if (mProvisioned && mBootCompleted && mJobServiceReady) {
synchronized(this) {
mSyncStorageEngine.restoreAllPeriodicSyncs();
// Dispatch any stashed messages.
@@ -2229,7 +2226,7 @@
*/
private boolean tryEnqueueMessageUntilReadyToRun(Message msg) {
synchronized (this) {
- if (!mBootCompleted || !mProvisioned) {
+ if (!mBootCompleted || !mProvisioned || !mJobServiceReady) {
// Need to copy the message bc looper will recycle it.
Message m = Message.obtain(msg);
mUnreadyQueue.add(m);
@@ -2252,6 +2249,8 @@
if (msg.what == MESSAGE_JOBSERVICE_OBJECT) {
Slog.i(TAG, "Got SyncJobService instance.");
mSyncJobService = (SyncJobService) msg.obj;
+ mJobServiceReady = true;
+ checkIfDeviceReady();
} else if (msg.what == SyncHandler.MESSAGE_ACCOUNTS_UPDATED) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Slog.v(TAG, "handleSyncHandlerMessage: MESSAGE_ACCOUNTS_UPDATED");
@@ -2972,7 +2971,6 @@
Slog.v(TAG, "removing all MESSAGE_MONITOR_SYNC & MESSAGE_SYNC_EXPIRED for "
+ activeSyncContext.toString());
}
- mSyncHandler.removeMessages(SyncHandler.MESSAGE_SYNC_EXPIRED, activeSyncContext);
mSyncHandler.removeMessages(SyncHandler.MESSAGE_MONITOR_SYNC, activeSyncContext);
}
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index 9ab4386..590d075 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -150,19 +150,9 @@
this.sourceTag = tag;
}
- if (this.sourceTag != null) {
- StringBuilder sb = new StringBuilder();
- sb.append(job.getService().getPackageName());
- sb.append('/');
- sb.append(this.sourceTag);
- if (sourcePackageName != null) {
- sb.append('/');
- sb.append(this.sourcePackageName);
- }
- this.batteryName = sb.toString();
- } else {
- this.batteryName = job.getService().flattenToShortString();
- }
+ this.batteryName = this.sourceTag != null
+ ? this.sourceTag + ":" + job.getService().getPackageName()
+ : job.getService().flattenToShortString();
this.tag = "*job*/" + this.batteryName;
this.earliestRunTimeElapsedMillis = earliestRunTimeElapsedMillis;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index e411579..9bdb149 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -3316,9 +3316,9 @@
mNotificationList.remove(index);
- cancelNotificationLocked(r, sendDelete, reason);
cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName,
REASON_GROUP_SUMMARY_CANCELED);
+ cancelNotificationLocked(r, sendDelete, reason);
updateLightsLocked();
}
}
diff --git a/services/core/java/com/android/server/os/SchedulingPolicyService.java b/services/core/java/com/android/server/os/SchedulingPolicyService.java
index 80faf473..f98012b 100644
--- a/services/core/java/com/android/server/os/SchedulingPolicyService.java
+++ b/services/core/java/com/android/server/os/SchedulingPolicyService.java
@@ -66,7 +66,7 @@
final int callingUid = Binder.getCallingUid();
switch (callingUid) {
case Process.AUDIOSERVER_UID: // fastcapture, fastmixer
- case Process.MEDIA_UID: // camera
+ case Process.CAMERASERVER_UID: // camera high frame rate recording
return true;
default:
return false;
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index f6255af..0a25402 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -148,7 +148,7 @@
// jobs because PackageDexOptimizer.performDexOpt is synchronized.
pm.performDexOpt(pkg,
/* instruction set */ null,
- /* checkProfiles */ false,
+ /* checkProfiles */ true,
PackageManagerService.REASON_BOOT,
/* force */ false);
}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index b3ac05c..f620274 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -137,21 +137,14 @@
boolean isProfileGuidedFilter = DexFile.isProfileGuidedCompilerFilter(targetCompilerFilter);
// If any part of the app is used by other apps, we cannot use profile-guided
// compilation.
- // Skip the check for forward locked packages since they don't share their code.
- if (isProfileGuidedFilter && !pkg.isForwardLocked()) {
- for (String path : paths) {
- if (isUsedByOtherApps(path)) {
- checkProfiles = false;
+ if (isProfileGuidedFilter && isUsedByOtherApps(pkg)) {
+ checkProfiles = false;
- targetCompilerFilter = getNonProfileGuidedCompilerFilter(targetCompilerFilter);
- if (DexFile.isProfileGuidedCompilerFilter(targetCompilerFilter)) {
- throw new IllegalStateException(targetCompilerFilter);
- }
- isProfileGuidedFilter = false;
-
- break;
- }
+ targetCompilerFilter = getNonProfileGuidedCompilerFilter(targetCompilerFilter);
+ if (DexFile.isProfileGuidedCompilerFilter(targetCompilerFilter)) {
+ throw new IllegalStateException(targetCompilerFilter);
}
+ isProfileGuidedFilter = false;
}
// If we're asked to take profile updates into account, check now.
@@ -281,20 +274,34 @@
mSystemReady = true;
}
- private boolean isUsedByOtherApps(String apkPath) {
- try {
- apkPath = new File(apkPath).getCanonicalPath();
- } catch (IOException e) {
- // Log an error but continue without it.
- Slog.w(TAG, "Failed to get canonical path", e);
+ /**
+ * Returns true if the profiling data collected for the given app indicate
+ * that the apps's APK has been loaded by another app.
+ * Note that this returns false for all forward-locked apps and apps without
+ * any collected profiling data.
+ */
+ public static boolean isUsedByOtherApps(PackageParser.Package pkg) {
+ if (pkg.isForwardLocked()) {
+ // Skip the check for forward locked packages since they don't share their code.
+ return false;
}
- String useMarker = apkPath.replace('/', '@');
- final int[] currentUserIds = UserManagerService.getInstance().getUserIds();
- for (int i = 0; i < currentUserIds.length; i++) {
- File profileDir = Environment.getDataProfilesDeForeignDexDirectory(currentUserIds[i]);
- File foreignUseMark = new File(profileDir, useMarker);
- if (foreignUseMark.exists()) {
- return true;
+
+ for (String apkPath : pkg.getAllCodePathsExcludingResourceOnly()) {
+ try {
+ apkPath = new File(apkPath).getCanonicalPath();
+ } catch (IOException e) {
+ // Log an error but continue without it.
+ Slog.w(TAG, "Failed to get canonical path", e);
+ }
+ String useMarker = apkPath.replace('/', '@');
+ final int[] currentUserIds = UserManagerService.getInstance().getUserIds();
+ for (int i = 0; i < currentUserIds.length; i++) {
+ File profileDir =
+ Environment.getDataProfilesDeForeignDexDirectory(currentUserIds[i]);
+ File foreignUseMark = new File(profileDir, useMarker);
+ if (foreignUseMark.exists()) {
+ return true;
+ }
}
}
return false;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 547379d..38144e4 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -7197,7 +7197,7 @@
performDexOpt(pkg.packageName,
null /* instructionSet */,
- false /* checkProfiles */,
+ true /* checkProfiles */,
causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT,
false /* force */);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 3c065ae..67cbcd8 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -69,11 +69,12 @@
long now = System.currentTimeMillis();
for (Iterator<PackageParser.Package> i = pkgs.iterator(); i.hasNext();) {
PackageParser.Package pkg = i.next();
- long then = pkg.getLatestPackageUseTimeInMills();
+ long then = pkg.getLatestForegroundPackageUseTimeInMills();
if (then + dexOptLRUThresholdInMills < now) {
if (DEBUG_DEXOPT) {
- Log.i(TAG, "Skipping dexopt of " + pkg.packageName + " last resumed: " +
- ((then == 0) ? "never" : new Date(then)));
+ Log.i(TAG, "Skipping dexopt of " + pkg.packageName +
+ " last used in foreground: " +
+ ((then == 0) ? "never" : new Date(then)));
}
i.remove();
skipped++;
@@ -117,6 +118,18 @@
}
remainingPkgs.removeAll(result);
+ // Give priority to apps used by other apps.
+ for (PackageParser.Package pkg : remainingPkgs) {
+ if (PackageDexOptimizer.isUsedByOtherApps(pkg)) {
+ if (DEBUG_DEXOPT) {
+ Log.i(TAG, "Adding app used by other apps " + result.size() + ": " +
+ pkg.packageName);
+ }
+ result.add(pkg);
+ }
+ }
+ remainingPkgs.removeAll(result);
+
// Filter out packages that aren't recently used, add all remaining apps.
// TODO: add a property to control this?
if (packageManagerService.isHistoricalPackageUsageAvailable()) {
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 364e9fa6..38a3f42 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -370,19 +370,6 @@
android.provider.Settings.Secure.LOCATION_MODE_OFF,
userId);
}
- // Send out notifications as some clients may want to reread the
- // value which actually changed due to a restriction having been
- // applied.
- final String property =
- android.provider.Settings.Secure.SYS_PROP_SETTING_VERSION;
- long version = SystemProperties.getLong(property, 0) + 1;
- SystemProperties.set(property, Long.toString(version));
-
- final String name = android.provider.Settings.Secure.LOCATION_PROVIDERS_ALLOWED;
- final Uri url = Uri.withAppendedPath(
- android.provider.Settings.Secure.CONTENT_URI, name);
- context.getContentResolver().notifyChange(url, null, true, userId);
-
break;
case UserManager.DISALLOW_DEBUGGING_FEATURES:
if (newValue) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 4e4b2f3..df74ed1 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -404,8 +404,9 @@
volatile boolean mEndCallKeyHandled;
volatile boolean mCameraGestureTriggeredDuringGoingToSleep;
volatile boolean mGoingToSleep;
+ volatile boolean mRecentsVisible;
+ volatile boolean mTvPictureInPictureVisible;
- boolean mRecentsVisible;
int mRecentAppsHeldModifiers;
boolean mLanguageSwitchKeyPressed;
@@ -712,7 +713,7 @@
private static final int MSG_POWER_LONG_PRESS = 14;
private static final int MSG_UPDATE_DREAMING_SLEEP_TOKEN = 15;
private static final int MSG_REQUEST_TRANSIENT_BARS = 16;
- private static final int MSG_REQUEST_TV_PICTURE_IN_PICTURE = 17;
+ private static final int MSG_SHOW_TV_PICTURE_IN_PICTURE_MENU = 17;
private static final int MSG_BACK_LONG_PRESS = 18;
private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS = 0;
@@ -775,8 +776,8 @@
requestTransientBars(targetBar);
}
break;
- case MSG_REQUEST_TV_PICTURE_IN_PICTURE:
- requestTvPictureInPictureInternal();
+ case MSG_SHOW_TV_PICTURE_IN_PICTURE_MENU:
+ showTvPictureInPictureMenuInternal();
break;
case MSG_BACK_LONG_PRESS:
backLongPress();
@@ -1457,22 +1458,18 @@
}
}
- private void requestTvPictureInPicture(KeyEvent event) {
- if (DEBUG_INPUT) Log.d(TAG, "requestTvPictureInPicture event=" + event);
- mHandler.removeMessages(MSG_REQUEST_TV_PICTURE_IN_PICTURE);
- Message msg = mHandler.obtainMessage(MSG_REQUEST_TV_PICTURE_IN_PICTURE);
+ private void showTvPictureInPictureMenu(KeyEvent event) {
+ if (DEBUG_INPUT) Log.d(TAG, "showTvPictureInPictureMenu event=" + event);
+ mHandler.removeMessages(MSG_SHOW_TV_PICTURE_IN_PICTURE_MENU);
+ Message msg = mHandler.obtainMessage(MSG_SHOW_TV_PICTURE_IN_PICTURE_MENU);
msg.setAsynchronous(true);
msg.sendToTarget();
}
- private void requestTvPictureInPictureInternal() {
- try {
- StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
- if (statusbar != null) {
- statusbar.requestTvPictureInPicture();
- }
- } catch (IllegalArgumentException e) {
- Slog.e(TAG, "Cannot handle picture-in-picture key", e);
+ private void showTvPictureInPictureMenuInternal() {
+ StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
+ if (statusbar != null) {
+ statusbar.showTvPictureInPictureMenu();
}
}
@@ -3787,6 +3784,7 @@
mStatusBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility);
mNavigationBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility);
mRecentsVisible = (visibility & View.RECENT_APPS_VISIBLE) > 0;
+ mTvPictureInPictureVisible = (visibility & View.TV_PICTURE_IN_PICTURE_VISIBLE) > 0;
// Reset any bits in mForceClearingStatusBarVisibility that
// are now clear.
@@ -4912,7 +4910,7 @@
}
}
- if (mWinShowWhenLocked != null &&
+ if (!mKeyguardHidden && mWinShowWhenLocked != null &&
mWinShowWhenLocked.getAppToken() != win.getAppToken() &&
(attrs.flags & FLAG_SHOW_WHEN_LOCKED) == 0) {
win.hideLw(false);
@@ -5674,10 +5672,16 @@
}
case KeyEvent.KEYCODE_WINDOW: {
if (mShortPressWindowBehavior == SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE) {
- if (!down) {
- requestTvPictureInPicture(event);
+ if (mTvPictureInPictureVisible) {
+ // Consumes the key only if picture-in-picture is visible
+ // to show picture-in-picture control menu.
+ // This gives a chance to the foreground activity
+ // to customize PIP key behavior.
+ if (!down) {
+ showTvPictureInPictureMenu(event);
+ }
+ result &= ~ACTION_PASS_TO_USER;
}
- result &= ~ACTION_PASS_TO_USER;
}
break;
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 52b2439..fb0dd2a 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -41,14 +41,9 @@
void toggleKeyboardShortcutsMenu(int deviceId);
/**
- * Request picture-in-picture.
- *
- * <p>
- * This is called when an user presses picture-in-picture key or equivalent.
- * TV device may start picture-in-picture from foreground activity if there's none.
- * Picture-in-picture overlay menu will be shown instead otherwise.
+ * Show TV picture-in-picture menu.
*/
- void requestTvPictureInPicture();
+ void showTvPictureInPictureMenu();
void setWindowState(int window, int state);
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index c630d4a..baa7f1e 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -295,10 +295,10 @@
}
@Override
- public void requestTvPictureInPicture() {
+ public void showTvPictureInPictureMenu() {
if (mBar != null) {
try {
- mBar.requestTvPictureInPicture();
+ mBar.showTvPictureInPictureMenu();
} catch (RemoteException ex) {}
}
}
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
index d90d922..91de797 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
@@ -536,16 +536,37 @@
}
/**
+ * Both versionCodes should be from a WebView provider package implemented by Chromium.
+ * VersionCodes from other kinds of packages won't make any sense in this method.
+ *
+ * An introduction to Chromium versionCode scheme:
+ * "BBBBPPPAX"
+ * BBBB: 4 digit branch number. It monotonically increases over time.
+ * PPP: patch number in the branch. It is padded with zeroes to the left. These three digits may
+ * change their meaning in the future.
+ * A: architecture digit.
+ * X: A digit to differentiate APKs for other reasons.
+ *
+ * This method takes the "BBBB" of versionCodes and compare them.
+ *
+ * @return true if versionCode1 is higher than or equal to versionCode2.
+ */
+ private static boolean versionCodeGE(int versionCode1, int versionCode2) {
+ int v1 = versionCode1 / 100000;
+ int v2 = versionCode2 / 100000;
+
+ return v1 >= v2;
+ }
+
+ /**
* Returns whether this provider is valid for use as a WebView provider.
*/
public boolean isValidProvider(WebViewProviderInfo configInfo,
PackageInfo packageInfo) {
- if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0
- && packageInfo.versionCode < getMinimumVersionCode()
+ if (!versionCodeGE(packageInfo.versionCode, getMinimumVersionCode())
&& !mSystemInterface.systemIsDebuggable()) {
- // Non-system package webview providers may be downgraded arbitrarily low, prevent
- // that by enforcing minimum version code. This check is only enforced for user
- // builds.
+ // Webview providers may be downgraded arbitrarily low, prevent that by enforcing
+ // minimum version code. This check is only enforced for user builds.
return false;
}
if (providerHasValidSignature(configInfo, packageInfo, mSystemInterface) &&
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 9f54b53..3b1f34a 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -515,6 +515,13 @@
}
}
+ void requestUpdateWallpaperIfNeeded() {
+ for (int i = allAppWindows.size() - 1; i >= 0; i--) {
+ final WindowState w = allAppWindows.get(i);
+ w.requestUpdateWallpaperIfNeeded();
+ }
+ }
+
boolean isRelaunching() {
return mPendingRelaunchCount > 0;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 0b66959..80bf803 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -4409,7 +4409,11 @@
if (wtoken.hidden || wtoken.mAppStopped) {
wtoken.allDrawn = false;
wtoken.deferClearAllDrawn = false;
- wtoken.waitingToShow = true;
+
+ // If the app was already visible, don't reset the waitingToShow state.
+ if (wtoken.hidden) {
+ wtoken.waitingToShow = true;
+ }
if (wtoken.clientHidden) {
// In the case where we are making an app visible
@@ -4422,6 +4426,8 @@
wtoken.sendAppVisibilityToClients();
}
}
+ wtoken.requestUpdateWallpaperIfNeeded();
+
if (DEBUG_ADD_REMOVE) Slog.v(
TAG_WM, "No longer Stopped: " + wtoken);
wtoken.mAppStopped = false;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index ae20ae3..dd88bea 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -73,6 +73,7 @@
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
import static android.view.WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
@@ -90,6 +91,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
@@ -2719,6 +2721,14 @@
mAnimateReplacingWindow = false;
}
+ void requestUpdateWallpaperIfNeeded() {
+ if (mDisplayContent != null && (mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) {
+ mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+ mDisplayContent.layoutNeeded = true;
+ mService.mWindowPlacerLocked.requestTraversal();
+ }
+ }
+
float translateToWindowX(float x) {
float winX = x - mFrame.left;
if (mEnforceSizeCompat) {
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index 6d0808f..2b6c916 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -32,8 +32,6 @@
import android.net.NetworkUtils;
import android.net.metrics.DhcpClientEvent;
import android.net.metrics.DhcpErrorEvent;
-import android.os.IBinder;
-import android.os.INetworkManagementService;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -121,6 +119,13 @@
* after pre DHCP action is complete */
public static final int CMD_PRE_DHCP_ACTION_COMPLETE = PUBLIC_BASE + 7;
+ /* Command and event notification to/from IpManager requesting the setting
+ * (or clearing) of an IPv4 LinkAddress.
+ */
+ public static final int CMD_CLEAR_LINKADDRESS = PUBLIC_BASE + 8;
+ public static final int CMD_CONFIGURE_LINKADDRESS = PUBLIC_BASE + 9;
+ public static final int EVENT_LINKADDRESS_CONFIGURED = PUBLIC_BASE + 10;
+
/* Message.arg1 arguments to CMD_POST_DHCP notification */
public static final int DHCP_SUCCESS = 1;
public static final int DHCP_FAILURE = 2;
@@ -157,7 +162,6 @@
// System services / libraries we use.
private final Context mContext;
private final Random mRandom;
- private final INetworkManagementService mNMService;
// Sockets.
// - We use a packet socket to receive, because servers send us packets bound for IP addresses
@@ -192,7 +196,8 @@
private State mDhcpInitState = new DhcpInitState();
private State mDhcpSelectingState = new DhcpSelectingState();
private State mDhcpRequestingState = new DhcpRequestingState();
- private State mDhcpHaveAddressState = new DhcpHaveAddressState();
+ private State mDhcpHaveLeaseState = new DhcpHaveLeaseState();
+ private State mConfiguringInterfaceState = new ConfiguringInterfaceState();
private State mDhcpBoundState = new DhcpBoundState();
private State mDhcpRenewingState = new DhcpRenewingState();
private State mDhcpRebindingState = new DhcpRebindingState();
@@ -219,19 +224,17 @@
addState(mWaitBeforeStartState, mDhcpState);
addState(mDhcpSelectingState, mDhcpState);
addState(mDhcpRequestingState, mDhcpState);
- addState(mDhcpHaveAddressState, mDhcpState);
- addState(mDhcpBoundState, mDhcpHaveAddressState);
- addState(mWaitBeforeRenewalState, mDhcpHaveAddressState);
- addState(mDhcpRenewingState, mDhcpHaveAddressState);
- addState(mDhcpRebindingState, mDhcpHaveAddressState);
+ addState(mDhcpHaveLeaseState, mDhcpState);
+ addState(mConfiguringInterfaceState, mDhcpHaveLeaseState);
+ addState(mDhcpBoundState, mDhcpHaveLeaseState);
+ addState(mWaitBeforeRenewalState, mDhcpHaveLeaseState);
+ addState(mDhcpRenewingState, mDhcpHaveLeaseState);
+ addState(mDhcpRebindingState, mDhcpHaveLeaseState);
addState(mDhcpInitRebootState, mDhcpState);
addState(mDhcpRebootingState, mDhcpState);
setInitialState(mStoppedState);
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- mNMService = INetworkManagementService.Stub.asInterface(b);
-
mRandom = new Random();
// Used to schedule packet retransmissions.
@@ -321,18 +324,6 @@
closeQuietly(mPacketSock);
}
- private boolean setIpAddress(LinkAddress address) {
- InterfaceConfiguration ifcg = new InterfaceConfiguration();
- ifcg.setLinkAddress(address);
- try {
- mNMService.setInterfaceConfig(mIfaceName, ifcg);
- } catch (RemoteException|IllegalStateException e) {
- Log.e(TAG, "Error configuring IP address " + address + ": ", e);
- return false;
- }
- return true;
- }
-
class ReceiveThread extends Thread {
private final byte[] mPacket = new byte[DhcpPacket.MAX_LENGTH];
@@ -382,7 +373,8 @@
Os.sendto(mPacketSock, buf.array(), 0, buf.limit(), 0, mInterfaceBroadcastAddr);
} else {
// It's safe to call getpeername here, because we only send unicast packets if we
- // have an IP address, and we connect the UDP socket in DhcpHaveAddressState#enter.
+ // have an IP address, and we connect the UDP socket before
+ // ConfiguringInterfaceState#exit.
if (DBG) Log.d(TAG, "Unicasting " + description + " to " + Os.getpeername(mUdpSock));
Os.write(mUdpSock, buf);
}
@@ -460,6 +452,7 @@
}
abstract class LoggingState extends State {
+ @Override
public void enter() {
if (STATE_DBG) Log.d(TAG, "Entering state " + getName());
DhcpClientEvent.logStateEvent(mIfaceName, getName());
@@ -759,7 +752,7 @@
mOffer = null;
Log.d(TAG, "Confirmed lease: " + mDhcpLease);
setDhcpLeaseExpiry(packet);
- transitionTo(mDhcpBoundState);
+ transitionTo(mConfiguringInterfaceState);
}
} else if (packet instanceof DhcpNakPacket) {
// TODO: Wait a while before returning into INIT state.
@@ -776,24 +769,52 @@
}
}
- class DhcpHaveAddressState extends LoggingState {
+ class DhcpHaveLeaseState extends LoggingState {
@Override
public void enter() {
super.enter();
- if (!setIpAddress(mDhcpLease.ipAddress) ||
- (mDhcpLease.serverAddress != null &&
- !connectUdpSock((mDhcpLease.serverAddress)))) {
- notifyFailure();
- // There's likely no point in going into DhcpInitState here, we'll probably just
- // repeat the transaction, get the same IP address as before, and fail.
- transitionTo(mStoppedState);
- }
}
@Override
public void exit() {
- if (DBG) Log.d(TAG, "Clearing IP address");
- setIpAddress(new LinkAddress("0.0.0.0/0"));
+ // Tell IpManager to clear the IPv4 address. There is no need to
+ // wait for confirmation since any subsequent packets are sent from
+ // INADDR_ANY anyway (DISCOVER, REQUEST).
+ mController.sendMessage(CMD_CLEAR_LINKADDRESS);
+ }
+ }
+
+ class ConfiguringInterfaceState extends LoggingState {
+ @Override
+ public void enter() {
+ super.enter();
+ mController.sendMessage(CMD_CONFIGURE_LINKADDRESS, mDhcpLease.ipAddress);
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ super.processMessage(message);
+ switch (message.what) {
+ case EVENT_LINKADDRESS_CONFIGURED:
+ if (mDhcpLease.serverAddress != null &&
+ !connectUdpSock(mDhcpLease.serverAddress)) {
+ // There's likely no point in going into DhcpInitState here, we'll probably
+ // just repeat the transaction, get the same IP address as before, and fail.
+ //
+ // NOTE: It is observed that connectUdpSock() basically never fails, due to
+ // SO_BINDTODEVICE. Examining the local socket address shows it will happily
+ // return an IPv4 address from another interface, or even return "0.0.0.0".
+ //
+ // TODO: Consider deleting this check, following testing on several kernels.
+ notifyFailure();
+ transitionTo(mStoppedState);
+ } else {
+ transitionTo(mDhcpBoundState);
+ }
+ return HANDLED;
+ default:
+ return NOT_HANDLED;
+ }
}
}
@@ -803,8 +824,8 @@
super.enter();
mOneshotTimeoutAlarm.cancel();
notifySuccess();
- // TODO: DhcpStateMachine only supported renewing at 50% of the lease time, and did not
- // support rebinding. Once the legacy DHCP client is gone, fix this.
+ // TODO: DhcpStateMachine only supported renewing at 50% of the lease time,
+ // and did not support rebinding. Now that the legacy DHCP client is gone, fix this.
scheduleRenew();
}
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index 2683673..fd98f46 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -41,7 +41,6 @@
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.IndentingPrintWriter;
import com.android.internal.util.State;
@@ -330,7 +329,6 @@
// TODO: Revert this hack once IpManager and Nat464Xlat work in concert.
private static final String CLAT_PREFIX = "v4-";
- private final Object mLock = new Object();
private final State mStoppedState = new StoppedState();
private final State mStoppingState = new StoppingState();
private final State mStartedState = new StartedState();
@@ -350,6 +348,7 @@
/**
* Non-final member variables accessed only from within our StateMachine.
*/
+ private LinkProperties mLinkProperties;
private ProvisioningConfiguration mConfiguration;
private IpReachabilityMonitor mIpReachabilityMonitor;
private DhcpClient mDhcpClient;
@@ -360,13 +359,6 @@
private boolean mMulticastFiltering;
private long mStartTimeMillis;
- /**
- * Member variables accessed both from within the StateMachine thread
- * and via accessors from other threads.
- */
- @GuardedBy("mLock")
- private LinkProperties mLinkProperties;
-
public IpManager(Context context, String ifName, Callback callback)
throws IllegalArgumentException {
this(context, ifName, callback, INetworkManagementService.Stub.asInterface(
@@ -506,12 +498,6 @@
sendMessage(CMD_SET_MULTICAST_FILTER, enabled);
}
- public LinkProperties getLinkProperties() {
- synchronized (mLock) {
- return new LinkProperties(mLinkProperties);
- }
- }
-
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
pw.println("APF dump:");
@@ -586,10 +572,8 @@
mTcpBufferSizes = "";
mHttpProxy = null;
- synchronized (mLock) {
- mLinkProperties = new LinkProperties();
- mLinkProperties.setInterfaceName(mInterfaceName);
- }
+ mLinkProperties = new LinkProperties();
+ mLinkProperties.setInterfaceName(mInterfaceName);
}
private void recordMetric(final int type) {
@@ -695,11 +679,8 @@
mIpReachabilityMonitor.updateLinkProperties(newLp);
}
- ProvisioningChange delta;
- synchronized (mLock) {
- delta = compareProvisioning(mLinkProperties, newLp);
- mLinkProperties = new LinkProperties(newLp);
- }
+ ProvisioningChange delta = compareProvisioning(mLinkProperties, newLp);
+ mLinkProperties = new LinkProperties(newLp);
if (DBG) {
switch (delta) {
@@ -714,9 +695,7 @@
}
private boolean linkPropertiesUnchanged(LinkProperties newLp) {
- synchronized (mLock) {
- return Objects.equals(newLp, mLinkProperties);
- }
+ return Objects.equals(newLp, mLinkProperties);
}
private LinkProperties assembleLinkProperties() {
@@ -791,6 +770,19 @@
return (delta != ProvisioningChange.LOST_PROVISIONING);
}
+ private boolean setIPv4Address(LinkAddress address) {
+ final InterfaceConfiguration ifcg = new InterfaceConfiguration();
+ ifcg.setLinkAddress(address);
+ try {
+ mNwService.setInterfaceConfig(mInterfaceName, ifcg);
+ if (VDBG) Log.d(mTag, "IPv4 configuration succeeded");
+ } catch (IllegalStateException | RemoteException e) {
+ Log.e(mTag, "IPv4 configuration failed: ", e);
+ return false;
+ }
+ return true;
+ }
+
private void clearIPv4Address() {
try {
final InterfaceConfiguration ifcg = new InterfaceConfiguration();
@@ -815,7 +807,6 @@
}
private void handleIPv4Failure() {
- // TODO: Figure out to de-dup this and the same code in DhcpClient.
clearIPv4Address();
mDhcpResults = null;
final LinkProperties newLp = assembleLinkProperties();
@@ -965,12 +956,12 @@
// If we have a StaticIpConfiguration attempt to apply it and
// handle the result accordingly.
if (mConfiguration.mStaticIpConfig != null) {
- if (applyStaticIpConfig()) {
+ if (setIPv4Address(mConfiguration.mStaticIpConfig.ipAddress)) {
handleIPv4Success(new DhcpResults(mConfiguration.mStaticIpConfig));
} else {
if (VDBG) { Log.d(mTag, "onProvisioningFailure()"); }
recordMetric(IPCE_IPMGR_PROVISIONING_FAIL);
- mCallback.onProvisioningFailure(getLinkProperties());
+ mCallback.onProvisioningFailure(new LinkProperties(mLinkProperties));
transitionTo(mStoppingState);
}
} else {
@@ -1071,6 +1062,21 @@
}
break;
+ case DhcpClient.CMD_CLEAR_LINKADDRESS:
+ clearIPv4Address();
+ break;
+
+ case DhcpClient.CMD_CONFIGURE_LINKADDRESS: {
+ final LinkAddress ipAddress = (LinkAddress) msg.obj;
+ if (setIPv4Address(ipAddress)) {
+ mDhcpClient.sendMessage(DhcpClient.EVENT_LINKADDRESS_CONFIGURED);
+ } else {
+ Log.e(mTag, "Failed to set IPv4 address!");
+ transitionTo(mStoppingState);
+ }
+ break;
+ }
+
case DhcpClient.CMD_POST_DHCP_ACTION: {
// Note that onPostDhcpAction() is likely to be
// asynchronous, and thus there is no guarantee that we
@@ -1103,20 +1109,5 @@
}
return HANDLED;
}
-
- private boolean applyStaticIpConfig() {
- final InterfaceConfiguration ifcg = new InterfaceConfiguration();
- ifcg.setLinkAddress(mConfiguration.mStaticIpConfig.ipAddress);
- ifcg.setInterfaceUp();
- try {
- mNwService.setInterfaceConfig(mInterfaceName, ifcg);
- if (DBG) Log.d(mTag, "Static IP configuration succeeded");
- } catch (IllegalStateException | RemoteException e) {
- Log.e(mTag, "Static IP configuration failed: ", e);
- return false;
- }
-
- return true;
- }
}
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index e8df24e..8f09bda 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -425,7 +425,12 @@
*/
public static final String KEY_CARRIER_METERED_APN_TYPES_STRINGS =
"carrier_metered_apn_types_strings";
-
+ /**
+ * Default APN types that are roamig-metered by the carrier
+ * @hide
+ */
+ public static final String KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS =
+ "carrier_metered_roaming_apn_types_strings";
/**
* CDMA carrier ERI (Enhanced Roaming Indicator) file name
* @hide
@@ -758,6 +763,8 @@
sDefaults.putInt(KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT, 7200);
sDefaults.putStringArray(KEY_CARRIER_METERED_APN_TYPES_STRINGS,
new String[]{"default", "mms", "dun", "supl"});
+ sDefaults.putStringArray(KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
+ new String[]{"default", "mms", "dun", "supl"});
sDefaults.putStringArray(KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY, null);
sDefaults.putStringArray(KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY, null);
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MovingSurfaceViewActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/MovingSurfaceViewActivity.java
index cd15ef1..fa25b45 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/MovingSurfaceViewActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/MovingSurfaceViewActivity.java
@@ -20,33 +20,36 @@
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.os.Bundle;
-import android.util.Log;
import android.view.Gravity;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
-import android.view.View;
import android.view.animation.LinearInterpolator;
import android.widget.FrameLayout;
public class MovingSurfaceViewActivity extends Activity implements Callback {
- static final String TAG = "MovingSurfaceView";
SurfaceView mSurfaceView;
ObjectAnimator mAnimator;
class MySurfaceView extends SurfaceView {
- boolean mSlowToggled;
+ boolean mSlow;
+ boolean mScaled;
+ int mToggle = 0;
public MySurfaceView(Context context) {
super(context);
- setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- mSlowToggled = !mSlowToggled;
- Log.d(TAG, "SLOW MODE: " + mSlowToggled);
- invalidate();
- }
+ setOnClickListener(v -> {
+ mToggle = (mToggle + 1) % 4;
+ mSlow = (mToggle & 0x2) != 0;
+ mScaled = (mToggle & 0x1) != 0;
+
+ mSurfaceView.setScaleX(mScaled ? 1.6f : 1f);
+ mSurfaceView.setScaleY(mScaled ? 0.8f : 1f);
+
+ setTitle("Slow=" + mSlow + ", scaled=" + mScaled);
+ invalidate();
});
setWillNotDraw(false);
}
@@ -54,7 +57,7 @@
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
- if (mSlowToggled) {
+ if (mSlow) {
try {
Thread.sleep(16);
} catch (InterruptedException e) {}
@@ -63,7 +66,7 @@
public void setMyTranslationY(float ty) {
setTranslationY(ty);
- if (mSlowToggled) {
+ if (mSlow) {
invalidate();
}
}
@@ -86,7 +89,7 @@
int size = (int) (200 * density);
content.addView(mSurfaceView, new FrameLayout.LayoutParams(
- size, size, Gravity.CENTER));
+ size, size, Gravity.CENTER_HORIZONTAL | Gravity.TOP));
mAnimator = ObjectAnimator.ofFloat(mSurfaceView, "myTranslationY",
0, size);
mAnimator.setRepeatMode(ObjectAnimator.REVERSE);
@@ -103,7 +106,7 @@
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Canvas canvas = holder.lockCanvas();
- canvas.drawARGB(0xFF, 0x00, 0xFF, 0x00);
+ canvas.drawColor(Color.WHITE);
holder.unlockCanvasAndPost(canvas);
}