summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk39
-rw-r--r--core/java/android/app/ActivityThread.java2
-rw-r--r--core/java/android/app/ContextImpl.java42
-rw-r--r--core/java/android/content/ContentResolver.java7
-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/Environment.java57
-rw-r--r--core/java/android/os/UserHandle.java6
-rw-r--r--core/java/android/preference/PreferenceFragment.java10
-rwxr-xr-xcore/java/android/provider/Settings.java12
-rw-r--r--core/java/com/android/internal/app/AssistUtils.java10
-rw-r--r--core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl6
-rw-r--r--core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl29
-rw-r--r--core/java/com/android/internal/util/MimeIconUtils.java230
-rw-r--r--core/res/res/drawable/ic_doc_apk.xml (renamed from packages/DocumentsUI/res/drawable/ic_doc_apk.xml)0
-rw-r--r--core/res/res/drawable/ic_doc_audio.xml (renamed from packages/DocumentsUI/res/drawable/ic_doc_audio.xml)0
-rw-r--r--core/res/res/drawable/ic_doc_certificate.xml (renamed from packages/DocumentsUI/res/drawable/ic_doc_certificate.xml)0
-rw-r--r--core/res/res/drawable/ic_doc_codes.xml (renamed from packages/DocumentsUI/res/drawable/ic_doc_codes.xml)0
-rw-r--r--core/res/res/drawable/ic_doc_compressed.xml (renamed from packages/DocumentsUI/res/drawable/ic_doc_compressed.xml)0
-rw-r--r--core/res/res/drawable/ic_doc_contact.xml (renamed from packages/DocumentsUI/res/drawable/ic_doc_contact.xml)0
-rw-r--r--core/res/res/drawable/ic_doc_document.xml (renamed from packages/DocumentsUI/res/drawable/ic_doc_document.xml)0
-rw-r--r--core/res/res/drawable/ic_doc_event.xml (renamed from packages/DocumentsUI/res/drawable/ic_doc_event.xml)0
-rw-r--r--core/res/res/drawable/ic_doc_excel.xml (renamed from packages/DocumentsUI/res/drawable/ic_doc_excel.xml)0
-rw-r--r--core/res/res/drawable/ic_doc_folder.xml24
-rw-r--r--core/res/res/drawable/ic_doc_font.xml (renamed from packages/DocumentsUI/res/drawable/ic_doc_font.xml)0
-rw-r--r--core/res/res/drawable/ic_doc_generic.xml (renamed from packages/DocumentsUI/res/drawable/ic_doc_generic.xml)0
-rw-r--r--core/res/res/drawable/ic_doc_image.xml (renamed from packages/DocumentsUI/res/drawable/ic_doc_image.xml)0
-rw-r--r--core/res/res/drawable/ic_doc_pdf.xml (renamed from packages/DocumentsUI/res/drawable/ic_doc_pdf.xml)0
-rw-r--r--core/res/res/drawable/ic_doc_powerpoint.xml (renamed from packages/DocumentsUI/res/drawable/ic_doc_powerpoint.xml)0
-rw-r--r--core/res/res/drawable/ic_doc_presentation.xml (renamed from packages/DocumentsUI/res/drawable/ic_doc_presentation.xml)0
-rw-r--r--core/res/res/drawable/ic_doc_spreadsheet.xml (renamed from packages/DocumentsUI/res/drawable/ic_doc_spreadsheet.xml)0
-rw-r--r--core/res/res/drawable/ic_doc_text.xml24
-rw-r--r--core/res/res/drawable/ic_doc_video.xml (renamed from packages/DocumentsUI/res/drawable/ic_doc_video.xml)0
-rw-r--r--core/res/res/drawable/ic_doc_word.xml (renamed from packages/DocumentsUI/res/drawable/ic_doc_word.xml)0
-rw-r--r--core/res/res/layout/date_picker_header_material.xml2
-rw-r--r--core/res/res/values-land/integers.xml2
-rw-r--r--core/res/res/values-watch/styles_material.xml4
-rw-r--r--core/res/res/values/integers.xml1
-rw-r--r--core/res/res/values/styles_material.xml4
-rw-r--r--core/res/res/values/symbols.xml26
-rw-r--r--media/java/android/media/MediaScanner.java25
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/IconUtils.java205
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java2
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java6
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java41
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistManager.java22
-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/screenshot/TakeScreenshotService.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java4
-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/PhoneStatusBar.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ReverseLinearLayout.java45
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java230
-rw-r--r--services/core/java/com/android/server/LockSettingsService.java7
-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/fingerprint/FingerprintService.java59
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java1
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java6
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java16
-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.java3
-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/RetailDemoModeService.java25
-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.java11
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java47
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java4
78 files changed, 1320 insertions, 673 deletions
diff --git a/Android.mk b/Android.mk
index 5766e46658c3..ba4e173ed0c7 100644
--- a/Android.mk
+++ b/Android.mk
@@ -308,6 +308,7 @@ LOCAL_SRC_FILES += \
core/java/com/android/internal/app/IEphemeralResolver.aidl \
core/java/com/android/internal/app/ISoundTriggerService.aidl \
core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl \
+ core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl \
core/java/com/android/internal/app/IVoiceInteractionSessionShowCallback.aidl \
core/java/com/android/internal/app/IVoiceInteractor.aidl \
core/java/com/android/internal/app/IVoiceInteractorCallback.aidl \
@@ -1060,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
@@ -1101,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/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 3f15a755bd79..fef1e2c63e46 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -6057,7 +6057,7 @@ public final class ActivityThread {
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
- Environment.initForCurrentUser();
+ Environment.init();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index e6ca52072079..8f424676dbe5 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -185,15 +185,6 @@ class ContextImpl extends Context {
@GuardedBy("mSync")
private File mCodeCacheDir;
- @GuardedBy("mSync")
- private File[] mExternalObbDirs;
- @GuardedBy("mSync")
- private File[] mExternalFilesDirs;
- @GuardedBy("mSync")
- private File[] mExternalCacheDirs;
- @GuardedBy("mSync")
- private File[] mExternalMediaDirs;
-
// The system service cache for the system services that are cached per-ContextImpl.
final Object[] mServiceCache = SystemServiceRegistry.createServiceCache();
@@ -562,17 +553,10 @@ class ContextImpl extends Context {
@Override
public File[] getExternalFilesDirs(String type) {
synchronized (mSync) {
- if (mExternalFilesDirs == null) {
- mExternalFilesDirs = Environment.buildExternalStorageAppFilesDirs(getPackageName());
- }
-
- // Splice in requested type, if any
- File[] dirs = mExternalFilesDirs;
+ File[] dirs = Environment.buildExternalStorageAppFilesDirs(getPackageName());
if (type != null) {
dirs = Environment.buildPaths(dirs, type);
}
-
- // Create dirs if needed
return ensureExternalDirsExistOrFilter(dirs);
}
}
@@ -586,12 +570,8 @@ class ContextImpl extends Context {
@Override
public File[] getObbDirs() {
synchronized (mSync) {
- if (mExternalObbDirs == null) {
- mExternalObbDirs = Environment.buildExternalStorageAppObbDirs(getPackageName());
- }
-
- // Create dirs if needed
- return ensureExternalDirsExistOrFilter(mExternalObbDirs);
+ File[] dirs = Environment.buildExternalStorageAppObbDirs(getPackageName());
+ return ensureExternalDirsExistOrFilter(dirs);
}
}
@@ -624,24 +604,16 @@ class ContextImpl extends Context {
@Override
public File[] getExternalCacheDirs() {
synchronized (mSync) {
- if (mExternalCacheDirs == null) {
- mExternalCacheDirs = Environment.buildExternalStorageAppCacheDirs(getPackageName());
- }
-
- // Create dirs if needed
- return ensureExternalDirsExistOrFilter(mExternalCacheDirs);
+ File[] dirs = Environment.buildExternalStorageAppCacheDirs(getPackageName());
+ return ensureExternalDirsExistOrFilter(dirs);
}
}
@Override
public File[] getExternalMediaDirs() {
synchronized (mSync) {
- if (mExternalMediaDirs == null) {
- mExternalMediaDirs = Environment.buildExternalStorageAppMediaDirs(getPackageName());
- }
-
- // Create dirs if needed
- return ensureExternalDirsExistOrFilter(mExternalMediaDirs);
+ File[] dirs = Environment.buildExternalStorageAppMediaDirs(getPackageName());
+ return ensureExternalDirsExistOrFilter(dirs);
}
}
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index c8d89204d8d4..b3320d6f6976 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -34,6 +34,7 @@ import android.database.CrossProcessCursorWrapper;
import android.database.Cursor;
import android.database.IContentObserver;
import android.graphics.Point;
+import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.CancellationSignal;
@@ -51,6 +52,7 @@ import android.util.EventLog;
import android.util.Log;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.MimeIconUtils;
import com.android.internal.util.Preconditions;
import dalvik.system.CloseGuard;
@@ -2693,4 +2695,9 @@ public abstract class ContentResolver {
public int resolveUserId(Uri uri) {
return ContentProvider.getUserIdFromUri(uri, mContext.getUserId());
}
+
+ /** @hide */
+ public Drawable getTypeDrawable(String mimeType) {
+ return MimeIconUtils.loadMimeIcon(mContext, mimeType);
+ }
}
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/Environment.java b/core/java/android/os/Environment.java
index 80927f368e0c..fa328090e90a 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -62,17 +62,34 @@ public class Environment {
private static final File DIR_ODM_ROOT = getDirectory(ENV_ODM_ROOT, "/odm");
private static final File DIR_VENDOR_ROOT = getDirectory(ENV_VENDOR_ROOT, "/vendor");
- private static UserEnvironment sCurrentUser;
- private static boolean sUserRequired;
+ // NoPreloadHolder to separate shared data from user-specific data, and to be able to initialize
+ // Environment without side effect (allowing a lazy init of the data where possible).
+ private static class NoPreloadHolder {
+ public final static UserEnvironment sCurrentUser;
+ public static boolean sUserRequired;
+
+ static {
+ sCurrentUser = new UserEnvironment(UserHandle.myUserId());
+ }
+
+ // Empty function to be able to trigger static initialization.
+ public static void init() {
+ }
- static {
- initForCurrentUser();
+ // Disallow allocation.
+ private NoPreloadHolder() {
+ }
}
/** {@hide} */
- public static void initForCurrentUser() {
- final int userId = UserHandle.myUserId();
- sCurrentUser = new UserEnvironment(userId);
+ public static void init() {
+ NoPreloadHolder.init();
+
+ // Check for expected outcome. We only allow one initialization, this will trigger if
+ // somebody tried to re-initialize.
+ if (NoPreloadHolder.sCurrentUser.mUserId != UserHandle.myUserId()) {
+ throw new IllegalStateException();
+ }
}
/** {@hide} */
@@ -428,7 +445,7 @@ public class Environment {
*/
public static File getExternalStorageDirectory() {
throwIfUserRequired();
- return sCurrentUser.getExternalDirs()[0];
+ return NoPreloadHolder.sCurrentUser.getExternalDirs()[0];
}
/** {@hide} */
@@ -612,7 +629,7 @@ public class Environment {
*/
public static File getExternalStoragePublicDirectory(String type) {
throwIfUserRequired();
- return sCurrentUser.buildExternalStoragePublicDirs(type)[0];
+ return NoPreloadHolder.sCurrentUser.buildExternalStoragePublicDirs(type)[0];
}
/**
@@ -621,7 +638,7 @@ public class Environment {
*/
public static File[] buildExternalStorageAndroidDataDirs() {
throwIfUserRequired();
- return sCurrentUser.buildExternalStorageAndroidDataDirs();
+ return NoPreloadHolder.sCurrentUser.buildExternalStorageAndroidDataDirs();
}
/**
@@ -630,7 +647,7 @@ public class Environment {
*/
public static File[] buildExternalStorageAppDataDirs(String packageName) {
throwIfUserRequired();
- return sCurrentUser.buildExternalStorageAppDataDirs(packageName);
+ return NoPreloadHolder.sCurrentUser.buildExternalStorageAppDataDirs(packageName);
}
/**
@@ -639,7 +656,7 @@ public class Environment {
*/
public static File[] buildExternalStorageAppMediaDirs(String packageName) {
throwIfUserRequired();
- return sCurrentUser.buildExternalStorageAppMediaDirs(packageName);
+ return NoPreloadHolder.sCurrentUser.buildExternalStorageAppMediaDirs(packageName);
}
/**
@@ -648,7 +665,7 @@ public class Environment {
*/
public static File[] buildExternalStorageAppObbDirs(String packageName) {
throwIfUserRequired();
- return sCurrentUser.buildExternalStorageAppObbDirs(packageName);
+ return NoPreloadHolder.sCurrentUser.buildExternalStorageAppObbDirs(packageName);
}
/**
@@ -657,7 +674,7 @@ public class Environment {
*/
public static File[] buildExternalStorageAppFilesDirs(String packageName) {
throwIfUserRequired();
- return sCurrentUser.buildExternalStorageAppFilesDirs(packageName);
+ return NoPreloadHolder.sCurrentUser.buildExternalStorageAppFilesDirs(packageName);
}
/**
@@ -666,7 +683,7 @@ public class Environment {
*/
public static File[] buildExternalStorageAppCacheDirs(String packageName) {
throwIfUserRequired();
- return sCurrentUser.buildExternalStorageAppCacheDirs(packageName);
+ return NoPreloadHolder.sCurrentUser.buildExternalStorageAppCacheDirs(packageName);
}
/**
@@ -770,7 +787,7 @@ public class Environment {
* {@link #MEDIA_BAD_REMOVAL}, or {@link #MEDIA_UNMOUNTABLE}.
*/
public static String getExternalStorageState() {
- final File externalDir = sCurrentUser.getExternalDirs()[0];
+ final File externalDir = NoPreloadHolder.sCurrentUser.getExternalDirs()[0];
return getExternalStorageState(externalDir);
}
@@ -811,7 +828,7 @@ public class Environment {
*/
public static boolean isExternalStorageRemovable() {
if (isStorageDisabled()) return false;
- final File externalDir = sCurrentUser.getExternalDirs()[0];
+ final File externalDir = NoPreloadHolder.sCurrentUser.getExternalDirs()[0];
return isExternalStorageRemovable(externalDir);
}
@@ -850,7 +867,7 @@ public class Environment {
*/
public static boolean isExternalStorageEmulated() {
if (isStorageDisabled()) return false;
- final File externalDir = sCurrentUser.getExternalDirs()[0];
+ final File externalDir = NoPreloadHolder.sCurrentUser.getExternalDirs()[0];
return isExternalStorageEmulated(externalDir);
}
@@ -885,11 +902,11 @@ public class Environment {
/** {@hide} */
public static void setUserRequired(boolean userRequired) {
- sUserRequired = userRequired;
+ NoPreloadHolder.sUserRequired = userRequired;
}
private static void throwIfUserRequired() {
- if (sUserRequired) {
+ if (NoPreloadHolder.sUserRequired) {
Log.wtf(TAG, "Path requests must specify a user by using UserEnvironment",
new Throwable());
}
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index b3f44536214b..10e3718601ea 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -297,7 +297,11 @@ public final class UserHandle implements Parcelable {
*/
@SystemApi
public static @UserIdInt int myUserId() {
- return getUserId(Process.myUid());
+ int myUid = Process.myUid();
+ if (myUid == 0) {
+ throw new IllegalStateException("myUserId unsupported in zygote.");
+ }
+ return getUserId(myUid);
}
/**
diff --git a/core/java/android/preference/PreferenceFragment.java b/core/java/android/preference/PreferenceFragment.java
index 3e496b6eb550..d4a3582cfbf6 100644
--- a/core/java/android/preference/PreferenceFragment.java
+++ b/core/java/android/preference/PreferenceFragment.java
@@ -189,12 +189,10 @@ public abstract class PreferenceFragment extends Fragment implements
0);
ListView lv = (ListView) view.findViewById(android.R.id.list);
- if (lv != null) {
- Drawable divider =
- a.getDrawable(com.android.internal.R.styleable.PreferenceFragment_divider);
- if (divider != null) {
- lv.setDivider(divider);
- }
+ if (lv != null
+ && a.hasValueOrEmpty(com.android.internal.R.styleable.PreferenceFragment_divider)) {
+ lv.setDivider(
+ a.getDrawable(com.android.internal.R.styleable.PreferenceFragment_divider));
}
a.recycle();
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index dae243b10762..bb8011080b44 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/com/android/internal/app/AssistUtils.java b/core/java/com/android/internal/app/AssistUtils.java
index d552e542e527..56c5cc9b69ec 100644
--- a/core/java/com/android/internal/app/AssistUtils.java
+++ b/core/java/com/android/internal/app/AssistUtils.java
@@ -132,6 +132,16 @@ public class AssistUtils {
}
}
+ public void registerVoiceInteractionSessionListener(IVoiceInteractionSessionListener listener) {
+ try {
+ if (mVoiceInteractionManagerService != null) {
+ mVoiceInteractionManagerService.registerVoiceInteractionSessionListener(listener);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to register voice interaction listener", e);
+ }
+ }
+
public ComponentName getAssistComponentForUser(int userId) {
final String setting = Settings.Secure.getStringForUser(mContext.getContentResolver(),
Settings.Secure.ASSISTANT, userId);
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index 1a963f30ca07..033dd13046ea 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -22,6 +22,7 @@ import android.os.Bundle;
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.app.IVoiceInteractionSessionListener;
import android.hardware.soundtrigger.IRecognitionStatusCallback;
import android.hardware.soundtrigger.SoundTrigger;
import android.service.voice.IVoiceInteractionService;
@@ -136,4 +137,9 @@ interface IVoiceInteractionManagerService {
* Called when the lockscreen got shown.
*/
void onLockscreenShown();
+
+ /**
+ * Register a voice interaction listener.
+ */
+ void registerVoiceInteractionSessionListener(IVoiceInteractionSessionListener listener);
}
diff --git a/core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl b/core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl
new file mode 100644
index 000000000000..87749d26e4a0
--- /dev/null
+++ b/core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl
@@ -0,0 +1,29 @@
+/*
+ * 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.internal.app;
+
+ oneway interface IVoiceInteractionSessionListener {
+ /**
+ * Called when a voice session is shown.
+ */
+ void onVoiceSessionShown();
+
+ /**
+ * Called when a voice session is hidden.
+ */
+ void onVoiceSessionHidden();
+ } \ No newline at end of file
diff --git a/core/java/com/android/internal/util/MimeIconUtils.java b/core/java/com/android/internal/util/MimeIconUtils.java
new file mode 100644
index 000000000000..841ec7ca994c
--- /dev/null
+++ b/core/java/com/android/internal/util/MimeIconUtils.java
@@ -0,0 +1,230 @@
+/*
+ * 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.internal.util;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.provider.DocumentsContract;
+
+import com.android.internal.R;
+
+import java.util.HashMap;
+
+public class MimeIconUtils {
+
+ private static HashMap<String, Integer> sMimeIcons = new HashMap<>();
+
+ private static void add(String mimeType, int resId) {
+ if (sMimeIcons.put(mimeType, resId) != null) {
+ throw new RuntimeException(mimeType + " already registered!");
+ }
+ }
+
+ static {
+ int icon;
+
+ // Package
+ icon = R.drawable.ic_doc_apk;
+ add("application/vnd.android.package-archive", icon);
+
+ // Audio
+ icon = R.drawable.ic_doc_audio;
+ add("application/ogg", icon);
+ add("application/x-flac", icon);
+
+ // Certificate
+ icon = R.drawable.ic_doc_certificate;
+ add("application/pgp-keys", icon);
+ add("application/pgp-signature", icon);
+ add("application/x-pkcs12", icon);
+ add("application/x-pkcs7-certreqresp", icon);
+ add("application/x-pkcs7-crl", icon);
+ add("application/x-x509-ca-cert", icon);
+ add("application/x-x509-user-cert", icon);
+ add("application/x-pkcs7-certificates", icon);
+ add("application/x-pkcs7-mime", icon);
+ add("application/x-pkcs7-signature", icon);
+
+ // Source code
+ icon = R.drawable.ic_doc_codes;
+ add("application/rdf+xml", icon);
+ add("application/rss+xml", icon);
+ add("application/x-object", icon);
+ add("application/xhtml+xml", icon);
+ add("text/css", icon);
+ add("text/html", icon);
+ add("text/xml", icon);
+ add("text/x-c++hdr", icon);
+ add("text/x-c++src", icon);
+ add("text/x-chdr", icon);
+ add("text/x-csrc", icon);
+ add("text/x-dsrc", icon);
+ add("text/x-csh", icon);
+ add("text/x-haskell", icon);
+ add("text/x-java", icon);
+ add("text/x-literate-haskell", icon);
+ add("text/x-pascal", icon);
+ add("text/x-tcl", icon);
+ add("text/x-tex", icon);
+ add("application/x-latex", icon);
+ add("application/x-texinfo", icon);
+ add("application/atom+xml", icon);
+ add("application/ecmascript", icon);
+ add("application/json", icon);
+ add("application/javascript", icon);
+ add("application/xml", icon);
+ add("text/javascript", icon);
+ add("application/x-javascript", icon);
+
+ // Compressed
+ icon = R.drawable.ic_doc_compressed;
+ add("application/mac-binhex40", icon);
+ add("application/rar", icon);
+ add("application/zip", icon);
+ add("application/x-apple-diskimage", icon);
+ add("application/x-debian-package", icon);
+ add("application/x-gtar", icon);
+ add("application/x-iso9660-image", icon);
+ add("application/x-lha", icon);
+ add("application/x-lzh", icon);
+ add("application/x-lzx", icon);
+ add("application/x-stuffit", icon);
+ add("application/x-tar", icon);
+ add("application/x-webarchive", icon);
+ add("application/x-webarchive-xml", icon);
+ add("application/gzip", icon);
+ add("application/x-7z-compressed", icon);
+ add("application/x-deb", icon);
+ add("application/x-rar-compressed", icon);
+
+ // Contact
+ icon = R.drawable.ic_doc_contact;
+ add("text/x-vcard", icon);
+ add("text/vcard", icon);
+
+ // Event
+ icon = R.drawable.ic_doc_event;
+ add("text/calendar", icon);
+ add("text/x-vcalendar", icon);
+
+ // Font
+ icon = R.drawable.ic_doc_font;
+ add("application/x-font", icon);
+ add("application/font-woff", icon);
+ add("application/x-font-woff", icon);
+ add("application/x-font-ttf", icon);
+
+ // Image
+ icon = R.drawable.ic_doc_image;
+ add("application/vnd.oasis.opendocument.graphics", icon);
+ add("application/vnd.oasis.opendocument.graphics-template", icon);
+ add("application/vnd.oasis.opendocument.image", icon);
+ add("application/vnd.stardivision.draw", icon);
+ add("application/vnd.sun.xml.draw", icon);
+ add("application/vnd.sun.xml.draw.template", icon);
+
+ // PDF
+ icon = R.drawable.ic_doc_pdf;
+ add("application/pdf", icon);
+
+ // Presentation
+ icon = R.drawable.ic_doc_presentation;
+ add("application/vnd.stardivision.impress", icon);
+ add("application/vnd.sun.xml.impress", icon);
+ add("application/vnd.sun.xml.impress.template", icon);
+ add("application/x-kpresenter", icon);
+ add("application/vnd.oasis.opendocument.presentation", icon);
+
+ // Spreadsheet
+ icon = R.drawable.ic_doc_spreadsheet;
+ add("application/vnd.oasis.opendocument.spreadsheet", icon);
+ add("application/vnd.oasis.opendocument.spreadsheet-template", icon);
+ add("application/vnd.stardivision.calc", icon);
+ add("application/vnd.sun.xml.calc", icon);
+ add("application/vnd.sun.xml.calc.template", icon);
+ add("application/x-kspread", icon);
+
+ // Document
+ icon = R.drawable.ic_doc_document;
+ add("application/vnd.oasis.opendocument.text", icon);
+ add("application/vnd.oasis.opendocument.text-master", icon);
+ add("application/vnd.oasis.opendocument.text-template", icon);
+ add("application/vnd.oasis.opendocument.text-web", icon);
+ add("application/vnd.stardivision.writer", icon);
+ add("application/vnd.stardivision.writer-global", icon);
+ add("application/vnd.sun.xml.writer", icon);
+ add("application/vnd.sun.xml.writer.global", icon);
+ add("application/vnd.sun.xml.writer.template", icon);
+ add("application/x-abiword", icon);
+ add("application/x-kword", icon);
+
+ // Video
+ icon = R.drawable.ic_doc_video;
+ add("application/x-quicktimeplayer", icon);
+ add("application/x-shockwave-flash", icon);
+
+ // Word
+ icon = R.drawable.ic_doc_word;
+ add("application/msword", icon);
+ add("application/vnd.openxmlformats-officedocument.wordprocessingml.document", icon);
+ add("application/vnd.openxmlformats-officedocument.wordprocessingml.template", icon);
+
+ // Excel
+ icon = R.drawable.ic_doc_excel;
+ add("application/vnd.ms-excel", icon);
+ add("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", icon);
+ add("application/vnd.openxmlformats-officedocument.spreadsheetml.template", icon);
+
+ // Powerpoint
+ icon = R.drawable.ic_doc_powerpoint;
+ add("application/vnd.ms-powerpoint", icon);
+ add("application/vnd.openxmlformats-officedocument.presentationml.presentation", icon);
+ add("application/vnd.openxmlformats-officedocument.presentationml.template", icon);
+ add("application/vnd.openxmlformats-officedocument.presentationml.slideshow", icon);
+ }
+
+ public static Drawable loadMimeIcon(Context context, String mimeType) {
+ if (DocumentsContract.Document.MIME_TYPE_DIR.equals(mimeType)) {
+ return context.getDrawable(R.drawable.ic_doc_folder);
+ }
+
+ // Look for exact match first
+ Integer resId = sMimeIcons.get(mimeType);
+ if (resId != null) {
+ return context.getDrawable(resId);
+ }
+
+ if (mimeType == null) {
+ // TODO: generic icon?
+ return null;
+ }
+
+ // Otherwise look for partial match
+ final String typeOnly = mimeType.split("/")[0];
+ if ("audio".equals(typeOnly)) {
+ return context.getDrawable(R.drawable.ic_doc_audio);
+ } else if ("image".equals(typeOnly)) {
+ return context.getDrawable(R.drawable.ic_doc_image);
+ } else if ("text".equals(typeOnly)) {
+ return context.getDrawable(R.drawable.ic_doc_text);
+ } else if ("video".equals(typeOnly)) {
+ return context.getDrawable(R.drawable.ic_doc_video);
+ } else {
+ return context.getDrawable(R.drawable.ic_doc_generic);
+ }
+ }
+}
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_apk.xml b/core/res/res/drawable/ic_doc_apk.xml
index 197445ef598d..197445ef598d 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_apk.xml
+++ b/core/res/res/drawable/ic_doc_apk.xml
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_audio.xml b/core/res/res/drawable/ic_doc_audio.xml
index 454eea3bbad9..454eea3bbad9 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_audio.xml
+++ b/core/res/res/drawable/ic_doc_audio.xml
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_certificate.xml b/core/res/res/drawable/ic_doc_certificate.xml
index b99baf6f4cd3..b99baf6f4cd3 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_certificate.xml
+++ b/core/res/res/drawable/ic_doc_certificate.xml
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_codes.xml b/core/res/res/drawable/ic_doc_codes.xml
index ea1c464890e7..ea1c464890e7 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_codes.xml
+++ b/core/res/res/drawable/ic_doc_codes.xml
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_compressed.xml b/core/res/res/drawable/ic_doc_compressed.xml
index e0eb669af33a..e0eb669af33a 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_compressed.xml
+++ b/core/res/res/drawable/ic_doc_compressed.xml
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_contact.xml b/core/res/res/drawable/ic_doc_contact.xml
index a77cb6ba1152..a77cb6ba1152 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_contact.xml
+++ b/core/res/res/drawable/ic_doc_contact.xml
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_document.xml b/core/res/res/drawable/ic_doc_document.xml
index 29251ad799f0..29251ad799f0 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_document.xml
+++ b/core/res/res/drawable/ic_doc_document.xml
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_event.xml b/core/res/res/drawable/ic_doc_event.xml
index 113f079c8670..113f079c8670 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_event.xml
+++ b/core/res/res/drawable/ic_doc_event.xml
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_excel.xml b/core/res/res/drawable/ic_doc_excel.xml
index 3ed27e1f7f62..3ed27e1f7f62 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_excel.xml
+++ b/core/res/res/drawable/ic_doc_excel.xml
diff --git a/core/res/res/drawable/ic_doc_folder.xml b/core/res/res/drawable/ic_doc_folder.xml
new file mode 100644
index 000000000000..dcbce010810e
--- /dev/null
+++ b/core/res/res/drawable/ic_doc_folder.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF737373"
+ android:pathData="M10 4H4c-1.1 0,-1.99.9,-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2,-.9 2,-2V8c0,-1.1,-.9,-2,-2,-2h-8l-2,-2z"/>
+</vector>
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_font.xml b/core/res/res/drawable/ic_doc_font.xml
index 4c13d71e47d0..4c13d71e47d0 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_font.xml
+++ b/core/res/res/drawable/ic_doc_font.xml
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_generic.xml b/core/res/res/drawable/ic_doc_generic.xml
index 006dfba4a3bb..006dfba4a3bb 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_generic.xml
+++ b/core/res/res/drawable/ic_doc_generic.xml
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_image.xml b/core/res/res/drawable/ic_doc_image.xml
index 23953f71fd5e..23953f71fd5e 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_image.xml
+++ b/core/res/res/drawable/ic_doc_image.xml
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_pdf.xml b/core/res/res/drawable/ic_doc_pdf.xml
index b2d01938c480..b2d01938c480 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_pdf.xml
+++ b/core/res/res/drawable/ic_doc_pdf.xml
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_powerpoint.xml b/core/res/res/drawable/ic_doc_powerpoint.xml
index aa5bfc818692..aa5bfc818692 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_powerpoint.xml
+++ b/core/res/res/drawable/ic_doc_powerpoint.xml
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_presentation.xml b/core/res/res/drawable/ic_doc_presentation.xml
index 7937bc12aa3b..7937bc12aa3b 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_presentation.xml
+++ b/core/res/res/drawable/ic_doc_presentation.xml
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_spreadsheet.xml b/core/res/res/drawable/ic_doc_spreadsheet.xml
index 1663c0a1ae1b..1663c0a1ae1b 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_spreadsheet.xml
+++ b/core/res/res/drawable/ic_doc_spreadsheet.xml
diff --git a/core/res/res/drawable/ic_doc_text.xml b/core/res/res/drawable/ic_doc_text.xml
new file mode 100644
index 000000000000..7fc04e811b1c
--- /dev/null
+++ b/core/res/res/drawable/ic_doc_text.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF737373"
+ android:pathData="M14 2H6c-1.1 0,-1.99.9,-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2,-.9 2,-2V8l-6,-6zm2 16H8v-2h8v2zm0,-4H8v-2h8v2zm-3,-5V3.5L18.5 9H13z"/>
+</vector>
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_video.xml b/core/res/res/drawable/ic_doc_video.xml
index ad4dae89ccdc..ad4dae89ccdc 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_video.xml
+++ b/core/res/res/drawable/ic_doc_video.xml
diff --git a/packages/DocumentsUI/res/drawable/ic_doc_word.xml b/core/res/res/drawable/ic_doc_word.xml
index 7a3a0ec9b5e0..7a3a0ec9b5e0 100644
--- a/packages/DocumentsUI/res/drawable/ic_doc_word.xml
+++ b/core/res/res/drawable/ic_doc_word.xml
diff --git a/core/res/res/layout/date_picker_header_material.xml b/core/res/res/layout/date_picker_header_material.xml
index a0e2b1de60d4..755317ee28ce 100644
--- a/core/res/res/layout/date_picker_header_material.xml
+++ b/core/res/res/layout/date_picker_header_material.xml
@@ -54,7 +54,7 @@
android:textAppearance="@style/TextAppearance.Material.DatePicker.DateLabel"
android:includeFontPadding="false"
android:gravity="start"
- android:maxLines="2"
+ android:maxLines="@integer/date_picker_header_max_lines_material"
android:ellipsize="none" />
</LinearLayout>
</LinearLayout>
diff --git a/core/res/res/values-land/integers.xml b/core/res/res/values-land/integers.xml
index 020fd23cec81..a5789895c970 100644
--- a/core/res/res/values-land/integers.xml
+++ b/core/res/res/values-land/integers.xml
@@ -23,4 +23,6 @@
<integer name="kg_widget_region_weight">45</integer>
<integer name="kg_security_flipper_weight">55</integer>
<integer name="kg_glowpad_rotation_offset">-90</integer>
+
+ <integer name="date_picker_header_max_lines_material">4</integer>
</resources>
diff --git a/core/res/res/values-watch/styles_material.xml b/core/res/res/values-watch/styles_material.xml
index c19cc72a8bff..daeeca2b050d 100644
--- a/core/res/res/values-watch/styles_material.xml
+++ b/core/res/res/values-watch/styles_material.xml
@@ -53,6 +53,10 @@ please see styles_device_defaults.xml.
<item name="wallpaperIntraCloseExitAnimation">@anim/slide_in_exit_micro</item>
</style>
+ <style name="PreferenceFragment.Material" parent="BasePreferenceFragment">
+ <item name="divider">@empty</item>
+ </style>
+
<style name="Widget.Material.TextView" parent="Widget.TextView">
<item name="breakStrategy">balanced</item>
</style>
diff --git a/core/res/res/values/integers.xml b/core/res/res/values/integers.xml
index 8f8d59e95782..71ac2f49ff71 100644
--- a/core/res/res/values/integers.xml
+++ b/core/res/res/values/integers.xml
@@ -26,4 +26,5 @@
<integer name="date_picker_mode">1</integer>
<integer name="time_picker_mode">1</integer>
+ <integer name="date_picker_header_max_lines_material">2</integer>
</resources>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 6e0ad3681e81..fad3488d2915 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -36,7 +36,7 @@ please see styles_device_defaults.xml.
<item name="layout">@layout/preference_material</item>
</style>
- <style name="PreferenceFragment.Material">
+ <style name="BasePreferenceFragment">
<item name="layout">@layout/preference_list_fragment_material</item>
<item name="paddingStart">@dimen/preference_fragment_padding_side_material</item>
<item name="paddingEnd">@dimen/preference_fragment_padding_side_material</item>
@@ -46,6 +46,8 @@ please see styles_device_defaults.xml.
<item name="clipToPadding">@bool/config_preferenceFragmentClipToPadding</item>
</style>
+ <style name="PreferenceFragment.Material" parent="BasePreferenceFragment"/>
+
<style name="PreferenceActivity.Material">
<item name="layout">@layout/preference_list_content_material</item>
<item name="headerLayout">@layout/preference_header_item_material</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d426d1aef072..a781f9a13b59 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2638,4 +2638,30 @@
<!-- Package name for the device provisioning package -->
<java-symbol type="string" name="config_deviceProvisioningPackage" />
+
+ <!-- Used for MimeIconUtils. -->
+ <java-symbol type="drawable" name="ic_doc_apk" />
+ <java-symbol type="drawable" name="ic_doc_audio" />
+ <java-symbol type="drawable" name="ic_doc_certificate" />
+ <java-symbol type="drawable" name="ic_doc_codes" />
+ <java-symbol type="drawable" name="ic_doc_compressed" />
+ <java-symbol type="drawable" name="ic_doc_contact" />
+ <java-symbol type="drawable" name="ic_doc_event" />
+ <java-symbol type="drawable" name="ic_doc_font" />
+ <java-symbol type="drawable" name="ic_doc_image" />
+ <java-symbol type="drawable" name="ic_doc_pdf" />
+ <java-symbol type="drawable" name="ic_doc_presentation" />
+ <java-symbol type="drawable" name="ic_doc_spreadsheet" />
+ <java-symbol type="drawable" name="ic_doc_document" />
+ <java-symbol type="drawable" name="ic_doc_video" />
+ <java-symbol type="drawable" name="ic_doc_word" />
+ <java-symbol type="drawable" name="ic_doc_excel" />
+ <java-symbol type="drawable" name="ic_doc_powerpoint" />
+ <java-symbol type="drawable" name="ic_doc_folder" />
+ <java-symbol type="drawable" name="ic_doc_audio" />
+ <java-symbol type="drawable" name="ic_doc_image" />
+ <java-symbol type="drawable" name="ic_doc_text" />
+ <java-symbol type="drawable" name="ic_doc_video" />
+ <java-symbol type="drawable" name="ic_doc_generic" />
+
</resources>
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/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java b/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java
index c28fae85cb8b..177ba0d20154 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java
@@ -23,181 +23,7 @@ import android.graphics.drawable.Drawable;
import android.provider.DocumentsContract.Document;
import android.util.TypedValue;
-import java.util.HashMap;
-
public class IconUtils {
-
- private static HashMap<String, Integer> sMimeIcons = new HashMap<>();
-
- private static void add(String mimeType, int resId) {
- if (sMimeIcons.put(mimeType, resId) != null) {
- throw new RuntimeException(mimeType + " already registered!");
- }
- }
-
- static {
- int icon;
-
- // Package
- icon = R.drawable.ic_doc_apk;
- add("application/vnd.android.package-archive", icon);
-
- // Audio
- icon = R.drawable.ic_doc_audio;
- add("application/ogg", icon);
- add("application/x-flac", icon);
-
- // Certificate
- icon = R.drawable.ic_doc_certificate;
- add("application/pgp-keys", icon);
- add("application/pgp-signature", icon);
- add("application/x-pkcs12", icon);
- add("application/x-pkcs7-certreqresp", icon);
- add("application/x-pkcs7-crl", icon);
- add("application/x-x509-ca-cert", icon);
- add("application/x-x509-user-cert", icon);
- add("application/x-pkcs7-certificates", icon);
- add("application/x-pkcs7-mime", icon);
- add("application/x-pkcs7-signature", icon);
-
- // Source code
- icon = R.drawable.ic_doc_codes;
- add("application/rdf+xml", icon);
- add("application/rss+xml", icon);
- add("application/x-object", icon);
- add("application/xhtml+xml", icon);
- add("text/css", icon);
- add("text/html", icon);
- add("text/xml", icon);
- add("text/x-c++hdr", icon);
- add("text/x-c++src", icon);
- add("text/x-chdr", icon);
- add("text/x-csrc", icon);
- add("text/x-dsrc", icon);
- add("text/x-csh", icon);
- add("text/x-haskell", icon);
- add("text/x-java", icon);
- add("text/x-literate-haskell", icon);
- add("text/x-pascal", icon);
- add("text/x-tcl", icon);
- add("text/x-tex", icon);
- add("application/x-latex", icon);
- add("application/x-texinfo", icon);
- add("application/atom+xml", icon);
- add("application/ecmascript", icon);
- add("application/json", icon);
- add("application/javascript", icon);
- add("application/xml", icon);
- add("text/javascript", icon);
- add("application/x-javascript", icon);
-
- // Compressed
- icon = R.drawable.ic_doc_compressed;
- add("application/mac-binhex40", icon);
- add("application/rar", icon);
- add("application/zip", icon);
- add("application/x-apple-diskimage", icon);
- add("application/x-debian-package", icon);
- add("application/x-gtar", icon);
- add("application/x-iso9660-image", icon);
- add("application/x-lha", icon);
- add("application/x-lzh", icon);
- add("application/x-lzx", icon);
- add("application/x-stuffit", icon);
- add("application/x-tar", icon);
- add("application/x-webarchive", icon);
- add("application/x-webarchive-xml", icon);
- add("application/gzip", icon);
- add("application/x-7z-compressed", icon);
- add("application/x-deb", icon);
- add("application/x-rar-compressed", icon);
-
- // Contact
- icon = R.drawable.ic_doc_contact;
- add("text/x-vcard", icon);
- add("text/vcard", icon);
-
- // Event
- icon = R.drawable.ic_doc_event;
- add("text/calendar", icon);
- add("text/x-vcalendar", icon);
-
- // Font
- icon = R.drawable.ic_doc_font;
- add("application/x-font", icon);
- add("application/font-woff", icon);
- add("application/x-font-woff", icon);
- add("application/x-font-ttf", icon);
-
- // Image
- icon = R.drawable.ic_doc_image;
- add("application/vnd.oasis.opendocument.graphics", icon);
- add("application/vnd.oasis.opendocument.graphics-template", icon);
- add("application/vnd.oasis.opendocument.image", icon);
- add("application/vnd.stardivision.draw", icon);
- add("application/vnd.sun.xml.draw", icon);
- add("application/vnd.sun.xml.draw.template", icon);
-
- // PDF
- icon = R.drawable.ic_doc_pdf;
- add("application/pdf", icon);
-
- // Presentation
- icon = R.drawable.ic_doc_presentation;
- add("application/vnd.stardivision.impress", icon);
- add("application/vnd.sun.xml.impress", icon);
- add("application/vnd.sun.xml.impress.template", icon);
- add("application/x-kpresenter", icon);
- add("application/vnd.oasis.opendocument.presentation", icon);
-
- // Spreadsheet
- icon = R.drawable.ic_doc_spreadsheet;
- add("application/vnd.oasis.opendocument.spreadsheet", icon);
- add("application/vnd.oasis.opendocument.spreadsheet-template", icon);
- add("application/vnd.stardivision.calc", icon);
- add("application/vnd.sun.xml.calc", icon);
- add("application/vnd.sun.xml.calc.template", icon);
- add("application/x-kspread", icon);
-
- // Document
- icon = R.drawable.ic_doc_document;
- add("application/vnd.oasis.opendocument.text", icon);
- add("application/vnd.oasis.opendocument.text-master", icon);
- add("application/vnd.oasis.opendocument.text-template", icon);
- add("application/vnd.oasis.opendocument.text-web", icon);
- add("application/vnd.stardivision.writer", icon);
- add("application/vnd.stardivision.writer-global", icon);
- add("application/vnd.sun.xml.writer", icon);
- add("application/vnd.sun.xml.writer.global", icon);
- add("application/vnd.sun.xml.writer.template", icon);
- add("application/x-abiword", icon);
- add("application/x-kword", icon);
-
- // Video
- icon = R.drawable.ic_doc_video;
- add("application/x-quicktimeplayer", icon);
- add("application/x-shockwave-flash", icon);
-
- // Word
- icon = R.drawable.ic_doc_word;
- add("application/msword", icon);
- add("application/vnd.openxmlformats-officedocument.wordprocessingml.document", icon);
- add("application/vnd.openxmlformats-officedocument.wordprocessingml.template", icon);
-
- // Excel
- icon = R.drawable.ic_doc_excel;
- add("application/vnd.ms-excel", icon);
- add("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", icon);
- add("application/vnd.openxmlformats-officedocument.spreadsheetml.template", icon);
-
- // Powerpoint
- icon = R.drawable.ic_doc_powerpoint;
- add("application/vnd.ms-powerpoint", icon);
- add("application/vnd.openxmlformats-officedocument.presentationml.presentation", icon);
- add("application/vnd.openxmlformats-officedocument.presentationml.template", icon);
- add("application/vnd.openxmlformats-officedocument.presentationml.slideshow", icon);
- }
-
public static Drawable loadPackageIcon(Context context, String authority, int icon) {
if (icon != 0) {
if (authority != null) {
@@ -225,7 +51,7 @@ public class IconUtils {
if (mode == State.MODE_GRID) {
return context.getDrawable(R.drawable.ic_grid_folder);
} else {
- return context.getDrawable(R.drawable.ic_doc_folder);
+ return context.getDrawable(com.android.internal.R.drawable.ic_doc_folder);
}
}
@@ -233,34 +59,7 @@ public class IconUtils {
}
public static Drawable loadMimeIcon(Context context, String mimeType) {
- if (Document.MIME_TYPE_DIR.equals(mimeType)) {
- return context.getDrawable(R.drawable.ic_doc_folder);
- }
-
- // Look for exact match first
- Integer resId = sMimeIcons.get(mimeType);
- if (resId != null) {
- return context.getDrawable(resId);
- }
-
- if (mimeType == null) {
- // TODO: generic icon?
- return null;
- }
-
- // Otherwise look for partial match
- final String typeOnly = mimeType.split("/")[0];
- if ("audio".equals(typeOnly)) {
- return context.getDrawable(R.drawable.ic_doc_audio);
- } else if ("image".equals(typeOnly)) {
- return context.getDrawable(R.drawable.ic_doc_image);
- } else if ("text".equals(typeOnly)) {
- return context.getDrawable(R.drawable.ic_doc_text);
- } else if ("video".equals(typeOnly)) {
- return context.getDrawable(R.drawable.ic_doc_video);
- } else {
- return context.getDrawable(R.drawable.ic_doc_generic);
- }
+ return context.getContentResolver().getTypeDrawable(mimeType);
}
public static Drawable applyTintColor(Context context, int drawableId, int tintColorId) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 870c3439b8a9..47df940bb1b5 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -1391,7 +1391,7 @@ public class DirectoryFragment extends Fragment
return mIconHelper.getDocumentIcon(mContext, doc.authority, doc.documentId,
doc.mimeType, doc.icon);
}
- return mContext.getDrawable(R.drawable.ic_doc_generic);
+ return mContext.getDrawable(com.android.internal.R.drawable.ic_doc_generic);
}
private String getTitle(List<DocumentInfo> docs) {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
index d54bdfd580eb..649dde049fc4 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
@@ -213,13 +213,13 @@ public class RootInfo implements Durable, Parcelable, Comparable<RootInfo> {
derivedIcon = R.drawable.ic_root_download;
} else if (isImages()) {
derivedType = TYPE_IMAGES;
- derivedIcon = R.drawable.ic_doc_image;
+ derivedIcon = com.android.internal.R.drawable.ic_doc_image;
} else if (isVideos()) {
derivedType = TYPE_VIDEO;
- derivedIcon = R.drawable.ic_doc_video;
+ derivedIcon = com.android.internal.R.drawable.ic_doc_video;
} else if (isAudio()) {
derivedType = TYPE_AUDIO;
- derivedIcon = R.drawable.ic_doc_audio;
+ derivedIcon = com.android.internal.R.drawable.ic_doc_audio;
} else if (isRecents()) {
derivedType = TYPE_RECENTS;
} else {
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/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index a5e771fa53a4..af2a2869bc30 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -15,6 +15,7 @@ import android.graphics.PixelFormat;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
+import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
@@ -28,9 +29,9 @@ import android.view.WindowManager;
import android.widget.ImageView;
import com.android.internal.app.AssistUtils;
+import com.android.internal.app.IVoiceInteractionSessionListener;
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
import com.android.systemui.R;
-import com.android.systemui.SystemUIFactory;
import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.statusbar.CommandQueue;
@@ -52,7 +53,7 @@ public class AssistManager {
private AssistOrbContainer mView;
private final BaseStatusBar mBar;
- private final AssistUtils mAssistUtils;
+ protected final AssistUtils mAssistUtils;
private IVoiceInteractionSessionShowCallback mShowCallback =
new IVoiceInteractionSessionShowCallback.Stub() {
@@ -82,6 +83,23 @@ public class AssistManager {
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
mAssistUtils = new AssistUtils(context);
mAssistDisclosure = new AssistDisclosure(context, new Handler());
+
+ registerVoiceInteractionSessionListener();
+ }
+
+ protected void registerVoiceInteractionSessionListener() {
+ mAssistUtils.registerVoiceInteractionSessionListener(
+ new IVoiceInteractionSessionListener.Stub() {
+ @Override
+ public void onVoiceSessionShown() throws RemoteException {
+ Log.v(TAG, "Voice open");
+ }
+
+ @Override
+ public void onVoiceSessionHidden() throws RemoteException {
+ Log.v(TAG, "Voice closed");
+ }
+ });
}
public void onConfigurationChanged() {
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/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 4badc420d74f..f3bae20e544a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -23,6 +23,8 @@ import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
+import android.os.UserManager;
+import android.util.Log;
import android.view.WindowManager;
public class TakeScreenshotService extends Service {
@@ -44,6 +46,16 @@ public class TakeScreenshotService extends Service {
}
}
};
+
+ // If the storage for this user is locked, we have no place to store
+ // the screenshot, so skip taking it instead of showing a misleading
+ // animation and error notification.
+ if (!getSystemService(UserManager.class).isUserUnlocked()) {
+ Log.w(TAG, "Skipping screenshot because storage is locked!");
+ post(finisher);
+ return;
+ }
+
if (mScreenshot == null) {
mScreenshot = new GlobalScreenshot(TakeScreenshotService.this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
index b5a48a3c67e9..b53a99907146 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
@@ -157,6 +157,10 @@ public class ButtonDispatcher {
}
}
+ public ArrayList<View> getViews() {
+ return mViews;
+ }
+
public View getCurrentView() {
return mCurrentView;
}
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/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index fcb1da0033bb..cb3089a4b1df 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -4307,6 +4307,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
mStackScroller.setActivatedChild(view);
}
+ public ButtonDispatcher getHomeButton() {
+ return mNavigationBarView.getHomeButton();
+ }
+
/**
* @param state The {@link StatusBarState} to set.
*/
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/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..85eee2bb27e2 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 {
@@ -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/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/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index be8e300c9352..1066434f10f1 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -72,6 +72,7 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
/**
@@ -90,10 +91,17 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
private static final String ACTION_LOCKOUT_RESET =
"com.android.server.fingerprint.ACTION_LOCKOUT_RESET";
+ private class PerformanceStats {
+ int accept; // number of accepted fingerprints
+ int reject; // number of rejected fingerprints
+ int acquire; // total number of acquisitions. Should be >= accept+reject due to poor image
+ // acquisition in some cases (too fast, too slow, dirty sensor, etc.)
+ int lockout; // total number of lockouts
+ }
+
private final ArrayList<FingerprintServiceLockoutResetMonitor> mLockoutMonitors =
new ArrayList<>();
private final AppOpsManager mAppOps;
-
private static final long FAIL_LOCKOUT_TIMEOUT_MS = 30*1000;
private static final int MAX_FAILED_ATTEMPTS = 5;
private static final long CANCEL_TIMEOUT_LIMIT = 3000; // max wait for onCancel() from HAL,in ms
@@ -110,6 +118,15 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
private ClientMonitor mCurrentClient;
private ClientMonitor mPendingClient;
private long mCurrentAuthenticatorId;
+ private PerformanceStats mPerformanceStats;
+
+ // Normal fingerprint authentications are tracked by mPerformanceMap.
+ private HashMap<Integer, PerformanceStats> mPerformanceMap
+ = new HashMap<Integer, PerformanceStats>();
+
+ // Transactions that make use of CryptoObjects are tracked by mCryptoPerformaceMap.
+ private HashMap<Integer, PerformanceStats> mCryptoPerformanceMap
+ = new HashMap<Integer, PerformanceStats>();
private Handler mHandler = new Handler() {
@Override
@@ -246,6 +263,11 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
if (client != null && client.onAuthenticated(fingerId, groupId)) {
removeClient(client);
}
+ if (fingerId != 0) {
+ mPerformanceStats.accept++;
+ } else {
+ mPerformanceStats.reject++;
+ }
}
protected void handleAcquired(long deviceId, int acquiredInfo) {
@@ -253,6 +275,11 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
if (client != null && client.onAcquired(acquiredInfo)) {
removeClient(client);
}
+ if (mPerformanceStats != null && !inLockoutMode()
+ && client instanceof AuthenticationClient) {
+ // ignore enrollment acquisitions or acquisitions when we're locked out
+ mPerformanceStats.acquire++;
+ }
}
protected void handleEnrollResult(long deviceId, int fingerId, int groupId, int remaining) {
@@ -505,6 +532,9 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
@Override
public boolean handleFailedAttempt() {
mFailedAttempts++;
+ if (mFailedAttempts == MAX_FAILED_ATTEMPTS) {
+ mPerformanceStats.lockout++;
+ }
if (inLockoutMode()) {
// Failing multiple times will continue to push out the lockout time.
scheduleLockoutReset();
@@ -742,12 +772,24 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
mHandler.post(new Runnable() {
@Override
public void run() {
- MetricsLogger.histogram(mContext, "fingerprint_token", opId != 0L ? 1 : 0);
if (!canUseFingerprint(opPackageName, true /* foregroundOnly */,
callingUid, pid)) {
if (DEBUG) Slog.v(TAG, "authenticate(): reject " + opPackageName);
return;
}
+
+ MetricsLogger.histogram(mContext, "fingerprint_token", opId != 0L ? 1 : 0);
+
+ // Get performance stats object for this user.
+ HashMap<Integer, PerformanceStats> pmap
+ = (opId == 0) ? mPerformanceMap : mCryptoPerformanceMap;
+ PerformanceStats stats = pmap.get(mCurrentUserId);
+ if (stats == null) {
+ stats = new PerformanceStats();
+ pmap.put(mCurrentUserId, stats);
+ }
+ mPerformanceStats = stats;
+
startAuthentication(token, opId, callingUserId, groupId, receiver,
flags, restricted, opPackageName);
}
@@ -924,9 +966,21 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
for (UserInfo user : UserManager.get(getContext()).getUsers()) {
final int userId = user.getUserHandle().getIdentifier();
final int N = mFingerprintUtils.getFingerprintsForUser(mContext, userId).size();
+ PerformanceStats stats = mPerformanceMap.get(userId);
+ PerformanceStats cryptoStats = mCryptoPerformanceMap.get(userId);
JSONObject set = new JSONObject();
set.put("id", userId);
set.put("count", N);
+ set.put("accept", (stats != null) ? stats.accept : 0);
+ set.put("reject", (stats != null) ? stats.reject : 0);
+ set.put("acquire", (stats != null) ? stats.acquire : 0);
+ set.put("lockout", (stats != null) ? stats.lockout : 0);
+ // cryptoStats measures statistics about secure fingerprint transactions
+ // (e.g. to unlock password storage, make secure purchases, etc.)
+ set.put("acceptCrypto", (cryptoStats != null) ? cryptoStats.accept : 0);
+ set.put("rejectCrypto", (cryptoStats != null) ? cryptoStats.reject : 0);
+ set.put("acquireCrypto", (cryptoStats != null) ? cryptoStats.acquire : 0);
+ set.put("lockoutCrypto", (cryptoStats != null) ? cryptoStats.lockout : 0);
sets.put(set);
}
@@ -947,6 +1001,7 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
private void updateActiveGroup(int userId, String clientPackage) {
IFingerprintDaemon daemon = getFingerprintDaemon();
+
if (daemon != null) {
try {
userId = getUserOrWorkProfileId(clientPackage, userId);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 1206263fb281..ec77dafb2914 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2286,6 +2286,7 @@ public class NotificationManagerService extends SystemService {
.setFlag(Notification.FLAG_AUTOGROUP_SUMMARY, true)
.setFlag(Notification.FLAG_GROUP_SUMMARY, true)
.setColor(adjustedSbn.getNotification().color)
+ .setLocalOnly(true)
.build();
summaryNotification.extras.putAll(extras);
Intent appIntent = getContext().getPackageManager()
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 bbffd328eecd..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
}
@@ -2961,6 +2967,14 @@ public class UserManagerService extends IUserManager.Stub {
* number is mismatched.
*/
public static void enforceSerialNumber(File file, int serialNumber) throws IOException {
+ if (StorageManager.isFileEncryptedEmulatedOnly()) {
+ // When we're emulating FBE, the directory may have been chmod
+ // 000'ed, meaning we can't read the serial number to enforce it;
+ // instead of destroying the user, just log a warning.
+ Slog.w(LOG_TAG, "Device is emulating FBE; assuming current serial number is valid");
+ return;
+ }
+
final int foundSerial = getSerialNumber(file);
Slog.v(LOG_TAG, "Found " + file + " with serial number " + foundSerial);
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 76424d82a2b0..fa3479cca5e7 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -284,6 +284,9 @@ public final class SystemServer {
// we've defined it before booting further.
Build.ensureFingerprintProperty();
+ // Initialize Environment for the system user.
+ Environment.init();
+
// Within the system server, it is an error to access Environment paths without
// explicitly specifying a user.
Environment.setUserRequired(true);
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/RetailDemoModeService.java b/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java
index a4d401318793..2529564107b2 100644
--- a/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java
+++ b/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java
@@ -16,6 +16,7 @@
package com.android.server.retaildemo;
+import android.Manifest;
import android.app.ActivityManagerInternal;
import android.app.ActivityManagerNative;
import android.app.AppGlobals;
@@ -32,6 +33,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.database.ContentObserver;
@@ -51,6 +53,7 @@ import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.MediaStore;
import android.provider.Settings;
import android.util.Slog;
import com.android.internal.os.BackgroundThread;
@@ -250,6 +253,26 @@ 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);
+
+ grantRuntimePermissionToCamera(userInfo.getUserHandle());
+ }
+
+ private void grantRuntimePermissionToCamera(UserHandle user) {
+ final Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
+ final PackageManager pm = getContext().getPackageManager();
+ final ResolveInfo handler = pm.resolveActivityAsUser(cameraIntent,
+ PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+ user.getIdentifier());
+ if (handler == null || handler.activityInfo == null) {
+ return;
+ }
+ try {
+ pm.grantRuntimePermission(handler.activityInfo.packageName,
+ Manifest.permission.ACCESS_FINE_LOCATION, user);
+ } catch (Exception e) {
+ // Ignore
+ }
+
}
void logSessionDuration() {
@@ -257,7 +280,7 @@ public class RetailDemoModeService extends SystemService {
synchronized (mActivityLock) {
sessionDuration = (int) ((mLastUserActivityTime - mFirstUserActivityTime) / 1000);
}
- MetricsLogger.count(getContext(), DEMO_SESSION_DURATION, sessionDuration);
+ MetricsLogger.histogram(getContext(), DEMO_SESSION_DURATION, sessionDuration);
}
private ActivityManagerService getActivityManager() {
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 23c58fe4ea6c..794a73e0fe35 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -41,6 +41,7 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Parcel;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
@@ -55,15 +56,16 @@ import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
+import com.android.internal.app.IVoiceInteractionSessionListener;
import com.android.internal.app.IVoiceInteractionManagerService;
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
-import com.android.server.soundtrigger.SoundTriggerInternal;
import com.android.server.SystemService;
import com.android.server.UiThread;
+import com.android.server.soundtrigger.SoundTriggerInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -1038,6 +1040,13 @@ public class VoiceInteractionManagerService extends SystemService {
}
@Override
+ public void registerVoiceInteractionSessionListener(
+ IVoiceInteractionSessionListener listener) {
+ enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE);
+ mImpl.registerVoiceInteractionSessionListener(listener);
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 3f9da4c154ea..52e1a9b51c35 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -30,6 +30,7 @@ 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;
@@ -41,6 +42,7 @@ import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.view.IWindowManager;
+import com.android.internal.app.IVoiceInteractionSessionListener;
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
import com.android.internal.app.IVoiceInteractor;
import com.android.server.LocalServices;
@@ -71,6 +73,9 @@ 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) {
@@ -353,10 +358,52 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
}
}
+ public void registerVoiceInteractionSessionListener(
+ IVoiceInteractionSessionListener listener) {
+ synchronized (mLock) {
+ mVoiceInteractionSessionListeners.register(listener);
+ }
+ }
+
@Override
public void sessionConnectionGone(VoiceInteractionSessionConnection connection) {
synchronized (mLock) {
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();
+ }
+ }
+
+ @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();
+ }
+ }
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index 0694911461e0..b0cc2aca8981 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -130,6 +130,8 @@ final class VoiceInteractionSessionConnection implements ServiceConnection {
public interface Callback {
public void sessionConnectionGone(VoiceInteractionSessionConnection connection);
+ public void onSessionShown(VoiceInteractionSessionConnection connection);
+ public void onSessionHidden(VoiceInteractionSessionConnection connection);
}
final ServiceConnection mFullConnection = new ServiceConnection() {
@@ -313,6 +315,7 @@ final class VoiceInteractionSessionConnection implements ServiceConnection {
} else if (showCallback != null) {
mPendingShowCallbacks.add(showCallback);
}
+ mCallback.onSessionShown(this);
return true;
}
if (showCallback != null) {
@@ -468,6 +471,7 @@ final class VoiceInteractionSessionConnection implements ServiceConnection {
} catch (RemoteException e) {
}
}
+ mCallback.onSessionHidden(this);
}
if (mFullyBound) {
mContext.unbindService(mFullConnection);