summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk38
-rw-r--r--api/current.txt7
-rw-r--r--api/system-current.txt7
-rw-r--r--api/test-current.txt7
-rw-r--r--core/java/android/app/ActivityManagerNative.java22
-rw-r--r--core/java/android/app/IActivityManager.java4
-rw-r--r--core/java/android/content/ContentProviderClient.java8
-rw-r--r--core/java/android/content/pm/PackageManager.java4
-rw-r--r--core/java/android/content/pm/ShortcutInfo.java12
-rw-r--r--core/java/android/content/pm/ShortcutManager.java22
-rw-r--r--core/java/android/net/ConnectivityManager.java5
-rw-r--r--core/java/android/net/NetworkPolicyManager.java81
-rw-r--r--core/java/android/net/metrics/ApfProgramEvent.java2
-rw-r--r--core/java/android/net/metrics/DhcpClientEvent.java9
-rw-r--r--core/java/android/net/metrics/IpReachabilityEvent.java38
-rw-r--r--core/java/android/os/Process.java20
-rwxr-xr-xcore/java/android/provider/Settings.java12
-rw-r--r--core/java/android/view/ThreadedRenderer.java12
-rw-r--r--core/jni/android_hardware_location_ContextHubService.cpp6
-rw-r--r--core/jni/android_view_ThreadedRenderer.cpp7
-rw-r--r--core/res/res/values/colors.xml2
-rw-r--r--docs/html/_redirects.yaml6
-rw-r--r--docs/html/distribute/engage/_book.yaml3
-rw-r--r--docs/html/distribute/engage/engage_toc.cs6
-rw-r--r--docs/html/distribute/engage/nearby.jd93
-rw-r--r--docs/html/distribute/monetize/_book.yaml3
-rw-r--r--docs/html/distribute/monetize/conversions.jd100
-rw-r--r--docs/html/distribute/monetize/monetize_toc.cs7
-rw-r--r--docs/html/distribute/monetize/payments.jd62
-rw-r--r--docs/html/distribute/users/promote-with-ads.jd130
-rw-r--r--docs/html/guide/topics/ui/accessibility/apps.jd8
-rw-r--r--docs/html/images/cards/card-drive-conversions_16-9_2x.pngbin0 -> 14685 bytes
-rw-r--r--docs/html/images/distribute/nearby_beacons.pngbin0 -> 123680 bytes
-rw-r--r--docs/html/images/distribute/nearby_connections.pngbin0 -> 172749 bytes
-rw-r--r--docs/html/images/distribute/nearby_messaging.pngbin0 -> 185170 bytes
-rw-r--r--docs/html/images/distribute/promote_ads_apps.pngbin155480 -> 142351 bytes
-rw-r--r--docs/html/images/distribute/promote_ads_gmail.pngbin74387 -> 86695 bytes
-rw-r--r--docs/html/images/distribute/promote_ads_play.pngbin109160 -> 98472 bytes
-rw-r--r--docs/html/images/distribute/promote_ads_search.pngbin146234 -> 93117 bytes
-rw-r--r--docs/html/images/distribute/promote_ads_web.pngbin212006 -> 99703 bytes
-rw-r--r--docs/html/images/distribute/promote_ads_youtube.pngbin157263 -> 138298 bytes
-rw-r--r--docs/html/images/paypal-logo.pngbin0 -> 5971 bytes
-rw-r--r--docs/html/jd_collections.js21
-rw-r--r--docs/html/jd_extras.js95
-rw-r--r--docs/html/jd_extras_en.js114
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp59
-rw-r--r--libs/hwui/renderthread/CanvasContext.h6
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp4
-rw-r--r--libs/hwui/renderthread/RenderProxy.h1
-rw-r--r--media/java/android/media/MediaScanner.java25
-rw-r--r--media/java/android/media/SoundPool.java29
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java41
-rw-r--r--packages/SystemUI/res/values/dimens.xml3
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ReverseLinearLayout.java45
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java2
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java230
-rw-r--r--services/core/java/com/android/server/LockSettingsService.java9
-rw-r--r--services/core/java/com/android/server/PinnerService.java125
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java126
-rw-r--r--services/core/java/com/android/server/am/PreBootBroadcaster.java6
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java3
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java2
-rw-r--r--services/core/java/com/android/server/am/UserController.java7
-rw-r--r--services/core/java/com/android/server/am/UserState.java1
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkAgentInfo.java184
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkMonitor.java119
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java6
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java8
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java2
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java1
-rw-r--r--services/java/com/android/server/SystemServer.java8
-rw-r--r--services/net/java/android/net/dhcp/DhcpClient.java28
-rw-r--r--services/net/java/android/net/ip/IpReachabilityMonitor.java22
-rw-r--r--services/retaildemo/java/com/android/server/retaildemo/PreloadAppsInstaller.java125
-rw-r--r--services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java121
-rw-r--r--services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java28
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java40
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java58
-rw-r--r--telecomm/java/android/telecom/ParcelableCallAnalytics.java94
88 files changed, 1892 insertions, 720 deletions
diff --git a/Android.mk b/Android.mk
index 24e402fcf45a..ba4e173ed0c7 100644
--- a/Android.mk
+++ b/Android.mk
@@ -1061,6 +1061,42 @@ LOCAL_DROIDDOC_OPTIONS:=\
-title "Android SDK" \
-proofread $(OUT_DOCS)/$(LOCAL_MODULE)-proofread.txt \
-sdkvalues $(OUT_DOCS) \
+ -hdf android.whichdoc offline
+
+LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk-dev
+
+include $(BUILD_DROIDDOC)
+
+static_doc_index_redirect := $(out_dir)/index.html
+$(static_doc_index_redirect): \
+ $(LOCAL_PATH)/docs/docs-preview-index.html | $(ACP)
+ $(hide) mkdir -p $(dir $@)
+ $(hide) $(ACP) $< $@
+
+$(full_target): $(static_doc_index_redirect)
+$(full_target): $(framework_built)
+
+
+# ==== static html in the sdk ==================================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES)
+LOCAL_INTERMEDIATE_SOURCES:=$(framework_docs_LOCAL_INTERMEDIATE_SOURCES)
+LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES)
+LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
+LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
+LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR)
+LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_ADDITIONAL_JAVA_DIR)
+LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES)
+
+LOCAL_MODULE := offline-sdk-referenceonly
+
+LOCAL_DROIDDOC_OPTIONS:=\
+ $(framework_docs_LOCAL_DROIDDOC_OPTIONS) \
+ -offlinemode \
+ -title "Android SDK" \
+ -proofread $(OUT_DOCS)/$(LOCAL_MODULE)-proofread.txt \
+ -sdkvalues $(OUT_DOCS) \
-hdf android.whichdoc offline \
-referenceonly
@@ -1102,7 +1138,7 @@ LOCAL_DROIDDOC_OPTIONS:= \
-hdf android.hasSamples true \
-samplesdir $(samples_dir)
-LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk
+LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk-dev
include $(BUILD_DROIDDOC)
diff --git a/api/current.txt b/api/current.txt
index b9c026988607..d5f36bdb081f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10063,17 +10063,14 @@ package android.content.pm {
method public android.content.ComponentName getActivity();
method public java.util.Set<java.lang.String> getCategories();
method public java.lang.CharSequence getDisabledMessage();
- method public int getDisabledMessageResourceId();
method public android.os.PersistableBundle getExtras();
method public java.lang.String getId();
method public android.content.Intent getIntent();
method public long getLastChangedTimestamp();
method public java.lang.CharSequence getLongLabel();
- method public int getLongLabelResourceId();
method public java.lang.String getPackage();
method public int getRank();
method public java.lang.CharSequence getShortLabel();
- method public int getShortLabelResourceId();
method public android.os.UserHandle getUserHandle();
method public boolean hasKeyFieldsOnly();
method public boolean isDeclaredInManifest();
@@ -10087,7 +10084,6 @@ package android.content.pm {
}
public static class ShortcutInfo.Builder {
- ctor public deprecated ShortcutInfo.Builder(android.content.Context);
ctor public ShortcutInfo.Builder(android.content.Context, java.lang.String);
method public android.content.pm.ShortcutInfo build();
method public android.content.pm.ShortcutInfo.Builder setActivity(android.content.ComponentName);
@@ -10095,7 +10091,6 @@ package android.content.pm {
method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.CharSequence);
method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle);
method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon);
- method public deprecated android.content.pm.ShortcutInfo.Builder setId(java.lang.String);
method public android.content.pm.ShortcutInfo.Builder setIntent(android.content.Intent);
method public android.content.pm.ShortcutInfo.Builder setLongLabel(java.lang.CharSequence);
method public android.content.pm.ShortcutInfo.Builder setRank(int);
@@ -10113,8 +10108,6 @@ package android.content.pm {
method public java.util.List<android.content.pm.ShortcutInfo> getManifestShortcuts();
method public int getMaxShortcutCountPerActivity();
method public java.util.List<android.content.pm.ShortcutInfo> getPinnedShortcuts();
- method public long getRateLimitResetTime();
- method public int getRemainingCallCount();
method public void removeAllDynamicShortcuts();
method public void removeDynamicShortcuts(java.util.List<java.lang.String>);
method public void reportShortcutUsed(java.lang.String);
diff --git a/api/system-current.txt b/api/system-current.txt
index b0fb9f3b1759..16c75cc5d747 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -10488,17 +10488,14 @@ package android.content.pm {
method public android.content.ComponentName getActivity();
method public java.util.Set<java.lang.String> getCategories();
method public java.lang.CharSequence getDisabledMessage();
- method public int getDisabledMessageResourceId();
method public android.os.PersistableBundle getExtras();
method public java.lang.String getId();
method public android.content.Intent getIntent();
method public long getLastChangedTimestamp();
method public java.lang.CharSequence getLongLabel();
- method public int getLongLabelResourceId();
method public java.lang.String getPackage();
method public int getRank();
method public java.lang.CharSequence getShortLabel();
- method public int getShortLabelResourceId();
method public android.os.UserHandle getUserHandle();
method public boolean hasKeyFieldsOnly();
method public boolean isDeclaredInManifest();
@@ -10512,7 +10509,6 @@ package android.content.pm {
}
public static class ShortcutInfo.Builder {
- ctor public deprecated ShortcutInfo.Builder(android.content.Context);
ctor public ShortcutInfo.Builder(android.content.Context, java.lang.String);
method public android.content.pm.ShortcutInfo build();
method public android.content.pm.ShortcutInfo.Builder setActivity(android.content.ComponentName);
@@ -10520,7 +10516,6 @@ package android.content.pm {
method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.CharSequence);
method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle);
method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon);
- method public deprecated android.content.pm.ShortcutInfo.Builder setId(java.lang.String);
method public android.content.pm.ShortcutInfo.Builder setIntent(android.content.Intent);
method public android.content.pm.ShortcutInfo.Builder setLongLabel(java.lang.CharSequence);
method public android.content.pm.ShortcutInfo.Builder setRank(int);
@@ -10538,8 +10533,6 @@ package android.content.pm {
method public java.util.List<android.content.pm.ShortcutInfo> getManifestShortcuts();
method public int getMaxShortcutCountPerActivity();
method public java.util.List<android.content.pm.ShortcutInfo> getPinnedShortcuts();
- method public long getRateLimitResetTime();
- method public int getRemainingCallCount();
method public void removeAllDynamicShortcuts();
method public void removeDynamicShortcuts(java.util.List<java.lang.String>);
method public void reportShortcutUsed(java.lang.String);
diff --git a/api/test-current.txt b/api/test-current.txt
index 1906c5efe79f..7fdbb6411ef4 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -10076,17 +10076,14 @@ package android.content.pm {
method public android.content.ComponentName getActivity();
method public java.util.Set<java.lang.String> getCategories();
method public java.lang.CharSequence getDisabledMessage();
- method public int getDisabledMessageResourceId();
method public android.os.PersistableBundle getExtras();
method public java.lang.String getId();
method public android.content.Intent getIntent();
method public long getLastChangedTimestamp();
method public java.lang.CharSequence getLongLabel();
- method public int getLongLabelResourceId();
method public java.lang.String getPackage();
method public int getRank();
method public java.lang.CharSequence getShortLabel();
- method public int getShortLabelResourceId();
method public android.os.UserHandle getUserHandle();
method public boolean hasKeyFieldsOnly();
method public boolean isDeclaredInManifest();
@@ -10100,7 +10097,6 @@ package android.content.pm {
}
public static class ShortcutInfo.Builder {
- ctor public deprecated ShortcutInfo.Builder(android.content.Context);
ctor public ShortcutInfo.Builder(android.content.Context, java.lang.String);
method public android.content.pm.ShortcutInfo build();
method public android.content.pm.ShortcutInfo.Builder setActivity(android.content.ComponentName);
@@ -10108,7 +10104,6 @@ package android.content.pm {
method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.CharSequence);
method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle);
method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon);
- method public deprecated android.content.pm.ShortcutInfo.Builder setId(java.lang.String);
method public android.content.pm.ShortcutInfo.Builder setIntent(android.content.Intent);
method public android.content.pm.ShortcutInfo.Builder setLongLabel(java.lang.CharSequence);
method public android.content.pm.ShortcutInfo.Builder setRank(int);
@@ -10127,8 +10122,6 @@ package android.content.pm {
method public java.util.List<android.content.pm.ShortcutInfo> getManifestShortcuts();
method public int getMaxShortcutCountPerActivity();
method public java.util.List<android.content.pm.ShortcutInfo> getPinnedShortcuts();
- method public long getRateLimitResetTime();
- method public int getRemainingCallCount();
method public void removeAllDynamicShortcuts();
method public void removeDynamicShortcuts(java.util.List<java.lang.String>);
method public void reportShortcutUsed(java.lang.String);
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 14f9db732514..277348a318b9 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -3003,6 +3003,13 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
reply.writeNoException();
return true;
}
+ case SET_RENDER_THREAD_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ final int tid = data.readInt();
+ setRenderThread(tid);
+ reply.writeNoException();
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
@@ -7040,7 +7047,7 @@ class ActivityManagerProxy implements IActivityManager
@Override
public void setVrThread(int tid)
- throws RemoteException {
+ throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
@@ -7052,5 +7059,18 @@ class ActivityManagerProxy implements IActivityManager
return;
}
+ public void setRenderThread(int tid)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(tid);
+ mRemote.transact(SET_RENDER_THREAD_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ return;
+ }
+
private IBinder mRemote;
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index d38fb94161f5..4a4202aa4ce8 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -659,6 +659,7 @@ public interface IActivityManager extends IInterface {
throws RemoteException;
public void setVrThread(int tid) throws RemoteException;
+ public void setRenderThread(int tid) throws RemoteException;
/*
* Private non-Binder interfaces
@@ -1046,5 +1047,8 @@ public interface IActivityManager extends IInterface {
int START_CONFIRM_DEVICE_CREDENTIAL_INTENT = IBinder.FIRST_CALL_TRANSACTION + 374;
int SEND_IDLE_JOB_TRIGGER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 375;
int SEND_INTENT_SENDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 376;
+
+ // Start of N MR1 transactions
int SET_VR_THREAD_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 377;
+ int SET_RENDER_THREAD_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 378;
}
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index 9221fbb50c96..e49eb34aeea8 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -147,13 +147,7 @@ public class ContentProviderClient implements AutoCloseable {
if (cursor == null) {
return null;
}
-
- if ("com.google.android.gms".equals(mPackageName)) {
- // They're casting to a concrete subclass, sigh
- return cursor;
- } else {
- return new CursorWrapperInner(cursor);
- }
+ return new CursorWrapperInner(cursor);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index fbe16c5db717..281d6f691bc2 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -146,6 +146,8 @@ public abstract class PackageManager {
MATCH_UNINSTALLED_PACKAGES,
MATCH_SYSTEM_ONLY,
MATCH_DEBUG_TRIAGED_MISSING,
+ MATCH_DISABLED_UNTIL_USED_COMPONENTS,
+ GET_DISABLED_UNTIL_USED_COMPONENTS,
GET_UNINSTALLED_PACKAGES,
})
@Retention(RetentionPolicy.SOURCE)
@@ -2879,6 +2881,7 @@ public abstract class PackageManager {
*
* @see #GET_META_DATA
* @see #GET_SHARED_LIBRARY_FILES
+ * @see #MATCH_DISABLED_UNTIL_USED_COMPONENTS
* @see #MATCH_SYSTEM_ONLY
* @see #MATCH_UNINSTALLED_PACKAGES
*/
@@ -3507,6 +3510,7 @@ public abstract class PackageManager {
*
* @see #GET_META_DATA
* @see #GET_SHARED_LIBRARY_FILES
+ * @see #MATCH_DISABLED_UNTIL_USED_COMPONENTS
* @see #MATCH_SYSTEM_ONLY
* @see #MATCH_UNINSTALLED_PACKAGES
*/
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 35370f0998fd..39e15e01bab4 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -696,7 +696,8 @@ public final class ShortcutInfo implements Parcelable {
private PersistableBundle mExtras;
/**
- * Old style constructor. STOPSHIP hide it before launch.
+ * Old style constructor.
+ * @hide
*/
@Deprecated
public Builder(Context context) {
@@ -704,7 +705,8 @@ public final class ShortcutInfo implements Parcelable {
}
/**
- * Used with the old style constructor, kept for unit tests. STOPSHIP hide it before launch.
+ * Used with the old style constructor, kept for unit tests.
+ * @hide
*/
@NonNull
@Deprecated
@@ -1004,7 +1006,7 @@ public final class ShortcutInfo implements Parcelable {
return mTitle;
}
- /** TODO Javadoc */
+ /** @hide */
public int getShortLabelResourceId() {
return mTitleResId;
}
@@ -1017,7 +1019,7 @@ public final class ShortcutInfo implements Parcelable {
return mText;
}
- /** TODO Javadoc */
+ /** @hide */
public int getLongLabelResourceId() {
return mTextResId;
}
@@ -1030,7 +1032,7 @@ public final class ShortcutInfo implements Parcelable {
return mDisabledMessage;
}
- /** TODO Javadoc */
+ /** @hide */
public int getDisabledMessageResourceId() {
return mDisabledMessageResId;
}
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index 7b3c48717263..1af63a01ce2b 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -63,24 +63,6 @@ import java.util.List;
* published, existing shortcuts with the same ID will be updated. Note this may include a
* pinned shortcut.
*
- * <h3>Rate limiting</h3>
- *
- * Calls to {@link #setDynamicShortcuts(List)}, {@link #addDynamicShortcuts(List)},
- * and {@link #updateShortcuts(List)} from <b>background applications</b> will be
- * rate-limited. An application can call these methods at most
- * {@link #getRemainingCallCount()} times until the rate-limiting counter is reset,
- * which happens at a certain time every day.
- *
- * <p>An application can use {@link #getRateLimitResetTime()} to get the next reset time.
- *
- * <p>Foreground applications (i.e. ones with a foreground activity or a foreground services)
- * will not be throttled. Also, when an application comes to foreground,
- * {@link #getRemainingCallCount()} will be reset to the initial value.
- *
- * <p>For testing purposes, use "Developer Options" (found in the Settings menu) to reset the
- * internal rate-limiting counter. Automated tests can use the following ADB shell command to
- * achieve the same effect:</p>
- * <pre>adb shell cmd shortcut reset-throttling</pre>
*
* <h3>Backup and Restore</h3>
*
@@ -328,6 +310,8 @@ public class ShortcutManager {
* before the rate limit counter is reset.
*
* @see #getRateLimitResetTime()
+ *
+ * @hide
*/
public int getRemainingCallCount() {
try {
@@ -342,6 +326,8 @@ public class ShortcutManager {
*
* @see #getRemainingCallCount()
* @see System#currentTimeMillis()
+ *
+ * @hide
*/
public long getRateLimitResetTime() {
try {
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index a45e6f51d803..8d4137957b3f 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -3103,14 +3103,11 @@ public class ConnectivityManager {
throw new IllegalArgumentException("Invalid NetworkCallback");
}
try {
+ // CallbackHandler will release callback when receiving CALLBACK_RELEASED.
mService.releaseNetworkRequest(networkCallback.networkRequest);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
-
- synchronized (sNetworkCallback) {
- sNetworkCallback.remove(networkCallback.networkRequest);
- }
}
/**
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 51c45e056092..11b861aef5aa 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -18,7 +18,6 @@ package android.net;
import static android.content.pm.PackageManager.GET_SIGNATURES;
import static android.net.NetworkPolicy.CYCLE_NONE;
-import static android.text.format.Time.MONTH_DAY;
import android.content.Context;
import android.content.Intent;
@@ -27,12 +26,13 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.text.format.Time;
import android.util.DebugUtils;
import com.google.android.collect.Sets;
+import java.util.Calendar;
import java.util.HashSet;
+import java.util.TimeZone;
/**
* Manager for creating and modifying network policy rules.
@@ -253,28 +253,18 @@ public class NetworkPolicyManager {
throw new IllegalArgumentException("Unable to compute boundary without cycleDay");
}
- final Time now = new Time(policy.cycleTimezone);
- now.set(currentTime);
-
- // first, find cycle boundary for current month
- final Time cycle = new Time(now);
- cycle.hour = cycle.minute = cycle.second = 0;
- snapToCycleDay(cycle, policy.cycleDay);
-
- if (Time.compare(cycle, now) >= 0) {
- // cycle boundary is beyond now, use last cycle boundary; start by
- // pushing ourselves squarely into last month.
- final Time lastMonth = new Time(now);
- lastMonth.hour = lastMonth.minute = lastMonth.second = 0;
- lastMonth.monthDay = 1;
- lastMonth.month -= 1;
- lastMonth.normalize(true);
-
- cycle.set(lastMonth);
- snapToCycleDay(cycle, policy.cycleDay);
+ final Calendar cal = Calendar.getInstance(TimeZone.getTimeZone(policy.cycleTimezone));
+ cal.setTimeInMillis(currentTime);
+ snapToCycleDay(cal, policy.cycleDay);
+
+ if (cal.getTimeInMillis() >= currentTime) {
+ // Cycle boundary is beyond now, use last cycle boundary
+ cal.set(Calendar.DAY_OF_MONTH, 1);
+ cal.add(Calendar.MONTH, -1);
+ snapToCycleDay(cal, policy.cycleDay);
}
- return cycle.toMillis(true);
+ return cal.getTimeInMillis();
}
/** {@hide} */
@@ -283,28 +273,18 @@ public class NetworkPolicyManager {
throw new IllegalArgumentException("Unable to compute boundary without cycleDay");
}
- final Time now = new Time(policy.cycleTimezone);
- now.set(currentTime);
-
- // first, find cycle boundary for current month
- final Time cycle = new Time(now);
- cycle.hour = cycle.minute = cycle.second = 0;
- snapToCycleDay(cycle, policy.cycleDay);
-
- if (Time.compare(cycle, now) <= 0) {
- // cycle boundary is before now, use next cycle boundary; start by
- // pushing ourselves squarely into next month.
- final Time nextMonth = new Time(now);
- nextMonth.hour = nextMonth.minute = nextMonth.second = 0;
- nextMonth.monthDay = 1;
- nextMonth.month += 1;
- nextMonth.normalize(true);
-
- cycle.set(nextMonth);
- snapToCycleDay(cycle, policy.cycleDay);
+ final Calendar cal = Calendar.getInstance(TimeZone.getTimeZone(policy.cycleTimezone));
+ cal.setTimeInMillis(currentTime);
+ snapToCycleDay(cal, policy.cycleDay);
+
+ if (cal.getTimeInMillis() <= currentTime) {
+ // Cycle boundary is before now, use next cycle boundary
+ cal.set(Calendar.DAY_OF_MONTH, 1);
+ cal.add(Calendar.MONTH, 1);
+ snapToCycleDay(cal, policy.cycleDay);
}
- return cycle.toMillis(true);
+ return cal.getTimeInMillis();
}
/**
@@ -313,16 +293,17 @@ public class NetworkPolicyManager {
*
* @hide
*/
- public static void snapToCycleDay(Time time, int cycleDay) {
- if (cycleDay > time.getActualMaximum(MONTH_DAY)) {
- // cycle day isn't valid this month; snap to last second of month
- time.month += 1;
- time.monthDay = 1;
- time.second = -1;
+ public static void snapToCycleDay(Calendar cal, int cycleDay) {
+ cal.set(Calendar.HOUR_OF_DAY, 0);
+ cal.set(Calendar.MINUTE, 0);
+ cal.set(Calendar.SECOND, 0);
+ if (cycleDay > cal.getActualMaximum(Calendar.DAY_OF_MONTH)) {
+ cal.set(Calendar.DAY_OF_MONTH, 1);
+ cal.add(Calendar.MONTH, 1);
+ cal.add(Calendar.SECOND, -1);
} else {
- time.monthDay = cycleDay;
+ cal.set(Calendar.DAY_OF_MONTH, cycleDay);
}
- time.normalize(true);
}
/**
diff --git a/core/java/android/net/metrics/ApfProgramEvent.java b/core/java/android/net/metrics/ApfProgramEvent.java
index e322dc1e1732..258d8e13951f 100644
--- a/core/java/android/net/metrics/ApfProgramEvent.java
+++ b/core/java/android/net/metrics/ApfProgramEvent.java
@@ -124,7 +124,7 @@ public final class ApfProgramEvent implements Parcelable {
for (int bit = set.nextSetBit(0); bit >= 0; bit = set.nextSetBit(bit+1)) {
names.add(Decoder.constants.get(bit));
}
- return TextUtils.join(", ", names);
+ return TextUtils.join("|", names);
}
final static class Decoder {
diff --git a/core/java/android/net/metrics/DhcpClientEvent.java b/core/java/android/net/metrics/DhcpClientEvent.java
index 169f571d8317..4a9ff0511246 100644
--- a/core/java/android/net/metrics/DhcpClientEvent.java
+++ b/core/java/android/net/metrics/DhcpClientEvent.java
@@ -21,11 +21,18 @@ import android.os.Parcel;
import android.os.Parcelable;
/**
- * An event recorded when a DhcpClient state machine transitions to a new state.
+ * An event recorded when a DhcpClient state machine transitions to a new state.
* {@hide}
*/
@SystemApi
public final class DhcpClientEvent implements Parcelable {
+
+ // Names for recording DhcpClient pseudo-state transitions.
+ /** {@hide} Represents transitions from DhcpInitState to DhcpBoundState */
+ public static final String INITIAL_BOUND = "InitialBoundState";
+ /** {@hide} Represents transitions from and to DhcpBoundState via DhcpRenewingState */
+ public static final String RENEWING_BOUND = "RenewingBoundState";
+
public final String ifName;
public final String msg;
public final int durationMs;
diff --git a/core/java/android/net/metrics/IpReachabilityEvent.java b/core/java/android/net/metrics/IpReachabilityEvent.java
index 7d0229191563..ee09e2292661 100644
--- a/core/java/android/net/metrics/IpReachabilityEvent.java
+++ b/core/java/android/net/metrics/IpReachabilityEvent.java
@@ -24,21 +24,31 @@ import android.util.SparseArray;
import com.android.internal.util.MessageUtils;
/**
+ * An event recorded when IpReachabilityMonitor sends a neighbor probe or receives
+ * a neighbor probe result.
* {@hide}
*/
@SystemApi
public final class IpReachabilityEvent implements Parcelable {
- public static final int PROBE = 1 << 8;
- public static final int NUD_FAILED = 2 << 8;
- public static final int PROVISIONING_LOST = 3 << 8;
+ // Event types.
+ /** A probe forced by IpReachabilityMonitor. */
+ public static final int PROBE = 1 << 8;
+ /** Neighbor unreachable after a forced probe. */
+ public static final int NUD_FAILED = 2 << 8;
+ /** Neighbor unreachable after a forced probe, IP provisioning is also lost. */
+ public static final int PROVISIONING_LOST = 3 << 8;
+ /** {@hide} Neighbor unreachable notification from kernel. */
+ public static final int NUD_FAILED_ORGANIC = 4 << 8;
+ /** {@hide} Neighbor unreachable notification from kernel, IP provisioning is also lost. */
+ public static final int PROVISIONING_LOST_ORGANIC = 5 << 8;
public final String ifName;
// eventType byte format (MSB to LSB):
// byte 0: unused
// byte 1: unused
// byte 2: type of event: PROBE, NUD_FAILED, PROVISIONING_LOST
- // byte 3: kernel errno from RTNetlink or IpReachabilityMonitor
+ // byte 3: when byte 2 == PROBE, errno code from RTNetlink or IpReachabilityMonitor.
public final int eventType;
/** {@hide} */
@@ -52,11 +62,13 @@ public final class IpReachabilityEvent implements Parcelable {
this.eventType = in.readInt();
}
+ @Override
public void writeToParcel(Parcel out, int flags) {
out.writeString(ifName);
out.writeInt(eventType);
}
+ @Override
public int describeContents() {
return 0;
}
@@ -81,10 +93,24 @@ public final class IpReachabilityEvent implements Parcelable {
public static void logProvisioningLost(String ifName) {
}
+ /**
+ * Returns the NUD failure event type code corresponding to the given conditions.
+ * {@hide}
+ */
+ public static int nudFailureEventType(boolean isFromProbe, boolean isProvisioningLost) {
+ if (isFromProbe) {
+ return isProvisioningLost ? PROVISIONING_LOST : NUD_FAILED;
+ } else {
+ return isProvisioningLost ? PROVISIONING_LOST_ORGANIC : NUD_FAILED_ORGANIC;
+ }
+ }
+
@Override
public String toString() {
- return String.format("IpReachabilityEvent(%s, %s)", ifName,
- Decoder.constants.get(eventType));
+ int hi = eventType & 0xff00;
+ int lo = eventType & 0x00ff;
+ String eventName = Decoder.constants.get(hi);
+ return String.format("IpReachabilityEvent(%s, %s:%02x)", ifName, eventName, lo);
}
final static class Decoder {
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index b9e46a5c60f1..c26d974b2cba 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -19,6 +19,7 @@ package android.os;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.system.Os;
+import android.system.OsConstants;
import android.util.Log;
import com.android.internal.os.Zygote;
import dalvik.system.VMRuntime;
@@ -328,7 +329,6 @@ public class Process {
*/
public static final int SCHED_RESET_ON_FORK = 0x40000000;
-
// Keep in sync with SP_* constants of enum type SchedPolicy
// declared in system/core/include/cutils/sched_policy.h,
// except THREAD_GROUP_DEFAULT does not correspond to any SP_* value.
@@ -1250,4 +1250,22 @@ public class Process {
* @hide
*/
public static final native void removeAllProcessGroups();
+
+ /**
+ * Check to see if a thread belongs to a given process. This may require
+ * more permissions than apps generally have.
+ * @return true if this thread belongs to a process
+ * @hide
+ */
+ public static final boolean isThreadInProcess(int tid, int pid) {
+ try {
+ if (Os.access("/proc/" + tid + "/task/" + pid, OsConstants.F_OK)) {
+ return true;
+ } else {
+ return false;
+ }
+ } catch (Exception e) {
+ return false;
+ }
+ }
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2c4598fe9d38..874f3f34cff3 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1548,7 +1548,7 @@ public final class Settings {
private IContentProvider lazyGetProvider(ContentResolver cr) {
IContentProvider cp = null;
- synchronized (this) {
+ synchronized (NameValueCache.this) {
cp = mContentProvider;
if (cp == null) {
cp = mContentProvider = cr.acquireProvider(mUri.getAuthority());
@@ -1575,7 +1575,7 @@ public final class Settings {
public String getStringForUser(ContentResolver cr, String name, final int userHandle) {
final boolean isSelf = (userHandle == UserHandle.myUserId());
if (isSelf) {
- synchronized (this) {
+ synchronized (NameValueCache.this) {
if (mGenerationTracker != null) {
if (mGenerationTracker.isGenerationChanged()) {
if (DEBUG) {
@@ -1608,7 +1608,7 @@ public final class Settings {
args.putInt(CALL_METHOD_USER_KEY, userHandle);
}
boolean needsGenerationTracker = false;
- synchronized (this) {
+ synchronized (NameValueCache.this) {
if (isSelf && mGenerationTracker == null) {
needsGenerationTracker = true;
if (args == null) {
@@ -1627,7 +1627,7 @@ public final class Settings {
String value = b.getString(Settings.NameValueTable.VALUE);
// Don't update our cache for reads of other users' data
if (isSelf) {
- synchronized (this) {
+ synchronized (NameValueCache.this) {
if (needsGenerationTracker) {
MemoryIntArray array = b.getParcelable(
CALL_METHOD_TRACK_GENERATION_KEY);
@@ -1644,7 +1644,7 @@ public final class Settings {
}
mGenerationTracker = new GenerationTracker(array, index,
generation, () -> {
- synchronized (this) {
+ synchronized (NameValueCache.this) {
Log.e(TAG, "Error accessing generation"
+ " tracker - removing");
if (mGenerationTracker != null) {
@@ -1685,7 +1685,7 @@ public final class Settings {
}
String value = c.moveToNext() ? c.getString(0) : null;
- synchronized (this) {
+ synchronized (NameValueCache.this) {
mValues.put(name, value);
}
if (LOCAL_LOGV) {
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index fcca7395fe8c..e129a067ec14 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -16,6 +16,7 @@
package android.view;
+import android.app.ActivityManagerNative;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.content.Context;
@@ -917,10 +918,20 @@ public final class ThreadedRenderer {
synchronized void init(Context context, long renderProxy) {
if (mInitialized) return;
mInitialized = true;
+ initSched(context, renderProxy);
initGraphicsStats(context, renderProxy);
initAssetAtlas(context, renderProxy);
}
+ private static void initSched(Context context, long renderProxy) {
+ try {
+ int tid = nGetRenderThreadTid(renderProxy);
+ ActivityManagerNative.getDefault().setRenderThread(tid);
+ } catch (Throwable t) {
+ Log.w(LOG_TAG, "Failed to set scheduler for RenderThread", t);
+ }
+ }
+
private static void initGraphicsStats(Context context, long renderProxy) {
try {
IBinder binder = ServiceManager.getService("graphicsstats");
@@ -979,6 +990,7 @@ public final class ThreadedRenderer {
private static native void nSetAtlas(long nativeProxy, GraphicBuffer buffer, long[] map);
private static native void nSetProcessStatsBuffer(long nativeProxy, int fd);
+ private static native int nGetRenderThreadTid(long nativeProxy);
private static native long nCreateRootRenderNode();
private static native long nCreateProxy(boolean translucent, long rootRenderNode);
diff --git a/core/jni/android_hardware_location_ContextHubService.cpp b/core/jni/android_hardware_location_ContextHubService.cpp
index 3881d6d9e99f..a56eba6c133f 100644
--- a/core/jni/android_hardware_location_ContextHubService.cpp
+++ b/core/jni/android_hardware_location_ContextHubService.cpp
@@ -186,9 +186,13 @@ static int set_dest_app(hub_message_t *msg, int id) {
static void send_query_for_apps() {
hub_message_t msg;
+ query_apps_request_t queryMsg;
+
+ queryMsg.app_name.id = NANOAPP_VENDOR_ALL_APPS;
msg.message_type = CONTEXT_HUB_QUERY_APPS;
- msg.message_len = 0;
+ msg.message_len = sizeof(queryMsg);
+ msg.message = &queryMsg;
for (int i = 0; i < db.hubInfo.numHubs; i++ ) {
ALOGD("Sending query for apps to hub %d", i);
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 61a0bda17855..595d5c61b8e8 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -543,6 +543,12 @@ static void android_view_ThreadedRenderer_setProcessStatsBuffer(JNIEnv* env, job
proxy->setProcessStatsBuffer(fd);
}
+static jint android_view_ThreadedRenderer_getRenderThreadTid(JNIEnv* env, jobject clazz,
+ jlong proxyPtr) {
+ RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+ return proxy->getRenderThreadTid();
+}
+
static jlong android_view_ThreadedRenderer_createRootRenderNode(JNIEnv* env, jobject clazz) {
RootRenderNode* node = new RootRenderNode(env);
node->incStrong(0);
@@ -858,6 +864,7 @@ const char* const kClassPathName = "android/view/ThreadedRenderer";
static const JNINativeMethod gMethods[] = {
{ "nSetAtlas", "(JLandroid/view/GraphicBuffer;[J)V", (void*) android_view_ThreadedRenderer_setAtlas },
{ "nSetProcessStatsBuffer", "(JI)V", (void*) android_view_ThreadedRenderer_setProcessStatsBuffer },
+ { "nGetRenderThreadTid", "(J)I", (void*) android_view_ThreadedRenderer_getRenderThreadTid },
{ "nCreateRootRenderNode", "()J", (void*) android_view_ThreadedRenderer_createRootRenderNode },
{ "nCreateProxy", "(ZJ)J", (void*) android_view_ThreadedRenderer_createProxy },
{ "nDeleteProxy", "(J)V", (void*) android_view_ThreadedRenderer_deleteProxy },
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index bddd2255aa81..10e2a4a10dc5 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -151,7 +151,7 @@
<color name="battery_saver_mode_color">#fff4511e</color><!-- deep orange 600 -->
<!-- Default user icon colors -->
- <color name="user_icon_1">#ff00bcd4</color><!-- teal 500 -->
+ <color name="user_icon_1">#ff00bcd4</color><!-- cyan 500 -->
<color name="user_icon_2">#ff3f51b5</color><!-- indigo 500 -->
<color name="user_icon_3">#ff4285f4</color><!-- blue 500 -->
<color name="user_icon_4">#ffe91e63</color><!-- pink 500 -->
diff --git a/docs/html/_redirects.yaml b/docs/html/_redirects.yaml
index cf56f2bb1268..11e06f110bec 100644
--- a/docs/html/_redirects.yaml
+++ b/docs/html/_redirects.yaml
@@ -149,9 +149,11 @@ redirects:
to: /google/play/licensing/index.html
- from: /google/play/billing/billing_about.html
to: /google/play/billing/index.html
-- from: /guide/developing/tools/
+- from: /guide/developing/tools/proguard.html
+ to: /studio/build/shrink-code.html
+- from: /guide/developing/tools/...
to: /studio/command-line/
-- from: /guide/developing/
+- from: /guide/developing/...
to: /studio/
- from: /tools/aidl.html
to: /guide/components/aidl.html
diff --git a/docs/html/distribute/engage/_book.yaml b/docs/html/distribute/engage/_book.yaml
index 87e819a8d84d..c371268677c7 100644
--- a/docs/html/distribute/engage/_book.yaml
+++ b/docs/html/distribute/engage/_book.yaml
@@ -31,3 +31,6 @@ toc:
- title: Get Feedback with Beta Tests
path: /distribute/engage/beta.html
+
+- title: Interact with Nearby Users
+ path: /distribute/engage/nearby.html
diff --git a/docs/html/distribute/engage/engage_toc.cs b/docs/html/distribute/engage/engage_toc.cs
index 4f3e0af03734..cc6e28442103 100644
--- a/docs/html/distribute/engage/engage_toc.cs
+++ b/docs/html/distribute/engage/engage_toc.cs
@@ -66,6 +66,12 @@
<span class="en">Get Feedback with Beta Tests</span></a>
</div>
</li>
+ <li class="nav-section">
+ <div class="nav-section-header empty" style="font-weight:normal"><a href="<?cs
+ var:toroot?>distribute/engage/nearby.html">
+ <span class="en">Interact with Nearby Users</span></a>
+ </div>
+ </li>
</ul>
<script type="text/javascript">
diff --git a/docs/html/distribute/engage/nearby.jd b/docs/html/distribute/engage/nearby.jd
new file mode 100644
index 000000000000..b1571f67e308
--- /dev/null
+++ b/docs/html/distribute/engage/nearby.jd
@@ -0,0 +1,93 @@
+page.title=Interact with Nearby Users
+page.metaDescription=Use the Nearby feature to interact with nearby people, devices, and beacons.
+page.image=images/distribute/nearby_connections.png
+page.tags="users, nearby, engage"
+@jd:body
+
+<p>Create experiences that seem magical for users who are in close proximity by using the unique
+close-range and cross-platform capabilities of Nearby. Set up multiplayer games, ad-hoc groups,
+sharing, or collaborative sessions so that your users can work or play together more easily when
+they're close.</p>
+
+<p>Learn more about <a href="https://developers.google.com/nearby/">how to add nearby interactions
+to your app or game</a>.</p>
+
+<div class="wrap">
+ <div class="cols" style="margin-top:1em;">
+ <div class="col-4of12">
+ <h3>
+ Messaging
+ </h3>
+ <img src="{@docRoot}images/distribute/nearby_messaging.png">
+ <p class="figure-caption">
+ Find nearby devices and share messages to enable rich interactions and collaboration
+ among users.
+ </p>
+ </div>
+
+ <div class="col-4of12">
+ <h3>
+ Connections
+ </h3>
+ <img src="{@docRoot}images/distribute/nearby_connections.png">
+ <p class="figure-caption">
+ Discover other local devices and create connections that enable real-time, cross-device
+ experiences.
+ </p>
+ </div>
+
+ <div class="col-4of12">
+ <h3>
+ Beacons
+ </h3>
+ <img src="{@docRoot}images/distribute/nearby_beacons.png">
+ <p class="figure-caption">
+ Receive messages from beacons using
+ <a href="https://developers.google.com/beacons/eddystone">Eddystone</a> and add context to
+ location-based apps and games.
+ </p>
+ </div>
+ </div>
+</div>
+
+<p class="note"><strong>Note:</strong> Nearby uses Bluetooth 2.0, Bluetooth 4.0, Wi-Fi, and an
+ultrasonic modem to function over distances of up to 100 feet.</p>
+
+<h2 id="best-practices">Best practices</h2>
+
+<p>The following list contains some helpful tips and best practices that will help you set up
+and use Nearby effectively:
+
+<ul>
+ <li>Use Nearby features sparingly and only when they're needed because they can consume battery
+ life quickly (up to 3.5 times faster than normal).
+ </li>
+
+ <li>Invoke Nearby explicitly with a button, switch, or special screen, and provide a visual
+ indication when the features are actively sending or receiving content.
+ </li>
+
+ <li>Ensure that users are aware of the data that is made visible by Nearby before
+ they start using the features.
+ </li>
+
+ <li>Stop any publish or subscribe operations when the user exits the app or stops the
+ activity that requires Nearby.
+ </li>
+
+ <li>Use the <em>earshot</em> option, which uses only the ultrasonic modem to send and receive
+ messages, to limit the range of Nearby messages to about five feet when privacy is important.
+ </li>
+
+ <li>Accelerate the exchange of messages (when appropriate) by making one device the publisher
+ only, and all other devices subscribers.
+ </li>
+</ul>
+
+<h2 id="related-resources">Related resources</h2>
+
+<div class="resource-widget resource-flow-layout col-13"
+ data-query="collection:distribute/users/nearby"
+ data-sortOrder="-timestamp"
+ data-cardSizes="9x3"
+ data-maxResults="6"></div>
diff --git a/docs/html/distribute/monetize/_book.yaml b/docs/html/distribute/monetize/_book.yaml
index 2ebc6956f31f..974e9edb530d 100644
--- a/docs/html/distribute/monetize/_book.yaml
+++ b/docs/html/distribute/monetize/_book.yaml
@@ -16,3 +16,6 @@ toc:
- title: Purchasing
path: /distribute/monetize/payments.html
+
+- title: Drive Conversions
+ path: /distribute/monetize/conversions.html
diff --git a/docs/html/distribute/monetize/conversions.jd b/docs/html/distribute/monetize/conversions.jd
new file mode 100644
index 000000000000..20b2333f33d6
--- /dev/null
+++ b/docs/html/distribute/monetize/conversions.jd
@@ -0,0 +1,100 @@
+page.title=Drive Conversions
+page.image=images/cards/card-drive-conversions_16-9_2x.png
+page.metaDescription=Discover where your users are coming from, drive engagement, and surface your in-app products to maximize your conversions.
+page.tags="conversions"
+
+@jd:body
+
+<div class="figure">
+ <img src="{@docRoot}images/cards/card-drive-conversions_16-9_2x.png">
+</div>
+
+<p>
+ Users who've made in-app purchases or converted in other ways are more likely
+ to do so again.
+ You can now easily discover where those users are coming from, drive engagement,
+ and surface your in-app products to maximize your conversions.
+</p>
+
+<div class="headerLine">
+ <h2 id="dicover">
+ Discover your most valuable channels
+ </h2>
+
+</div>
+
+<p>
+ From the <strong>User Acquisition</strong> page in the Google Play Developer Console, explore
+ how users convert into spenders across your acquisition channels and find the best prospects
+ for app install campaigns.
+</p>
+
+<p>For more information, view the guide on how to <a class="external-link"
+ href="https://support.google.com/googleplay/android-developer/answer/6263332">
+ measure your app's user acquisition</a> in the Google Play Developer Console Help Center.</p>
+
+</p>
+
+<div class="headerLine">
+ <h2 id="adwords">
+ Re-engage users with AdWords ads
+ </h2>
+
+</div>
+
+<p>
+ Bring users back to your app by creating an AdWords re-engagement campaign.
+ Use display and search ads that appear only to users who have installed your app.
+ Use deep links to bring them to where they'll find the content or actions they're
+ searching for.
+</p>
+
+<p>For more information, view the guide on <a class="external-link"
+ href="https://support.google.com/adwords/topic/3119078?hl=en">campaign settings</a> in the
+ AdWords Help Center.</p>
+
+<div class="headerLine">
+ <h2 id="gift-cards">
+ Drive spending with AdMob in-app purchase ads
+ </h2>
+</div>
+
+<p>Use your Google Analytics data to create Remarketing Audience lists for the high-value
+ users most likely to purchase products within your app. Then create an AdMob campaign that
+ targets these users to increase their awareness of your products.</p>
+
+<p>For more information, view the guide on how to <a class="external-link"
+ href="https://www.google.com/admob/promote.html">drive more in-app purchases and installs</a>
+ in the AdMob platform.</p>
+
+<div class="headerLine">
+ <h2 id="tips">
+ Tips
+ </h2>
+</div>
+<ul>
+<li>Add <a class="external-link"
+ href="https://developers.google.com/app-indexing/webmasters/app">deep links</a>
+ to your app so ads bring users directly to
+ conversion activities.</li>
+<li>Track what users do in your app by installing the
+ AdWords <a class="external-link"
+ href="https://developers.google.com/app-conversion-tracking">
+ Conversion Tracking SDK</a>.</li>
+<li>Link your Google Analytics and AdMob accounts to share audience lists.</li>
+<li>Make conversion ads compelling, such as promoting a booking search or
+ in-app product special offer.</li>
+<li>Use the AdMob Conversion Optimizer with existing campaigns. Predictions
+ are more accurate when there is more data to work with.</li>
+<li>Re-engage with your app's users across the Display Network with remarketing
+ lists in AdMob and with search keywords in AdWords.</li>
+</ul>
+
+
+<div class="headerLine"><h2 id="related-resources">Related resources</h2></div>
+
+<div class="resource-widget resource-flow-layout col-13"
+ data-query="collection:distribute/monetize/conversions"
+ data-sortOrder="-timestamp"
+ data-cardSizes="9x3"
+ data-maxResults="8"></div>
diff --git a/docs/html/distribute/monetize/monetize_toc.cs b/docs/html/distribute/monetize/monetize_toc.cs
index a3aa50fe1688..b58663328f21 100644
--- a/docs/html/distribute/monetize/monetize_toc.cs
+++ b/docs/html/distribute/monetize/monetize_toc.cs
@@ -34,6 +34,12 @@
</a>
</div>
</li>
+ <li class="nav-section">
+ <div class="nav-section-header empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/monetize/conversions.html">
+ <span class="en">Drive Conversions</span>
+ </a>
+ </div>
+ </li>
</ul>
@@ -44,4 +50,3 @@
changeNavLang(getLangPref());
//-->
</script>
-
diff --git a/docs/html/distribute/monetize/payments.jd b/docs/html/distribute/monetize/payments.jd
index 7d972bb02216..004e47eda93b 100644
--- a/docs/html/distribute/monetize/payments.jd
+++ b/docs/html/distribute/monetize/payments.jd
@@ -15,36 +15,43 @@ page.tags="google play", "payments", "gift card"
instantly with a streamlined, consistent purchasing process and convenient
payment methods.
</p>
-
+<p><strong>Key facts</strong></p>
+<ul>
+<li>Direct carrier billing in over 35 countries.</li>
+<li>Google Play gift cards in over 25 countries.</li>
+<li>PayPal in over 20 countries.</li>
+<li>Developers can sell their apps from over 75 countries.</li>
+<li>Users can buy apps in over 135 countries.</li>
+</ul>
<div class="headerLine">
<h2 id="dcb">
- Direct Carrier Billing
+ Direct carrier billing
</h2>
</div>
<p>
- Users pay by charging their monthly carrier bills . The benefit of Direct
- Carrier Billing is that it opens up markets where credit cards are less
- common, as purchases are charged to your customers’ monthly mobile phone
+ Users pay by charging their monthly carrier bills. The benefit of direct
+ carrier billing is that it opens up markets where credit cards are less
+ common, as purchases are charged to your customers' monthly mobile phone
bills. This option is available to users in key markets
around the world. Many more will get the option in the months ahead.
</p>
<div class="headerLine">
<h2 id="credit">
- Credit Cards
+ Credit cards
</h2>
</div>
<p>
- Users can pay using any credit card that they’ve registered in Google Play.
- Credit Cards added to Google Play are stored in the user’s Google wallet and
- available for in-app purchases and instant buys too. To make it easy for
- users to get started, registration is offered as a part of the initial device
- setup process.
+ Users can pay using any credit card that they've registered in Google Play.
+ The credit cards that a user adds to Google Play are stored in the user's Google wallet.
+ They are available for in-app purchases and instant buys. It's easy for
+ users to get started, as the initial device setup process allows users to register a credit
+ card that they can use in Google Play.
</p>
<div class="headerLine">
@@ -62,16 +69,40 @@ page.tags="google play", "payments", "gift card"
<p>
Gift cards enable users to add value to their Google Play balance by entering
a unique code printed on a card purchased online or from major retailers.
- More information gift cards can be found <a href=
+ More information on gift cards can be found <a href=
"http://play.google.com/intl/en-US_us/about/giftcards/" target=
"_android">here</a>.
</p>
<p style="clear:both">
</p>
+
+<div class="headerLine">
+ <h2 id="paypal">
+ PayPal
+ </h2>
+
+
+</div>
+
+<div class="figure">
+ <img src="{@docRoot}images/paypal-logo.png">
+</div>
+
+<p>
+ Users with PayPal accounts can buy apps and digital content on Google Play using any
+ of their available payment methods. They sign into their PayPal account and
+ complete the purchase. The popularity of PayPal for payments on the web gives
+ you more customers.
+</p>
+
+<p style="clear:both">
+</p>
+
+
<div class="headerLine">
<h2 id="balance">
- Google Play Balance
+ Google Play balance
</h2>
@@ -95,14 +126,13 @@ page.tags="google play", "payments", "gift card"
The payment methods available to users may vary based on location, carrier
network, and other factors.
</p>
-
+
<p style="clear:both">
</p>
-<div class="headerLine"><h2 id="related-resources">Related Resources</h2></div>
+<div class="headerLine"><h2 id="related-resources">Related resources</h2></div>
<div class="resource-widget resource-flow-layout col-13"
data-query="collection:distribute/monetize/paymentmethods"
data-sortOrder="-timestamp"
data-cardSizes="9x3"
data-maxResults="8"></div>
-
diff --git a/docs/html/distribute/users/promote-with-ads.jd b/docs/html/distribute/users/promote-with-ads.jd
index 2db4ca306107..d99f449b31a5 100644
--- a/docs/html/distribute/users/promote-with-ads.jd
+++ b/docs/html/distribute/users/promote-with-ads.jd
@@ -6,20 +6,27 @@ page.tags="users, ads, adwords"
<p>Users have a huge amount of choice when it comes to which apps they install and
use, so it’s important to actively find new ways to promote your app and drive
-ongoing engagement. AdWords is a powerful and effective way to do both.</p>
+ongoing engagement. AdWords campaigns, which you create in the
+<a href="http://play.google.com/apps/publish">Google Play Developer Console</a>,
+are a powerful and effective way to do both.</p>
-<h2 id=drive_installs>Drive installs</h2>
+<h2 id="drive_installs">Drive installs with universal app campaigns</h2>
-<p><a href="http://adwords.google.com">AdWords</a> promotes your app to interested
-users where they spend time on phones and
-tablets – with app install ads on Google Search, YouTube, Gmail, and within
-apps and across the web on the Google Display Network. AdWords is a powerful
-way to scale app promotion across Google networks and find customers that are
-most likely to install your app. </p>
+<p><a href="http://adwords.google.com">AdWords</a> is a powerful way to scale
+app promotion across Google networks and find customers who are most likely to
+install your app. AdWords promotes your app to interested users where they spend
+time on phones and tablets – with app install ads on Google Play, Google Search,
+YouTube, Gmail, and within apps and across the web.</p>
-<p><a href="https://support.google.com/adwords/answer/6032059">Get started with AdWords
-app install ads</a>.</p>
+<p>By creating a <em>universal app camapign</em>, you can reach all of these
+networks. This type of campaign allocates ads, bids, and budgets automatically,
+making it easier to improve install volume for your app.</p>
+
+<p>To learn more about creating universal ad campaigns, read the article about
+<a class="external-link" href="https://support.google.com/googleplay/android-developer/answer/6262700">creating
+an AdWords campaign for your app</a> in the Google Play Developer Console Help
+Center.</p>
<div class="wrap">
<div class="cols" style="margin-top:1em;">
@@ -27,18 +34,16 @@ app install ads</a>.</p>
<h3>
From Google Play
</h3>
- <img src="/images/distribute/promote_ads_play.png">
+ <img src="{@docRoot}images/distribute/promote_ads_play.png">
<p class="figure-caption">
- Promote your app on Google Play when users are searching and browsing
- for apps.
+ Reach users as they search for apps and games on Google Play.
</p>
</div>
-
<div class="col-4of12">
<h3>
- From search
+ From Google Search
</h3>
- <img src="/images/distribute/promote_ads_search.png">
+ <img src="{@docRoot}images/distribute/promote_ads_search.png">
<p class="figure-caption">
Connect with users as they search for content and services provided by
your app.
@@ -49,47 +54,32 @@ app install ads</a>.</p>
<h3>
From YouTube
</h3>
- <img src="/images/distribute/promote_ads_youtube.png">
+ <img src="{@docRoot}images/distribute/promote_ads_youtube.png">
<p class="figure-caption">
Promote your app when users are watching related videos.
</p>
</div>
- </div>
-</div>
-<div class="wrap">
- <div class="cols" style="margin-top:1em;">
<div class="col-4of12">
<h3>
From apps
</h3>
- <img src="/images/distribute/promote_ads_apps.png">
+ <img src="{@docRoot}images/distribute/promote_ads_apps.png">
<p class="figure-caption">
Reach users while they’re engaged with apps and games across the AdMob
network.
</p>
</div>
-
<div class="col-4of12">
<h3>
From the web
</h3>
- <img src="/images/distribute/promote_ads_web.png">
+ <img src="{@docRoot}images/distribute/promote_ads_web.png">
<p class="figure-caption">
Reach users while they’re engaged with websites across the Google
Display Network.
</p>
</div>
-
- <div class="col-4of12">
- <h3>
- From Gmail
- </h3>
- <img src="/images/distribute/promote_ads_gmail.png">
- <p class="figure-caption">
- Promote your app while users communicate and get things done in Gmail.
- </p>
- </div>
</div>
</div>
@@ -130,77 +120,7 @@ app install ads</a>.</p>
</li>
</ul>
-<h2 id="engage_with_users">
- Engage with users
-</h2>
-
-<p>
- Getting a user to install an app is one thing, but you'll also want them to
- open it regularly. AdWords offers app re-engagement tools to help your app
- stay in mind with users who’ve already installed it on their phone. AdWords
- can remind them of key features and encourage them to try your app again, or
- help them complete an activity they didn't know your app could handle.
-</p>
-
-<div class="wrap">
- <div class="cols" style="margin-top:1em;">
- <div class="col-4of12">
- <h3>
- From search
- </h3>
- <img src="/images/distribute/promote_ads.png">
- <p class="figure-caption">
- Add deep links to your app, then bring users straight to relevant app
- content when they’re searching.
- </p>
- </div>
-
- <div class="col-4of12">
- <h3>
- From apps
- </h3>
- <img src="/images/distribute/promote_ads_inapp.png">
- <p class="figure-caption">
- Use remarketing and deep links to bring users to just the right place
- in your app to re-engage and convert, from other apps and games they
- love.
- </p>
- </div>
- </div>
-</div>
-
-<h3>
- Tips
-</h3>
-
-<ul>
- <li>Track what users do in your app after they’ve clicked an ad, by
- installing the AdWords <a href=
- "https://developers.google.com/app-conversion-tracking/">conversion tracking
- SDK</a>.
- </li>
-
- <li>Advertise a compelling reason for users to re-engage with your app (such
- as a reminder or a special offer).
- </li>
-
- <li>
- <a href="https://developers.google.com/app-indexing/webmasters/app">Add
- deep links</a> to your app that’ll take users directly to the parts of your
- app that will be most relevant and interesting to them, where they can
- easily take action.
- </li>
-
- <li>Re-engage your app users across the display network with remarketing
- lists and search with keywords.
- </li>
-
- <li>Use remarketing lists to target high value users so that you can drive
- more conversions in your app.
- </li>
-</ul>
-
-<h2 id="related-resources">Related Resources</h2>
+<h2 id="related-resources">Related resources</h2>
<div class="resource-widget resource-flow-layout col-13"
data-query="collection:distribute/users/promotewithads"
diff --git a/docs/html/guide/topics/ui/accessibility/apps.jd b/docs/html/guide/topics/ui/accessibility/apps.jd
index 90781f7e81ae..eb639e37027d 100644
--- a/docs/html/guide/topics/ui/accessibility/apps.jd
+++ b/docs/html/guide/topics/ui/accessibility/apps.jd
@@ -454,18 +454,20 @@ following example code demonstrates a basic implementation of this method.</p>
<pre>
&#64;Override
-public void dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
- super.dispatchPopulateAccessibilityEvent(event);
+public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
// Call the super implementation to populate its text to the event, which
// calls onPopulateAccessibilityEvent() on API Level 14 and up.
-
+ boolean completed = super.dispatchPopulateAccessibilityEvent(event);
+
// In case this is running on a API revision earlier that 14, check
// the text content of the event and add an appropriate text
// description for this custom view:
CharSequence text = getText();
if (!TextUtils.isEmpty(text)) {
event.getText().add(text);
+ return true;
}
+ return completed;
}
</pre>
diff --git a/docs/html/images/cards/card-drive-conversions_16-9_2x.png b/docs/html/images/cards/card-drive-conversions_16-9_2x.png
new file mode 100644
index 000000000000..34480124567b
--- /dev/null
+++ b/docs/html/images/cards/card-drive-conversions_16-9_2x.png
Binary files differ
diff --git a/docs/html/images/distribute/nearby_beacons.png b/docs/html/images/distribute/nearby_beacons.png
new file mode 100644
index 000000000000..aba2f39e7b1a
--- /dev/null
+++ b/docs/html/images/distribute/nearby_beacons.png
Binary files differ
diff --git a/docs/html/images/distribute/nearby_connections.png b/docs/html/images/distribute/nearby_connections.png
new file mode 100644
index 000000000000..52e6daa7028e
--- /dev/null
+++ b/docs/html/images/distribute/nearby_connections.png
Binary files differ
diff --git a/docs/html/images/distribute/nearby_messaging.png b/docs/html/images/distribute/nearby_messaging.png
new file mode 100644
index 000000000000..6ae2d00505fd
--- /dev/null
+++ b/docs/html/images/distribute/nearby_messaging.png
Binary files differ
diff --git a/docs/html/images/distribute/promote_ads_apps.png b/docs/html/images/distribute/promote_ads_apps.png
index 2f578651e7f4..1c25be34e80c 100644
--- a/docs/html/images/distribute/promote_ads_apps.png
+++ b/docs/html/images/distribute/promote_ads_apps.png
Binary files differ
diff --git a/docs/html/images/distribute/promote_ads_gmail.png b/docs/html/images/distribute/promote_ads_gmail.png
index 1d21b4a3cdf9..c1013fc76034 100644
--- a/docs/html/images/distribute/promote_ads_gmail.png
+++ b/docs/html/images/distribute/promote_ads_gmail.png
Binary files differ
diff --git a/docs/html/images/distribute/promote_ads_play.png b/docs/html/images/distribute/promote_ads_play.png
index 1cf51b273f9c..ae0f84bf87f9 100644
--- a/docs/html/images/distribute/promote_ads_play.png
+++ b/docs/html/images/distribute/promote_ads_play.png
Binary files differ
diff --git a/docs/html/images/distribute/promote_ads_search.png b/docs/html/images/distribute/promote_ads_search.png
index 27c0b38e2f04..adcede154e3f 100644
--- a/docs/html/images/distribute/promote_ads_search.png
+++ b/docs/html/images/distribute/promote_ads_search.png
Binary files differ
diff --git a/docs/html/images/distribute/promote_ads_web.png b/docs/html/images/distribute/promote_ads_web.png
index 588a3d4487a1..8fefed167848 100644
--- a/docs/html/images/distribute/promote_ads_web.png
+++ b/docs/html/images/distribute/promote_ads_web.png
Binary files differ
diff --git a/docs/html/images/distribute/promote_ads_youtube.png b/docs/html/images/distribute/promote_ads_youtube.png
index e88a7965f6de..ad44e512f9d6 100644
--- a/docs/html/images/distribute/promote_ads_youtube.png
+++ b/docs/html/images/distribute/promote_ads_youtube.png
Binary files differ
diff --git a/docs/html/images/paypal-logo.png b/docs/html/images/paypal-logo.png
new file mode 100644
index 000000000000..3e08b95cb52e
--- /dev/null
+++ b/docs/html/images/paypal-logo.png
Binary files differ
diff --git a/docs/html/jd_collections.js b/docs/html/jd_collections.js
index aa0620aefd0c..03efa86ffaaa 100644
--- a/docs/html/jd_collections.js
+++ b/docs/html/jd_collections.js
@@ -419,7 +419,8 @@ var RESOURCE_COLLECTIONS = {
"distribute/engage/easy-signin.html",
"distribute/analyze/build-better-apps.html",
"distribute/engage/gcm.html",
- "distribute/engage/beta.html"
+ "distribute/engage/beta.html",
+ "distribute/engage/nearby.html"
]
},
"distribute/monetize": {
@@ -430,6 +431,7 @@ var RESOURCE_COLLECTIONS = {
"distribute/monetize/ads.html",
"distribute/monetize/ecommerce.html",
"distribute/monetize/payments.html",
+ "distribute/monetize/conversions.html",
"distribute/analyze/understand-user-value.html",
]
},
@@ -761,6 +763,14 @@ var RESOURCE_COLLECTIONS = {
"https://support.google.com/adwords/answer/6167162"
]
},
+ "distribute/users/nearby": {
+ "title": "",
+ "resources": [
+ "https://developers.google.com/nearby/",
+ "https://www.youtube.com/watch?v=hultDpBS22s",
+ "https://developers.google.com/beacons"
+ ]
+ },
"distribute/users/buildbuzz": {
"title": "",
"resources": [
@@ -1556,6 +1566,15 @@ var RESOURCE_COLLECTIONS = {
"https://support.google.com/googleplay/answer/2651410"
]
},
+ "distribute/monetize/conversions": {
+ "title": "",
+ "resources": [
+ "https://support.google.com/adwords/answer/2471188",
+ "https://developers.google.com/app-conversion-tracking/",
+ "https://support.google.com/analytics/answer/2611404",
+ "https://support.google.com/adwords/answer/1704341"
+ ]
+ },
"autolanding": {
"title": "",
"resources": [
diff --git a/docs/html/jd_extras.js b/docs/html/jd_extras.js
index e5347d970492..44ccafafb45f 100644
--- a/docs/html/jd_extras.js
+++ b/docs/html/jd_extras.js
@@ -1942,7 +1942,7 @@ DISTRIBUTE_RESOURCES = DISTRIBUTE_RESOURCES.concat([
"url": "https://support.google.com/googleplay/answer/2651410",
"timestamp": null,
"image": "images/play_dev.jpg",
- "title": "Google Play Accepted Payment Methods",
+ "title": "Google Play accepted payment methods",
"summary": "Support details on the payment methods supported in Google Play.",
"keywords": ["gift card"],
"type": "distribute",
@@ -1951,6 +1951,59 @@ DISTRIBUTE_RESOURCES = DISTRIBUTE_RESOURCES.concat([
{
"lang": "en",
"group": "",
+ "tags": [],
+ "url": "https://support.google.com/adwords/answer/2471188",
+ "timestamp": null,
+ "image": "images/play_dev.jpg",
+ "title": "AdWords Conversion Optimizer",
+ "summary": "Learn how Conversion Optimizer works to find the users who are most likely to convert and to serve them your conversion ads.",
+ "keywords": [],
+ "type": "distribute",
+ "titleFriendly": ""
+ },
+ {
+ "lang": "en",
+ "group": "",
+ "tags": [],
+ "url": "https://developers.google.com/app-conversion-tracking/",
+ "timestamp": null,
+ "image": "images/play_dev.jpg",
+ "title": "Track conversions with the AdWords SDK or server API",
+ "summary": "Use the lightweight AdWords app SDK or server-to-server API to track remarketing conversions.",
+ "keywords": [],
+ "type": "distribute",
+ "titleFriendly": ""
+ },
+ {
+ "lang": "en",
+ "group": "",
+ "tags": [],
+ "url": "https://support.google.com/analytics/answer/2611404",
+ "timestamp": null,
+ "image": "images/play_dev.jpg",
+ "title": "Create Remarketing Audiences in Google Analytics",
+ "summary": "Learn how to use preconfigured audiences created by the Analytics team or create your own to use in your conversion campaigns.",
+ "keywords": [],
+ "type": "distribute",
+ "titleFriendly": ""
+ },
+ {
+ "lang": "en",
+ "group": "",
+ "tags": [],
+ "url": "https://support.google.com/adwords/answer/1704341",
+ "timestamp": null,
+ "image": "images/play_dev.jpg",
+ "title": "Link your Google Analytics and AdWords accounts",
+ "summary": "Gain greater insight into how AdWords is driving app engagement and conversions, and use this insight to improve your ads and app.",
+ "keywords": [],
+ "type": "distribute",
+ "titleFriendly": ""
+ },
+
+ {
+ "lang": "en",
+ "group": "",
"tags": ["plus", "social"],
"url": "https://plus.google.com/+AndroidDevelopers/",
"timestamp": null,
@@ -2719,7 +2772,6 @@ DISTRIBUTE_RESOURCES = DISTRIBUTE_RESOURCES.concat([
"type": "material design",
"titleFriendly": ""
},
-
{
"lang": "en",
"group": "",
@@ -2737,6 +2789,45 @@ DISTRIBUTE_RESOURCES = DISTRIBUTE_RESOURCES.concat([
"lang": "en",
"group": "",
"tags": [],
+ "url": "https://developers.google.com/nearby/",
+ "timestamp": null,
+ "image": "images/play_dev.jpg",
+ "title": "Create features based on proximity",
+ "summary": "Build simple interactions between nearby devices and people.",
+ "keywords": ["nearby", "engage"],
+ "type": "distribute",
+ "titleFriendly": ""
+ },
+ {
+ "lang": "en",
+ "group": "",
+ "tags": [],
+ "url": "https://www.youtube.com/watch?v=hultDpBS22s",
+ "timestamp": null,
+ "image": "images/play_dev.jpg",
+ "title": "Use Nearby Messages to collaborate",
+ "summary": "Nearby Messages is perfect for setting up ad-hoc groups, collaborative sessions, or sharing resources with people in a co-located space.",
+ "keywords": ["nearby", "engage"],
+ "type": "distribute",
+ "titleFriendly": ""
+ },
+ {
+ "lang": "en",
+ "group": "",
+ "tags": [],
+ "url": "https://developers.google.com/beacons",
+ "timestamp": null,
+ "image": "images/play_dev.jpg",
+ "title": "Mark up the world using beacons",
+ "summary": "Give your users better location and proximity experiences by providing a strong context signal for their devices in the form of Bluetooth low energy (BLE) beacons with Eddystone.",
+ "keywords": ["nearby", "engage"],
+ "type": "distribute",
+ "titleFriendly": ""
+ },
+ {
+ "lang": "en",
+ "group": "",
+ "tags": [],
"url": "https://support.google.com/adwords/answer/6167164",
"timestamp": null,
"image": "distribute/images/advertising.jpg",
diff --git a/docs/html/jd_extras_en.js b/docs/html/jd_extras_en.js
index 9cc110ddfc7b..434f2116a0b4 100644
--- a/docs/html/jd_extras_en.js
+++ b/docs/html/jd_extras_en.js
@@ -1943,13 +1943,65 @@ METADATA['en'].extras = METADATA['en'].extras.concat([
"url": "https://support.google.com/googleplay/answer/2651410",
"timestamp": null,
"image": "images/cards/google-play_2x.png",
- "title": "Google Play Accepted Payment Methods",
+ "title": "Google Play accepted payment methods",
"summary": "Support details on the payment methods supported in Google Play.",
"keywords": ["gift card"],
"type": "distribute",
"category": "google play"
},
{
+ "lang": "en",
+ "group": "",
+ "tags": [],
+ "url": "https://support.google.com/adwords/answer/2471188",
+ "timestamp": null,
+ "image": "images/play_dev.jpg",
+ "title": "AdWords Conversion Optimizer",
+ "summary": "Learn how Conversion Optimizer works to find the users who are most likely to convert and to serve them your conversion ads.",
+ "keywords": [],
+ "type": "distribute",
+ "titleFriendly": ""
+ },
+ {
+ "lang": "en",
+ "group": "",
+ "tags": [],
+ "url": "https://developers.google.com/app-conversion-tracking/",
+ "timestamp": null,
+ "image": "images/play_dev.jpg",
+ "title": "Track conversions with the AdWords SDK or server API",
+ "summary": "Use the lightweight AdWords app SDK or server-to-server API to track remarketing conversions.",
+ "keywords": [],
+ "type": "distribute",
+ "titleFriendly": ""
+ },
+ {
+ "lang": "en",
+ "group": "",
+ "tags": [],
+ "url": "https://support.google.com/analytics/answer/2611404",
+ "timestamp": null,
+ "image": "images/play_dev.jpg",
+ "title": "Create Remarketing Audiences in Google Analytics",
+ "summary": "Learn how to use preconfigured audiences created by the Analytics team or create your own to use in your conversion campaigns.",
+ "keywords": [],
+ "type": "distribute",
+ "titleFriendly": ""
+ },
+ {
+ "lang": "en",
+ "group": "",
+ "tags": [],
+ "url": "https://support.google.com/adwords/answer/1704341",
+ "timestamp": null,
+ "image": "images/play_dev.jpg",
+ "title": "Link your Google Analytics and AdWords accounts",
+ "summary": "Gain greater insight into how AdWords is driving app engagement and conversions, and use this insight to improve your ads and app.",
+ "keywords": [],
+ "type": "distribute",
+ "titleFriendly": ""
+ },
+ {
"lang": "en",
"group": "",
"tags": ["plus", "social"],
@@ -2699,6 +2751,45 @@ METADATA['en'].extras = METADATA['en'].extras.concat([
"lang": "en",
"group": "",
"tags": [],
+ "url": "https://developers.google.com/nearby/",
+ "timestamp": null,
+ "image": "images/play_dev.jpg",
+ "title": "Create features based on proximity",
+ "summary": "Build simple interactions between nearby devices and people.",
+ "keywords": ["nearby", "engage"],
+ "type": "distribute",
+ "titleFriendly": ""
+ },
+ {
+ "lang": "en",
+ "group": "",
+ "tags": [],
+ "url": "https://www.youtube.com/watch?v=hultDpBS22s",
+ "timestamp": null,
+ "image": "images/play_dev.jpg",
+ "title": "Use Nearby Messages to collaborate",
+ "summary": "Nearby Messages is perfect for setting up ad-hoc groups, collaborative sessions, or sharing resources with people in a co-located space.",
+ "keywords": ["nearby", "engage"],
+ "type": "distribute",
+ "titleFriendly": ""
+ },
+ {
+ "lang": "en",
+ "group": "",
+ "tags": [],
+ "url": "https://developers.google.com/beacons",
+ "timestamp": null,
+ "image": "images/play_dev.jpg",
+ "title": "Mark up the world using beacons",
+ "summary": "Give your users better location and proximity experiences by providing a strong context signal for their devices in the form of Bluetooth low energy (BLE) beacons with Eddystone.",
+ "keywords": ["nearby", "engage"],
+ "type": "distribute",
+ "titleFriendly": ""
+ },
+ {
+ "lang": "en",
+ "group": "",
+ "tags": [],
"url": "https://support.google.com/adwords/answer/6167164",
"timestamp": null,
"image": "distribute/images/advertising.jpg",
@@ -4160,7 +4251,8 @@ METADATA['en'].collections = {
"distribute/engage/easy-signin.html",
"distribute/analyze/build-better-apps.html",
"distribute/engage/gcm.html",
- "distribute/engage/beta.html"
+ "distribute/engage/beta.html",
+ "distribute/engage/nearby.html"
]
},
"distribute/monetize": {
@@ -4171,6 +4263,7 @@ METADATA['en'].collections = {
"distribute/monetize/ads.html",
"distribute/monetize/ecommerce.html",
"distribute/monetize/payments.html",
+ "distribute/monetize/conversions.html",
"distribute/analyze/understand-user-value.html",
]
},
@@ -4480,6 +4573,14 @@ METADATA['en'].collections = {
"https://support.google.com/adwords/answer/6167162"
]
},
+ "distribute/users/nearby": {
+ "title": "",
+ "resources": [
+ "https://developers.google.com/nearby/",
+ "https://www.youtube.com/watch?v=hultDpBS22s",
+ "https://developers.google.com/beacons"
+ ]
+ },
"distribute/users/buildbuzz": {
"title": "",
"resources": [
@@ -5117,6 +5218,15 @@ METADATA['en'].collections = {
"https://support.google.com/googleplay/answer/2651410"
]
},
+ "distribute/monetize/conversions": {
+ "title": "",
+ "resources": [
+ "https://support.google.com/adwords/answer/2471188",
+ "https://developers.google.com/app-conversion-tracking/",
+ "https://support.google.com/analytics/answer/2611404",
+ "https://support.google.com/adwords/answer/1704341"
+ ]
+ },
"topic/libraries": {
"title": "",
"resources": [
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index ceef9c788fc1..5003c6aefb8b 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -198,6 +198,45 @@ static bool wasSkipped(FrameInfo* info) {
return info && ((*info)[FrameInfoIndex::Flags] & FrameInfoFlags::SkippedFrame);
}
+bool CanvasContext::isSwapChainStuffed() {
+ if (mSwapHistory.size() != mSwapHistory.capacity()) {
+ // We want at least 3 frames of history before attempting to
+ // guess if the queue is stuffed
+ return false;
+ }
+ nsecs_t frameInterval = mRenderThread.timeLord().frameIntervalNanos();
+ auto& swapA = mSwapHistory[0];
+
+ // Was there a happy queue & dequeue time? If so, don't
+ // consider it stuffed
+ if (swapA.dequeueDuration < 3_ms
+ && swapA.queueDuration < 3_ms) {
+ return false;
+ }
+
+ for (size_t i = 1; i < mSwapHistory.size(); i++) {
+ auto& swapB = mSwapHistory[i];
+
+ // If there's a frameInterval gap we effectively already dropped a frame,
+ // so consider the queue healthy.
+ if (swapA.swapCompletedTime - swapB.swapCompletedTime > frameInterval) {
+ return false;
+ }
+
+ // Was there a happy queue & dequeue time? If so, don't
+ // consider it stuffed
+ if (swapB.dequeueDuration < 3_ms
+ && swapB.queueDuration < 3_ms) {
+ return false;
+ }
+
+ swapA = swapB;
+ }
+
+ // All signs point to a stuffed swap chain
+ return true;
+}
+
void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo,
int64_t syncQueued, RenderNode* target) {
mRenderThread.removeFrameCallback(this);
@@ -243,7 +282,12 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo,
if (CC_LIKELY(mSwapHistory.size())) {
nsecs_t latestVsync = mRenderThread.timeLord().latestVsync();
- const SwapHistory& lastSwap = mSwapHistory.back();
+ SwapHistory& lastSwap = mSwapHistory.back();
+ int durationUs;
+ mNativeSurface->query(NATIVE_WINDOW_LAST_DEQUEUE_DURATION, &durationUs);
+ lastSwap.dequeueDuration = us2ns(durationUs);
+ mNativeSurface->query(NATIVE_WINDOW_LAST_QUEUE_DURATION, &durationUs);
+ lastSwap.queueDuration = us2ns(durationUs);
nsecs_t vsyncDelta = std::abs(lastSwap.vsyncTime - latestVsync);
// The slight fudge-factor is to deal with cases where
// the vsync was estimated due to being slow handling the signal.
@@ -253,15 +297,12 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo,
// Already drew for this vsync pulse, UI draw request missed
// the deadline for RT animations
info.out.canDrawThisFrame = false;
- } else if (lastSwap.swapTime < latestVsync) {
+ } else if (vsyncDelta >= mRenderThread.timeLord().frameIntervalNanos()) {
+ // It's been at least an entire frame interval, assume
+ // the buffer queue is fine
info.out.canDrawThisFrame = true;
} else {
- // We're maybe behind? Find out for sure
- int runningBehind = 0;
- // TODO: Have this method be on Surface, too, not just ANativeWindow...
- ANativeWindow* window = mNativeSurface.get();
- window->query(window, NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &runningBehind);
- info.out.canDrawThisFrame = !runningBehind;
+ info.out.canDrawThisFrame = !isSwapChainStuffed();
}
} else {
info.out.canDrawThisFrame = true;
@@ -516,7 +557,7 @@ void CanvasContext::draw() {
}
SwapHistory& swap = mSwapHistory.next();
swap.damage = screenDirty;
- swap.swapTime = systemTime(CLOCK_MONOTONIC);
+ swap.swapCompletedTime = systemTime(CLOCK_MONOTONIC);
swap.vsyncTime = mRenderThread.timeLord().latestVsync();
mHaveNewSurface = false;
mFrameNumber = -1;
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index a6eb7adc3568..b0d980b94308 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -180,6 +180,8 @@ private:
void waitOnFences();
+ bool isSwapChainStuffed();
+
EGLint mLastFrameWidth = 0;
EGLint mLastFrameHeight = 0;
@@ -198,7 +200,9 @@ private:
struct SwapHistory {
SkRect damage;
nsecs_t vsyncTime;
- nsecs_t swapTime;
+ nsecs_t swapCompletedTime;
+ nsecs_t dequeueDuration;
+ nsecs_t queueDuration;
};
RingBuffer<SwapHistory, 3> mSwapHistory;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 54af2829cf40..06a24b248bf6 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -514,6 +514,10 @@ void RenderProxy::setProcessStatsBuffer(int fd) {
post(task);
}
+int RenderProxy::getRenderThreadTid() {
+ return mRenderThread.getTid();
+}
+
CREATE_BRIDGE3(addRenderNode, CanvasContext* context, RenderNode* node, bool placeFront) {
args->context->addRenderNode(args->node, args->placeFront);
return nullptr;
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 898b31421aad..e31062c83734 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -115,6 +115,7 @@ public:
ANDROID_API void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size);
ANDROID_API void setProcessStatsBuffer(int fd);
+ ANDROID_API int getRenderThreadTid();
ANDROID_API void serializeDisplayListTree();
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 5fd85d19b9b9..c8ab5f9bc429 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -60,11 +60,14 @@ import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
+import java.text.SimpleDateFormat;
+import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Locale;
+import java.util.TimeZone;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -451,6 +454,8 @@ public class MediaScanner implements AutoCloseable {
private class MyMediaScannerClient implements MediaScannerClient {
+ private final SimpleDateFormat mDateFormatter;
+
private String mArtist;
private String mAlbumArtist; // use this if mArtist is missing
private String mAlbum;
@@ -463,6 +468,7 @@ public class MediaScanner implements AutoCloseable {
private int mYear;
private int mDuration;
private String mPath;
+ private long mDate;
private long mLastModified;
private long mFileSize;
private String mWriter;
@@ -472,6 +478,11 @@ public class MediaScanner implements AutoCloseable {
private int mWidth;
private int mHeight;
+ public MyMediaScannerClient() {
+ mDateFormatter = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
+ mDateFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
+ }
+
public FileEntry beginFile(String path, String mimeType, long lastModified,
long fileSize, boolean isDirectory, boolean noMedia) {
mMimeType = mimeType;
@@ -537,6 +548,7 @@ public class MediaScanner implements AutoCloseable {
mYear = 0;
mDuration = 0;
mPath = path;
+ mDate = 0;
mLastModified = lastModified;
mWriter = null;
mCompilation = 0;
@@ -627,6 +639,14 @@ public class MediaScanner implements AutoCloseable {
return result;
}
+ private long parseDate(String date) {
+ try {
+ return mDateFormatter.parse(date).getTime();
+ } catch (ParseException e) {
+ return 0;
+ }
+ }
+
private int parseSubstring(String s, int start, int defaultValue) {
int length = s.length();
if (start == length) return defaultValue;
@@ -684,6 +704,8 @@ public class MediaScanner implements AutoCloseable {
mCompilation = parseSubstring(value, 0, 0);
} else if (name.equalsIgnoreCase("isdrm")) {
mIsDrm = (parseSubstring(value, 0, 0) == 1);
+ } else if (name.equalsIgnoreCase("date")) {
+ mDate = parseDate(value);
} else if (name.equalsIgnoreCase("width")) {
mWidth = parseSubstring(value, 0, 0);
} else if (name.equalsIgnoreCase("height")) {
@@ -830,6 +852,9 @@ public class MediaScanner implements AutoCloseable {
if (resolution != null) {
map.put(Video.Media.RESOLUTION, resolution);
}
+ if (mDate > 0) {
+ map.put(Video.Media.DATE_TAKEN, mDate);
+ }
} else if (MediaFile.isImageFileType(mFileType)) {
// FIXME - add DESCRIPTION
} else if (MediaFile.isAudioFileType(mFileType)) {
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index 3164930aa749..5ede1d5f7c91 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -133,6 +133,8 @@ public class SoundPool {
private final IAppOpsService mAppOps;
private final IAppOpsCallback mAppOpsCallback;
+ private static IAudioService sService;
+
/**
* Constructor. Constructs a SoundPool object with the following
* characteristics:
@@ -492,7 +494,34 @@ public class SoundPool {
}
}
+ private static IAudioService getService()
+ {
+ if (sService != null) {
+ return sService;
+ }
+ IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
+ sService = IAudioService.Stub.asInterface(b);
+ return sService;
+ }
+
private boolean isRestricted() {
+ IAudioService service = getService();
+ boolean cameraSoundForced = false;
+
+ try {
+ cameraSoundForced = service.isCameraSoundForced();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Cannot access AudioService in isRestricted()");
+ }
+
+ if (cameraSoundForced &&
+ ((mAttributes.getAllFlags() & AudioAttributes.FLAG_AUDIBILITY_ENFORCED) != 0)
+// FIXME: should also check usage when set properly by camera app
+// && (mAttributes.getUsage() == AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
+ ) {
+ return false;
+ }
+
if ((mAttributes.getAllFlags() & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0) {
return false;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java b/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java
index ff1c8665b3d2..b04948b7de5f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java
@@ -22,6 +22,9 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Build;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.SubscriptionInfo;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.util.Log;
@@ -37,6 +40,8 @@ import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import static android.content.Context.TELEPHONY_SERVICE;
+
public class DeviceInfoUtils {
private static final String TAG = "DeviceInfoUtils";
@@ -169,4 +174,40 @@ public class DeviceInfoUtils {
}
}
+ public static String getFormattedPhoneNumber(Context context, SubscriptionInfo subscriptionInfo) {
+ String formattedNumber = null;
+ if (subscriptionInfo != null) {
+ final TelephonyManager telephonyManager =
+ (TelephonyManager) context.getSystemService(TELEPHONY_SERVICE);
+ final String rawNumber =
+ telephonyManager.getLine1Number(subscriptionInfo.getSubscriptionId());
+ if (!TextUtils.isEmpty(rawNumber)) {
+ formattedNumber = PhoneNumberUtils.formatNumber(rawNumber);
+ }
+
+ }
+ return formattedNumber;
+ }
+
+ public static String getFormattedPhoneNumbers(Context context,
+ List<SubscriptionInfo> subscriptionInfo) {
+ StringBuilder sb = new StringBuilder();
+ if (subscriptionInfo != null) {
+ final TelephonyManager telephonyManager =
+ (TelephonyManager) context.getSystemService(TELEPHONY_SERVICE);
+ final int count = subscriptionInfo.size();
+ for (int i = 0; i < count; i++) {
+ final String rawNumber = telephonyManager.getLine1Number(
+ subscriptionInfo.get(i).getSubscriptionId());
+ if (!TextUtils.isEmpty(rawNumber)) {
+ sb.append(PhoneNumberUtils.formatNumber(rawNumber));
+ if (i < count - 1) {
+ sb.append("\n");
+ }
+ }
+ }
+ }
+ return sb.toString();
+ }
+
}
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index cd861e14b18b..e1cbbc5c9c54 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -141,6 +141,9 @@
<!-- Margin start of the system icons super container -->
<dimen name="system_icons_super_container_margin_start">16dp</dimen>
+ <!-- Margin end of the system icons super container when the avatar is missing. -->
+ <dimen name="system_icons_super_container_avatarless_margin_end">6dp</dimen>
+
<!-- Width for the notification panel and related windows -->
<dimen name="match_parent">-1px</dimen>
<dimen name="standard_notification_panel_width">416dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 0f356e00e572..7bdb1c499bd9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -801,9 +801,13 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
Recents.getTaskLoader().dump(prefix, writer);
String id = Integer.toHexString(System.identityHashCode(this));
+ long lastStackActiveTime = Prefs.getLong(this,
+ Prefs.Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME, -1);
writer.print(prefix); writer.print(TAG);
writer.print(" visible="); writer.print(mIsVisible ? "Y" : "N");
+ writer.print(" lastStackTaskActiveTime="); writer.print(lastStackActiveTime);
+ writer.print(" currentTime="); writer.print(System.currentTimeMillis());
writer.print(" [0x"); writer.print(id); writer.print("]");
writer.println();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 7e1deec3429a..f9e59e7ccf99 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -609,7 +609,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
stackLayout.setSystemInsets(systemInsets);
if (stack != null) {
stackLayout.getTaskStackBounds(displayRect, windowRect, systemInsets.top,
- systemInsets.right, mTaskStackBounds);
+ systemInsets.left, systemInsets.right, mTaskStackBounds);
stackLayout.reset();
stackLayout.initialize(displayRect, windowRect, mTaskStackBounds,
TaskStackLayoutAlgorithm.StackState.getStackStateForStack(stack));
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 26200d0fac47..b5753ba39a43 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -511,8 +511,8 @@ public class TaskStack {
int top = dockArea.bottom < 1f
? 0
: insets.top;
- layoutAlgorithm.getTaskStackBounds(displayRect, windowRectOut, top, insets.right,
- taskStackBounds);
+ layoutAlgorithm.getTaskStackBounds(displayRect, windowRectOut, top, insets.left,
+ insets.right, taskStackBounds);
return taskStackBounds;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
index e3fe1abf8b20..89789bce6cf4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -1058,9 +1058,9 @@ public class TaskStackLayoutAlgorithm {
* top and right system insets (but not the bottom inset) and left/right paddings, but _not_
* the top/bottom padding or insets.
*/
- public void getTaskStackBounds(Rect displayRect, Rect windowRect, int topInset, int rightInset,
- Rect taskStackBounds) {
- taskStackBounds.set(windowRect.left, windowRect.top + topInset,
+ public void getTaskStackBounds(Rect displayRect, Rect windowRect, int topInset, int leftInset,
+ int rightInset, Rect taskStackBounds) {
+ taskStackBounds.set(windowRect.left + leftInset, windowRect.top + topInset,
windowRect.right - rightInset, windowRect.bottom);
// Ensure that the new width is at most the smaller display edge size
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 586a8bcd8920..21780a6e8864 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -1191,7 +1191,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
// bounds have changed. This is because we may get spurious measures while dragging where
// our current stack bounds reflect the target drop region.
mLayoutAlgorithm.getTaskStackBounds(mDisplayRect, new Rect(0, 0, width, height),
- mLayoutAlgorithm.mSystemInsets.top, mLayoutAlgorithm.mSystemInsets.right, mTmpRect);
+ mLayoutAlgorithm.mSystemInsets.top, mLayoutAlgorithm.mSystemInsets.left,
+ mLayoutAlgorithm.mSystemInsets.right, mTmpRect);
if (!mTmpRect.equals(mStableStackBounds)) {
mStableStackBounds.set(mTmpRect);
mStackBounds.set(mTmpRect);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 42f398d8d83f..93ed1398c2a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.phone;
import android.content.Context;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.TypedValue;
@@ -59,6 +60,7 @@ public class KeyguardStatusBarView extends RelativeLayout
private UserSwitcherController mUserSwitcherController;
private int mSystemIconsSwitcherHiddenExpandedMargin;
+ private int mSystemIconsBaseMargin;
private View mSystemIconsContainer;
public KeyguardStatusBarView(Context context, AttributeSet attrs) {
@@ -137,8 +139,11 @@ public class KeyguardStatusBarView extends RelativeLayout
}
private void loadDimens() {
- mSystemIconsSwitcherHiddenExpandedMargin = getResources().getDimensionPixelSize(
+ Resources res = getResources();
+ mSystemIconsSwitcherHiddenExpandedMargin = res.getDimensionPixelSize(
R.dimen.system_icons_switcher_hidden_expanded_margin);
+ mSystemIconsBaseMargin = res.getDimensionPixelSize(
+ R.dimen.system_icons_super_container_avatarless_margin_end);
}
private void updateVisibilities() {
@@ -166,7 +171,13 @@ public class KeyguardStatusBarView extends RelativeLayout
private void updateSystemIconsLayoutParams() {
RelativeLayout.LayoutParams lp =
(LayoutParams) mSystemIconsSuperContainer.getLayoutParams();
- int marginEnd = mKeyguardUserSwitcherShowing ? mSystemIconsSwitcherHiddenExpandedMargin : 0;
+ // If the avatar icon is gone, we need to have some end margin to display the system icons
+ // correctly.
+ int baseMarginEnd = mMultiUserSwitch.getVisibility() == View.GONE
+ ? mSystemIconsBaseMargin
+ : 0;
+ int marginEnd = mKeyguardUserSwitcherShowing ? mSystemIconsSwitcherHiddenExpandedMargin :
+ baseMarginEnd;
if (marginEnd != lp.getMarginEnd()) {
lp.setMarginEnd(marginEnd);
mSystemIconsSuperContainer.setLayoutParams(lp);
@@ -301,6 +312,7 @@ public class KeyguardStatusBarView extends RelativeLayout
mMultiUserSwitch.setAlpha(1f);
} else {
updateVisibilities();
+ updateSystemIconsLayoutParams();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
index b7faf15250d1..dd46b085e005 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
@@ -28,6 +28,7 @@ import android.widget.LinearLayout;
import android.widget.Space;
import com.android.systemui.R;
+import com.android.systemui.SystemUIFactory;
import com.android.systemui.statusbar.policy.KeyButtonView;
import com.android.systemui.tuner.TunerService;
@@ -70,8 +71,6 @@ public class NavigationBarInflaterView extends FrameLayout implements TunerServi
private View mLastRot0;
private View mLastRot90;
- private boolean mAlternativeOrder;
-
public NavigationBarInflaterView(Context context, AttributeSet attrs) {
super(context, attrs);
mDensity = context.getResources().getConfiguration().densityDpi;
@@ -115,7 +114,6 @@ public class NavigationBarInflaterView extends FrameLayout implements TunerServi
false);
mRot90.setId(R.id.rot90);
addView(mRot90);
- updateAlternativeOrder();
if (getParent() instanceof NavigationBarView) {
((NavigationBarView) getParent()).updateRotatedViews();
}
@@ -154,20 +152,6 @@ public class NavigationBarInflaterView extends FrameLayout implements TunerServi
}
}
- public void setAlternativeOrder(boolean alternativeOrder) {
- if (alternativeOrder != mAlternativeOrder) {
- mAlternativeOrder = alternativeOrder;
- updateAlternativeOrder();
- }
- }
-
- private void updateAlternativeOrder() {
- ((ReverseLinearLayout) mRot90.findViewById(R.id.ends_group)).setAlternativeOrder(
- mAlternativeOrder);
- ((ReverseLinearLayout) mRot90.findViewById(R.id.center_group)).setAlternativeOrder(
- mAlternativeOrder);
- }
-
private void initiallyFill(ButtonDispatcher buttonDispatcher) {
addAll(buttonDispatcher, (ViewGroup) mRot0.findViewById(R.id.ends_group));
addAll(buttonDispatcher, (ViewGroup) mRot0.findViewById(R.id.center_group));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 23aeae8c5b33..53fe6ce3efa9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -99,8 +99,6 @@ public class NavigationBarView extends LinearLayout {
private final SparseArray<ButtonDispatcher> mButtonDisatchers = new SparseArray<>();
private Configuration mConfiguration;
- private NavigationBarInflaterView mNavigationInflaterView;
-
private class NavTransitionListener implements TransitionListener {
private boolean mBackTransitioning;
private boolean mHomeAppearing;
@@ -474,10 +472,9 @@ public class NavigationBarView extends LinearLayout {
@Override
public void onFinishInflate() {
- mNavigationInflaterView = (NavigationBarInflaterView) findViewById(
- R.id.navigation_inflater);
updateRotatedViews();
- mNavigationInflaterView.setButtonDispatchers(mButtonDisatchers);
+ ((NavigationBarInflaterView) findViewById(R.id.navigation_inflater)).setButtonDispatchers(
+ mButtonDisatchers);
getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener);
@@ -533,7 +530,6 @@ public class NavigationBarView extends LinearLayout {
}
mCurrentView = mRotatedViews[rot];
mCurrentView.setVisibility(View.VISIBLE);
- mNavigationInflaterView.setAlternativeOrder(rot == Surface.ROTATION_90);
for (int i = 0; i < mButtonDisatchers.size(); i++) {
mButtonDisatchers.valueAt(i).setCurrentView(mCurrentView);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ReverseLinearLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ReverseLinearLayout.java
index f45967a0a0a6..3682aa1b06f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ReverseLinearLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ReverseLinearLayout.java
@@ -30,11 +30,7 @@ import java.util.ArrayList;
*/
public class ReverseLinearLayout extends LinearLayout {
- /** If true, the layout is reversed vs. a regular linear layout */
- private boolean mIsLayoutReverse;
-
- /** If true, the layout is opposite to it's natural reversity from the layout direction */
- private boolean mIsAlternativeOrder;
+ private boolean mIsLayoutRtl;
public ReverseLinearLayout(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
@@ -43,50 +39,45 @@ public class ReverseLinearLayout extends LinearLayout {
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- updateOrder();
+ mIsLayoutRtl = getResources().getConfiguration()
+ .getLayoutDirection() == LAYOUT_DIRECTION_RTL;
}
@Override
public void addView(View child) {
reversParams(child.getLayoutParams());
- if (mIsLayoutReverse) {
- super.addView(child, 0);
- } else {
+ if (mIsLayoutRtl) {
super.addView(child);
+ } else {
+ super.addView(child, 0);
}
}
@Override
public void addView(View child, ViewGroup.LayoutParams params) {
reversParams(params);
- if (mIsLayoutReverse) {
- super.addView(child, 0, params);
- } else {
+ if (mIsLayoutRtl) {
super.addView(child, params);
+ } else {
+ super.addView(child, 0, params);
}
}
@Override
- public void onRtlPropertiesChanged(int layoutDirection) {
- super.onRtlPropertiesChanged(layoutDirection);
- updateOrder();
- }
-
- public void setAlternativeOrder(boolean alternative) {
- mIsAlternativeOrder = alternative;
- updateOrder();
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ updateRTLOrder();
}
/**
* In landscape, the LinearLayout is not auto mirrored since it is vertical. Therefore we
* have to do it manually
*/
- private void updateOrder() {
- boolean isLayoutRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
- boolean isLayoutReverse = isLayoutRtl ^ mIsAlternativeOrder;
-
- if (mIsLayoutReverse != isLayoutReverse) {
- // reversity changed, swap the order of all views.
+ private void updateRTLOrder() {
+ boolean isLayoutRtl = getResources().getConfiguration()
+ .getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+ if (mIsLayoutRtl != isLayoutRtl) {
+ // RTL changed, swap the order of all views.
int childCount = getChildCount();
ArrayList<View> childList = new ArrayList<>(childCount);
for (int i = 0; i < childCount; i++) {
@@ -96,7 +87,7 @@ public class ReverseLinearLayout extends LinearLayout {
for (int i = childCount - 1; i >= 0; i--) {
super.addView(childList.get(i));
}
- mIsLayoutReverse = isLayoutReverse;
+ mIsLayoutRtl = isLayoutRtl;
}
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index feceb149ae3f..6defd0f5baef 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -1905,7 +1905,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
SomeArgs args = SomeArgs.obtain();
args.arg1 = widget.host;
args.arg2 = widget.host.callbacks;
- args.arg3 = updateViews;
+ args.arg3 = updateViews.clone();
args.arg4 = requestTime;
args.argi1 = widget.appWidgetId;
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index fb5b3f882efc..b12a9619e506 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -94,6 +94,7 @@ import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
@@ -125,6 +126,7 @@ import com.android.internal.net.VpnProfile;
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.MessageUtils;
+import com.android.internal.util.WakeupMessage;
import com.android.internal.util.XmlUtils;
import com.android.server.am.BatteryStatsService;
import com.android.server.connectivity.DataConnectionStats;
@@ -171,7 +173,7 @@ import java.util.TreeSet;
*/
public class ConnectivityService extends IConnectivityManager.Stub
implements PendingIntent.OnFinished {
- private static final String TAG = "ConnectivityService";
+ private static final String TAG = ConnectivityService.class.getSimpleName();
private static final boolean DBG = true;
private static final boolean VDBG = false;
@@ -191,6 +193,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
// connect anyway?" dialog after the user selects a network that doesn't validate.
private static final int PROMPT_UNVALIDATED_DELAY_MS = 8 * 1000;
+ // Default to 30s linger time-out. Modifiable only for testing.
+ private static final String LINGER_DELAY_PROPERTY = "persist.netmon.linger";
+ private static final int DEFAULT_LINGER_DELAY_MS = 30_000;
+ @VisibleForTesting
+ protected int mLingerDelayMs; // Can't be final, or test subclass constructors can't change it.
+
// How long to delay to removal of a pending intent based request.
// See Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS
private final int mReleasePendingIntentDelayMs;
@@ -239,7 +247,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
private static final int DISABLED = 0;
private static final SparseArray<String> sMagicDecoderRing = MessageUtils.findMessageNames(
- new Class[] { AsyncChannel.class, ConnectivityService.class, NetworkAgent.class });
+ new Class[] { AsyncChannel.class, ConnectivityService.class, NetworkAgent.class,
+ NetworkAgentInfo.class });
private enum ReapUnvalidatedNetworks {
// Tear down networks that have no chance (e.g. even if validated) of becoming
@@ -681,6 +690,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
mReleasePendingIntentDelayMs = Settings.Secure.getInt(context.getContentResolver(),
Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, 5_000);
+ mLingerDelayMs = SystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
+
mContext = checkNotNull(context, "missing Context");
mNetd = checkNotNull(netManager, "missing INetworkManagementService");
mStatsService = checkNotNull(statsService, "missing INetworkStatsService");
@@ -1905,7 +1916,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
pw.println(nai.toString());
pw.increaseIndent();
- pw.println("Requests:");
+ pw.println(String.format("Requests: %d request/%d total",
+ nai.numRequestNetworkRequests(), nai.numNetworkRequests()));
pw.increaseIndent();
for (int i = 0; i < nai.numNetworkRequests(); i++) {
pw.println(nai.requestAt(i).toString());
@@ -1913,7 +1925,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
pw.decreaseIndent();
pw.println("Lingered:");
pw.increaseIndent();
- for (NetworkRequest nr : nai.networkLingered) pw.println(nr.toString());
+ nai.dumpLingerTimers(pw);
pw.decreaseIndent();
pw.decreaseIndent();
}
@@ -2158,13 +2170,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
break;
}
- case NetworkMonitor.EVENT_NETWORK_LINGER_COMPLETE: {
- NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj;
- if (isLiveNetworkAgent(nai, msg.what)) {
- handleLingerComplete(nai);
- }
- break;
- }
case NetworkMonitor.EVENT_PROVISIONING_NOTIFICATION: {
final int netId = msg.arg2;
final boolean visible = (msg.arg1 != 0);
@@ -2197,33 +2202,50 @@ public class ConnectivityService extends IConnectivityManager.Stub
return true;
}
+ private boolean maybeHandleNetworkAgentInfoMessage(Message msg) {
+ switch (msg.what) {
+ default:
+ return false;
+ case NetworkAgentInfo.EVENT_NETWORK_LINGER_COMPLETE: {
+ NetworkAgentInfo nai = (NetworkAgentInfo) msg.obj;
+ if (nai != null && isLiveNetworkAgent(nai, msg.what)) {
+ handleLingerComplete(nai);
+ }
+ break;
+ }
+ }
+ return true;
+ }
+
@Override
public void handleMessage(Message msg) {
- if (!maybeHandleAsyncChannelMessage(msg) && !maybeHandleNetworkMonitorMessage(msg)) {
+ if (!maybeHandleAsyncChannelMessage(msg) &&
+ !maybeHandleNetworkMonitorMessage(msg) &&
+ !maybeHandleNetworkAgentInfoMessage(msg)) {
maybeHandleNetworkAgentMessage(msg);
}
}
}
- private void linger(NetworkAgentInfo nai) {
- nai.lingering = true;
- logNetworkEvent(nai, NetworkEvent.NETWORK_LINGER);
- nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_LINGER);
- notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING);
- }
-
- // Cancel any lingering so the linger timeout doesn't teardown a network.
- // This should be called when a network begins satisfying a NetworkRequest.
- // Note: depending on what state the NetworkMonitor is in (e.g.,
- // if it's awaiting captive portal login, or if validation failed), this
- // may trigger a re-evaluation of the network.
- private void unlinger(NetworkAgentInfo nai) {
- nai.networkLingered.clear();
- if (!nai.lingering) return;
- nai.lingering = false;
- logNetworkEvent(nai, NetworkEvent.NETWORK_UNLINGER);
- if (VDBG) log("Canceling linger of " + nai.name());
- nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
+ private void updateLingerState(NetworkAgentInfo nai, long now) {
+ // 1. Update the linger timer. If it's changed, reschedule or cancel the alarm.
+ // 2. If the network was lingering and there are now requests, unlinger it.
+ // 3. If this network is unneeded (which implies it is not lingering), and there is at least
+ // one lingered request, start lingering.
+ nai.updateLingerTimer();
+ if (nai.isLingering() && nai.numRequestNetworkRequests() > 0) {
+ if (DBG) log("Unlingering " + nai.name());
+ nai.unlinger();
+ logNetworkEvent(nai, NetworkEvent.NETWORK_UNLINGER);
+ } else if (unneeded(nai) && nai.getLingerExpiry() > 0) { // unneeded() calls isLingering()
+ int lingerTime = (int) (nai.getLingerExpiry() - now);
+ if (DBG) {
+ Log.d(TAG, "Lingering " + nai.name() + " for " + lingerTime + "ms");
+ }
+ nai.linger();
+ logNetworkEvent(nai, NetworkEvent.NETWORK_LINGER);
+ notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING, lingerTime);
+ }
}
private void handleAsyncChannelHalfConnect(Message msg) {
@@ -2313,6 +2335,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
sendUpdatedScoreToFactories(request, 0);
}
}
+ nai.clearLingerState();
if (nai.isSatisfyingRequest(mDefaultRequest.requestId)) {
removeDataActivityTracking(nai);
notifyLockdownVpn(nai);
@@ -2400,7 +2423,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
// This is whether it is satisfying any NetworkRequests or were it to become validated,
// would it have a chance of satisfying any NetworkRequests.
private boolean unneeded(NetworkAgentInfo nai) {
- if (!nai.everConnected || nai.isVPN() || nai.lingering) return false;
+ if (!nai.everConnected || nai.isVPN() ||
+ nai.isLingering() || nai.numRequestNetworkRequests() > 0) {
+ return false;
+ }
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
// If this Network is already the highest scoring Network for a request, or if
// there is hope for it to become one if it validated, then it is needed.
@@ -2445,33 +2471,40 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
mNetworkRequestInfoLogs.log("RELEASE " + nri);
if (nri.request.isRequest()) {
+ boolean wasKept = false;
+ NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId);
+ if (nai != null) {
+ nai.removeRequest(nri.request.requestId);
+ if (VDBG) {
+ log(" Removing from current network " + nai.name() +
+ ", leaving " + nai.numNetworkRequests() + " requests.");
+ }
+ // If there are still lingered requests on this network, don't tear it down,
+ // but resume lingering instead.
+ updateLingerState(nai, SystemClock.elapsedRealtime());
+ if (unneeded(nai)) {
+ if (DBG) log("no live requests for " + nai.name() + "; disconnecting");
+ teardownUnneededNetwork(nai);
+ } else {
+ wasKept = true;
+ }
+ mNetworkForRequestId.remove(nri.request.requestId);
+ }
+
+ // TODO: remove this code once we know that the Slog.wtf is never hit.
+ //
// Find all networks that are satisfying this request and remove the request
// from their request lists.
// TODO - it's my understanding that for a request there is only a single
// network satisfying it, so this loop is wasteful
- boolean wasKept = false;
- for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
- if (nai.isSatisfyingRequest(nri.request.requestId)) {
- nai.removeRequest(nri.request.requestId);
- if (VDBG) {
- log(" Removing from current network " + nai.name() +
- ", leaving " + nai.numNetworkRequests() + " requests.");
- }
- if (unneeded(nai)) {
- if (DBG) log("no live requests for " + nai.name() + "; disconnecting");
- teardownUnneededNetwork(nai);
- } else {
- // suspect there should only be one pass through here
- // but if any were kept do the check below
- wasKept |= true;
- }
+ for (NetworkAgentInfo otherNai : mNetworkAgentInfos.values()) {
+ if (otherNai.isSatisfyingRequest(nri.request.requestId) && otherNai != nai) {
+ Slog.wtf(TAG, "Request " + nri.request + " satisfied by " +
+ otherNai.name() + ", but mNetworkAgentInfos says " +
+ (nai != null ? nai.name() : "null"));
}
}
- NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId);
- if (nai != null) {
- mNetworkForRequestId.remove(nri.request.requestId);
- }
// Maintain the illusion. When this request arrived, we might have pretended
// that a network connected to serve it, even though the network was already
// connected. Now that this request has gone away, we might have to pretend
@@ -2512,7 +2545,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
}
- callCallbackForRequest(nri, null, ConnectivityManager.CALLBACK_RELEASED);
+ callCallbackForRequest(nri, null, ConnectivityManager.CALLBACK_RELEASED, 0);
}
}
@@ -4499,7 +4532,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
private void callCallbackForRequest(NetworkRequestInfo nri,
- NetworkAgentInfo networkAgent, int notificationType) {
+ NetworkAgentInfo networkAgent, int notificationType, int arg1) {
if (nri.messenger == null) return; // Default request has no msgr
Bundle bundle = new Bundle();
bundle.putParcelable(NetworkRequest.class.getSimpleName(),
@@ -4511,7 +4544,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
switch (notificationType) {
case ConnectivityManager.CALLBACK_LOSING: {
- msg.arg1 = 30 * 1000; // TODO - read this from NetworkMonitor
+ msg.arg1 = arg1;
break;
}
case ConnectivityManager.CALLBACK_CAP_CHANGED: {
@@ -4558,7 +4591,14 @@ public class ConnectivityService extends IConnectivityManager.Stub
return;
}
if (DBG) log("handleLingerComplete for " + oldNetwork.name());
- teardownUnneededNetwork(oldNetwork);
+
+ // If we get here it means that the last linger timeout for this network expired. So there
+ // must be no other active linger timers, and we must stop lingering.
+ oldNetwork.clearLingerState();
+
+ if (unneeded(oldNetwork)) {
+ teardownUnneededNetwork(oldNetwork);
+ }
}
private void makeDefault(NetworkAgentInfo newNetwork) {
@@ -4603,7 +4643,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// performed to tear down unvalidated networks that have no chance (i.e. even if
// validated) of becoming the highest scoring network.
private void rematchNetworkAndRequests(NetworkAgentInfo newNetwork,
- ReapUnvalidatedNetworks reapUnvalidatedNetworks) {
+ ReapUnvalidatedNetworks reapUnvalidatedNetworks, long now) {
if (!newNetwork.everConnected) return;
boolean keep = newNetwork.isVPN();
boolean isNewDefault = false;
@@ -4649,12 +4689,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (currentNetwork != null) {
if (VDBG) log(" accepting network in place of " + currentNetwork.name());
currentNetwork.removeRequest(nri.request.requestId);
- currentNetwork.networkLingered.add(nri.request);
+ currentNetwork.lingerRequest(nri.request, now, mLingerDelayMs);
affectedNetworks.add(currentNetwork);
} else {
if (VDBG) log(" accepting network in place of null");
}
- unlinger(newNetwork);
+ newNetwork.unlingerRequest(nri.request);
mNetworkForRequestId.put(nri.request.requestId, newNetwork);
if (!newNetwork.addRequest(nri.request)) {
Slog.wtf(TAG, "BUG: " + newNetwork.name() + " already has " + nri.request);
@@ -4702,23 +4742,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// a) be requested and b) change is NET_CAPABILITY_TRUSTED,
// so this code is only incorrect for a network that loses
// the TRUSTED capability, which is a rare case.
- callCallbackForRequest(nri, newNetwork, ConnectivityManager.CALLBACK_LOST);
- }
- }
- // Linger any networks that are no longer needed.
- for (NetworkAgentInfo nai : affectedNetworks) {
- if (nai.lingering) {
- // Already lingered. Nothing to do. This can only happen if "nai" is in
- // "affectedNetworks" twice. The reasoning being that to get added to
- // "affectedNetworks", "nai" must have been satisfying a NetworkRequest
- // (i.e. not lingered) so it could have only been lingered by this loop.
- // unneeded(nai) will be false and we'll call unlinger() below which would
- // be bad, so handle it here.
- } else if (unneeded(nai)) {
- linger(nai);
- } else {
- // Clear nai.networkLingered we might have added above.
- unlinger(nai);
+ callCallbackForRequest(nri, newNetwork, ConnectivityManager.CALLBACK_LOST, 0);
}
}
if (isNewDefault) {
@@ -4743,6 +4767,15 @@ public class ConnectivityService extends IConnectivityManager.Stub
// before LegacyTypeTracker sends legacy broadcasts
for (NetworkRequestInfo nri : addedRequests) notifyNetworkCallback(newNetwork, nri);
+ // Linger any networks that are no longer needed. This should be done after sending the
+ // available callback for newNetwork.
+ for (NetworkAgentInfo nai : affectedNetworks) {
+ updateLingerState(nai, now);
+ }
+ // Possibly unlinger newNetwork. Unlingering a network does not send any callbacks so it
+ // does not need to be done in any particular order.
+ updateLingerState(newNetwork, now);
+
if (isNewDefault) {
// Maintain the illusion: since the legacy API only
// understands one network at a time, we must pretend
@@ -4808,8 +4841,19 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (reapUnvalidatedNetworks == ReapUnvalidatedNetworks.REAP) {
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
if (unneeded(nai)) {
- if (DBG) log("Reaping " + nai.name());
- teardownUnneededNetwork(nai);
+ if (nai.getLingerExpiry() > 0) {
+ // This network has active linger timers and no requests, but is not
+ // lingering. Linger it.
+ //
+ // One way (the only way?) this can happen if this network is unvalidated
+ // and became unneeded due to another network improving its score to the
+ // point where this network will no longer be able to satisfy any requests
+ // even if it validates.
+ updateLingerState(nai, now);
+ } else {
+ if (DBG) log("Reaping " + nai.name());
+ teardownUnneededNetwork(nai);
+ }
}
}
}
@@ -4836,8 +4880,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
// Optimization: Only reprocess "changed" if its score improved. This is safe because it
// can only add more NetworkRequests satisfied by "changed", and this is exactly what
// rematchNetworkAndRequests() handles.
+ final long now = SystemClock.elapsedRealtime();
if (changed != null && oldScore < changed.getCurrentScore()) {
- rematchNetworkAndRequests(changed, ReapUnvalidatedNetworks.REAP);
+ rematchNetworkAndRequests(changed, ReapUnvalidatedNetworks.REAP, now);
} else {
final NetworkAgentInfo[] nais = mNetworkAgentInfos.values().toArray(
new NetworkAgentInfo[mNetworkAgentInfos.size()]);
@@ -4851,7 +4896,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
// is complete could incorrectly teardown a network that hasn't yet been
// rematched.
(nai != nais[nais.length-1]) ? ReapUnvalidatedNetworks.DONT_REAP
- : ReapUnvalidatedNetworks.REAP);
+ : ReapUnvalidatedNetworks.REAP,
+ now);
}
}
}
@@ -4961,7 +5007,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
updateSignalStrengthThresholds(networkAgent, "CONNECT", null);
// Consider network even though it is not yet validated.
- rematchNetworkAndRequests(networkAgent, ReapUnvalidatedNetworks.REAP);
+ final long now = SystemClock.elapsedRealtime();
+ rematchNetworkAndRequests(networkAgent, ReapUnvalidatedNetworks.REAP, now);
// This has to happen after matching the requests, because callbacks are just requests.
notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_PRECHECK);
@@ -5009,14 +5056,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
// notify only this one new request of the current state
protected void notifyNetworkCallback(NetworkAgentInfo nai, NetworkRequestInfo nri) {
int notifyType = ConnectivityManager.CALLBACK_AVAILABLE;
- // TODO - read state from monitor to decide what to send.
-// if (nai.networkMonitor.isLingering()) {
-// notifyType = NetworkCallbacks.LOSING;
-// } else if (nai.networkMonitor.isEvaluating()) {
-// notifyType = NetworkCallbacks.callCallbackForRequest(request, nai, notifyType);
-// }
if (nri.mPendingIntent == null) {
- callCallbackForRequest(nri, nai, notifyType);
+ callCallbackForRequest(nri, nai, notifyType, 0);
} else {
sendPendingIntentForRequest(nri, nai, notifyType);
}
@@ -5068,20 +5109,24 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
- protected void notifyNetworkCallbacks(NetworkAgentInfo networkAgent, int notifyType) {
+ protected void notifyNetworkCallbacks(NetworkAgentInfo networkAgent, int notifyType, int arg1) {
if (VDBG) log("notifyType " + notifyTypeToName(notifyType) + " for " + networkAgent.name());
for (int i = 0; i < networkAgent.numNetworkRequests(); i++) {
NetworkRequest nr = networkAgent.requestAt(i);
NetworkRequestInfo nri = mNetworkRequests.get(nr);
if (VDBG) log(" sending notification for " + nr);
if (nri.mPendingIntent == null) {
- callCallbackForRequest(nri, networkAgent, notifyType);
+ callCallbackForRequest(nri, networkAgent, notifyType, arg1);
} else {
sendPendingIntentForRequest(nri, networkAgent, notifyType);
}
}
}
+ protected void notifyNetworkCallbacks(NetworkAgentInfo networkAgent, int notifyType) {
+ notifyNetworkCallbacks(networkAgent, notifyType, 0);
+ }
+
private String notifyTypeToName(int notifyType) {
switch (notifyType) {
case ConnectivityManager.CALLBACK_PRECHECK: return "PRECHECK";
@@ -5212,6 +5257,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
return new NetworkMonitor(context, handler, nai, defaultRequest);
}
+ @VisibleForTesting
+ public WakeupMessage makeWakeupMessage(Context c, Handler h, String s, int cmd, Object obj) {
+ return new WakeupMessage(c, h, s, cmd, 0, 0, obj);
+ }
+
private void logDefaultNetworkEvent(NetworkAgentInfo newNai, NetworkAgentInfo prevNai) {
int newNetid = NETID_UNSET;
int prevNetid = NETID_UNSET;
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index d64fe32cca55..8e0114a0815c 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -300,7 +300,8 @@ public class LockSettingsService extends ILockSettings.Stub {
for (int i = 0; i < users.size(); i++) {
UserInfo user = users.get(i);
UserHandle userHandle = user.getUserHandle();
- if (!mUserManager.isUserUnlockingOrUnlocked(userHandle)) {
+ final boolean isSecure = mStorage.hasPassword(user.id) || mStorage.hasPattern(user.id);
+ if (isSecure && !mUserManager.isUserUnlockingOrUnlocked(userHandle)) {
if (!user.isManagedProfile()) {
showEncryptionNotification(userHandle);
} else {
@@ -347,7 +348,7 @@ public class LockSettingsService extends ILockSettings.Stub {
CharSequence detail = r.getText(
com.android.internal.R.string.user_encrypted_detail);
- PendingIntent intent = PendingIntent.getBroadcast(mContext, 0, ACTION_NULL,
+ PendingIntent intent = PendingIntent.getActivity(mContext, 0, ACTION_NULL,
PendingIntent.FLAG_UPDATE_CURRENT);
showEncryptionNotification(user, title, message, detail, intent);
@@ -407,7 +408,9 @@ public class LockSettingsService extends ILockSettings.Stub {
List<UserInfo> profiles = mUserManager.getProfiles(userId);
for (int i = 0; i < profiles.size(); i++) {
UserInfo profile = profiles.get(i);
- if (profile.isManagedProfile()) {
+ final boolean isSecure =
+ mStorage.hasPassword(profile.id) || mStorage.hasPattern(profile.id);
+ if (isSecure && profile.isManagedProfile()) {
UserHandle userHandle = profile.getUserHandle();
if (!mUserManager.isUserUnlockingOrUnlocked(userHandle) &&
!mUserManager.isQuietModeEnabled(userHandle)) {
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index e63f5365bff8..eaf317a46bc1 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -28,6 +28,10 @@ import android.util.EventLog;
import android.util.Slog;
import android.os.Binder;
import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
import android.provider.MediaStore;
import android.system.ErrnoException;
import android.system.Os;
@@ -35,6 +39,7 @@ import android.system.OsConstants;
import android.system.StructStat;
import com.android.internal.app.ResolverActivity;
+import com.android.internal.os.BackgroundThread;
import dalvik.system.VMRuntime;
@@ -64,6 +69,8 @@ public final class PinnerService extends SystemService {
private final long MAX_CAMERA_PIN_SIZE = 50 * (1 << 20); //50MB max
+ private PinnerHandler mPinnerHandler = null;
+
public PinnerService(Context context) {
super(context);
@@ -71,6 +78,7 @@ public final class PinnerService extends SystemService {
mContext = context;
mShouldPinCamera = context.getResources().getBoolean(
com.android.internal.R.bool.config_pinnerCameraApp);
+ mPinnerHandler = new PinnerHandler(BackgroundThread.get().getLooper());
}
@Override
@@ -80,22 +88,8 @@ public final class PinnerService extends SystemService {
}
mBinderService = new BinderService();
publishBinderService("pinner", mBinderService);
-
- // Files to pin come from the overlay and can be specified per-device config
- String[] filesToPin = mContext.getResources().getStringArray(
- com.android.internal.R.array.config_defaultPinnerServiceFiles);
- // Continue trying to pin remaining files even if there is a failure
- for (int i = 0; i < filesToPin.length; i++){
- PinnedFile pf = pinFile(filesToPin[i], 0, 0, 0);
- if (pf != null) {
- mPinnedFiles.add(pf);
- if (DEBUG) {
- Slog.i(TAG, "Pinned file = " + pf.mFilename);
- }
- } else {
- Slog.e(TAG, "Failed to pin file = " + filesToPin[i]);
- }
- }
+ mPinnerHandler.sendMessage(
+ mPinnerHandler.obtainMessage(PinnerHandler.PIN_ONSTART_MSG));
}
/**
@@ -106,27 +100,57 @@ public final class PinnerService extends SystemService {
*/
@Override
public void onUnlockUser(int userHandle) {
- handlePin(userHandle);
+ mPinnerHandler.sendMessage(
+ mPinnerHandler.obtainMessage(PinnerHandler.PIN_CAMERA_MSG, userHandle, 0));
}
/**
- * Pin camera on user switch.
- * If more than one user is using the device
- * each user may set a different preference for the camera app.
- * Make sure that user's preference is pinned into memory.
- */
+ * Pin camera on user switch.
+ * If more than one user is using the device
+ * each user may set a different preference for the camera app.
+ * Make sure that user's preference is pinned into memory.
+ */
@Override
public void onSwitchUser(int userHandle) {
- handlePin(userHandle);
+ mPinnerHandler.sendMessage(
+ mPinnerHandler.obtainMessage(PinnerHandler.PIN_CAMERA_MSG, userHandle, 0));
}
- private void handlePin(int userHandle) {
+ /**
+ * Handler for on start pinning message
+ */
+ private void handlePinOnStart() {
+ // Files to pin come from the overlay and can be specified per-device config
+ String[] filesToPin = mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_defaultPinnerServiceFiles);
+ synchronized(this) {
+ // Continue trying to pin remaining files even if there is a failure
+ for (int i = 0; i < filesToPin.length; i++){
+ PinnedFile pf = pinFile(filesToPin[i], 0, 0, 0);
+ if (pf != null) {
+ mPinnedFiles.add(pf);
+ if (DEBUG) {
+ Slog.i(TAG, "Pinned file = " + pf.mFilename);
+ }
+ } else {
+ Slog.e(TAG, "Failed to pin file = " + filesToPin[i]);
+ }
+ }
+ }
+ }
+
+ /**
+ * Handler for camera pinning message
+ */
+ private void handlePinCamera(int userHandle) {
if (mShouldPinCamera) {
- boolean success = pinCamera(userHandle);
- if (!success) {
- //this is not necessarily an error
- if (DEBUG) {
- Slog.v(TAG, "Failed to pin camera.");
+ synchronized(this) {
+ boolean success = pinCamera(userHandle);
+ if (!success) {
+ //this is not necessarily an error
+ if (DEBUG) {
+ Slog.v(TAG, "Failed to pin camera.");
+ }
}
}
}
@@ -316,11 +340,13 @@ public final class PinnerService extends SystemService {
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
pw.println("Pinned Files:");
- for (int i = 0; i < mPinnedFiles.size(); i++) {
- pw.println(mPinnedFiles.get(i).mFilename);
- }
- for (int i = 0; i < mPinnedCameraFiles.size(); i++) {
- pw.println(mPinnedCameraFiles.get(i).mFilename);
+ synchronized(this) {
+ for (int i = 0; i < mPinnedFiles.size(); i++) {
+ pw.println(mPinnedFiles.get(i).mFilename);
+ }
+ for (int i = 0; i < mPinnedCameraFiles.size(); i++) {
+ pw.println(mPinnedCameraFiles.get(i).mFilename);
+ }
}
}
}
@@ -336,4 +362,35 @@ public final class PinnerService extends SystemService {
mFilename = filename;
}
}
+
+ final class PinnerHandler extends Handler {
+ static final int PIN_CAMERA_MSG = 4000;
+ static final int PIN_ONSTART_MSG = 4001;
+
+ public PinnerHandler(Looper looper) {
+ super(looper, null, true);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+
+ case PIN_CAMERA_MSG:
+ {
+ handlePinCamera(msg.arg1);
+ }
+ break;
+
+ case PIN_ONSTART_MSG:
+ {
+ handlePinOnStart();
+ }
+ break;
+
+ default:
+ super.handleMessage(msg);
+ }
+ }
+ }
+
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e5579e29c208..5bfb90f9bb57 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -569,6 +569,9 @@ public final class ActivityManagerService extends ActivityManagerNative
private boolean mShowDialogs = true;
private boolean mInVrMode = false;
+ // Whether we should use SCHED_FIFO for UI and RenderThreads.
+ private boolean mUseFifoUiScheduling = false;
+
BroadcastQueue mFgBroadcastQueue;
BroadcastQueue mBgBroadcastQueue;
// Convenient for easy iteration over the queues. Foreground is first
@@ -2657,6 +2660,10 @@ public final class ActivityManagerService extends ActivityManagerNative
GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
+ if (SystemProperties.getInt("sys.use_fifo_ui", 0) != 0) {
+ mUseFifoUiScheduling = true;
+ }
+
mTrackingAssociations = "1".equals(SystemProperties.get("debug.track-associations"));
mConfiguration.setToDefaults();
@@ -6661,8 +6668,7 @@ public final class ActivityManagerService extends ActivityManagerNative
@Override
public void showBootMessage(final CharSequence msg, final boolean always) {
if (Binder.getCallingUid() != Process.myUid()) {
- // These days only the core system can call this, so apps can't get in
- // the way of what we show about running them.
+ throw new SecurityException();
}
mWindowManager.showBootMessage(msg, always);
}
@@ -12533,6 +12539,10 @@ public final class ActivityManagerService extends ActivityManagerNative
final int pid = Binder.getCallingPid();
proc = mPidsSelfLocked.get(pid);
if (proc != null && mInVrMode && tid >= 0) {
+ // ensure the tid belongs to the process
+ if (!Process.isThreadInProcess(pid, tid)) {
+ throw new IllegalArgumentException("VR thread does not belong to process");
+ }
// reset existing VR thread to CFS
if (proc.vrThreadTid != 0) {
Process.setThreadScheduler(proc.vrThreadTid, Process.SCHED_OTHER, 0);
@@ -12551,6 +12561,40 @@ public final class ActivityManagerService extends ActivityManagerNative
}
@Override
+ public void setRenderThread(int tid) {
+ synchronized (this) {
+ ProcessRecord proc;
+ synchronized (mPidsSelfLocked) {
+ int pid = Binder.getCallingPid();
+ proc = mPidsSelfLocked.get(pid);
+ if (mUseFifoUiScheduling && proc != null && proc.renderThreadTid == 0 && tid > 0) {
+ // ensure the tid belongs to the process
+ if (!Process.isThreadInProcess(pid, tid)) {
+ throw new IllegalArgumentException(
+ "Render thread does not belong to process");
+ }
+ proc.renderThreadTid = tid;
+ if (DEBUG_OOM_ADJ) {
+ Slog.d("UI_FIFO", "Set RenderThread tid " + tid + " for pid " + pid);
+ }
+ // promote to FIFO now
+ if (proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
+ if (DEBUG_OOM_ADJ) Slog.d("UI_FIFO", "Promoting " + tid + "out of band");
+ Process.setThreadScheduler(proc.renderThreadTid,
+ Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1);
+ }
+ } else {
+ if (DEBUG_OOM_ADJ) {
+ Slog.d("UI_FIFO", "Didn't set thread from setRenderThread? " +
+ "PID: " + pid + ", TID: " + tid + " FIFO: " +
+ mUseFifoUiScheduling);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
public int setVrMode(IBinder token, boolean enabled, ComponentName packageName) {
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) {
throw new UnsupportedOperationException("VR mode not supported on this device!");
@@ -19548,7 +19592,7 @@ public final class ActivityManagerService extends ActivityManagerNative
adj = ProcessList.FOREGROUND_APP_ADJ;
if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
if ((cr.flags&Context.BIND_IMPORTANT) != 0) {
- schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
+ schedGroup = ProcessList.SCHED_GROUP_TOP_APP_BOUND;
} else {
schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
}
@@ -20165,47 +20209,65 @@ public final class ActivityManagerService extends ActivityManagerNative
processGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
break;
case ProcessList.SCHED_GROUP_TOP_APP:
+ case ProcessList.SCHED_GROUP_TOP_APP_BOUND:
processGroup = Process.THREAD_GROUP_TOP_APP;
break;
default:
processGroup = Process.THREAD_GROUP_DEFAULT;
break;
}
- if (true) {
- long oldId = Binder.clearCallingIdentity();
- try {
- Process.setProcessGroup(app.pid, processGroup);
- if (app.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
- // do nothing if we already switched to RT
- if (oldSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
- // Switch VR thread for app to SCHED_FIFO
- if (mInVrMode && app.vrThreadTid != 0) {
- Process.setThreadScheduler(app.vrThreadTid,
+ long oldId = Binder.clearCallingIdentity();
+ try {
+ Process.setProcessGroup(app.pid, processGroup);
+ if (app.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
+ // do nothing if we already switched to RT
+ if (oldSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
+ // Switch VR thread for app to SCHED_FIFO
+ if (mInVrMode && app.vrThreadTid != 0) {
+ Process.setThreadScheduler(app.vrThreadTid,
+ Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1);
+ }
+ if (mUseFifoUiScheduling) {
+ // Switch UI pipeline for app to SCHED_FIFO
+ app.savedPriority = Process.getThreadPriority(app.pid);
+ Process.setThreadScheduler(app.pid,
+ Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1);
+ if (app.renderThreadTid != 0) {
+ Process.setThreadScheduler(app.renderThreadTid,
Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1);
+ if (DEBUG_OOM_ADJ) {
+ Slog.d("UI_FIFO", "Set RenderThread (TID " +
+ app.renderThreadTid + ") to FIFO");
+ }
+ } else {
+ if (DEBUG_OOM_ADJ) {
+ Slog.d("UI_FIFO", "Not setting RenderThread TID");
+ }
}
}
- } else if (oldSchedGroup == ProcessList.SCHED_GROUP_TOP_APP &&
- app.curSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
- // Reset VR thread to SCHED_OTHER
- // Safe to do even if we're not in VR mode
- if (app.vrThreadTid != 0) {
- Process.setThreadScheduler(app.vrThreadTid, Process.SCHED_OTHER, 0);
- }
}
- } catch (Exception e) {
- Slog.w(TAG, "Failed setting process group of " + app.pid
- + " to " + app.curSchedGroup);
- e.printStackTrace();
- } finally {
- Binder.restoreCallingIdentity(oldId);
- }
- } else {
- if (app.thread != null) {
- try {
- app.thread.setSchedulingGroup(processGroup);
- } catch (RemoteException e) {
+ } else if (oldSchedGroup == ProcessList.SCHED_GROUP_TOP_APP &&
+ app.curSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
+ // Reset VR thread to SCHED_OTHER
+ // Safe to do even if we're not in VR mode
+ if (app.vrThreadTid != 0) {
+ Process.setThreadScheduler(app.vrThreadTid, Process.SCHED_OTHER, 0);
+ }
+ if (mUseFifoUiScheduling) {
+ // Reset UI pipeline to SCHED_OTHER
+ Process.setThreadScheduler(app.pid, Process.SCHED_OTHER, 0);
+ Process.setThreadScheduler(app.renderThreadTid,
+ Process.SCHED_OTHER, 0);
+ Process.setThreadPriority(app.pid, app.savedPriority);
+ Process.setThreadPriority(app.renderThreadTid, -4);
}
}
+ } catch (Exception e) {
+ Slog.w(TAG, "Failed setting process group of " + app.pid
+ + " to " + app.curSchedGroup);
+ e.printStackTrace();
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
}
}
}
diff --git a/services/core/java/com/android/server/am/PreBootBroadcaster.java b/services/core/java/com/android/server/am/PreBootBroadcaster.java
index 3ed3d9a29c69..a7d09d0882bf 100644
--- a/services/core/java/com/android/server/am/PreBootBroadcaster.java
+++ b/services/core/java/com/android/server/am/PreBootBroadcaster.java
@@ -57,7 +57,7 @@ public abstract class PreBootBroadcaster extends IIntentReceiver.Stub {
private int mIndex = 0;
public PreBootBroadcaster(ActivityManagerService service, int userId,
- ProgressReporter progress) {
+ ProgressReporter progress, boolean quiet) {
mService = service;
mUserId = userId;
mProgress = progress;
@@ -68,7 +68,9 @@ public abstract class PreBootBroadcaster extends IIntentReceiver.Stub {
mTargets = mService.mContext.getPackageManager().queryBroadcastReceiversAsUser(mIntent,
MATCH_SYSTEM_ONLY, UserHandle.of(userId));
- mHandler.obtainMessage(MSG_SHOW).sendToTarget();
+ if (!quiet) {
+ mHandler.obtainMessage(MSG_SHOW).sendToTarget();
+ }
}
public void sendNext() {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index f073e5cd4fda..475b155d8d21 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -130,6 +130,9 @@ final class ProcessList {
static final int SCHED_GROUP_DEFAULT = 1;
// Activity manager's version of Process.THREAD_GROUP_TOP_APP
static final int SCHED_GROUP_TOP_APP = 2;
+ // Activity manager's version of Process.THREAD_GROUP_TOP_APP
+ // Disambiguate between actual top app and processes bound to the top app
+ static final int SCHED_GROUP_TOP_APP_BOUND = 3;
// The minimum number of cached apps we want to be able to keep around,
// without empty apps being able to push them out of memory.
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 0f7c89dc9506..dad383dff22f 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -103,6 +103,8 @@ final class ProcessRecord {
int repProcState = PROCESS_STATE_NONEXISTENT; // Last reported process state
int setProcState = PROCESS_STATE_NONEXISTENT; // Last set process state in process tracker
int pssProcState = PROCESS_STATE_NONEXISTENT; // Currently requesting pss for
+ int savedPriority; // Previous priority value if we're switching to non-SCHED_OTHER
+ int renderThreadTid; // TID for RenderThread
boolean serviceb; // Process currently is on the service B list
boolean serviceHighRam; // We are forcing to service B list due to its RAM use
boolean setIsForeground; // Running foreground UI when last set?
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index d25f2cb76d74..4de09bd5318e 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -351,7 +351,11 @@ final class UserController {
// PRE_BOOT receivers are finished to avoid ANR'ing apps
final UserInfo info = getUserInfo(userId);
if (!Objects.equals(info.lastLoggedInFingerprint, Build.FINGERPRINT)) {
- new PreBootBroadcaster(mService, userId, null) {
+ // Suppress double notifications for managed profiles that
+ // were unlocked automatically (no challenge token required)
+ // as part of their parent user being unlocked.
+ final boolean quiet = info.isManagedProfile() && !uss.tokenProvided;
+ new PreBootBroadcaster(mService, userId, null, quiet) {
@Override
public void onFinished() {
finishUserUnlockedCompleted(uss);
@@ -972,6 +976,7 @@ final class UserController {
return false;
} else {
uss.mUnlockProgress.addListener(listener);
+ uss.tokenProvided = (token != null);
}
finishUserUnlocking(uss);
diff --git a/services/core/java/com/android/server/am/UserState.java b/services/core/java/com/android/server/am/UserState.java
index 952283eb7290..ff8014cbb985 100644
--- a/services/core/java/com/android/server/am/UserState.java
+++ b/services/core/java/com/android/server/am/UserState.java
@@ -54,6 +54,7 @@ public final class UserState {
public int state = STATE_BOOTING;
public int lastState = STATE_BOOTING;
public boolean switching;
+ public boolean tokenProvided;
/**
* The last time that a provider was reported to usage stats as being brought to important
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 15b872d1b332..7a25df68599f 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -28,14 +28,21 @@ import android.net.NetworkRequest;
import android.net.NetworkState;
import android.os.Handler;
import android.os.Messenger;
+import android.os.SystemClock;
+import android.util.Log;
import android.util.SparseArray;
import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.WakeupMessage;
import com.android.server.ConnectivityService;
import com.android.server.connectivity.NetworkMonitor;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Comparator;
+import java.util.Objects;
+import java.util.SortedSet;
+import java.util.TreeSet;
/**
* A bag class used by ConnectivityService for holding a collection of most recent
@@ -143,12 +150,69 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
// Whether a captive portal was found during the last network validation attempt.
public boolean lastCaptivePortalDetected;
- // Indicates whether the network is lingering. Networks are lingered when they become unneeded
- // as a result of their NetworkRequests being satisfied by a different network, so as to allow
- // communication to wrap up before the network is taken down. This usually only happens to the
- // default network. Lingering ends with either the linger timeout expiring and the network
- // being taken down, or the network satisfying a request again.
- public boolean lingering;
+ // Networks are lingered when they become unneeded as a result of their NetworkRequests being
+ // satisfied by a higher-scoring network. so as to allow communication to wrap up before the
+ // network is taken down. This usually only happens to the default network. Lingering ends with
+ // either the linger timeout expiring and the network being taken down, or the network
+ // satisfying a request again.
+ public static class LingerTimer implements Comparable<LingerTimer> {
+ public final NetworkRequest request;
+ public final long expiryMs;
+
+ public LingerTimer(NetworkRequest request, long expiryMs) {
+ this.request = request;
+ this.expiryMs = expiryMs;
+ }
+ public boolean equals(Object o) {
+ if (!(o instanceof LingerTimer)) return false;
+ LingerTimer other = (LingerTimer) o;
+ return (request.requestId == other.request.requestId) && (expiryMs == other.expiryMs);
+ }
+ public int hashCode() {
+ return Objects.hash(request.requestId, expiryMs);
+ }
+ public int compareTo(LingerTimer other) {
+ return (expiryMs != other.expiryMs) ?
+ Long.compare(expiryMs, other.expiryMs) :
+ Integer.compare(request.requestId, other.request.requestId);
+ }
+ public String toString() {
+ return String.format("%s, expires %dms", request.toString(),
+ expiryMs - SystemClock.elapsedRealtime());
+ }
+ }
+
+ /**
+ * Inform ConnectivityService that the network LINGER period has
+ * expired.
+ * obj = this NetworkAgentInfo
+ */
+ public static final int EVENT_NETWORK_LINGER_COMPLETE = 1001;
+
+ // All linger timers for this network, sorted by expiry time. A linger timer is added whenever
+ // a request is moved to a network with a better score, regardless of whether the network is or
+ // was lingering or not.
+ // TODO: determine if we can replace this with a smaller or unsorted data structure. (e.g.,
+ // SparseLongArray) combined with the timestamp of when the last timer is scheduled to fire.
+ private final SortedSet<LingerTimer> mLingerTimers = new TreeSet<>();
+
+ // For fast lookups. Indexes into mLingerTimers by request ID.
+ private final SparseArray<LingerTimer> mLingerTimerForRequest = new SparseArray<>();
+
+ // Linger expiry timer. Armed whenever mLingerTimers is non-empty, regardless of whether the
+ // network is lingering or not. Always set to the expiry of the LingerTimer that expires last.
+ // When the timer fires, all linger state is cleared, and if the network has no requests, it is
+ // torn down.
+ private WakeupMessage mLingerMessage;
+
+ // Linger expiry. Holds the expiry time of the linger timer, or 0 if the timer is not armed.
+ private long mLingerExpiryMs;
+
+ // Whether the network is lingering or not. Must be maintained separately from the above because
+ // it depends on the state of other networks and requests, which only ConnectivityService knows.
+ // (Example: we don't linger a network if it would become the best for a NetworkRequest if it
+ // validated).
+ private boolean mLingering;
// This represents the last score received from the NetworkAgent.
private int currentScore;
@@ -165,8 +229,6 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
private final SparseArray<NetworkRequest> mNetworkRequests = new SparseArray<>();
// The list of NetworkRequests that this Network previously satisfied with the highest
// score. A non-empty list indicates that if this Network was validated it is lingered.
- // NOTE: This list is only used for debugging.
- public final ArrayList<NetworkRequest> networkLingered = new ArrayList<NetworkRequest>();
// How many of the satisfied requests are actual requests and not listens.
private int mNumRequestNetworkRequests = 0;
@@ -176,6 +238,12 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
// Used by ConnectivityService to keep track of 464xlat.
public Nat464Xlat clatd;
+ private static final String TAG = ConnectivityService.class.getSimpleName();
+ private static final boolean VDBG = false;
+ private final ConnectivityService mConnService;
+ private final Context mContext;
+ private final Handler mHandler;
+
public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info,
LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler,
NetworkMisc misc, NetworkRequest defaultRequest, ConnectivityService connService) {
@@ -186,7 +254,10 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
linkProperties = lp;
networkCapabilities = nc;
currentScore = score;
- networkMonitor = connService.createNetworkMonitor(context, handler, this, defaultRequest);
+ mConnService = connService;
+ mContext = context;
+ mHandler = handler;
+ networkMonitor = mConnService.createNetworkMonitor(context, handler, this, defaultRequest);
networkMisc = misc;
}
@@ -213,8 +284,12 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
*/
public void removeRequest(int requestId) {
NetworkRequest existing = mNetworkRequests.get(requestId);
- if (existing != null && existing.isRequest()) mNumRequestNetworkRequests--;
+ if (existing == null) return;
mNetworkRequests.remove(requestId);
+ if (existing.isRequest()) {
+ mNumRequestNetworkRequests--;
+ unlingerRequest(existing);
+ }
}
/**
@@ -316,13 +391,100 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
}
}
+ /**
+ * Sets the specified request to linger on this network for the specified time. Called by
+ * ConnectivityService when the request is moved to another network with a higher score.
+ */
+ public void lingerRequest(NetworkRequest request, long now, long duration) {
+ if (mLingerTimerForRequest.get(request.requestId) != null) {
+ // Cannot happen. Once a request is lingering on a particular network, we cannot
+ // re-linger it unless that network becomes the best for that request again, in which
+ // case we should have unlingered it.
+ Log.wtf(TAG, this.name() + ": request " + request.requestId + " already lingered");
+ }
+ final long expiryMs = now + duration;
+ LingerTimer timer = new LingerTimer(request, expiryMs);
+ if (VDBG) Log.d(TAG, "Adding LingerTimer " + timer + " to " + this.name());
+ mLingerTimers.add(timer);
+ mLingerTimerForRequest.put(request.requestId, timer);
+ }
+
+ /**
+ * Cancel lingering. Called by ConnectivityService when a request is added to this network.
+ */
+ public void unlingerRequest(NetworkRequest request) {
+ LingerTimer timer = mLingerTimerForRequest.get(request.requestId);
+ if (timer != null) {
+ if (VDBG) Log.d(TAG, "Removing LingerTimer " + timer + " from " + this.name());
+ mLingerTimers.remove(timer);
+ mLingerTimerForRequest.remove(request.requestId);
+ }
+ }
+
+ public long getLingerExpiry() {
+ return mLingerExpiryMs;
+ }
+
+ public void updateLingerTimer() {
+ long newExpiry = mLingerTimers.isEmpty() ? 0 : mLingerTimers.last().expiryMs;
+ if (newExpiry == mLingerExpiryMs) return;
+
+ // Even if we're going to reschedule the timer, cancel it first. This is because the
+ // semantics of WakeupMessage guarantee that if cancel is called then the alarm will
+ // never call its callback (handleLingerComplete), even if it has already fired.
+ // WakeupMessage makes no such guarantees about rescheduling a message, so if mLingerMessage
+ // has already been dispatched, rescheduling to some time in the future it won't stop it
+ // from calling its callback immediately.
+ if (mLingerMessage != null) {
+ mLingerMessage.cancel();
+ mLingerMessage = null;
+ }
+
+ if (newExpiry > 0) {
+ mLingerMessage = mConnService.makeWakeupMessage(
+ mContext, mHandler,
+ "NETWORK_LINGER_COMPLETE." + network.netId,
+ EVENT_NETWORK_LINGER_COMPLETE, this);
+ mLingerMessage.schedule(newExpiry);
+ }
+
+ mLingerExpiryMs = newExpiry;
+ }
+
+ public void linger() {
+ mLingering = true;
+ }
+
+ public void unlinger() {
+ mLingering = false;
+ }
+
+ public boolean isLingering() {
+ return mLingering;
+ }
+
+ public void clearLingerState() {
+ if (mLingerMessage != null) {
+ mLingerMessage.cancel();
+ mLingerMessage = null;
+ }
+ mLingerTimers.clear();
+ mLingerTimerForRequest.clear();
+ updateLingerTimer(); // Sets mLingerExpiryMs, cancels and nulls out mLingerMessage.
+ mLingering = false;
+ }
+
+ public void dumpLingerTimers(PrintWriter pw) {
+ for (LingerTimer timer : mLingerTimers) { pw.println(timer); }
+ }
+
public String toString() {
return "NetworkAgentInfo{ ni{" + networkInfo + "} " +
"network{" + network + "} nethandle{" + network.getNetworkHandle() + "} " +
"lp{" + linkProperties + "} " +
"nc{" + networkCapabilities + "} Score{" + getCurrentScore() + "} " +
"everValidated{" + everValidated + "} lastValidated{" + lastValidated + "} " +
- "created{" + created + "} lingering{" + lingering + "} " +
+ "created{" + created + "} lingering{" + isLingering() + "} " +
"explicitlySelected{" + networkMisc.explicitlySelected + "} " +
"acceptUnvalidated{" + networkMisc.acceptUnvalidated + "} " +
"everCaptivePortalDetected{" + everCaptivePortalDetected + "} " +
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index eeddff53e8ce..92c4577f3e0c 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -132,31 +132,6 @@ public class NetworkMonitor extends StateMachine {
public static final int EVENT_NETWORK_TESTED = BASE + 2;
/**
- * Inform NetworkMonitor to linger a network. The Monitor should
- * start a timer and/or start watching for zero live connections while
- * moving towards LINGER_COMPLETE. After the Linger period expires
- * (or other events mark the end of the linger state) the LINGER_COMPLETE
- * event should be sent and the network will be shut down. If a
- * CMD_NETWORK_CONNECTED happens before the LINGER completes
- * it indicates further desire to keep the network alive and so
- * the LINGER is aborted.
- */
- public static final int CMD_NETWORK_LINGER = BASE + 3;
-
- /**
- * Message to self indicating linger delay has expired.
- * arg1 = Token to ignore old messages.
- */
- private static final int CMD_LINGER_EXPIRED = BASE + 4;
-
- /**
- * Inform ConnectivityService that the network LINGER period has
- * expired.
- * obj = NetworkAgentInfo
- */
- public static final int EVENT_NETWORK_LINGER_COMPLETE = BASE + 5;
-
- /**
* Message to self indicating it's time to evaluate a network's connectivity.
* arg1 = Token to ignore old messages.
*/
@@ -204,12 +179,6 @@ public class NetworkMonitor extends StateMachine {
*/
private static final int CMD_CAPTIVE_PORTAL_RECHECK = BASE + 12;
- private static final String LINGER_DELAY_PROPERTY = "persist.netmon.linger";
- // Default to 30s linger time-out. Modifyable only for testing.
- private static int DEFAULT_LINGER_DELAY_MS = 30000;
- private final int mLingerDelayMs;
- private int mLingerToken = 0;
-
// Start mReevaluateDelayMs at this value and double.
private static final int INITIAL_REEVALUATE_DELAY_MS = 1000;
private static final int MAX_REEVALUATE_DELAY_MS = 10*60*1000;
@@ -248,7 +217,6 @@ public class NetworkMonitor extends StateMachine {
private final State mMaybeNotifyState = new MaybeNotifyState();
private final State mEvaluatingState = new EvaluatingState();
private final State mCaptivePortalState = new CaptivePortalState();
- private final State mLingeringState = new LingeringState();
private CustomIntentReceiver mLaunchCaptivePortalAppBroadcastReceiver = null;
@@ -275,11 +243,8 @@ public class NetworkMonitor extends StateMachine {
addState(mMaybeNotifyState, mDefaultState);
addState(mEvaluatingState, mMaybeNotifyState);
addState(mCaptivePortalState, mMaybeNotifyState);
- addState(mLingeringState, mDefaultState);
setInitialState(mDefaultState);
- mLingerDelayMs = SystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
-
mIsCaptivePortalCheckEnabled = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.CAPTIVE_PORTAL_DETECTION_ENABLED, 1) == 1;
mUseHttps = Settings.Global.getInt(mContext.getContentResolver(),
@@ -308,10 +273,6 @@ public class NetworkMonitor extends StateMachine {
@Override
public boolean processMessage(Message message) {
switch (message.what) {
- case CMD_NETWORK_LINGER:
- log("Lingering");
- transitionTo(mLingeringState);
- return HANDLED;
case CMD_NETWORK_CONNECTED:
logNetworkEvent(NetworkEvent.NETWORK_CONNECTED);
transitionTo(mEvaluatingState);
@@ -617,72 +578,6 @@ public class NetworkMonitor extends StateMachine {
}
}
- // Being in the LingeringState State indicates a Network's validated bit is true and it once
- // was the highest scoring Network satisfying a particular NetworkRequest, but since then
- // another Network satisfied the NetworkRequest with a higher score and hence this Network
- // is "lingered" for a fixed period of time before it is disconnected. This period of time
- // allows apps to wrap up communication and allows for seamless reactivation if the other
- // higher scoring Network happens to disconnect.
- private class LingeringState extends State {
- private static final String ACTION_LINGER_EXPIRED = "android.net.netmon.lingerExpired";
-
- private WakeupMessage mWakeupMessage;
-
- @Override
- public void enter() {
- mEvaluationTimer.reset();
- final String cmdName = ACTION_LINGER_EXPIRED + "." + mNetId;
- mWakeupMessage = makeWakeupMessage(mContext, getHandler(), cmdName, CMD_LINGER_EXPIRED);
- long wakeupTime = SystemClock.elapsedRealtime() + mLingerDelayMs;
- mWakeupMessage.schedule(wakeupTime);
- }
-
- @Override
- public boolean processMessage(Message message) {
- switch (message.what) {
- case CMD_NETWORK_CONNECTED:
- log("Unlingered");
- // If already validated, go straight to validated state.
- if (mNetworkAgentInfo.lastValidated) {
- transitionTo(mValidatedState);
- return HANDLED;
- }
- return NOT_HANDLED;
- case CMD_LINGER_EXPIRED:
- mConnectivityServiceHandler.sendMessage(
- obtainMessage(EVENT_NETWORK_LINGER_COMPLETE, mNetworkAgentInfo));
- return HANDLED;
- case CMD_FORCE_REEVALUATION:
- // Ignore reevaluation attempts when lingering. A reevaluation could result
- // in a transition to the validated state which would abort the linger
- // timeout. Lingering is the result of score assessment; validity is
- // irrelevant.
- return HANDLED;
- case CMD_CAPTIVE_PORTAL_APP_FINISHED:
- // Ignore user network determination as this could abort linger timeout.
- // Networks are only lingered once validated because:
- // - Unvalidated networks are never lingered (see rematchNetworkAndRequests).
- // - Once validated, a Network's validated bit is never cleared.
- // Since networks are only lingered after being validated a user's
- // determination will not change the death sentence that lingering entails:
- // - If the user wants to use the network or bypasses the captive portal,
- // the network's score will not be increased beyond its current value
- // because it is already validated. Without a score increase there is no
- // chance of reactivation (i.e. aborting linger timeout).
- // - If the user does not want the network, lingering will disconnect the
- // network anyhow.
- return HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
-
- @Override
- public void exit() {
- mWakeupMessage.cancel();
- }
- }
-
private static String getCaptivePortalServerUrl(Context context, boolean isHttps) {
String server = Settings.Global.getString(context.getContentResolver(),
Settings.Global.CAPTIVE_PORTAL_SERVER);
@@ -996,20 +891,6 @@ public class NetworkMonitor extends StateMachine {
PERMISSION_ACCESS_NETWORK_CONDITIONS);
}
- // Allow tests to override linger time.
- @VisibleForTesting
- public static void SetDefaultLingerTime(int time_ms) {
- if (Process.myUid() == Process.SYSTEM_UID) {
- throw new SecurityException("SetDefaultLingerTime only for internal testing.");
- }
- DEFAULT_LINGER_DELAY_MS = time_ms;
- }
-
- @VisibleForTesting
- protected WakeupMessage makeWakeupMessage(Context c, Handler h, String s, int i) {
- return new WakeupMessage(c, h, s, i);
- }
-
private void logNetworkEvent(int evtype) {
mMetricsLog.log(new NetworkEvent(mNetId, evtype));
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index acd805591c41..84ebdd15947d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -11947,6 +11947,12 @@ public class PackageManagerService extends IPackageManager.Stub {
return false;
}
+ if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
+ Slog.w(TAG, "Cannot suspend/un-suspend package \"" + packageName
+ + "\": protected package");
+ return false;
+ }
+
return true;
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 4ea8b02d9207..d750cbf17f66 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -38,6 +38,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
@@ -2919,10 +2920,15 @@ public class UserManagerService extends IUserManager.Stub {
com.android.internal.R.string.config_demoModeLauncherComponent);
if (!TextUtils.isEmpty(demoLauncher)) {
ComponentName componentToEnable = ComponentName.unflattenFromString(demoLauncher);
+ String demoLauncherPkg = componentToEnable.getPackageName();
try {
- AppGlobals.getPackageManager().setComponentEnabledSetting(componentToEnable,
+ final IPackageManager iPm = AppGlobals.getPackageManager();
+ iPm.setComponentEnabledSetting(componentToEnable,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED, /* flags= */ 0,
/* userId= */ userId);
+ iPm.setApplicationEnabledSetting(demoLauncherPkg,
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED, /* flags= */ 0,
+ /* userId= */ userId, null);
} catch (RemoteException re) {
// Internal, shouldn't happen
}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 4553f8e07cda..2b581562d680 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -1057,7 +1057,7 @@ public class AppTransition implements Dump {
final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
final int thumbHeightI = mTmpRect.height();
final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
- final int thumbStartX = mTmpRect.left - containingFrame.left;
+ final int thumbStartX = mTmpRect.left - containingFrame.left - contentInsets.left;
final int thumbStartY = mTmpRect.top - containingFrame.top;
switch (thumbTransitState) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c42d4617b179..c9123fd2227f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -4427,6 +4427,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
intent.putExtra(DeviceAdminReceiver.EXTRA_CHOOSE_PRIVATE_KEY_URI, uri);
intent.putExtra(DeviceAdminReceiver.EXTRA_CHOOSE_PRIVATE_KEY_ALIAS, alias);
intent.putExtra(DeviceAdminReceiver.EXTRA_CHOOSE_PRIVATE_KEY_RESPONSE, response);
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
final long id = mInjector.binderClearCallingIdentity();
try {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index fa3479cca5e7..2236caebdc7c 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -727,14 +727,6 @@ public final class SystemServer {
}
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
- try {
- ActivityManagerNative.getDefault().showBootMessage(
- context.getResources().getText(
- com.android.internal.R.string.android_upgrading_starting_apps),
- false);
- } catch (RemoteException e) {
- }
-
if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
if (!disableNonCoreServices) {
traceBeginAndSlog("StartLockSettingsService");
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index 83cfc010b4e2..ffbea9fce279 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -194,6 +194,10 @@ public class DhcpClient extends StateMachine {
private long mDhcpLeaseExpiry;
private DhcpResults mOffer;
+ // Milliseconds SystemClock timestamps used to record transition times to DhcpBoundState.
+ private long mLastInitEnterTime;
+ private long mLastBoundExitTime;
+
// States.
private State mStoppedState = new StoppedState();
private State mDhcpState = new DhcpState();
@@ -498,13 +502,12 @@ public class DhcpClient extends StateMachine {
public void enter() {
if (STATE_DBG) Log.d(TAG, "Entering state " + getName());
mEnterTimeMs = SystemClock.elapsedRealtime();
- // TODO: record time for Init -> Bound and Bound -> Renewing -> Bound
}
@Override
public void exit() {
long durationMs = SystemClock.elapsedRealtime() - mEnterTimeMs;
- mMetricsLog.log(new DhcpClientEvent(mIfaceName, getName(), (int) durationMs));
+ logState(getName(), (int) durationMs);
}
private String messageName(int what) {
@@ -742,6 +745,7 @@ public class DhcpClient extends StateMachine {
public void enter() {
super.enter();
startNewTransaction();
+ mLastInitEnterTime = SystemClock.elapsedRealtime();
}
protected boolean sendPacket() {
@@ -866,6 +870,13 @@ public class DhcpClient extends StateMachine {
}
scheduleLeaseTimers();
+ logTimeToBoundState();
+ }
+
+ @Override
+ public void exit() {
+ super.exit();
+ mLastBoundExitTime = SystemClock.elapsedRealtime();
}
@Override
@@ -883,6 +894,15 @@ public class DhcpClient extends StateMachine {
return NOT_HANDLED;
}
}
+
+ private void logTimeToBoundState() {
+ long now = SystemClock.elapsedRealtime();
+ if (mLastBoundExitTime > mLastInitEnterTime) {
+ logState(DhcpClientEvent.RENEWING_BOUND, (int)(now - mLastBoundExitTime));
+ } else {
+ logState(DhcpClientEvent.INITIAL_BOUND, (int)(now - mLastInitEnterTime));
+ }
+ }
}
abstract class DhcpReacquiringState extends PacketRetransmittingState {
@@ -993,4 +1013,8 @@ public class DhcpClient extends StateMachine {
private void logError(int errorCode) {
mMetricsLog.log(new DhcpErrorEvent(mIfaceName, errorCode));
}
+
+ private void logState(String name, int durationMs) {
+ mMetricsLog.log(new DhcpClientEvent(mIfaceName, name, durationMs));
+ }
}
diff --git a/services/net/java/android/net/ip/IpReachabilityMonitor.java b/services/net/java/android/net/ip/IpReachabilityMonitor.java
index afb644f3546b..c6da3c31fd61 100644
--- a/services/net/java/android/net/ip/IpReachabilityMonitor.java
+++ b/services/net/java/android/net/ip/IpReachabilityMonitor.java
@@ -129,7 +129,6 @@ import java.util.Set;
* state it may be best for the link to disconnect completely and
* reconnect afresh.
*
- *
* @hide
*/
public class IpReachabilityMonitor {
@@ -163,6 +162,8 @@ public class IpReachabilityMonitor {
private int mIpWatchListVersion;
@GuardedBy("mLock")
private boolean mRunning;
+ // Time in milliseconds of the last forced probe request.
+ private volatile long mLastProbeTimeMs;
/**
* Make the kernel perform neighbor reachability detection (IPv4 ARP or IPv6 ND)
@@ -339,7 +340,7 @@ public class IpReachabilityMonitor {
private void handleNeighborLost(String msg) {
InetAddress ip = null;
- ProvisioningChange delta;
+ final ProvisioningChange delta;
synchronized (mLock) {
LinkProperties whatIfLp = new LinkProperties(mLinkProperties);
@@ -368,10 +369,8 @@ public class IpReachabilityMonitor {
// an InetAddress argument.
mCallback.notifyLost(ip, logMsg);
}
- logEvent(IpReachabilityEvent.PROVISIONING_LOST, 0);
- } else {
- logEvent(IpReachabilityEvent.NUD_FAILED, 0);
}
+ logNudFailed(delta);
}
public void probeAll() {
@@ -397,9 +396,10 @@ public class IpReachabilityMonitor {
final int returnValue = probeNeighbor(mInterfaceIndex, target);
logEvent(IpReachabilityEvent.PROBE, returnValue);
}
+ mLastProbeTimeMs = SystemClock.elapsedRealtime();
}
- private long getProbeWakeLockDuration() {
+ private static long getProbeWakeLockDuration() {
// Ideally, this would be computed by examining the values of:
//
// /proc/sys/net/ipv[46]/neigh/<ifname>/ucast_solicit
@@ -416,7 +416,15 @@ public class IpReachabilityMonitor {
}
private void logEvent(int probeType, int errorCode) {
- int eventType = probeType | (errorCode & 0xff );
+ int eventType = probeType | (errorCode & 0xff);
+ mMetricsLog.log(new IpReachabilityEvent(mInterfaceName, eventType));
+ }
+
+ private void logNudFailed(ProvisioningChange delta) {
+ long duration = SystemClock.elapsedRealtime() - mLastProbeTimeMs;
+ boolean isFromProbe = (duration < getProbeWakeLockDuration());
+ boolean isProvisioningLost = (delta == ProvisioningChange.LOST_PROVISIONING);
+ int eventType = IpReachabilityEvent.nudFailureEventType(isFromProbe, isProvisioningLost);
mMetricsLog.log(new IpReachabilityEvent(mInterfaceName, eventType));
}
diff --git a/services/retaildemo/java/com/android/server/retaildemo/PreloadAppsInstaller.java b/services/retaildemo/java/com/android/server/retaildemo/PreloadAppsInstaller.java
new file mode 100644
index 000000000000..848017063bff
--- /dev/null
+++ b/services/retaildemo/java/com/android/server/retaildemo/PreloadAppsInstaller.java
@@ -0,0 +1,125 @@
+/*
+ * 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.server.retaildemo;
+
+import android.app.AppGlobals;
+import android.app.PackageInstallObserver;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * Helper class for installing preloaded APKs
+ */
+class PreloadAppsInstaller {
+ private static final String SYSTEM_SERVER_PACKAGE_NAME = "android";
+ private static String TAG = PreloadAppsInstaller.class.getSimpleName();
+ private static final String PRELOAD_APK_EXT = ".apk.preload";
+ private static boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final IPackageManager mPackageManager;
+ private final File preloadsAppsDirectory;
+
+ private final Map<String, String> mApkToPackageMap;
+
+ PreloadAppsInstaller() {
+ this(AppGlobals.getPackageManager(), Environment.getDataPreloadsAppsDirectory());
+ }
+
+ @VisibleForTesting
+ PreloadAppsInstaller(IPackageManager packageManager, File preloadsAppsDirectory) {
+ mPackageManager = packageManager;
+ mApkToPackageMap = Collections.synchronizedMap(new ArrayMap<>());
+ this.preloadsAppsDirectory = preloadsAppsDirectory;
+ }
+
+ void installApps(int userId) {
+ File[] files = preloadsAppsDirectory.listFiles();
+ if (ArrayUtils.isEmpty(files)) {
+ return;
+ }
+ for (File file : files) {
+ String apkName = file.getName();
+ if (apkName.endsWith(PRELOAD_APK_EXT) && file.isFile()) {
+ String packageName = mApkToPackageMap.get(apkName);
+ if (packageName != null) {
+ try {
+ installExistingPackage(packageName, userId);
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to install existing package " + packageName, e);
+ }
+ } else {
+ try {
+ installPackage(file, userId);
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to install package from " + file, e);
+ }
+ }
+ }
+ }
+ }
+
+ private void installExistingPackage(String packageName, int userId) {
+ if (DEBUG) {
+ Log.d(TAG, "installExistingPackage " + packageName + " u" + userId);
+ }
+ try {
+ mPackageManager.installExistingPackageAsUser(packageName, userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private void installPackage(File file, final int userId) throws IOException, RemoteException {
+ final String apkName = file.getName();
+ if (DEBUG) {
+ Log.d(TAG, "installPackage " + apkName + " u" + userId);
+ }
+ mPackageManager.installPackageAsUser(file.getPath(), new PackageInstallObserver() {
+ @Override
+ public void onPackageInstalled(String basePackageName, int returnCode, String msg,
+ Bundle extras) {
+ if (DEBUG) {
+ Log.d(TAG, "Package " + basePackageName + " installed u" + userId
+ + " returnCode: " + returnCode + " msg: " + msg);
+ }
+ if (returnCode == PackageManager.INSTALL_SUCCEEDED) {
+ mApkToPackageMap.put(apkName, basePackageName);
+ // Install on user 0 so that the package is cached when demo user is re-created
+ installExistingPackage(basePackageName, UserHandle.USER_SYSTEM);
+ } else if (returnCode == PackageManager.INSTALL_FAILED_ALREADY_EXISTS) {
+ installExistingPackage(basePackageName, userId);
+ }
+ }
+ }.getBinder(), 0, SYSTEM_SERVER_PACKAGE_NAME, userId);
+ }
+
+} \ No newline at end of file
diff --git a/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java b/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java
index bcc35b8eeff5..a92498b22171 100644
--- a/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java
+++ b/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java
@@ -115,6 +115,7 @@ public class RetailDemoModeService extends SystemService {
private CameraManager mCameraManager;
private String[] mCameraIdsWithFlash;
private Configuration mSystemUserConfiguration;
+ private PreloadAppsInstaller mPreloadAppsInstaller;
final Object mActivityLock = new Object();
// Whether the newly created demo user has interacted with the screen yet
@@ -278,6 +279,7 @@ public class RetailDemoModeService extends SystemService {
synchronized (mActivityLock) {
mFirstUserActivityTime = mLastUserActivityTime = SystemClock.uptimeMillis();
}
+ mPreloadAppsInstaller = new PreloadAppsInstaller();
}
private Notification createResetNotification() {
@@ -328,6 +330,8 @@ public class RetailDemoModeService extends SystemService {
um.setUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS, true, user);
Settings.Secure.putIntForUser(getContext().getContentResolver(),
Settings.Secure.SKIP_FIRST_USE_HINTS, 1, userInfo.id);
+ Settings.Secure.putIntForUser(getContext().getContentResolver(),
+ Settings.Global.PACKAGE_VERIFIER_ENABLE, 0, userInfo.id);
grantRuntimePermissionToCamera(userInfo.getUserHandle());
}
@@ -499,6 +503,12 @@ public class RetailDemoModeService extends SystemService {
}
MetricsLogger.count(getContext(), DEMO_SESSION_COUNT, 1);
mHandler.removeMessages(MSG_INACTIVITY_TIME_OUT);
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mPreloadAppsInstaller.installApps(userId);
+ }
+ });
}
private RetailDemoModeServiceInternal mLocalService = new RetailDemoModeServiceInternal() {
diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
index d424717fec68..ba77b03c5e73 100644
--- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
@@ -96,6 +96,7 @@ public class ConnectivityServiceTest extends AndroidTestCase {
private static final String TAG = "ConnectivityServiceTest";
private static final int TIMEOUT_MS = 500;
+ private static final int TEST_LINGER_DELAY_MS = 120;
private BroadcastInterceptingContext mServiceContext;
private WrappedConnectivityService mService;
@@ -330,7 +331,8 @@ public class ConnectivityServiceTest extends AndroidTestCase {
* @param validated Indicate if network should pretend to be validated.
*/
public void connect(boolean validated) {
- assertEquals(mNetworkInfo.getDetailedState(), DetailedState.IDLE);
+ assertEquals("MockNetworkAgents can only be connected once",
+ mNetworkInfo.getDetailedState(), DetailedState.IDLE);
assertFalse(mNetworkCapabilities.hasCapability(NET_CAPABILITY_INTERNET));
NetworkCallback callback = null;
@@ -548,6 +550,11 @@ public class ConnectivityServiceTest extends AndroidTestCase {
super(context, handler, cmdName, cmd);
}
+ public FakeWakeupMessage(Context context, Handler handler, String cmdName, int cmd,
+ int arg1, int arg2, Object obj) {
+ super(context, handler, cmdName, cmd, arg1, arg2, obj);
+ }
+
@Override
public void schedule(long when) {
long delayMs = when - SystemClock.elapsedRealtime();
@@ -556,12 +563,13 @@ public class ConnectivityServiceTest extends AndroidTestCase {
fail("Attempting to send msg more than " + UNREASONABLY_LONG_WAIT +
"ms into the future: " + delayMs);
}
- mHandler.sendEmptyMessageDelayed(mCmd, delayMs);
+ Message msg = mHandler.obtainMessage(mCmd, mArg1, mArg2, mObj);
+ mHandler.sendMessageDelayed(msg, delayMs);
}
@Override
public void cancel() {
- mHandler.removeMessages(mCmd);
+ mHandler.removeMessages(mCmd, mObj);
}
@Override
@@ -585,12 +593,6 @@ public class ConnectivityServiceTest extends AndroidTestCase {
protected CaptivePortalProbeResult isCaptivePortal() {
return new CaptivePortalProbeResult(gen204ProbeResult, gen204ProbeRedirectUrl);
}
-
- @Override
- protected WakeupMessage makeWakeupMessage(
- Context context, Handler handler, String cmdName, int cmd) {
- return new FakeWakeupMessage(context, handler, cmdName, cmd);
- }
}
private class WrappedConnectivityService extends ConnectivityService {
@@ -599,6 +601,7 @@ public class ConnectivityServiceTest extends AndroidTestCase {
public WrappedConnectivityService(Context context, INetworkManagementService netManager,
INetworkStatsService statsService, INetworkPolicyManager policyManager) {
super(context, netManager, statsService, policyManager);
+ mLingerDelayMs = TEST_LINGER_DELAY_MS;
}
@Override
@@ -642,6 +645,12 @@ public class ConnectivityServiceTest extends AndroidTestCase {
return monitor;
}
+ @Override
+ public WakeupMessage makeWakeupMessage(
+ Context context, Handler handler, String cmdName, int cmd, Object obj) {
+ return new FakeWakeupMessage(context, handler, cmdName, cmd, 0, 0, obj);
+ }
+
public WrappedNetworkMonitor getLastCreatedWrappedNetworkMonitor() {
return mLastCreatedNetworkMonitor;
}
@@ -686,8 +695,6 @@ public class ConnectivityServiceTest extends AndroidTestCase {
public void setUp() throws Exception {
super.setUp();
- NetworkMonitor.SetDefaultLingerTime(120);
-
// InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not.
// http://b/25897652 .
if (Looper.myLooper() == null) {
@@ -1051,44 +1058,60 @@ public class ConnectivityServiceTest extends AndroidTestCase {
private class CallbackInfo {
public final CallbackState state;
public final Network network;
- public CallbackInfo(CallbackState s, Network n) { state = s; network = n; }
+ public Object arg;
+ public CallbackInfo(CallbackState s, Network n, Object o) {
+ state = s; network = n; arg = o;
+ }
public String toString() { return String.format("%s (%s)", state, network); }
public boolean equals(Object o) {
if (!(o instanceof CallbackInfo)) return false;
+ // Ignore timeMs, since it's unpredictable.
CallbackInfo other = (CallbackInfo) o;
return state == other.state && Objects.equals(network, other.network);
}
}
private final LinkedBlockingQueue<CallbackInfo> mCallbacks = new LinkedBlockingQueue<>();
- private void setLastCallback(CallbackState state, Network network) {
- mCallbacks.offer(new CallbackInfo(state, network));
+ private void setLastCallback(CallbackState state, Network network, Object o) {
+ mCallbacks.offer(new CallbackInfo(state, network, o));
}
public void onAvailable(Network network) {
- setLastCallback(CallbackState.AVAILABLE, network);
+ setLastCallback(CallbackState.AVAILABLE, network, null);
}
public void onLosing(Network network, int maxMsToLive) {
- setLastCallback(CallbackState.LOSING, network);
+ setLastCallback(CallbackState.LOSING, network, maxMsToLive /* autoboxed int */);
}
public void onLost(Network network) {
- setLastCallback(CallbackState.LOST, network);
+ setLastCallback(CallbackState.LOST, network, null);
}
- void expectCallback(CallbackState state, MockNetworkAgent mockAgent) {
+ void expectCallback(CallbackState state, MockNetworkAgent mockAgent, int timeoutMs) {
CallbackInfo expected = new CallbackInfo(
- state,
- (mockAgent != null) ? mockAgent.getNetwork() : null);
+ state, (mockAgent != null) ? mockAgent.getNetwork() : null, 0);
+ CallbackInfo actual;
try {
- assertEquals("Unexpected callback:",
- expected, mCallbacks.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ actual = mCallbacks.poll(timeoutMs, TimeUnit.MILLISECONDS);
+ assertEquals("Unexpected callback:", expected, actual);
} catch (InterruptedException e) {
fail("Did not receive expected " + expected + " after " + TIMEOUT_MS + "ms");
+ actual = null; // Or the compiler can't tell it's never used uninitialized.
+ }
+ if (state == CallbackState.LOSING) {
+ String msg = String.format(
+ "Invalid linger time value %d, must be between %d and %d",
+ actual.arg, 0, TEST_LINGER_DELAY_MS);
+ int maxMsToLive = (Integer) actual.arg;
+ assertTrue(msg, 0 <= maxMsToLive && maxMsToLive <= TEST_LINGER_DELAY_MS);
}
}
+ void expectCallback(CallbackState state, MockNetworkAgent mockAgent) {
+ expectCallback(state, mockAgent, TIMEOUT_MS);
+ }
+
void assertNoCallback() {
mService.waitForIdle();
CallbackInfo c = mCallbacks.peek();
@@ -1249,6 +1272,8 @@ public class ConnectivityServiceTest extends AndroidTestCase {
}
callback.expectCallback(CallbackState.LOSING, oldNetwork);
+ // TODO: should we send an AVAILABLE callback to newNetwork, to indicate that it is no
+ // longer lingering?
defaultCallback.expectCallback(CallbackState.AVAILABLE, newNetwork);
assertEquals(newNetwork.getNetwork(), mCm.getActiveNetwork());
}
@@ -1306,8 +1331,8 @@ public class ConnectivityServiceTest extends AndroidTestCase {
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.adjustScore(50);
mWiFiNetworkAgent.connect(false); // Score: 70
- callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
@@ -1318,24 +1343,24 @@ public class ConnectivityServiceTest extends AndroidTestCase {
defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
- // Bring up wifi, then validate it. In this case we do not linger cell. What happens is that
- // when wifi connects, we don't linger because cell could potentially become the default
- // network if it validated. Then, when wifi validates, we re-evaluate cell, see it has no
- // requests, and tear it down because it's unneeded.
- // TODO: can we linger in this case?
+ // Bring up wifi, then validate it. Previous versions would immediately tear down cell, but
+ // it's arguably correct to linger it, since it was the default network before it validated.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
- callback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+ callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ mCellNetworkAgent.disconnect();
+ callback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+ defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
- // The current code has a bug: if a network is lingering, and we add and then remove a
- // request from it, we forget that the network was lingering and tear it down immediately.
+ // If a network is lingering, and we add and remove a request from it, resume lingering.
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
callback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
@@ -1350,11 +1375,43 @@ public class ConnectivityServiceTest extends AndroidTestCase {
.addTransportType(TRANSPORT_CELLULAR).build();
NetworkCallback noopCallback = new NetworkCallback();
mCm.requestNetwork(cellRequest, noopCallback);
+ // TODO: should this cause an AVAILABLE callback, to indicate that the network is no longer
+ // lingering?
mCm.unregisterNetworkCallback(noopCallback);
- callback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+ callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+
+ // Similar to the above: lingering can start even after the lingered request is removed.
+ // Disconnect wifi and switch to cell.
+ mWiFiNetworkAgent.disconnect();
+ callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+
+ // Cell is now the default network. Pin it with a cell-specific request.
+ noopCallback = new NetworkCallback(); // Can't reuse NetworkCallbacks. http://b/20701525
+ mCm.requestNetwork(cellRequest, noopCallback);
+
+ // Now connect wifi, and expect it to become the default network.
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(true);
+ callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ // The default request is lingering on cell, but nothing happens to cell, and we send no
+ // callbacks for it, because it's kept up by cellRequest.
+ callback.assertNoCallback();
+ // Now unregister cellRequest and expect cell to start lingering.
+ mCm.unregisterNetworkCallback(noopCallback);
+ callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+ // Let linger run its course.
+ callback.assertNoCallback();
+ callback.expectCallback(CallbackState.LOST, mCellNetworkAgent,
+ TEST_LINGER_DELAY_MS /* timeoutMs */);
+
+ // Clean up.
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
mCm.unregisterNetworkCallback(callback);
mCm.unregisterNetworkCallback(defaultCallback);
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 622e46efdf2c..979f160d3c7b 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -20,6 +20,7 @@ import static android.content.Intent.ACTION_UID_REMOVED;
import static android.content.Intent.EXTRA_UID;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.NetworkPolicy.CYCLE_NONE;
import static android.net.NetworkPolicy.LIMIT_DISABLED;
import static android.net.NetworkPolicy.WARNING_DISABLED;
import static android.net.NetworkPolicyManager.POLICY_NONE;
@@ -86,7 +87,9 @@ import org.easymock.EasyMock;
import org.easymock.IAnswer;
import java.io.File;
+import java.util.Calendar;
import java.util.LinkedHashSet;
+import java.util.TimeZone;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
@@ -141,8 +144,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
private static final int PID_2 = 401;
private static final int PID_3 = 402;
- @Override
- public void setUp() throws Exception {
+ public void _setUp() throws Exception {
super.setUp();
setCurrentTimeMillis(TEST_START);
@@ -229,8 +231,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
}
- @Override
- public void tearDown() throws Exception {
+ public void _tearDown() throws Exception {
for (File file : mPolicyDir.listFiles()) {
file.delete();
}
@@ -263,6 +264,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
backgroundChanged.get();
}
+ @Suppress
public void testPidForegroundCombined() throws Exception {
IdleFuture idle;
@@ -310,6 +312,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
assertFalse(mService.isUidForeground(UID_A));
}
+ @Suppress
public void testScreenChangesRules() throws Exception {
Future<Void> future;
@@ -351,6 +354,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
verifyAndReset();
}
+ @Suppress
public void testPolicyNone() throws Exception {
Future<Void> future;
@@ -381,6 +385,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
verifyAndReset();
}
+ @Suppress
public void testPolicyReject() throws Exception {
Future<Void> future;
@@ -412,6 +417,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
verifyAndReset();
}
+ @Suppress
public void testPolicyRejectAddRemove() throws Exception {
Future<Void> future;
@@ -576,6 +582,17 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
computeLastCycleBoundary(parseTime("2013-01-14T15:11:00.000-08:00"), policy));
}
+ public void testLastCycleBoundaryDST() throws Exception {
+ final long currentTime = parseTime("1989-01-02T07:30:00.000");
+ final long expectedCycle = parseTime("1988-12-03T02:00:00.000Z");
+
+ final NetworkPolicy policy = new NetworkPolicy(
+ sTemplateWifi, 3, "America/Argentina/Buenos_Aires", 1024L, 1024L, false);
+ final long actualCycle = computeLastCycleBoundary(currentTime, policy);
+ assertTimeEquals(expectedCycle, actualCycle);
+ }
+
+ @Suppress
public void testNetworkPolicyAppliedCycleLastMonth() throws Exception {
NetworkState[] state = null;
NetworkStats stats = null;
@@ -628,6 +645,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
verifyAndReset();
}
+ @Suppress
public void testUidRemovedPolicyCleared() throws Exception {
Future<Void> future;
@@ -652,6 +670,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
verifyAndReset();
}
+ @Suppress
public void testOverWarningLimitNotification() throws Exception {
NetworkState[] state = null;
NetworkStats stats = null;
@@ -783,6 +802,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
}
}
+ @Suppress
public void testMeteredNetworkWithoutLimit() throws Exception {
NetworkState[] state = null;
NetworkStats stats = null;
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 794a73e0fe35..43d2a1f1156c 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -86,6 +86,9 @@ public class VoiceInteractionManagerService extends SystemService {
final TreeSet<Integer> mLoadedKeyphraseIds;
SoundTriggerInternal mSoundTriggerInternal;
+ private final RemoteCallbackList<IVoiceInteractionSessionListener>
+ mVoiceInteractionSessionListeners = new RemoteCallbackList<>();
+
public VoiceInteractionManagerService(Context context) {
super(context);
mContext = context;
@@ -1043,7 +1046,42 @@ public class VoiceInteractionManagerService extends SystemService {
public void registerVoiceInteractionSessionListener(
IVoiceInteractionSessionListener listener) {
enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE);
- mImpl.registerVoiceInteractionSessionListener(listener);
+ synchronized (this) {
+ mVoiceInteractionSessionListeners.register(listener);
+ }
+ }
+
+ public void onSessionShown() {
+ synchronized (this) {
+ final int size = mVoiceInteractionSessionListeners.beginBroadcast();
+ for (int i = 0; i < size; ++i) {
+ final IVoiceInteractionSessionListener listener =
+ mVoiceInteractionSessionListeners.getBroadcastItem(i);
+ try {
+ listener.onVoiceSessionShown();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error delivering voice interaction open event.", e);
+ }
+ }
+ mVoiceInteractionSessionListeners.finishBroadcast();
+ }
+ }
+
+ public void onSessionHidden() {
+ synchronized (this) {
+ final int size = mVoiceInteractionSessionListeners.beginBroadcast();
+ for (int i = 0; i < size; ++i) {
+ final IVoiceInteractionSessionListener listener =
+ mVoiceInteractionSessionListeners.getBroadcastItem(i);
+ try {
+ listener.onVoiceSessionHidden();
+
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error delivering voice interaction closed event.", e);
+ }
+ }
+ mVoiceInteractionSessionListeners.finishBroadcast();
+ }
}
@Override
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 52e1a9b51c35..a46ccee0b96d 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -30,7 +30,6 @@ import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
-import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
@@ -60,7 +59,7 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
final Context mContext;
final Handler mHandler;
- final Object mLock;
+ final VoiceInteractionManagerService.VoiceInteractionManagerServiceStub mServiceStub;
final int mUser;
final ComponentName mComponent;
final IActivityManager mAm;
@@ -73,16 +72,13 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
VoiceInteractionSessionConnection mActiveSession;
int mDisabledShowContext;
- private final RemoteCallbackList<IVoiceInteractionSessionListener>
- mVoiceInteractionSessionListeners = new RemoteCallbackList<>();
-
final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
String reason = intent.getStringExtra("reason");
if (!CLOSE_REASON_VOICE_INTERACTION.equals(reason) && !"dream".equals(reason)) {
- synchronized (mLock) {
+ synchronized (mServiceStub) {
if (mActiveSession != null && mActiveSession.mSession != null) {
try {
mActiveSession.mSession.closeSystemDialogs();
@@ -98,7 +94,7 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
final ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
- synchronized (mLock) {
+ synchronized (mServiceStub) {
mService = IVoiceInteractionService.Stub.asInterface(service);
try {
mService.ready();
@@ -113,11 +109,12 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
}
};
- VoiceInteractionManagerServiceImpl(Context context, Handler handler, Object lock,
+ VoiceInteractionManagerServiceImpl(Context context, Handler handler,
+ VoiceInteractionManagerService.VoiceInteractionManagerServiceStub stub,
int userHandle, ComponentName service) {
mContext = context;
mHandler = handler;
- mLock = lock;
+ mServiceStub = stub;
mUser = userHandle;
mComponent = service;
mAm = ActivityManagerNative.getDefault();
@@ -153,8 +150,9 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
public boolean showSessionLocked(Bundle args, int flags,
IVoiceInteractionSessionShowCallback showCallback, IBinder activityToken) {
if (mActiveSession == null) {
- mActiveSession = new VoiceInteractionSessionConnection(mLock, mSessionComponentName,
- mUser, mContext, this, mInfo.getServiceInfo().applicationInfo.uid, mHandler);
+ mActiveSession = new VoiceInteractionSessionConnection(mServiceStub,
+ mSessionComponentName, mUser, mContext, this,
+ mInfo.getServiceInfo().applicationInfo.uid, mHandler);
}
List<IBinder> activityTokens = null;
if (activityToken == null) {
@@ -358,52 +356,20 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
}
}
- public void registerVoiceInteractionSessionListener(
- IVoiceInteractionSessionListener listener) {
- synchronized (mLock) {
- mVoiceInteractionSessionListeners.register(listener);
- }
- }
-
@Override
public void sessionConnectionGone(VoiceInteractionSessionConnection connection) {
- synchronized (mLock) {
+ synchronized (mServiceStub) {
finishLocked(connection.mToken, false);
}
}
@Override
public void onSessionShown(VoiceInteractionSessionConnection connection) {
- synchronized (mLock) {
- final int size = mVoiceInteractionSessionListeners.beginBroadcast();
- for (int i = 0; i < size; ++i) {
- final IVoiceInteractionSessionListener listener =
- mVoiceInteractionSessionListeners.getBroadcastItem(i);
- try {
- listener.onVoiceSessionShown();
- } catch (RemoteException e) {
- Slog.e(TAG, "Error delivering voice interaction open event.", e);
- }
- }
- mVoiceInteractionSessionListeners.finishBroadcast();
- }
+ mServiceStub.onSessionShown();
}
@Override
public void onSessionHidden(VoiceInteractionSessionConnection connection) {
- synchronized (mLock) {
- final int size = mVoiceInteractionSessionListeners.beginBroadcast();
- for (int i = 0; i < size; ++i) {
- final IVoiceInteractionSessionListener listener =
- mVoiceInteractionSessionListeners.getBroadcastItem(i);
- try {
- listener.onVoiceSessionHidden();
-
- } catch (RemoteException e) {
- Slog.e(TAG, "Error delivering voice interaction closed event.", e);
- }
- }
- mVoiceInteractionSessionListeners.finishBroadcast();
- }
+ mServiceStub.onSessionHidden();
}
}
diff --git a/telecomm/java/android/telecom/ParcelableCallAnalytics.java b/telecomm/java/android/telecom/ParcelableCallAnalytics.java
index 0ee9babe6659..383d10baeb12 100644
--- a/telecomm/java/android/telecom/ParcelableCallAnalytics.java
+++ b/telecomm/java/android/telecom/ParcelableCallAnalytics.java
@@ -21,6 +21,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import java.util.ArrayList;
+import java.util.LinkedList;
import java.util.List;
/**
@@ -28,6 +29,68 @@ import java.util.List;
*/
@SystemApi
public class ParcelableCallAnalytics implements Parcelable {
+ /** {@hide} */
+ public static final class VideoEvent implements Parcelable {
+ public static final int SEND_LOCAL_SESSION_MODIFY_REQUEST = 0;
+ public static final int SEND_LOCAL_SESSION_MODIFY_RESPONSE = 1;
+ public static final int RECEIVE_REMOTE_SESSION_MODIFY_REQUEST = 2;
+ public static final int RECEIVE_REMOTE_SESSION_MODIFY_RESPONSE = 3;
+
+ public static final Parcelable.Creator<VideoEvent> CREATOR =
+ new Parcelable.Creator<VideoEvent> () {
+
+ @Override
+ public VideoEvent createFromParcel(Parcel in) {
+ return new VideoEvent(in);
+ }
+
+ @Override
+ public VideoEvent[] newArray(int size) {
+ return new VideoEvent[size];
+ }
+ };
+
+ private int mEventName;
+ private long mTimeSinceLastEvent;
+ private int mVideoState;
+
+ public VideoEvent(int eventName, long timeSinceLastEvent, int videoState) {
+ mEventName = eventName;
+ mTimeSinceLastEvent = timeSinceLastEvent;
+ mVideoState = videoState;
+ }
+
+ VideoEvent(Parcel in) {
+ mEventName = in.readInt();
+ mTimeSinceLastEvent = in.readLong();
+ mVideoState = in.readInt();
+ }
+
+ public int getEventName() {
+ return mEventName;
+ }
+
+ public long getTimeSinceLastEvent() {
+ return mTimeSinceLastEvent;
+ }
+
+ public int getVideoState() {
+ return mVideoState;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mEventName);
+ out.writeLong(mTimeSinceLastEvent);
+ out.writeInt(mVideoState);
+ }
+ }
+
public static final class AnalyticsEvent implements Parcelable {
public static final int SET_SELECT_PHONE_ACCOUNT = 0;
public static final int SET_ACTIVE = 1;
@@ -250,6 +313,12 @@ public class ParcelableCallAnalytics implements Parcelable {
// A map from event-pair names to their durations.
private final List<EventTiming> eventTimings;
+ // Whether the call has ever been a video call.
+ private boolean isVideoCall = false;
+
+ // A list of video events that have occurred.
+ private List<VideoEvent> videoEvents;
+
public ParcelableCallAnalytics(long startTimeMillis, long callDurationMillis, int callType,
boolean isAdditionalCall, boolean isInterrupted, int callTechnologies,
int callTerminationCode, boolean isEmergencyCall, String connectionService,
@@ -284,6 +353,9 @@ public class ParcelableCallAnalytics implements Parcelable {
in.readTypedList(analyticsEvents, AnalyticsEvent.CREATOR);
eventTimings = new ArrayList<>();
in.readTypedList(eventTimings, EventTiming.CREATOR);
+ isVideoCall = readByteAsBoolean(in);
+ videoEvents = new LinkedList<>();
+ in.readTypedList(videoEvents, VideoEvent.CREATOR);
}
public void writeToParcel(Parcel out, int flags) {
@@ -299,6 +371,18 @@ public class ParcelableCallAnalytics implements Parcelable {
writeBooleanAsByte(out, isCreatedFromExistingConnection);
out.writeTypedList(analyticsEvents);
out.writeTypedList(eventTimings);
+ writeBooleanAsByte(out, isVideoCall);
+ out.writeTypedList(videoEvents);
+ }
+
+ /** {@hide} */
+ public void setIsVideoCall(boolean isVideoCall) {
+ this.isVideoCall = isVideoCall;
+ }
+
+ /** {@hide} */
+ public void setVideoEvents(List<VideoEvent> videoEvents) {
+ this.videoEvents = videoEvents;
}
public long getStartTimeMillis() {
@@ -349,6 +433,16 @@ public class ParcelableCallAnalytics implements Parcelable {
return eventTimings;
}
+ /** {@hide} */
+ public boolean isVideoCall() {
+ return isVideoCall;
+ }
+
+ /** {@hide} */
+ public List<VideoEvent> getVideoEvents() {
+ return videoEvents;
+ }
+
@Override
public int describeContents() {
return 0;